ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/RpictSimulManager.cpp
Revision: 2.9
Committed: Sun Aug 25 03:13:07 2024 UTC (8 months, 1 week ago) by greg
Branch: MAIN
Changes since 2.8: +19 -13 lines
Log Message:
perf(rxpict): Minor improvement -- still needs work on sampling

File Contents

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