ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/RpictSimulManager.cpp
Revision: 2.13
Committed: Fri Jan 10 19:09:12 2025 UTC (3 months, 2 weeks ago) by greg
Branch: MAIN
Changes since 2.12: +5 -4 lines
Log Message:
perf(rxpict): Improved error-handling

File Contents

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