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

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.9 static const char RCSid[] = "$Id: RpictSimulManager.cpp,v 2.8 2024/08/23 02:08:28 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 greg 2.9 #define DEBUG 1 // XXX temporary!
13    
14 greg 2.1 #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 greg 2.2 error(INTERNAL, "botched color space type in SetPixel()");
56 greg 2.1 }
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 greg 2.2 case RDTscolr:
85     case RDTscolor:
86 greg 2.1 primp = NULL;
87     break;
88 greg 2.2 default:
89     error(INTERNAL, "botched color space type in SetColorSpace()");
90 greg 2.1 }
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 greg 2.2 const int ty = (r->rno-1) / rsp->TWidth();
145     const int tx = r->rno-1 - (RNUMBER)ty*rsp->TWidth();
146 greg 2.1
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 greg 2.9 DCHECK(doneMap.OffBitMap(x,y),
202     CONSISTENCY, "illegal pixel index in ComputPixel()");
203 greg 2.1 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 greg 2.8 pacc.SetPixel(x, y, scblack);
210 greg 2.1 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 greg 2.2 return EnqueueRay(rodir[0], rodir[1], (RNUMBER)y*TWidth()+x+1);
235 greg 2.1 }
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 greg 2.2 RpictSimulManager::FillSquare(const int x, const int y, const int noff[4][2])
269 greg 2.1 {
270     SCOLOR pval[4];
271     float dist[4];
272     int i, j;
273     // assumes 4 corners are valid!
274 greg 2.9 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 greg 2.1 pacc.GetPixel(x+noff[i][0], y+noff[i][1], pval[i], &dist[i]);
278 greg 2.9 }
279 greg 2.1 i = abs(noff[1][0]-noff[0][0]);
280 greg 2.2 j = abs(noff[1][1]-noff[0][1]); // i==j for diamond fill
281     const int slen = (i > j) ? i : j;
282 greg 2.1 const bool spectr = (pacc.NC() > 3);
283 greg 2.2 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 greg 2.1 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 greg 2.8 SetQuincunx(ABitMap2 *bmp2, int noff[4][2], const int spc, bool odd, int x0, int y)
337 greg 2.1 {
338 greg 2.8 if (odd) { // order neighbors CCW
339 greg 2.1 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 greg 2.8 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 greg 2.1 }
366    
367     // Render (or finish rendering) current tile
368     bool
369 greg 2.8 RpictSimulManager::RenderRect(const int x0, const int y0)
370 greg 2.1 {
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 greg 2.8 SetQuincunx(&sampMap, noff, 1<<sp2, layer&1, x0, y0);
385 greg 2.1 sampMap -= doneSamples; // avoid resampling pixels
386     // Are we into adaptive sampling realm?
387 greg 2.9 if (noff[0][0]*noff[0][0] + noff[0][1]*noff[0][1] < psample*psample) {
388 greg 2.2 if (FlushQueue() < 0) // need results to check thresholds
389 greg 2.1 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 greg 2.9 #if 0
395     XXX Need to fix directions for spreading!!
396 greg 2.1 // spread sampling to neighbors...
397     const ABitMap2 origSampMap = sampMap;
398 greg 2.2 for (x = 4; x--; ) {
399 greg 2.1 ABitMap2 stamp = origSampMap;
400 greg 2.2 stamp.Shift(noff[x][0], noff[x][1]);
401 greg 2.1 sampMap |= stamp;
402     } // ...but don't resample what's done
403     sampMap -= doneSamples;
404 greg 2.9 #endif
405 greg 2.1 // 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 greg 2.8
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 greg 2.1 }
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 greg 2.8 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 greg 2.1 }
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 greg 2.8 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 greg 2.1 }
501    
502 greg 2.4 // 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 greg 2.8 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 greg 2.4 }
523    
524 greg 2.1 // 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 greg 2.3 RpictSimulManager::LowerBar(int v, int ytop)
555 greg 2.1 {
556     if (!barPix | !barDepth | (v > THeight()) | !tvw.type)
557     return false;
558 greg 2.3 if (v <= 0) return !v;
559     if ((ytop -= v) <= 0)
560     return true;
561 greg 2.2 tvw.voff -= double(v)/THeight();
562     ptvw.voff -= double(v)/THeight();
563 greg 2.1 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 greg 2.9 if (ytop < THeight()) // mark what we won't do as finished
574 greg 2.3 doneMap.ClearRect(0, 0, TWidth(), THeight()-ytop, true);
575 greg 2.1 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 greg 2.9 // update spectral sampling
598 greg 2.1 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 greg 2.8 if (!RenderRect(0, THeight()-ytop)) // render this bar
619 greg 2.1 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 greg 2.2 error(INTERNAL, "botched output color type in RenderBelow()");
658 greg 2.1 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 greg 2.3 if (lastOut > 0 && !LowerBar(vstep, ytop))
667 greg 2.1 return false;
668     ytop -= vstep;
669     }
670     delete [] parr;
671     delete [] zarr;
672     if (prCB)
673     (*prCB)(100.);
674     return true;
675     }
676    
677 greg 2.5 // Open new output picture file (and optional depth file)
678 greg 2.1 RenderDataType
679 greg 2.5 RpictSimulManager::NewOutput(FILE *pdfp[2], const char *pfname,
680     RenderDataType dt, const char *dfname)
681 greg 2.1 {
682 greg 2.5 pdfp[0] = pdfp[1] = NULL;
683 greg 2.1 if (!RDTcolorT(dt))
684 greg 2.9 error(INTERNAL, "missing color output type in NewOutput()");
685 greg 2.1 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 greg 2.5 error(INTERNAL, "depth output requires file name and type in NewOutput()");
693     int fd = 1;
694 greg 2.1 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 greg 2.9 return RDTnone; // may be expected in sequence run
707 greg 2.1 }
708     if (fd == 1)
709 greg 2.5 pdfp[0] = stdout;
710     else if (!(pdfp[0] = fdopen(fd, "w")))
711 greg 2.1 error(SYSTEM, "failure calling fdopen()");
712 greg 2.5 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 greg 2.1 }
717 greg 2.5 fputs(VIEWSTR, pdfp[0]); fprintview(&vw, pdfp[0]); fputc('\n', pdfp[0]);
718 greg 2.1 if (frameNo > 0)
719 greg 2.5 fprintf(pdfp[0], "FRAME=%d\n", frameNo);
720 greg 2.1 double pasp = viewaspect(&vw) * GetWidth() / GetHeight();
721 greg 2.2 if ((0.99 > pasp) | (pasp > 1.01))
722 greg 2.5 fputaspect(pasp, pdfp[0]);
723     fputnow(pdfp[0]);
724 greg 2.1 switch (RDTcolorT(dt)) { // set primaries and picture format
725     case RDTrgbe:
726     if (!prims | (prims == xyzprims)) prims = stdprims;
727 greg 2.5 fputprims(prims, pdfp[0]);
728     fputformat(COLRFMT, pdfp[0]);
729 greg 2.1 break;
730     case RDTxyze:
731     prims = xyzprims;
732 greg 2.5 fputformat(CIEFMT, pdfp[0]);
733 greg 2.1 break;
734     case RDTscolr:
735     prims = NULL;
736 greg 2.5 fputwlsplit(WLPART, pdfp[0]);
737     fputncomp(NCSAMP, pdfp[0]);
738     fputformat(SPECFMT, pdfp[0]);
739 greg 2.1 break;
740     case RDTrgb:
741     if (!prims | (prims == xyzprims)) prims = stdprims;
742 greg 2.5 fputprims(prims, pdfp[0]);
743     fputncomp(3, pdfp[0]);
744     fputendian(pdfp[0]);
745     fputformat("float", pdfp[0]);
746 greg 2.1 break;
747     case RDTxyz:
748     prims = xyzprims;
749 greg 2.5 fputprims(prims, pdfp[0]);
750     fputncomp(3, pdfp[0]);
751     fputendian(pdfp[0]);
752     fputformat("float", pdfp[0]);
753 greg 2.1 break;
754     case RDTscolor:
755     prims = NULL;
756 greg 2.5 fputwlsplit(WLPART, pdfp[0]);
757     fputncomp(NCSAMP, pdfp[0]);
758     fputendian(pdfp[0]);
759     fputformat("float", pdfp[0]);
760 greg 2.1 break;
761 greg 2.9 default:; // pro forma - caught this above
762 greg 2.1 }
763 greg 2.9 fputc('\n', pdfp[0]); // flush picture header
764 greg 2.5 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 greg 2.6 if (dfname) { // open depth output
772 greg 2.1 if (dfname[0] == '!')
773 greg 2.5 pdfp[1] = popen(dfname+1, "w");
774 greg 2.1 else
775 greg 2.5 pdfp[1] = fopen(dfname, "w");
776     if (!pdfp[1]) {
777 greg 2.1 sprintf(errmsg, "cannot open depth output '%s'", dfname);
778     error(SYSTEM, errmsg);
779 greg 2.5 fclose(pdfp[0]);
780     pdfp[0] = NULL;
781 greg 2.1 return RDTnone;
782     }
783 greg 2.5 SET_FILE_BINARY(pdfp[1]);
784 greg 2.1 }
785     if (RDTdepthT(dt) == RDTdshort) { // write header for 16-bit depth?
786 greg 2.5 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 greg 2.1 }
800 greg 2.5 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 greg 2.6 // add resolution string(s)
819     fprtresolu(GetWidth(), GetHeight(), pdfp[0]);
820     if (RDTdepthT(dt) == RDTdshort)
821     fprtresolu(GetWidth(), GetHeight(), pdfp[1]);
822 greg 2.5
823 greg 2.8 const int bheight = (psample > 1) ? int(4*psample+.99) : 8;
824     const int vstep = bheight >> (psample > 1);
825 greg 2.1
826     NewBar(bheight); // render frame if we can
827 greg 2.5 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 greg 2.1 Cleanup();
831     return RDTnone;
832     }
833     NewBar(); // clean up and return
834 greg 2.5 if (pdfp[0] != stdout)
835     fclose(pdfp[0]);
836     if (pdfp[1]) {
837 greg 2.1 if (dfname[0] == '!') {
838 greg 2.5 int status = pclose(pdfp[1]);
839 greg 2.1 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 greg 2.5 fclose(pdfp[1]);
847 greg 2.1 }
848     return dt;
849     }
850    
851     // passed struct for header line callback
852 greg 2.5 static struct HeaderInfo {
853 greg 2.1 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 greg 2.5 } hinfo; // XXX single copy to hold custom primitives
874 greg 2.1
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 greg 2.5 // Reopen output file(s), leaving pointers at end of (each) header
916 greg 2.1 RenderDataType
917 greg 2.5 RpictSimulManager::ReopenOutput(FILE *pdfp[2], const char *pfname, const char *dfname)
918 greg 2.1 {
919 greg 2.5 extern const char HDRSTR[];
920    
921     if (!pfname || pfname[0] == '!') {
922     pdfp[0] = pdfp[1] = NULL;
923 greg 2.1 return RDTnone;
924 greg 2.5 }
925 greg 2.1 RenderDataType dt = RDTnone;
926 greg 2.5 pdfp[1] = NULL;
927     pdfp[0] = fopen(pfname, "r+");
928     if (!pdfp[0]) {
929 greg 2.1 sprintf(errmsg, "cannot reopen output picture '%s'", pfname);
930     error(SYSTEM, errmsg);
931     return RDTnone;
932     }
933 greg 2.5 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 greg 2.1 return RDTnone;
938     }
939     if (!hinfo.gotview) {
940     sprintf(errmsg, "missing view for '%s'", pfname);
941     error(USER, errmsg);
942 greg 2.5 fclose(pdfp[0]);
943     pdfp[0] = NULL;
944 greg 2.1 return RDTnone;
945     }
946     if (hinfo.ncomp < 3) {
947     sprintf(errmsg, "bad # components (%d) in '%s'", hinfo.ncomp, pfname);
948     error(USER, errmsg);
949 greg 2.5 fclose(pdfp[0]);
950     pdfp[0] = NULL;
951 greg 2.1 return RDTnone;
952     }
953 greg 2.5 // set rendering/output space
954 greg 2.1 if (!strcmp(hinfo.fmt, COLRFMT)) {
955 greg 2.5 prims = hinfo.prims; // XXX static array
956 greg 2.1 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 greg 2.5 fclose(pdfp[0]);
972     pdfp[0] = NULL;
973 greg 2.1 return RDTnone;
974     }
975 greg 2.5 NCSAMP = hinfo.ncomp; // overrides global setting
976 greg 2.1 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 greg 2.5 fclose(pdfp[0]);
983     pdfp[0] = NULL;
984 greg 2.1 return RDTnone;
985     }
986     if (hinfo.ncomp == 3) {
987 greg 2.5 prims = hinfo.prims; // custom primaries?
988 greg 2.1 int n = 8*hinfo.gotprims;
989     while (n--)
990     if (!FABSEQ(hinfo.prims[0][n], stdprims[0][n]))
991     break;
992 greg 2.5 if (n < 0) // standard primaries?
993 greg 2.1 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 greg 2.5 } 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 greg 2.7 if (getbinary(buf, 1, len+1, pdfp[1]) < len+1) {
1038 greg 2.5 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 greg 2.7 rewind(pdfp[1]);
1048 greg 2.5 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 greg 2.1 bytesPer = sizeof(float)*hinfo.ncomp;
1098 greg 2.5 break;
1099     default:
1100 greg 2.1 sprintf(errmsg, "unknown format (%s) for '%s'", hinfo.fmt, pfname);
1101     error(USER, errmsg);
1102 greg 2.5 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 greg 2.1 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 greg 2.5 error(CONSISTENCY, "unexpected resolution change in ResumeFrame()");
1121     fclose(pdfp[0]);
1122     if (pdfp[1]) fclose(pdfp[1]);
1123 greg 2.1 return RDTnone;
1124     }
1125 greg 2.5 long dataStart = ftell(pdfp[0]); // picture starting point
1126 greg 2.1 if (dataStart < 0) {
1127     sprintf(errmsg, "cannot seek on '%s'", pfname);
1128     error(SYSTEM, errmsg);
1129 greg 2.5 fclose(pdfp[0]);
1130     if (pdfp[1]) fclose(pdfp[1]);
1131 greg 2.1 return RDTnone;
1132     }
1133     long doneScans = 0;
1134     if (bytesPer) { // fixed-width records?
1135 greg 2.5 fseek(pdfp[0], 0, SEEK_END);
1136     long dataEnd = ftell(pdfp[0]);
1137 greg 2.1 doneScans = (dataEnd - dataStart)/(bytesPer*GetWidth());
1138     if (dataEnd-dataStart > bytesPer*GetWidth()*doneScans)
1139 greg 2.5 fseek(pdfp[0], dataStart + bytesPer*GetWidth()*doneScans, SEEK_SET);
1140 greg 2.1 } else { // else get compressed scanlines
1141     COLR * scan = (COLR *)tempbuffer(sizeof(COLR)*GetWidth());
1142 greg 2.5 while (freadcolrs(scan, GetWidth(), pdfp[0]) >= 0)
1143 greg 2.1 ++doneScans;
1144 greg 2.5 if (!feof(pdfp[0])) {
1145 greg 2.1 sprintf(errmsg, "error reading compressed scanline from '%s'", pfname);
1146     error(USER, errmsg);
1147 greg 2.5 fclose(pdfp[0]);
1148     if (pdfp[1]) fclose(pdfp[1]);
1149 greg 2.1 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 greg 2.5 fclose(pdfp[0]);
1156     if (pdfp[1]) fclose(pdfp[1]);
1157 greg 2.1 return dt;
1158     }
1159     if (!doneScans) {
1160     sprintf(errmsg, "restarting empty frame '%s'", pfname);
1161     error(WARNING, errmsg);
1162     }
1163 greg 2.5 long toSkip = 0;
1164 greg 2.6 switch (RDTdepthT(dt)) { // append depth file, too?
1165 greg 2.5 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 greg 2.1 return RDTnone;
1175     }
1176 greg 2.5 toSkip = 2L*GetWidth()*doneScans;
1177     break;
1178     default:;
1179 greg 2.6 } // fseek() needed for output
1180     if (pdfp[1] && fseek(pdfp[1], toSkip, SEEK_CUR) < 0) {
1181 greg 2.5 sprintf(errmsg, "cannot seek on depth file '%s'", dfname);
1182     error(SYSTEM, errmsg);
1183     fclose(pdfp[0]); fclose(pdfp[1]);
1184     return RDTnone;
1185 greg 2.1 }
1186 greg 2.8 int bheight = (psample > 1) ? int(4*psample+.99) : 8;
1187 greg 2.1 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 greg 2.5 if (!RenderBelow(GetHeight()-doneScans, vstep, pdfp[0], dt, pdfp[1])) {
1194     fclose(pdfp[0]);
1195     if (pdfp[1]) fclose(pdfp[1]);
1196 greg 2.1 Cleanup();
1197     return RDTnone;
1198     }
1199     NewBar(); // close up and return success
1200 greg 2.5 fclose(pdfp[0]);
1201     if (pdfp[1]) fclose(pdfp[1]);
1202 greg 2.1 return dt;
1203     }