ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/RpictSimulManager.cpp
Revision: 2.1
Committed: Wed Aug 14 20:05:23 2024 UTC (8 months, 2 weeks ago) by greg
Branch: MAIN
Log Message:
feat(rxpict): Added new C++ picture rendering tool with multi-processing and spectral output

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id$";
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, "missing 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 default: // RDTscolr | RDTscolor
83 primp = NULL;
84 break;
85 }
86 dtyp = RDTnewCT(dtyp, cs);
87 if (primp)
88 xyz2myrgbmat[0][0] = 0;
89 return true;
90 }
91
92 /*
93 * Set up rendering frame (call after octree loaded)
94 * Overall dimensions may be adjusted for view,
95 * optional pixel aspect ratio and tile grid
96 * Increments frameNo if >0
97 */
98 bool
99 RpictSimulManager::NewFrame(const VIEW &v, int xydim[2], double *ap, const int *tgrid)
100 {
101 double pasp = 1.;
102
103 if (!xydim) return false;
104 if (!ap) ap = &pasp;
105 pvw = vw; // save previous view for motion blur
106 vw = v;
107 const char * verr = setview(&vw);
108 if (verr) {
109 error(WARNING, verr);
110 vw = pvw;
111 return false;
112 }
113 const double va = viewaspect(&vw);
114 normaspect(va, ap, &xydim[0], &xydim[1]);
115 // set up tiling?
116 if (tgrid && (tgrid[0] > 0) & (tgrid[1] > 0) & (tgrid[0]*tgrid[1] > 1)) {
117 if ((8*tgrid[0] >= xydim[0]) | (8*tgrid[1] >= xydim[1])) {
118 error(WARNING, "Excessive tiling for image size");
119 return false;
120 }
121 xydim[0] -= xydim[0] % (tgsize[0] = tgrid[0]);
122 xydim[1] -= xydim[1] % (tgsize[1] = tgrid[1]);
123 *ap = va * xydim[0] / xydim[1];
124 } else
125 tgsize[0] = tgsize[1] = 1;
126
127 if (vw.vaft > FTINY) rtFlags |= RTlimDist;
128 else rtFlags &= ~RTlimDist;
129 hvres[0] = xydim[0]; hvres[1] = xydim[1];
130 thvres[0] = hvres[0]/tgsize[0]; // presumed tile width
131 thvres[1] = hvres[1]/tgsize[1]; // ...and height
132 frameNo += (frameNo > 0); // caller may override after
133 return true;
134 }
135
136 // Call-back for rendered pixel
137 int
138 RpictSimulManager::RtCall(RAY *r, void *cd)
139 {
140 RpictSimulManager * rsp = (RpictSimulManager *)cd;
141 const int ty = r->rno / rsp->TWidth();
142 const int tx = r->rno - (RNUMBER)ty*rsp->TWidth();
143
144 if (ty >= rsp->THeight()) {
145 error(INTERNAL, "bad pixel calculation position in RtCall()");
146 return -1;
147 }
148 if (!rsp->doneMap.TestAndSet(tx, ty)) {
149 error(WARNING, "duplicate pixel calculation");
150 return 0;
151 }
152 return rsp->pacc.SetPixel(tx, ty, r);
153 }
154
155 // Set up the specified tile (or entire image if NULL)
156 bool
157 RpictSimulManager::SetTile(const int ti[2])
158 {
159 tvw = vw; ptvw = pvw;
160
161 if (ti) {
162 if ((ti[0] < 0) | (ti[0] >= tgsize[0]) |
163 (ti[1] < 0) | (ti[1] >= tgsize[1])) {
164 error(INTERNAL, "illegal tile specification in SetTile()");
165 return false;
166 }
167 const char * verr = cropview(&tvw,
168 (double)ti[0]/tgsize[0],
169 (double)ti[1]/tgsize[1],
170 (ti[0]+1.)/tgsize[0],
171 (ti[1]+1.)/tgsize[1]);
172 if (verr) {
173 sprintf(errmsg, "crop failure @ tile (%d,%d)/(%d,%d): %s",
174 ti[0], ti[1], tgsize[0], tgsize[1], verr);
175 error(USER, errmsg);
176 return false;
177 } // previous tile view for blur
178 if (!ptvw.type | (mblur <= FTINY) ||
179 cropview(&ptvw, (double)ti[0]/tgsize[0],
180 (double)ti[1]/tgsize[1],
181 (ti[0]+1.)/tgsize[0],
182 (ti[1]+1.)/tgsize[1]))
183 ptvw.type = 0;
184
185 } else if ((tgsize[0] > 1) | (tgsize[1] > 1)) {
186 error(INTERNAL, "missing tile specification in SetTile()");
187 return false;
188 }
189 return doneMap.NewBitMap(TWidth(), THeight());
190 }
191
192 #define pixjitter() (.5+dstrpix*(.5-frandom()))
193
194 // Send the indicated pixel to ray tracer
195 bool
196 RpictSimulManager::ComputePixel(int x, int y)
197 {
198 static const SCOLOR scBlack = {0};
199 int i;
200 FVECT rodir[2];
201 double hpos = (x+pixjitter())/TWidth();
202 double vpos = (y+pixjitter())/THeight();
203 double dlim = viewray(rodir[0], rodir[1], &tvw, hpos, vpos);
204 if (dlim < -FTINY) { // off view?
205 pacc.SetPixel(x, y, scBlack);
206 doneMap.Set(x, y);
207 return true;
208 }
209 if (ptvw.type) { // add motion blur if requested
210 FVECT rorg2, rdir2;
211 double dlim2 = viewray(rorg2, rdir2, &ptvw, hpos, vpos);
212 if (dlim2 >= -FTINY) {
213 const double d = mblur*(.5-frandom());
214 dlim = (1.-d)*dlim + d*dlim2;
215 for (i = 3; i--; ) {
216 rodir[0][i] = (1.-d)*rodir[0][i] + d*rorg2[i];
217 rodir[1][i] = (1.-d)*rodir[1][i] + d*rdir2[i];
218 }
219 if (normalize(rodir[1]) == 0)
220 return false;
221 }
222 }
223 // depth-of-field blur if any
224 if (!jitteraperture(rodir[0], rodir[1], &tvw, dblur))
225 return false;
226 // include aft clipping distance?
227 for (i = (dlim > FTINY)*3; i--; )
228 rodir[1][i] *= dlim;
229
230 return EnqueueRay(rodir[0], rodir[1], (RNUMBER)y*TWidth()+x);
231 }
232
233 // Check if neighbor differences are below pixel sampling threshold
234 bool
235 RpictSimulManager::BelowSampThresh(int x, int y, const int noff[4][2]) const
236 {
237 SCOLOR pval[4];
238 float dist[4];
239 int i, j;
240
241 for (i = 4; i--; ) { // get pixels from tile store
242 int px = x + noff[i][0];
243 int py = y + noff[i][1];
244 if (!doneMap.Check(px, py) ||
245 !pacc.GetPixel(px, py, pval[i], &dist[i]))
246 return false;
247 }
248 const bool spectr = (pacc.NC() > 3);
249 for (i = 4; --i; ) // do pairwise comparisons
250 for (j = i; j--; ) {
251 if (pacc.DepthType() &&
252 (dist[i] - dist[j] > maxdiff*dist[j]) |
253 (dist[j] - dist[i] > maxdiff*dist[i]))
254 return false;
255 if (spectr ? sbigsdiff(pval[i], pval[j], maxdiff) :
256 bigdiff(pval[i], pval[j], maxdiff))
257 return false;
258 }
259 return true; // linear interpolation OK
260 }
261
262 // Fill an interior square patch with interpolated values
263 void
264 RpictSimulManager::FillSquare(int x, int y, const int noff[4][2])
265 {
266 SCOLOR pval[4];
267 float dist[4];
268 int i, j;
269 // assumes 4 corners are valid!
270 for (i = 4; i--; )
271 pacc.GetPixel(x+noff[i][0], y+noff[i][1], pval[i], &dist[i]);
272
273 i = abs(noff[1][0]-noff[0][0]);
274 j = abs(noff[1][1]-noff[0][1]);
275 const int slen = i > j ? i : j;
276 const double sf = 1./slen;
277 const bool spectr = (pacc.NC() > 3);
278 for (i = 0; i <= slen; i++) { // bilinear interpolant
279 const double c1 = i*sf;
280 for (j = 0; j <= slen; j++) {
281 const double c2 = j*sf;
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*(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 threshold
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 (int yoff = -(1<<(sp2-1));
382 yoff <= 1<<(sp2-1); yoff += 1<<sp2)
383 for (int xoff = -(1<<(sp2-1));
384 xoff <= 1<<(sp2-1); xoff += 1<<sp2) {
385 ABitMap2 stamp = origSampMap;
386 stamp.Shift(xoff, yoff);
387 sampMap |= stamp;
388 } // ...but don't resample what's done
389 sampMap -= doneSamples;
390 // interpolate smooth regions
391 fillMap -= sampMap;
392 for (x = y = 0; fillMap.Find(&x, &y); x++)
393 FillSquare(x, y, noff);
394 doneSamples |= doneMap;
395 } // compute required ray samples
396 for (x = y = 0; sampMap.Find(&x, &y); x++)
397 if (!ComputePixel(x, y))
398 return false;
399 doneSamples |= sampMap; // samples now done or at least queued
400 fprintf(stderr, "Sampled %ld pixels at (sp2,layer)=(%d,%d)\n",
401 (long)sampMap.SumTotal(), sp2, layer);
402 fprintf(stderr, "\t%ld pixels (%.3f%%) completed (+%ld in process)\n",
403 (long)doneMap.SumTotal(), 100.*doneMap.SumTotal()/doneMap.Width()/doneMap.Height(),
404 (long)(doneSamples.SumTotal()-doneMap.SumTotal()));
405 sp2 -= layer++ & 1; // next denser sampling
406 }
407 if (FlushQueue() < 0) // make sure we got everyone
408 return false;
409 x = y = 0;
410 if (doneMap.Find(&x, &y, false)) {
411 sprintf(errmsg, "missed %ld tile pixels, e.g. (%d,%d)",
412 (long)doneMap.Width()*doneMap.Height() -
413 doneMap.SumTotal(), x, y);
414 error(WARNING, errmsg);
415 }
416 if ((prCB != NULL) & (barPix == NULL))
417 (*prCB)(100.);
418 return true;
419 }
420
421 /*
422 * Render the specified tile in frame
423 * Tile pixels are contiguous unless ystride != 0
424 * Tiles numbered from upper-left at (0,0)
425 * Pixel type influenced by this->prims assignment
426 */
427 bool
428 RpictSimulManager::RenderTile(COLORV *rp, int ystride, float *zp, const int *tile)
429 {
430 if (!rp | (GetWidth() <= 0) | (GetHeight() <= 0) | !vw.type)
431 return false;
432 if (!ystride) // contiguous rows?
433 ystride = TWidth();
434 pacc.Init(rp, ystride, zp);
435 if (prims == xyzprims)
436 pacc.SetColorSpace(RDTxyz);
437 else if (prims)
438 pacc.SetColorSpace(RDTrgb, prims);
439
440 return SetTile(tile) && RenderRect();
441 }
442
443 // Same but store as common-exponent COLR or SCOLR
444 bool
445 RpictSimulManager::RenderTile(COLRV *bp, int ystride, float *zp, const int *tile)
446 {
447 if (!bp | (GetWidth() <= 0) | (GetHeight() <= 0) | !vw.type)
448 return false;
449 if (!ystride) // contiguous rows?
450 ystride = TWidth();
451 pacc.Init(bp, ystride, zp);
452 if (prims == xyzprims)
453 pacc.SetColorSpace(RDTxyze);
454 else if (prims)
455 pacc.SetColorSpace(RDTrgbe, prims);
456
457 return SetTile(tile) && RenderRect();
458 }
459
460 // Same but also use 16-bit encoded depth buffer
461 bool
462 RpictSimulManager::RenderTile(COLRV *bp, int ystride, short *dp, const int *tile)
463 {
464 if (!bp | (GetWidth() <= 0) | (GetHeight() <= 0) | !vw.type)
465 return false;
466 if (!ystride) // contiguous rows?
467 ystride = TWidth();
468 pacc.Init(bp, ystride, dp);
469 if (prims == xyzprims)
470 pacc.SetColorSpace(RDTxyze);
471 else if (prims)
472 pacc.SetColorSpace(RDTrgbe, prims);
473
474 return SetTile(tile) && RenderRect();
475 }
476
477 // Allocate a new render bar
478 void
479 RpictSimulManager::NewBar(int ht)
480 {
481 delete [] barPix;
482 delete [] barDepth;
483 if (ht > GetHeight()) ht = GetHeight();
484 if ((ht <= 0) | (GetWidth() <= 0)) {
485 doneMap.NewBitMap(0,0);
486 pacc.Init();
487 barPix = NULL; barDepth = NULL;
488 return;
489 }
490 thvres[0] = GetWidth();
491 thvres[1] = ht;
492 const int NC = prims ? 3 : NCSAMP;
493 barPix = new COLORV [ht*thvres[0]*NC];
494 barDepth = new float [ht*thvres[0]];
495 pacc.Init(barPix + (ht-1)*thvres[0]*NC,
496 -thvres[0], barDepth + (ht-1)*thvres[0]);
497 if (prims == xyzprims)
498 pacc.SetColorSpace(RDTxyz);
499 else if (prims)
500 pacc.SetColorSpace(RDTrgb, prims);
501
502 doneMap.NewBitMap(TWidth(), THeight());
503 }
504
505 // Shift render bar area the specified amount down the frame
506 bool
507 RpictSimulManager::LowerBar(int v)
508 {
509 if (v <= 0) return !v;
510 if (!barPix | !barDepth | (v > THeight()) | !tvw.type)
511 return false;
512 tvw.voff -= double(v)/GetHeight();
513 ptvw.voff -= double(v)/GetHeight();
514 if (v == THeight()) {
515 doneMap.ClearBitMap();
516 return true;
517 }
518 const int NC = pacc.NC();
519 doneMap.Shift(0, v, false); // lift finished pixel samples
520 memmove(barPix, barPix + NC*TWidth()*v,
521 sizeof(COLORV)*NC*TWidth()*(THeight()-v));
522 memmove(barDepth, barDepth + TWidth()*v,
523 sizeof(float)*TWidth()*(THeight()-v));
524 return true;
525 }
526
527 // Continue rendering from the specified position
528 bool
529 RpictSimulManager::RenderBelow(int ytop, const int vstep, FILE *pfp, const int dt, FILE *dfp)
530 {
531 if (ytop <= 0)
532 return true;
533 ptvw = pvw; // set starting bar's view
534 tvw = vw;
535 const char * verr = cropview(&tvw, 0., double(ytop-THeight())/GetHeight(),
536 1., double(ytop)/GetHeight());
537 if (verr) {
538 sprintf(errmsg, "illegal render bar below y=%d: %s", ytop, verr);
539 error(INTERNAL, errmsg);
540 return false;
541 }
542 if (!ptvw.type | (mblur <= FTINY) ||
543 cropview(&ptvw, 0., double(ytop-THeight())/GetHeight(),
544 1., double(ytop)/GetHeight()))
545 ptvw.type = 0;
546 // set up spectral sampling
547 if (setspectrsamp(CNDX, WLPART) <= 0) {
548 error(USER, "unsupported spectral sampling");
549 return false;
550 }
551 COLORV ** parr = NULL; // set up tiny source drawing
552 float ** zarr = NULL;
553 if (!ptvw.type && directvis && dblur <= FTINY) {
554 parr = new COLORV * [THeight()];
555 zarr = new float * [THeight()];
556 for (int n = THeight(); n-- > 0; ) {
557 parr[THeight()-1-n] = barPix + pacc.NC()*TWidth()*n;
558 zarr[THeight()-1-n] = barDepth + TWidth()*n;
559 }
560 ourview = vw; hres = GetWidth(); vres = GetHeight();
561 init_drawsources(psample);
562 }
563 int lastOut = ytop; // render down frame
564 while (ytop > 0) {
565 if (ytop < THeight()) // mark what we won't do as finished
566 doneMap.ClearRect(0, 0, TWidth(), THeight()-ytop, true);
567 if (prCB)
568 (*prCB)(100.*(GetHeight()-ytop)/GetHeight());
569 if (!RenderRect()) // render this bar
570 return false;
571 int nlines = lastOut - ytop + THeight();
572 if (nlines > ytop)
573 nlines = ytop;
574 else if (parr) // drawing sources?
575 drawsources(parr, prims, zarr,
576 0, hres, lastOut-nlines, nlines);
577
578 if (dfp) { // write out depth scanlines?
579 const float * dp = barDepth + TWidth()*(ytop-lastOut);
580 if (RDTdepthT(dt) == RDTdshort) {
581 for (int n = TWidth()*nlines; n-- > 0; dp++)
582 if (putint(depth2code(*dp, pacc.refDepth), 2, dfp) == EOF)
583 error(SYSTEM, "cannot write 16-bit depth buffer");
584 } else if (putbinary(dp, sizeof(float), TWidth()*nlines, dfp)
585 != TWidth()*nlines)
586 error(SYSTEM, "cannot write raw depth buffer");
587 }
588 COLORV * bpos = barPix + pacc.NC()*TWidth()*(ytop-lastOut);
589 while (nlines-- > 0) { // write pixel scanlines
590 switch (RDTcolorT(dt)) {
591 case RDTrgbe:
592 case RDTxyze:
593 if (fwritescan((COLOR *)bpos, TWidth(), pfp) < 0)
594 error(SYSTEM, "cannot write RGBE/XYZE output");
595 break;
596 case RDTscolr:
597 if (fwritesscan(bpos, pacc.NC(), TWidth(), pfp) < 0)
598 error(SYSTEM, "cannot write SCOLOR output");
599 break;
600 case RDTrgb:
601 case RDTxyz:
602 case RDTscolor:
603 if (putbinary(bpos, sizeof(COLORV)*pacc.NC(), TWidth(), pfp)
604 != TWidth())
605 error(SYSTEM, "cannot write SCOLOR output");
606 break;
607 default:
608 error(INTERNAL, "missing output color type in RenderBelow()");
609 break;
610 }
611 bpos += pacc.NC()*TWidth();
612 --lastOut;
613 } // flush each scan bar
614 if (fflush(pfp) == EOF || (dfp && fflush(dfp) == EOF))
615 error(SYSTEM, "output write error");
616 // advance down the frame
617 if (lastOut > 0 && !LowerBar(vstep))
618 return false;
619 ytop -= vstep;
620 }
621 delete [] parr;
622 delete [] zarr;
623 if (prCB)
624 (*prCB)(100.);
625 return true;
626 }
627
628 /*
629 * Render and write a frame to the named file
630 * Include any header lines set prior to call
631 * Picture file must not already exist
632 * Picture to stdout if pfname==NULL
633 * Depth written to a command if dfname[0]=='!'
634 */
635 RenderDataType
636 RpictSimulManager::RenderFrame(const char *pfname, RenderDataType dt, const char *dfname)
637 {
638 int fd = 1;
639 FILE * pfp = NULL;
640 FILE * dfp = NULL;
641
642 if (!RDTcolorT(dt))
643 error(INTERNAL, "missing pixel output type in RenderFrame()");
644 if (NCSAMP == 3) {
645 if (RDTcolorT(dt) == RDTscolr)
646 dt = RDTnewCT(dt, prims==xyzprims ? RDTxyze : RDTrgbe);
647 else if (RDTcolorT(dt) == RDTscolor)
648 dt = RDTnewCT(dt, prims==xyzprims ? RDTxyz : RDTrgb);
649 }
650 if (!RDTdepthT(dt) ^ !dfname)
651 error(INTERNAL, "depth output requires file name and type in RenderFrame()");
652 if (pfname) { // open picture output file
653 if (pfname[0] == '!') {
654 error(INTERNAL, "writing picture to a command not supported");
655 return RDTnone;
656 }
657 fd = open(pfname, O_WRONLY|O_CREAT|O_EXCL, 0666);
658 }
659 if (fd < 0) {
660 if ((frameNo <= 0) | (errno != EEXIST)) {
661 sprintf(errmsg, "cannot open picture file '%s'", pfname);
662 error(SYSTEM, errmsg);
663 }
664 return RDTnone; // expected in parallel sequence
665 }
666 if (fd == 1)
667 pfp = stdout;
668 else if (!(pfp = fdopen(fd, "w")))
669 error(SYSTEM, "failure calling fdopen()");
670 SET_FILE_BINARY(pfp); // write picture header
671 if ((pfp != stdout) | (frameNo <= 1)) {
672 newheader("RADIANCE", pfp);
673 fputs(GetHeader(), pfp);
674 }
675 fputs(VIEWSTR, pfp); fprintview(&vw, pfp); fputc('\n', pfp);
676 if (frameNo > 0)
677 fprintf(pfp, "FRAME=%d\n", frameNo);
678 double pasp = viewaspect(&vw) * GetWidth() / GetHeight();
679 if (!FABSEQ(pasp, 1.0))
680 fputaspect(pasp, pfp);
681 fputnow(pfp);
682 switch (RDTcolorT(dt)) { // set primaries and picture format
683 case RDTrgbe:
684 if (!prims | (prims == xyzprims)) prims = stdprims;
685 fputprims(prims, pfp);
686 fputformat(COLRFMT, pfp);
687 break;
688 case RDTxyze:
689 prims = xyzprims;
690 fputformat(CIEFMT, pfp);
691 break;
692 case RDTscolr:
693 prims = NULL;
694 fputwlsplit(WLPART, pfp);
695 fputncomp(NCSAMP, pfp);
696 fputformat(SPECFMT, pfp);
697 break;
698 case RDTrgb:
699 if (!prims | (prims == xyzprims)) prims = stdprims;
700 fputprims(prims, pfp);
701 fputncomp(3, pfp);
702 fputendian(pfp);
703 fputformat("float", pfp);
704 break;
705 case RDTxyz:
706 prims = xyzprims;
707 fputprims(prims, pfp);
708 fputncomp(3, pfp);
709 fputendian(pfp);
710 fputformat("float", pfp);
711 break;
712 case RDTscolor:
713 prims = NULL;
714 fputwlsplit(WLPART, pfp);
715 fputncomp(NCSAMP, pfp);
716 fputendian(pfp);
717 fputformat("float", pfp);
718 break;
719 default:;
720 }
721 fputc('\n', pfp); // end picture header
722 fprtresolu(GetWidth(), GetHeight(), pfp);
723 if (dfname) {
724 if (dfname[0] == '!')
725 dfp = popen(dfname+1, "w");
726 else
727 dfp = fopen(dfname, "w");
728 if (!dfp) {
729 sprintf(errmsg, "cannot open depth output '%s'", dfname);
730 error(SYSTEM, errmsg);
731 return RDTnone;
732 }
733 SET_FILE_BINARY(dfp);
734 }
735 if (RDTdepthT(dt) == RDTdshort) { // write header for 16-bit depth?
736 newheader("RADIANCE", dfp);
737 fputs(GetHeader(), dfp);
738 fputs(VIEWSTR, dfp); fprintview(&vw, dfp); fputc('\n', dfp);
739 fputs(DEPTHSTR, dfp); fputs(dunit, dfp); fputc('\n', dfp);
740 fputformat(DEPTH16FMT, dfp);
741 fputc('\n', dfp); // end-of-info
742 fprtresolu(GetWidth(), GetHeight(), dfp);
743 }
744 const int bheight = (psample > 1) ? int(2*psample+.99) : 4;
745 const int vstep = bheight >> (psample > 1);
746
747 NewBar(bheight); // render frame if we can
748 if (!RenderBelow(GetHeight(), vstep, pfp, dt, dfp)) {
749 fclose(pfp);
750 if (dfp) (dfname[0] == '!') ? pclose(dfp) : fclose(dfp);
751 Cleanup();
752 return RDTnone;
753 }
754 NewBar(); // clean up and return
755 if (pfp != stdout)
756 fclose(pfp);
757 if (dfp) {
758 if (dfname[0] == '!') {
759 int status = pclose(dfp);
760 if (status) {
761 sprintf(errmsg, "depth output (%s) error status: %d",
762 dfname, status);
763 error(USER, errmsg);
764 return RDTnone;
765 }
766 } else
767 fclose(dfp);
768 }
769 return dt;
770 }
771
772 // passed struct for header line callback
773 struct HeaderInfo {
774 char fmt[MAXFMTLEN];
775 char depth_unit[32];
776 int ncomp;
777 RGBPRIMS prims;
778 VIEW vw;
779 bool gotWL;
780 bool gotprims;
781 bool gotview;
782 bool endianMatch;
783
784 HeaderInfo() {
785 strcpy(fmt, "MISSING");
786 depth_unit[0] = '\0';
787 ncomp = 3;
788 vw = stdview;
789 gotWL = false;
790 gotprims = false;
791 gotview = false;
792 endianMatch = true;
793 }
794 };
795
796 // helper function checks header line and records req. info.
797 static int
798 head_check(char *s, void *p)
799 {
800 HeaderInfo * hp = (HeaderInfo *)p;
801 int rval;
802
803 if (isncomp(s)) {
804 hp->ncomp = ncompval(s);
805 return 1;
806 }
807 if (iswlsplit(s)) {
808 hp->gotWL = wlsplitval(WLPART, s);
809 return 1;
810 }
811 if (isprims(s)) {
812 hp->gotprims = primsval(hp->prims, s);
813 return 1;
814 }
815 if (isview(s)) {
816 hp->gotview |= (sscanview(&hp->vw, s) > 0);
817 return 1;
818 }
819 if (!strncmp(s, DEPTHSTR, LDEPTHSTR)) {
820 strlcpy(hp->depth_unit, s+LDEPTHSTR, sizeof(hp->depth_unit));
821 char * cp = hp->depth_unit;
822 while (*cp) cp++;
823 while (cp > hp->depth_unit && isspace(cp[-1])) cp--;
824 *cp = '\0';
825 return 1;
826 }
827 if ((rval = isbigendian(s)) >= 0) {
828 hp->endianMatch = (rval == nativebigendian());
829 return 1;
830 }
831 if (formatval(hp->fmt, s))
832 return 1;
833 return 0;
834 }
835
836 // Resume partially finished rendering
837 // Picture file must exist
838 RenderDataType
839 RpictSimulManager::ResumeFrame(const char *pfname, const char *dfname)
840 {
841 if (!pfname || pfname[0] == '!')
842 return RDTnone;
843
844 RenderDataType dt = RDTnone;
845 FILE * dfp = NULL;
846 FILE * pfp = fopen(pfname, "r+");
847 if (!pfp) {
848 sprintf(errmsg, "cannot reopen output picture '%s'", pfname);
849 error(SYSTEM, errmsg);
850 return RDTnone;
851 }
852 SET_FILE_BINARY(pfp);
853 HeaderInfo hinfo; // read header information & dimensions
854 RESOLU res;
855 if (getheader(pfp, head_check, &hinfo) < 0) {
856 fclose(pfp);
857 return RDTnone;
858 }
859 if (!fgetsresolu(&res, pfp) || res.rt != PIXSTANDARD) {
860 sprintf(errmsg, "missing/bad resolution for '%s'", pfname);
861 error(USER, errmsg);
862 fclose(pfp);
863 return RDTnone;
864 }
865 if (!hinfo.gotview) {
866 sprintf(errmsg, "missing view for '%s'", pfname);
867 error(USER, errmsg);
868 fclose(pfp);
869 return RDTnone;
870 }
871 if (hinfo.ncomp < 3) {
872 sprintf(errmsg, "bad # components (%d) in '%s'", hinfo.ncomp, pfname);
873 error(USER, errmsg);
874 fclose(pfp);
875 return RDTnone;
876 }
877 int bytesPer = 0; // complicated part to set rendering/output space
878 if (!strcmp(hinfo.fmt, COLRFMT)) {
879 prims = hinfo.prims;
880 int n = 8*hinfo.gotprims;
881 while (n--)
882 if (!FABSEQ(hinfo.prims[0][n], stdprims[0][n]))
883 break;
884 if (n < 0)
885 prims = stdprims;
886 dt = RDTnewCT(dt, RDTrgbe);
887 } else if (!strcmp(hinfo.fmt, CIEFMT)) {
888 prims = xyzprims;
889 dt = RDTnewCT(dt, RDTxyze);
890 } else if (!strcmp(hinfo.fmt, SPECFMT)) {
891 if ((hinfo.ncomp <= 3) | (hinfo.ncomp > MAXCSAMP)) {
892 sprintf(errmsg, "incompatible sample count (%d) in '%s'",
893 hinfo.ncomp, pfname);
894 error(USER, errmsg);
895 fclose(pfp);
896 return RDTnone;
897 }
898 NCSAMP = hinfo.ncomp; // overrides global setting
899 prims = NULL;
900 dt = RDTnewCT(dt, RDTscolr);
901 bytesPer = hinfo.ncomp + 1; // XXX assumes no compression
902 } else if (!strcmp(hinfo.fmt, "float")) {
903 if (!hinfo.endianMatch) {
904 sprintf(errmsg, "incompatible byte ordering in '%s'", pfname);
905 error(USER, errmsg);
906 fclose(pfp);
907 return RDTnone;
908 }
909 if (hinfo.ncomp == 3) {
910 prims = hinfo.prims; // custom primaries?
911 int n = 8*hinfo.gotprims;
912 while (n--)
913 if (!FABSEQ(hinfo.prims[0][n], stdprims[0][n]))
914 break;
915 if (n < 0) // standard primaries?
916 prims = stdprims;
917 else if (hinfo.gotprims) { // or check if XYZ
918 for (n = 8; n--; )
919 if (!FABSEQ(prims[0][n], xyzprims[0][n]))
920 break;
921 if (n < 0)
922 prims = xyzprims;
923 }
924 if (prims == xyzprims)
925 dt = RDTnewCT(dt, RDTxyz);
926 else
927 dt = RDTnewCT(dt, RDTrgb);
928 } else {
929 NCSAMP = hinfo.ncomp; // overrides global setting
930 prims = NULL;
931 dt = RDTnewCT(dt, RDTscolor);
932 }
933 bytesPer = sizeof(float)*hinfo.ncomp;
934 } else {
935 sprintf(errmsg, "unknown format (%s) for '%s'", hinfo.fmt, pfname);
936 error(USER, errmsg);
937 fclose(pfp);
938 return RDTnone;
939 }
940 vw.type = 0; // set up new (unreferenced) frame
941 frameNo = 0;
942 int hvdim[2] = {res.xr, res.yr};
943 double noAdj = 0;
944 if (!NewFrame(hinfo.vw, hvdim, &noAdj) ||
945 (hvdim[0] != res.xr) | (hvdim[1] != res.yr)) {
946 fclose(pfp);
947 return RDTnone;
948 }
949 long dataStart = ftell(pfp); // picture starting point
950 if (dataStart < 0) {
951 sprintf(errmsg, "cannot seek on '%s'", pfname);
952 error(SYSTEM, errmsg);
953 fclose(pfp);
954 return RDTnone;
955 }
956 long doneScans = 0;
957 if (bytesPer) { // fixed-width records?
958 fseek(pfp, 0, SEEK_END);
959 long dataEnd = ftell(pfp);
960 doneScans = (dataEnd - dataStart)/(bytesPer*GetWidth());
961 if (dataEnd-dataStart > bytesPer*GetWidth()*doneScans)
962 fseek(pfp, dataStart + bytesPer*GetWidth()*doneScans, SEEK_SET);
963 } else { // else get compressed scanlines
964 COLR * scan = (COLR *)tempbuffer(sizeof(COLR)*GetWidth());
965 while (freadcolrs(scan, GetWidth(), pfp) >= 0)
966 ++doneScans;
967 if (!feof(pfp)) {
968 sprintf(errmsg, "error reading compressed scanline from '%s'", pfname);
969 error(USER, errmsg);
970 fclose(pfp);
971 return RDTnone;
972 }
973 }
974 if (doneScans >= GetHeight()) { // nothing left to do?
975 sprintf(errmsg, "output file '%s' is already complete", pfname);
976 error(WARNING, errmsg);
977 fclose(pfp);
978 return dt;
979 }
980 if (!doneScans) {
981 sprintf(errmsg, "restarting empty frame '%s'", pfname);
982 error(WARNING, errmsg);
983 }
984 if (dfname) { // append depth file, too?
985 if (dfname[0] == '!') {
986 error(USER, "depth data cannot be reloaded from command");
987 fclose(pfp);
988 return RDTnone;
989 }
990 dfp = fopen(dfname, "a");
991 if (!dfp) {
992 sprintf(errmsg, "cannot reopen depth file '%s'", dfname);
993 error(SYSTEM, errmsg);
994 fclose(pfp);
995 return RDTnone;
996 }
997 SET_FILE_BINARY(dfp);
998 const long dflen = ftell(dfp);
999 if (dflen != sizeof(float)*GetWidth()*doneScans) {
1000 fclose(dfp);
1001 dfp = fopen(dfname, "r+");
1002 if (!dfp) return RDTnone; // WTH?
1003 SET_FILE_BINARY(dfp);
1004 }
1005 if (dflen < sizeof(float)*GetWidth()*doneScans) {
1006 HeaderInfo dinfo;
1007 if (getheader(dfp, head_check, &dinfo) < 0)
1008 sprintf(errmsg, "bad header in encoded depth file '%s'",
1009 dfname);
1010 else if (strcmp(dinfo.fmt, DEPTH16FMT))
1011 sprintf(errmsg, "wrong format (%s) for depth file '%s'",
1012 dinfo.fmt, dfname);
1013 else if (!SetReferenceDepth(dinfo.depth_unit))
1014 sprintf(errmsg, "bad/missing reference depth (%s) in '%s'",
1015 dinfo.depth_unit, dfname);
1016 else if (!fscnresolu(hvdim, hvdim+1, dfp) ||
1017 (hvdim[0] != GetWidth()) | (hvdim[1] != GetHeight()))
1018 sprintf(errmsg, "bad/mismatched resolution in '%s'",
1019 dfname);
1020 else
1021 errmsg[0] = '\0';
1022
1023 if (errmsg[0]) {
1024 error(USER, errmsg);
1025 fclose(dfp);
1026 fclose(pfp);
1027 return RDTnone;
1028 }
1029 const long dStart = ftell(dfp);
1030 if (dflen-dStart < 2*GetWidth()*doneScans) {
1031 sprintf(errmsg, "missing %ld depths in '%s'",
1032 (long)GetWidth()*doneScans - (dflen-dStart)/2,
1033 dfname);
1034 error(WARNING, errmsg);
1035 }
1036 fseek(dfp, dStart + 2*GetWidth()*doneScans, SEEK_SET);
1037 dt = RDTnewDT(dt, RDTdshort);
1038 } else {
1039 if (dflen > sizeof(float)*GetWidth()*doneScans)
1040 fseek(dfp, sizeof(float)*GetWidth()*doneScans, SEEK_SET);
1041 dt = RDTnewDT(dt, RDTdfloat);
1042 }
1043 }
1044 int bheight = (psample > 1) ? int(2*psample+.99) : 4;
1045 if (bheight > GetHeight()-doneScans)
1046 bheight = GetHeight()-doneScans;
1047 int vstep = bheight >> (psample > 1);
1048 vstep += !vstep;
1049
1050 NewBar(bheight); // render remainder if we can
1051 if (!RenderBelow(GetHeight()-doneScans, vstep, pfp, dt, dfp)) {
1052 fclose(pfp);
1053 if (dfp) fclose(dfp);
1054 Cleanup();
1055 return RDTnone;
1056 }
1057 NewBar(); // close up and return success
1058 fclose(pfp);
1059 if (dfp) fclose(dfp);
1060 return dt;
1061 }