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

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.8 static const char RCSid[] = "$Id: RpictSimulManager.cpp,v 2.7 2024/08/22 00:44:02 greg Exp $";
3 greg 2.1 #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 greg 2.2 error(INTERNAL, "botched color space type in SetPixel()");
54 greg 2.1 }
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 greg 2.2 case RDTscolr:
83     case RDTscolor:
84 greg 2.1 primp = NULL;
85     break;
86 greg 2.2 default:
87     error(INTERNAL, "botched color space type in SetColorSpace()");
88 greg 2.1 }
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 greg 2.2 const int ty = (r->rno-1) / rsp->TWidth();
143     const int tx = r->rno-1 - (RNUMBER)ty*rsp->TWidth();
144 greg 2.1
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 greg 2.8 pacc.SetPixel(x, y, scblack);
206 greg 2.1 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 greg 2.2 return EnqueueRay(rodir[0], rodir[1], (RNUMBER)y*TWidth()+x+1);
231 greg 2.1 }
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 greg 2.2 RpictSimulManager::FillSquare(const int x, const int y, const int noff[4][2])
265 greg 2.1 {
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 greg 2.2 j = abs(noff[1][1]-noff[0][1]); // i==j for diamond fill
275     const int slen = (i > j) ? i : j;
276 greg 2.1 const bool spectr = (pacc.NC() > 3);
277 greg 2.2 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 greg 2.1 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 greg 2.8 SetQuincunx(ABitMap2 *bmp2, int noff[4][2], const int spc, bool odd, int x0, int y)
331 greg 2.1 {
332 greg 2.8 if (odd) { // order neighbors CCW
333 greg 2.1 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 greg 2.8 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 greg 2.1 }
360    
361     // Render (or finish rendering) current tile
362     bool
363 greg 2.8 RpictSimulManager::RenderRect(const int x0, const int y0)
364 greg 2.1 {
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 greg 2.8 SetQuincunx(&sampMap, noff, 1<<sp2, layer&1, x0, y0);
379 greg 2.1 sampMap -= doneSamples; // avoid resampling pixels
380     // Are we into adaptive sampling realm?
381 greg 2.8 if (noff[0][0]*noff[0][0] + noff[0][1]*noff[0][1] < 4*psample*psample) {
382 greg 2.2 if (FlushQueue() < 0) // need results to check thresholds
383 greg 2.1 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 greg 2.2 for (x = 4; x--; ) {
391 greg 2.1 ABitMap2 stamp = origSampMap;
392 greg 2.2 stamp.Shift(noff[x][0], noff[x][1]);
393 greg 2.1 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 greg 2.8
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 greg 2.1 }
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 greg 2.8 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 greg 2.1 }
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 greg 2.8 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 greg 2.1 }
492    
493 greg 2.4 // 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 greg 2.8 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 greg 2.4 }
514    
515 greg 2.1 // 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 greg 2.3 RpictSimulManager::LowerBar(int v, int ytop)
546 greg 2.1 {
547     if (!barPix | !barDepth | (v > THeight()) | !tvw.type)
548     return false;
549 greg 2.3 if (v <= 0) return !v;
550     if ((ytop -= v) <= 0)
551     return true;
552 greg 2.2 tvw.voff -= double(v)/THeight();
553     ptvw.voff -= double(v)/THeight();
554 greg 2.1 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 greg 2.3 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 greg 2.1 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 greg 2.8 if (!RenderRect(0, THeight()-ytop)) // render this bar
613 greg 2.1 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 greg 2.2 error(INTERNAL, "botched output color type in RenderBelow()");
652 greg 2.1 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 greg 2.3 if (lastOut > 0 && !LowerBar(vstep, ytop))
661 greg 2.1 return false;
662     ytop -= vstep;
663     }
664     delete [] parr;
665     delete [] zarr;
666     if (prCB)
667     (*prCB)(100.);
668     return true;
669     }
670    
671 greg 2.5 // Open new output picture file (and optional depth file)
672 greg 2.1 RenderDataType
673 greg 2.5 RpictSimulManager::NewOutput(FILE *pdfp[2], const char *pfname,
674     RenderDataType dt, const char *dfname)
675 greg 2.1 {
676 greg 2.5 pdfp[0] = pdfp[1] = NULL;
677 greg 2.1 if (!RDTcolorT(dt))
678 greg 2.5 error(INTERNAL, "botched color output type in NewOutput()");
679 greg 2.1 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 greg 2.5 error(INTERNAL, "depth output requires file name and type in NewOutput()");
687     int fd = 1;
688 greg 2.1 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 greg 2.5 pdfp[0] = stdout;
704     else if (!(pdfp[0] = fdopen(fd, "w")))
705 greg 2.1 error(SYSTEM, "failure calling fdopen()");
706 greg 2.5 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 greg 2.1 }
711 greg 2.5 fputs(VIEWSTR, pdfp[0]); fprintview(&vw, pdfp[0]); fputc('\n', pdfp[0]);
712 greg 2.1 if (frameNo > 0)
713 greg 2.5 fprintf(pdfp[0], "FRAME=%d\n", frameNo);
714 greg 2.1 double pasp = viewaspect(&vw) * GetWidth() / GetHeight();
715 greg 2.2 if ((0.99 > pasp) | (pasp > 1.01))
716 greg 2.5 fputaspect(pasp, pdfp[0]);
717     fputnow(pdfp[0]);
718 greg 2.1 switch (RDTcolorT(dt)) { // set primaries and picture format
719     case RDTrgbe:
720     if (!prims | (prims == xyzprims)) prims = stdprims;
721 greg 2.5 fputprims(prims, pdfp[0]);
722     fputformat(COLRFMT, pdfp[0]);
723 greg 2.1 break;
724     case RDTxyze:
725     prims = xyzprims;
726 greg 2.5 fputformat(CIEFMT, pdfp[0]);
727 greg 2.1 break;
728     case RDTscolr:
729     prims = NULL;
730 greg 2.5 fputwlsplit(WLPART, pdfp[0]);
731     fputncomp(NCSAMP, pdfp[0]);
732     fputformat(SPECFMT, pdfp[0]);
733 greg 2.1 break;
734     case RDTrgb:
735     if (!prims | (prims == xyzprims)) prims = stdprims;
736 greg 2.5 fputprims(prims, pdfp[0]);
737     fputncomp(3, pdfp[0]);
738     fputendian(pdfp[0]);
739     fputformat("float", pdfp[0]);
740 greg 2.1 break;
741     case RDTxyz:
742     prims = xyzprims;
743 greg 2.5 fputprims(prims, pdfp[0]);
744     fputncomp(3, pdfp[0]);
745     fputendian(pdfp[0]);
746     fputformat("float", pdfp[0]);
747 greg 2.1 break;
748     case RDTscolor:
749     prims = NULL;
750 greg 2.5 fputwlsplit(WLPART, pdfp[0]);
751     fputncomp(NCSAMP, pdfp[0]);
752     fputendian(pdfp[0]);
753     fputformat("float", pdfp[0]);
754 greg 2.1 break;
755     default:;
756     }
757 greg 2.5 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 greg 2.6 if (dfname) { // open depth output
766 greg 2.1 if (dfname[0] == '!')
767 greg 2.5 pdfp[1] = popen(dfname+1, "w");
768 greg 2.1 else
769 greg 2.5 pdfp[1] = fopen(dfname, "w");
770     if (!pdfp[1]) {
771 greg 2.1 sprintf(errmsg, "cannot open depth output '%s'", dfname);
772     error(SYSTEM, errmsg);
773 greg 2.5 fclose(pdfp[0]);
774     pdfp[0] = NULL;
775 greg 2.1 return RDTnone;
776     }
777 greg 2.5 SET_FILE_BINARY(pdfp[1]);
778 greg 2.1 }
779     if (RDTdepthT(dt) == RDTdshort) { // write header for 16-bit depth?
780 greg 2.5 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 greg 2.1 }
794 greg 2.5 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 greg 2.6 // add resolution string(s)
813     fprtresolu(GetWidth(), GetHeight(), pdfp[0]);
814     if (RDTdepthT(dt) == RDTdshort)
815     fprtresolu(GetWidth(), GetHeight(), pdfp[1]);
816 greg 2.5
817 greg 2.8 const int bheight = (psample > 1) ? int(4*psample+.99) : 8;
818     const int vstep = bheight >> (psample > 1);
819 greg 2.1
820     NewBar(bheight); // render frame if we can
821 greg 2.5 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 greg 2.1 Cleanup();
825     return RDTnone;
826     }
827     NewBar(); // clean up and return
828 greg 2.5 if (pdfp[0] != stdout)
829     fclose(pdfp[0]);
830     if (pdfp[1]) {
831 greg 2.1 if (dfname[0] == '!') {
832 greg 2.5 int status = pclose(pdfp[1]);
833 greg 2.1 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 greg 2.5 fclose(pdfp[1]);
841 greg 2.1 }
842     return dt;
843     }
844    
845     // passed struct for header line callback
846 greg 2.5 static struct HeaderInfo {
847 greg 2.1 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 greg 2.5 } hinfo; // XXX single copy to hold custom primitives
868 greg 2.1
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 greg 2.5 // Reopen output file(s), leaving pointers at end of (each) header
910 greg 2.1 RenderDataType
911 greg 2.5 RpictSimulManager::ReopenOutput(FILE *pdfp[2], const char *pfname, const char *dfname)
912 greg 2.1 {
913 greg 2.5 extern const char HDRSTR[];
914    
915     if (!pfname || pfname[0] == '!') {
916     pdfp[0] = pdfp[1] = NULL;
917 greg 2.1 return RDTnone;
918 greg 2.5 }
919 greg 2.1 RenderDataType dt = RDTnone;
920 greg 2.5 pdfp[1] = NULL;
921     pdfp[0] = fopen(pfname, "r+");
922     if (!pdfp[0]) {
923 greg 2.1 sprintf(errmsg, "cannot reopen output picture '%s'", pfname);
924     error(SYSTEM, errmsg);
925     return RDTnone;
926     }
927 greg 2.5 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 greg 2.1 return RDTnone;
932     }
933     if (!hinfo.gotview) {
934     sprintf(errmsg, "missing view for '%s'", pfname);
935     error(USER, errmsg);
936 greg 2.5 fclose(pdfp[0]);
937     pdfp[0] = NULL;
938 greg 2.1 return RDTnone;
939     }
940     if (hinfo.ncomp < 3) {
941     sprintf(errmsg, "bad # components (%d) in '%s'", hinfo.ncomp, pfname);
942     error(USER, errmsg);
943 greg 2.5 fclose(pdfp[0]);
944     pdfp[0] = NULL;
945 greg 2.1 return RDTnone;
946     }
947 greg 2.5 // set rendering/output space
948 greg 2.1 if (!strcmp(hinfo.fmt, COLRFMT)) {
949 greg 2.5 prims = hinfo.prims; // XXX static array
950 greg 2.1 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 greg 2.5 fclose(pdfp[0]);
966     pdfp[0] = NULL;
967 greg 2.1 return RDTnone;
968     }
969 greg 2.5 NCSAMP = hinfo.ncomp; // overrides global setting
970 greg 2.1 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 greg 2.5 fclose(pdfp[0]);
977     pdfp[0] = NULL;
978 greg 2.1 return RDTnone;
979     }
980     if (hinfo.ncomp == 3) {
981 greg 2.5 prims = hinfo.prims; // custom primaries?
982 greg 2.1 int n = 8*hinfo.gotprims;
983     while (n--)
984     if (!FABSEQ(hinfo.prims[0][n], stdprims[0][n]))
985     break;
986 greg 2.5 if (n < 0) // standard primaries?
987 greg 2.1 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 greg 2.5 } 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 greg 2.7 if (getbinary(buf, 1, len+1, pdfp[1]) < len+1) {
1032 greg 2.5 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 greg 2.7 rewind(pdfp[1]);
1042 greg 2.5 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 greg 2.1 bytesPer = sizeof(float)*hinfo.ncomp;
1092 greg 2.5 break;
1093     default:
1094 greg 2.1 sprintf(errmsg, "unknown format (%s) for '%s'", hinfo.fmt, pfname);
1095     error(USER, errmsg);
1096 greg 2.5 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 greg 2.1 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 greg 2.5 error(CONSISTENCY, "unexpected resolution change in ResumeFrame()");
1115     fclose(pdfp[0]);
1116     if (pdfp[1]) fclose(pdfp[1]);
1117 greg 2.1 return RDTnone;
1118     }
1119 greg 2.5 long dataStart = ftell(pdfp[0]); // picture starting point
1120 greg 2.1 if (dataStart < 0) {
1121     sprintf(errmsg, "cannot seek on '%s'", pfname);
1122     error(SYSTEM, errmsg);
1123 greg 2.5 fclose(pdfp[0]);
1124     if (pdfp[1]) fclose(pdfp[1]);
1125 greg 2.1 return RDTnone;
1126     }
1127     long doneScans = 0;
1128     if (bytesPer) { // fixed-width records?
1129 greg 2.5 fseek(pdfp[0], 0, SEEK_END);
1130     long dataEnd = ftell(pdfp[0]);
1131 greg 2.1 doneScans = (dataEnd - dataStart)/(bytesPer*GetWidth());
1132     if (dataEnd-dataStart > bytesPer*GetWidth()*doneScans)
1133 greg 2.5 fseek(pdfp[0], dataStart + bytesPer*GetWidth()*doneScans, SEEK_SET);
1134 greg 2.1 } else { // else get compressed scanlines
1135     COLR * scan = (COLR *)tempbuffer(sizeof(COLR)*GetWidth());
1136 greg 2.5 while (freadcolrs(scan, GetWidth(), pdfp[0]) >= 0)
1137 greg 2.1 ++doneScans;
1138 greg 2.5 if (!feof(pdfp[0])) {
1139 greg 2.1 sprintf(errmsg, "error reading compressed scanline from '%s'", pfname);
1140     error(USER, errmsg);
1141 greg 2.5 fclose(pdfp[0]);
1142     if (pdfp[1]) fclose(pdfp[1]);
1143 greg 2.1 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 greg 2.5 fclose(pdfp[0]);
1150     if (pdfp[1]) fclose(pdfp[1]);
1151 greg 2.1 return dt;
1152     }
1153     if (!doneScans) {
1154     sprintf(errmsg, "restarting empty frame '%s'", pfname);
1155     error(WARNING, errmsg);
1156     }
1157 greg 2.5 long toSkip = 0;
1158 greg 2.6 switch (RDTdepthT(dt)) { // append depth file, too?
1159 greg 2.5 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 greg 2.1 return RDTnone;
1169     }
1170 greg 2.5 toSkip = 2L*GetWidth()*doneScans;
1171     break;
1172     default:;
1173 greg 2.6 } // fseek() needed for output
1174     if (pdfp[1] && fseek(pdfp[1], toSkip, SEEK_CUR) < 0) {
1175 greg 2.5 sprintf(errmsg, "cannot seek on depth file '%s'", dfname);
1176     error(SYSTEM, errmsg);
1177     fclose(pdfp[0]); fclose(pdfp[1]);
1178     return RDTnone;
1179 greg 2.1 }
1180 greg 2.8 int bheight = (psample > 1) ? int(4*psample+.99) : 8;
1181 greg 2.1 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 greg 2.5 if (!RenderBelow(GetHeight()-doneScans, vstep, pdfp[0], dt, pdfp[1])) {
1188     fclose(pdfp[0]);
1189     if (pdfp[1]) fclose(pdfp[1]);
1190 greg 2.1 Cleanup();
1191     return RDTnone;
1192     }
1193     NewBar(); // close up and return success
1194 greg 2.5 fclose(pdfp[0]);
1195     if (pdfp[1]) fclose(pdfp[1]);
1196 greg 2.1 return dt;
1197     }