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