ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/RpictSimulManager.cpp
Revision: 2.11
Committed: Tue Aug 27 18:50:01 2024 UTC (8 months, 3 weeks ago) by greg
Branch: MAIN
Changes since 2.10: +5 -2 lines
Log Message:
fix(rxpict): Allow out-of-visible rendering

File Contents

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