ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/RpictSimulManager.cpp
Revision: 2.8
Committed: Fri Aug 23 02:08:28 2024 UTC (8 months, 1 week ago) by greg
Branch: MAIN
Changes since 2.7: +52 -29 lines
Log Message:
perf(rxpict): Improved quincunx sampling code -- still needs work

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: RpictSimulManager.cpp,v 2.7 2024/08/22 00:44:02 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 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+1);
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(const int x, const 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]); // i==j for diamond fill
275 const int slen = (i > j) ? i : j;
276 const bool spectr = (pacc.NC() > 3);
277 for (i = slen+1 + (i==j)*slen; i--; ) {
278 const double c1 = (i>slen ? i-slen-.5 : (double)i)/slen;
279 for (j = slen + (i<=slen); j--; ) {
280 const double c2 = (j + (i>slen)*.5)/slen;
281 const int px = int(x + (1.-c1)*(1.-c2)*noff[0][0] +
282 c1*(1.-c2)*noff[1][0] +
283 (1.-c1)*c2*noff[2][0] +
284 c1*c2*noff[3][0] + .5);
285 const int py = int(y + (1.-c1)*(1.-c2)*noff[0][1] +
286 c1*(1.-c2)*noff[1][1] +
287 (1.-c1)*c2*noff[2][1] +
288 c1*c2*noff[3][1] + .5);
289 if (!doneMap.TestAndSet(px, py))
290 continue;
291 float zval = 0;
292 if (pacc.DepthType())
293 zval = (1.-c1)*(1.-c2)*dist[0] + c1*(1.-c2)*dist[1] +
294 (1.-c1)*c2*dist[2] + c1*c2*dist[3];
295 if (spectr) { // XXX assumes pacc.NC() == NCSAMP
296 SCOLOR ipval, tpval;
297 copyscolor(ipval, pval[0]);
298 scalescolor(ipval, (1.-c1)*(1.-c2));
299 copyscolor(tpval, pval[1]);
300 scalescolor(tpval, c1*(1.-c2));
301 saddscolor(ipval, tpval);
302 copyscolor(tpval, pval[2]);
303 scalescolor(tpval, (1.-c1)*c2);
304 saddscolor(ipval, tpval);
305 copyscolor(tpval, pval[3]);
306 scalescolor(tpval, c1*c2);
307 saddscolor(ipval, tpval);
308 pacc.SetPixel(px, py, ipval, zval);
309 } else { // tristimulus interpolation
310 COLOR ipval, tpval;
311 copycolor(ipval, pval[0]);
312 scalecolor(ipval, (1.-c1)*(1.-c2));
313 copycolor(tpval, pval[1]);
314 scalecolor(tpval, c1*(1.-c2));
315 addcolor(ipval, tpval);
316 copycolor(tpval, pval[2]);
317 scalecolor(tpval, (1.-c1)*c2);
318 addcolor(ipval, tpval);
319 copycolor(tpval, pval[3]);
320 scalecolor(tpval, c1*c2);
321 addcolor(ipval, tpval);
322 pacc.SetPixel(px, py, ipval, zval);
323 }
324 }
325 }
326 }
327
328 // helper function to set up quincunx sampling
329 static void
330 SetQuincunx(ABitMap2 *bmp2, int noff[4][2], const int spc, bool odd, int x0, int y)
331 {
332 if (odd) { // order neighbors CCW
333 noff[0][0] = spc>>1; noff[0][1] = 0;
334 noff[1][0] = 0; noff[1][1] = spc>>1;
335 noff[2][0] = -(spc>>1); noff[2][1] = 0;
336 noff[3][0] = 0; noff[3][1] = -(spc>>1);
337 } else {
338 noff[0][0] = spc>>1; noff[0][1] = spc>>1;
339 noff[1][0] = -(spc>>1); noff[1][1] = spc>>1;
340 noff[2][0] = -(spc>>1); noff[2][1] = -(spc>>1);
341 noff[3][0] = spc>>1; noff[3][1] = -(spc>>1);
342 }
343 int nsteps; // non-negative range
344 if (x0 < -(spc>>1)) {
345 nsteps = (spc-1 - x0 - (spc>>1))/spc;
346 x0 += nsteps*spc;
347 }
348 if (y < 0) { // get past y==0
349 nsteps = ((spc>>1)-1 - y)/(spc>>1);
350 y += nsteps*(spc>>1);
351 odd ^= nsteps&1;
352 }
353 while (y < bmp2->Height()) {
354 for (int x = x0 + odd*(spc>>1); x < bmp2->Width(); x += spc)
355 bmp2->Set(x, y);
356 y += spc>>1;
357 odd = !odd;
358 }
359 }
360
361 // Render (or finish rendering) current tile
362 bool
363 RpictSimulManager::RenderRect(const int x0, const int y0)
364 {
365 if (!tvw.type || !Ready()) {
366 error(INTERNAL, "need octree and view for RenderRect()");
367 return false;
368 }
369 ABitMap2 doneSamples = doneMap;
370 int sp2 = ceil(log2((TWidth()>THeight() ? TWidth() : THeight()) - 1.));
371 int layer = 0;
372 int x, y;
373 while (sp2 > 0) {
374 ABitMap2 sampMap(TWidth(), THeight());
375 int noff[4][2];
376 if ((prCB != NULL) & (barPix == NULL))
377 (*prCB)(100.*doneMap.SumTotal()/doneMap.Width()/doneMap.Height());
378 SetQuincunx(&sampMap, noff, 1<<sp2, layer&1, x0, y0);
379 sampMap -= doneSamples; // avoid resampling pixels
380 // Are we into adaptive sampling realm?
381 if (noff[0][0]*noff[0][0] + noff[0][1]*noff[0][1] < 4*psample*psample) {
382 if (FlushQueue() < 0) // need results to check thresholds
383 return false;
384 ABitMap2 fillMap = sampMap;
385 for (x = y = 0; sampMap.Find(&x, &y); x++)
386 if (BelowSampThresh(x, y, noff))
387 sampMap.Reset(x, y);
388 // spread sampling to neighbors...
389 const ABitMap2 origSampMap = sampMap;
390 for (x = 4; x--; ) {
391 ABitMap2 stamp = origSampMap;
392 stamp.Shift(noff[x][0], noff[x][1]);
393 sampMap |= stamp;
394 } // ...but don't resample what's done
395 sampMap -= doneSamples;
396 // interpolate smooth regions
397 fillMap -= sampMap;
398 for (x = y = 0; fillMap.Find(&x, &y); x++)
399 FillSquare(x, y, noff);
400 doneSamples |= doneMap;
401 } // compute required ray samples
402 for (x = y = 0; sampMap.Find(&x, &y); x++)
403 if (!ComputePixel(x, y))
404 return false;
405 doneSamples |= sampMap; // samples now done or at least queued
406 sp2 -= layer++ & 1; // next denser sampling
407 }
408 if (FlushQueue() < 0) // make sure we got everyone
409 return false;
410 x = y = 0;
411 if (doneMap.Find(&x, &y, false)) {
412 sprintf(errmsg, "missed %ld tile pixels, e.g. (%d,%d)",
413 (long)doneMap.Width()*doneMap.Height() -
414 doneMap.SumTotal(), x, y);
415 error(WARNING, errmsg);
416 }
417 if ((prCB != NULL) & (barPix == NULL))
418 (*prCB)(100.);
419 return true;
420 }
421
422 /*
423 * Render the specified tile in frame
424 * Tile pixels are contiguous unless ystride != 0
425 * Tiles numbered from upper-left at (0,0)
426 * Pixel type influenced by this->prims assignment
427 */
428 bool
429 RpictSimulManager::RenderTile(COLORV *rp, int ystride, float *zp, const int *tile)
430 {
431 if (!rp | (GetWidth() <= 0) | (GetHeight() <= 0) | !vw.type)
432 return false;
433 if (!ystride) // contiguous rows?
434 ystride = TWidth();
435 pacc.Init(rp, ystride, zp);
436 if (prims == xyzprims)
437 pacc.SetColorSpace(RDTxyz);
438 else if (prims)
439 pacc.SetColorSpace(RDTrgb, prims);
440
441 int x0=0, y0=0;
442 if (tile) {
443 x0 = -tile[0]*TWidth();
444 y0 = -tile[1]*THeight();
445 }
446 return SetTile(tile) && RenderRect(x0, y0);
447 }
448
449 // Same but store as common-exponent COLR or SCOLR
450 bool
451 RpictSimulManager::RenderTile(COLRV *bp, int ystride, float *zp, const int *tile)
452 {
453 if (!bp | (GetWidth() <= 0) | (GetHeight() <= 0) | !vw.type)
454 return false;
455 if (!ystride) // contiguous rows?
456 ystride = TWidth();
457 pacc.Init(bp, ystride, zp);
458 if (prims == xyzprims)
459 pacc.SetColorSpace(RDTxyze);
460 else if (prims)
461 pacc.SetColorSpace(RDTrgbe, prims);
462
463 int x0=0, y0=0;
464 if (tile) {
465 x0 = -tile[0]*TWidth();
466 y0 = -tile[1]*THeight();
467 }
468 return SetTile(tile) && RenderRect(x0, y0);
469 }
470
471 // Same but also use 16-bit encoded depth buffer
472 bool
473 RpictSimulManager::RenderTile(COLRV *bp, int ystride, short *dp, const int *tile)
474 {
475 if (!bp | (GetWidth() <= 0) | (GetHeight() <= 0) | !vw.type)
476 return false;
477 if (!ystride) // contiguous rows?
478 ystride = TWidth();
479 pacc.Init(bp, ystride, dp);
480 if (prims == xyzprims)
481 pacc.SetColorSpace(RDTxyze);
482 else if (prims)
483 pacc.SetColorSpace(RDTrgbe, prims);
484
485 int x0=0, y0=0;
486 if (tile) {
487 x0 = -tile[0]*TWidth();
488 y0 = -tile[1]*THeight();
489 }
490 return SetTile(tile) && RenderRect(x0, y0);
491 }
492
493 // Back to float color with 16-bit depth
494 bool
495 RpictSimulManager::RenderTile(COLORV *rp, int ystride, short *dp, const int *tile)
496 {
497 if (!rp | (GetWidth() <= 0) | (GetHeight() <= 0) | !vw.type)
498 return false;
499 if (!ystride) // contiguous rows?
500 ystride = TWidth();
501 pacc.Init(rp, ystride, dp);
502 if (prims == xyzprims)
503 pacc.SetColorSpace(RDTxyz);
504 else if (prims)
505 pacc.SetColorSpace(RDTrgb, prims);
506
507 int x0=0, y0=0;
508 if (tile) {
509 x0 = -tile[0]*TWidth();
510 y0 = -tile[1]*THeight();
511 }
512 return SetTile(tile) && RenderRect(x0, y0);
513 }
514
515 // Allocate a new render bar
516 void
517 RpictSimulManager::NewBar(int ht)
518 {
519 delete [] barPix;
520 delete [] barDepth;
521 if (ht > GetHeight()) ht = GetHeight();
522 if ((ht <= 0) | (GetWidth() <= 0)) {
523 doneMap.NewBitMap(0,0);
524 pacc.Init();
525 barPix = NULL; barDepth = NULL;
526 return;
527 }
528 thvres[0] = GetWidth();
529 thvres[1] = ht;
530 const int NC = prims ? 3 : NCSAMP;
531 barPix = new COLORV [ht*thvres[0]*NC];
532 barDepth = new float [ht*thvres[0]];
533 pacc.Init(barPix + (ht-1)*thvres[0]*NC,
534 -thvres[0], barDepth + (ht-1)*thvres[0]);
535 if (prims == xyzprims)
536 pacc.SetColorSpace(RDTxyz);
537 else if (prims)
538 pacc.SetColorSpace(RDTrgb, prims);
539
540 doneMap.NewBitMap(TWidth(), THeight());
541 }
542
543 // Shift render bar area the specified amount down the frame
544 bool
545 RpictSimulManager::LowerBar(int v, int ytop)
546 {
547 if (!barPix | !barDepth | (v > THeight()) | !tvw.type)
548 return false;
549 if (v <= 0) return !v;
550 if ((ytop -= v) <= 0)
551 return true;
552 tvw.voff -= double(v)/THeight();
553 ptvw.voff -= double(v)/THeight();
554 if (v == THeight()) {
555 doneMap.ClearBitMap();
556 return true;
557 }
558 const int NC = pacc.NC();
559 doneMap.Shift(0, v, false); // lift finished pixel samples
560 memmove(barPix, barPix + NC*TWidth()*v,
561 sizeof(COLORV)*NC*TWidth()*(THeight()-v));
562 memmove(barDepth, barDepth + TWidth()*v,
563 sizeof(float)*TWidth()*(THeight()-v));
564 if (ytop < THeight()) { // mark what we won't do as finished
565 doneMap.ClearRect(0, 0, TWidth(), THeight()-ytop, true);
566 memset(barPix, 0, sizeof(COLORV)*NC*TWidth()*(THeight()-ytop));
567 memset(barDepth, 0, sizeof(float)*TWidth()*(THeight()-ytop));
568 }
569 return true;
570 }
571
572 // Continue rendering from the specified position
573 bool
574 RpictSimulManager::RenderBelow(int ytop, const int vstep, FILE *pfp, const int dt, FILE *dfp)
575 {
576 if (ytop <= 0)
577 return true;
578 ptvw = pvw; // set starting bar's view
579 tvw = vw;
580 const char * verr = cropview(&tvw, 0., double(ytop-THeight())/GetHeight(),
581 1., double(ytop)/GetHeight());
582 if (verr) {
583 sprintf(errmsg, "illegal render bar below y=%d: %s", ytop, verr);
584 error(INTERNAL, errmsg);
585 return false;
586 }
587 if (!ptvw.type | (mblur <= FTINY) ||
588 cropview(&ptvw, 0., double(ytop-THeight())/GetHeight(),
589 1., double(ytop)/GetHeight()))
590 ptvw.type = 0;
591 // set up spectral sampling
592 if (setspectrsamp(CNDX, WLPART) <= 0) {
593 error(USER, "unsupported spectral sampling");
594 return false;
595 }
596 COLORV ** parr = NULL; // set up tiny source drawing
597 float ** zarr = NULL;
598 if (!ptvw.type && directvis && dblur <= FTINY) {
599 parr = new COLORV * [THeight()];
600 zarr = new float * [THeight()];
601 for (int n = THeight(); n-- > 0; ) {
602 parr[THeight()-1-n] = barPix + pacc.NC()*TWidth()*n;
603 zarr[THeight()-1-n] = barDepth + TWidth()*n;
604 }
605 ourview = vw; hres = GetWidth(); vres = GetHeight();
606 init_drawsources(psample);
607 }
608 int lastOut = ytop; // render down frame
609 while (ytop > 0) {
610 if (prCB)
611 (*prCB)(100.*(GetHeight()-ytop)/GetHeight());
612 if (!RenderRect(0, THeight()-ytop)) // render this bar
613 return false;
614 int nlines = lastOut - ytop + THeight();
615 if (nlines > ytop)
616 nlines = ytop;
617 else if (parr) // drawing sources?
618 drawsources(parr, prims, zarr,
619 0, hres, lastOut-nlines, nlines);
620
621 if (dfp) { // write out depth scanlines?
622 const float * dp = barDepth + TWidth()*(ytop-lastOut);
623 if (RDTdepthT(dt) == RDTdshort) {
624 for (int n = TWidth()*nlines; n-- > 0; dp++)
625 if (putint(depth2code(*dp, pacc.refDepth), 2, dfp) == EOF)
626 error(SYSTEM, "cannot write 16-bit depth buffer");
627 } else if (putbinary(dp, sizeof(float), TWidth()*nlines, dfp)
628 != TWidth()*nlines)
629 error(SYSTEM, "cannot write raw depth buffer");
630 }
631 COLORV * bpos = barPix + pacc.NC()*TWidth()*(ytop-lastOut);
632 while (nlines-- > 0) { // write pixel scanlines
633 switch (RDTcolorT(dt)) {
634 case RDTrgbe:
635 case RDTxyze:
636 if (fwritescan((COLOR *)bpos, TWidth(), pfp) < 0)
637 error(SYSTEM, "cannot write RGBE/XYZE output");
638 break;
639 case RDTscolr:
640 if (fwritesscan(bpos, pacc.NC(), TWidth(), pfp) < 0)
641 error(SYSTEM, "cannot write SCOLOR output");
642 break;
643 case RDTrgb:
644 case RDTxyz:
645 case RDTscolor:
646 if (putbinary(bpos, sizeof(COLORV)*pacc.NC(), TWidth(), pfp)
647 != TWidth())
648 error(SYSTEM, "cannot write SCOLOR output");
649 break;
650 default:
651 error(INTERNAL, "botched output color type in RenderBelow()");
652 break;
653 }
654 bpos += pacc.NC()*TWidth();
655 --lastOut;
656 } // flush each scan bar
657 if (fflush(pfp) == EOF || (dfp && fflush(dfp) == EOF))
658 error(SYSTEM, "output write error");
659 // advance down the frame
660 if (lastOut > 0 && !LowerBar(vstep, ytop))
661 return false;
662 ytop -= vstep;
663 }
664 delete [] parr;
665 delete [] zarr;
666 if (prCB)
667 (*prCB)(100.);
668 return true;
669 }
670
671 // Open new output picture file (and optional depth file)
672 RenderDataType
673 RpictSimulManager::NewOutput(FILE *pdfp[2], const char *pfname,
674 RenderDataType dt, const char *dfname)
675 {
676 pdfp[0] = pdfp[1] = NULL;
677 if (!RDTcolorT(dt))
678 error(INTERNAL, "botched color output type in NewOutput()");
679 if (NCSAMP == 3) {
680 if (RDTcolorT(dt) == RDTscolr)
681 dt = RDTnewCT(dt, prims==xyzprims ? RDTxyze : RDTrgbe);
682 else if (RDTcolorT(dt) == RDTscolor)
683 dt = RDTnewCT(dt, prims==xyzprims ? RDTxyz : RDTrgb);
684 }
685 if (!RDTdepthT(dt) ^ !dfname)
686 error(INTERNAL, "depth output requires file name and type in NewOutput()");
687 int fd = 1;
688 if (pfname) { // open picture output file
689 if (pfname[0] == '!') {
690 error(INTERNAL, "writing picture to a command not supported");
691 return RDTnone;
692 }
693 fd = open(pfname, O_WRONLY|O_CREAT|O_EXCL, 0666);
694 }
695 if (fd < 0) {
696 if ((frameNo <= 0) | (errno != EEXIST)) {
697 sprintf(errmsg, "cannot open picture file '%s'", pfname);
698 error(SYSTEM, errmsg);
699 }
700 return RDTnone; // expected in parallel sequence
701 }
702 if (fd == 1)
703 pdfp[0] = stdout;
704 else if (!(pdfp[0] = fdopen(fd, "w")))
705 error(SYSTEM, "failure calling fdopen()");
706 SET_FILE_BINARY(pdfp[0]); // write picture header
707 if ((pdfp[0] != stdout) | (frameNo <= 1)) {
708 newheader("RADIANCE", pdfp[0]);
709 fputs(GetHeader(), pdfp[0]);
710 }
711 fputs(VIEWSTR, pdfp[0]); fprintview(&vw, pdfp[0]); fputc('\n', pdfp[0]);
712 if (frameNo > 0)
713 fprintf(pdfp[0], "FRAME=%d\n", frameNo);
714 double pasp = viewaspect(&vw) * GetWidth() / GetHeight();
715 if ((0.99 > pasp) | (pasp > 1.01))
716 fputaspect(pasp, pdfp[0]);
717 fputnow(pdfp[0]);
718 switch (RDTcolorT(dt)) { // set primaries and picture format
719 case RDTrgbe:
720 if (!prims | (prims == xyzprims)) prims = stdprims;
721 fputprims(prims, pdfp[0]);
722 fputformat(COLRFMT, pdfp[0]);
723 break;
724 case RDTxyze:
725 prims = xyzprims;
726 fputformat(CIEFMT, pdfp[0]);
727 break;
728 case RDTscolr:
729 prims = NULL;
730 fputwlsplit(WLPART, pdfp[0]);
731 fputncomp(NCSAMP, pdfp[0]);
732 fputformat(SPECFMT, pdfp[0]);
733 break;
734 case RDTrgb:
735 if (!prims | (prims == xyzprims)) prims = stdprims;
736 fputprims(prims, pdfp[0]);
737 fputncomp(3, pdfp[0]);
738 fputendian(pdfp[0]);
739 fputformat("float", pdfp[0]);
740 break;
741 case RDTxyz:
742 prims = xyzprims;
743 fputprims(prims, pdfp[0]);
744 fputncomp(3, pdfp[0]);
745 fputendian(pdfp[0]);
746 fputformat("float", pdfp[0]);
747 break;
748 case RDTscolor:
749 prims = NULL;
750 fputwlsplit(WLPART, pdfp[0]);
751 fputncomp(NCSAMP, pdfp[0]);
752 fputendian(pdfp[0]);
753 fputformat("float", pdfp[0]);
754 break;
755 default:;
756 }
757 fputc('\n', pdfp[0]); // flush picture header + resolution
758 if (fflush(pdfp[0]) == EOF) {
759 sprintf(errmsg, "cannot write header to picture '%s'", pfname);
760 error(SYSTEM, errmsg);
761 fclose(pdfp[0]);
762 pdfp[0] = NULL;
763 return RDTnone;
764 }
765 if (dfname) { // open depth output
766 if (dfname[0] == '!')
767 pdfp[1] = popen(dfname+1, "w");
768 else
769 pdfp[1] = fopen(dfname, "w");
770 if (!pdfp[1]) {
771 sprintf(errmsg, "cannot open depth output '%s'", dfname);
772 error(SYSTEM, errmsg);
773 fclose(pdfp[0]);
774 pdfp[0] = NULL;
775 return RDTnone;
776 }
777 SET_FILE_BINARY(pdfp[1]);
778 }
779 if (RDTdepthT(dt) == RDTdshort) { // write header for 16-bit depth?
780 newheader("RADIANCE", pdfp[1]);
781 fputs(GetHeader(), pdfp[1]);
782 fputs(VIEWSTR, pdfp[1]); fprintview(&vw, pdfp[1]); fputc('\n', pdfp[1]);
783 fputs(DEPTHSTR, pdfp[1]); fputs(dunit, pdfp[1]); fputc('\n', pdfp[1]);
784 fputformat(DEPTH16FMT, pdfp[1]);
785 fputc('\n', pdfp[1]); // end-of-info
786 if (fflush(pdfp[1]) == EOF) {
787 sprintf(errmsg, "cannot write header to '%s'", dfname);
788 error(SYSTEM, errmsg);
789 fclose(pdfp[0]); fclose(pdfp[1]);
790 pdfp[0] = pdfp[1] = NULL;
791 return RDTnone;
792 }
793 }
794 return dt; // ready to roll
795 }
796
797 /*
798 * Render and write a frame to the named file
799 * Include any header lines set prior to call
800 * Picture file must not exist
801 * Write pixels to stdout if !pfname
802 * Write depth to a command if dfname[0]=='!'
803 */
804 RenderDataType
805 RpictSimulManager::RenderFrame(const char *pfname, RenderDataType dt, const char *dfname)
806 {
807 FILE *pdfp[2];
808 // prepare output file(s)
809 dt = NewOutput(pdfp, pfname, dt, dfname);
810 if (dt == RDTnone)
811 return RDTnone;
812 // add resolution string(s)
813 fprtresolu(GetWidth(), GetHeight(), pdfp[0]);
814 if (RDTdepthT(dt) == RDTdshort)
815 fprtresolu(GetWidth(), GetHeight(), pdfp[1]);
816
817 const int bheight = (psample > 1) ? int(4*psample+.99) : 8;
818 const int vstep = bheight >> (psample > 1);
819
820 NewBar(bheight); // render frame if we can
821 if (!RenderBelow(GetHeight(), vstep, pdfp[0], dt, pdfp[1])) {
822 fclose(pdfp[0]);
823 if (pdfp[1]) (dfname[0] == '!') ? pclose(pdfp[1]) : fclose(pdfp[1]);
824 Cleanup();
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 (!dfname) // no depth file?
1012 return dt;
1013
1014 if (dfname[0] == '!') {
1015 error(USER, "depth data cannot be reloaded from command");
1016 fclose(pdfp[0]);
1017 pdfp[0] = NULL;
1018 return RDTnone;
1019 }
1020 pdfp[1] = fopen(dfname, "r+");
1021 if (!pdfp[1]) {
1022 sprintf(errmsg, "cannot reopen depth file '%s'", dfname);
1023 error(SYSTEM, errmsg);
1024 fclose(pdfp[0]);
1025 pdfp[0] = NULL;
1026 return RDTnone;
1027 }
1028 SET_FILE_BINARY(pdfp[1]);
1029 int n, len = strlen(HDRSTR);
1030 char buf[32]; // sniff for 16-bit header
1031 if (getbinary(buf, 1, len+1, pdfp[1]) < len+1) {
1032 sprintf(errmsg, "empty depth file '%s'", dfname);
1033 error(SYSTEM, errmsg);
1034 fclose(pdfp[0]); fclose(pdfp[1]);
1035 pdfp[0] = pdfp[1] = NULL;
1036 return RDTnone;
1037 }
1038 for (n = 0; n < len; n++)
1039 if (buf[n] != HDRSTR[n])
1040 break; // not a Radiance header
1041 rewind(pdfp[1]);
1042 if ((n < len) | !isprint(buf[len]))
1043 return RDTnewDT(dt, RDTdfloat);
1044
1045 HeaderInfo dinfo; // thinking it's 16-bit encoded
1046 if (getheader(pdfp[1], head_check, &dinfo) < 0)
1047 sprintf(errmsg, "bad header in encoded depth file '%s'",
1048 dfname);
1049 else if (strcmp(dinfo.fmt, DEPTH16FMT))
1050 sprintf(errmsg, "wrong format (%s) for depth file '%s'",
1051 dinfo.fmt, dfname);
1052 else if (!SetReferenceDepth(dinfo.depth_unit))
1053 sprintf(errmsg, "bad/missing reference depth (%s) in '%s'",
1054 dinfo.depth_unit, dfname);
1055 else
1056 errmsg[0] = '\0';
1057
1058 if (errmsg[0]) {
1059 error(USER, errmsg);
1060 fclose(pdfp[1]); fclose(pdfp[0]);
1061 pdfp[0] = pdfp[1] = NULL;
1062 return RDTnone;
1063 }
1064 return RDTnewDT(dt, RDTdshort);
1065 }
1066
1067 // Resume partially finished rendering
1068 // Picture file must exist
1069 RenderDataType
1070 RpictSimulManager::ResumeFrame(const char *pfname, const char *dfname)
1071 {
1072 FILE *pdfp[2];
1073
1074 RenderDataType dt = ReopenOutput(pdfp, pfname, dfname);
1075 if (dt == RDTnone)
1076 return RDTnone;
1077
1078 int bytesPer = 0; // figure out how far we got...
1079 switch (RDTcolorT(dt)) {
1080 case RDTrgbe:
1081 case RDTxyze:
1082 break;
1083 case RDTscolr:
1084 bytesPer = hinfo.ncomp + 1; // XXX assumes no compression
1085 break;
1086 case RDTrgb:
1087 case RDTxyz:
1088 bytesPer = sizeof(float)*3;
1089 break;
1090 case RDTscolor:
1091 bytesPer = sizeof(float)*hinfo.ncomp;
1092 break;
1093 default:
1094 sprintf(errmsg, "unknown format (%s) for '%s'", hinfo.fmt, pfname);
1095 error(USER, errmsg);
1096 fclose(pdfp[0]);
1097 if (pdfp[1]) fclose(pdfp[1]);
1098 return RDTnone;
1099 }
1100 RESOLU res;
1101 if (!fgetsresolu(&res, pdfp[0]) || res.rt != PIXSTANDARD) {
1102 sprintf(errmsg, "missing/bad resolution for '%s'", pfname);
1103 error(USER, errmsg);
1104 fclose(pdfp[0]);
1105 if (pdfp[1]) fclose(pdfp[1]);
1106 return RDTnone;
1107 }
1108 vw.type = 0; // set up new (unreferenced) frame
1109 frameNo = 0;
1110 int hvdim[2] = {res.xr, res.yr};
1111 double noAdj = 0;
1112 if (!NewFrame(hinfo.vw, hvdim, &noAdj) ||
1113 (hvdim[0] != res.xr) | (hvdim[1] != res.yr)) {
1114 error(CONSISTENCY, "unexpected resolution change in ResumeFrame()");
1115 fclose(pdfp[0]);
1116 if (pdfp[1]) fclose(pdfp[1]);
1117 return RDTnone;
1118 }
1119 long dataStart = ftell(pdfp[0]); // picture starting point
1120 if (dataStart < 0) {
1121 sprintf(errmsg, "cannot seek on '%s'", pfname);
1122 error(SYSTEM, errmsg);
1123 fclose(pdfp[0]);
1124 if (pdfp[1]) fclose(pdfp[1]);
1125 return RDTnone;
1126 }
1127 long doneScans = 0;
1128 if (bytesPer) { // fixed-width records?
1129 fseek(pdfp[0], 0, SEEK_END);
1130 long dataEnd = ftell(pdfp[0]);
1131 doneScans = (dataEnd - dataStart)/(bytesPer*GetWidth());
1132 if (dataEnd-dataStart > bytesPer*GetWidth()*doneScans)
1133 fseek(pdfp[0], dataStart + bytesPer*GetWidth()*doneScans, SEEK_SET);
1134 } else { // else get compressed scanlines
1135 COLR * scan = (COLR *)tempbuffer(sizeof(COLR)*GetWidth());
1136 while (freadcolrs(scan, GetWidth(), pdfp[0]) >= 0)
1137 ++doneScans;
1138 if (!feof(pdfp[0])) {
1139 sprintf(errmsg, "error reading compressed scanline from '%s'", pfname);
1140 error(USER, errmsg);
1141 fclose(pdfp[0]);
1142 if (pdfp[1]) fclose(pdfp[1]);
1143 return RDTnone;
1144 }
1145 }
1146 if (doneScans >= GetHeight()) { // nothing left to do?
1147 sprintf(errmsg, "output file '%s' is already complete", pfname);
1148 error(WARNING, errmsg);
1149 fclose(pdfp[0]);
1150 if (pdfp[1]) fclose(pdfp[1]);
1151 return dt;
1152 }
1153 if (!doneScans) {
1154 sprintf(errmsg, "restarting empty frame '%s'", pfname);
1155 error(WARNING, errmsg);
1156 }
1157 long toSkip = 0;
1158 switch (RDTdepthT(dt)) { // append depth file, too?
1159 case RDTdfloat:
1160 toSkip = sizeof(float)*GetWidth()*doneScans;
1161 break;
1162 case RDTdshort:
1163 if (!fgetsresolu(&res, pdfp[1]) || (res.rt != PIXSTANDARD) |
1164 (res.xr != GetWidth()) | (res.yr != GetHeight())) {
1165 sprintf(errmsg, "missing/bad resolution for '%s'", dfname);
1166 error(USER, errmsg);
1167 fclose(pdfp[0]); fclose(pdfp[0]);
1168 return RDTnone;
1169 }
1170 toSkip = 2L*GetWidth()*doneScans;
1171 break;
1172 default:;
1173 } // fseek() needed for output
1174 if (pdfp[1] && fseek(pdfp[1], toSkip, SEEK_CUR) < 0) {
1175 sprintf(errmsg, "cannot seek on depth file '%s'", dfname);
1176 error(SYSTEM, errmsg);
1177 fclose(pdfp[0]); fclose(pdfp[1]);
1178 return RDTnone;
1179 }
1180 int bheight = (psample > 1) ? int(4*psample+.99) : 8;
1181 if (bheight > GetHeight()-doneScans)
1182 bheight = GetHeight()-doneScans;
1183 int vstep = bheight >> (psample > 1);
1184 vstep += !vstep;
1185
1186 NewBar(bheight); // render remainder if we can
1187 if (!RenderBelow(GetHeight()-doneScans, vstep, pdfp[0], dt, pdfp[1])) {
1188 fclose(pdfp[0]);
1189 if (pdfp[1]) fclose(pdfp[1]);
1190 Cleanup();
1191 return RDTnone;
1192 }
1193 NewBar(); // close up and return success
1194 fclose(pdfp[0]);
1195 if (pdfp[1]) fclose(pdfp[1]);
1196 return dt;
1197 }