ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrep.c
(Generate patch)

Comparing ray/src/cv/bsdfrep.c (file contents):
Revision 2.11 by greg, Thu Nov 22 06:07:17 2012 UTC vs.
Revision 2.35 by greg, Tue Sep 7 20:13:13 2021 UTC

# Line 9 | Line 9 | static const char RCSid[] = "$Id$";
9  
10   #define _USE_MATH_DEFINES
11   #include <stdlib.h>
12 #include <string.h>
12   #include <math.h>
13   #include "rtio.h"
14   #include "resolu.h"
15   #include "bsdfrep.h"
16 + #include "random.h"
17 +                                /* name and manufacturer if known */
18 + char                    bsdf_name[256];
19 + char                    bsdf_manuf[256];
20                                  /* active grid resolution */
21   int                     grid_res = GRIDRES;
22  
# Line 26 | Line 29 | int                    single_plane_incident = -1;
29   int                     input_orient = 0;
30   int                     output_orient = 0;
31  
32 +                                /* represented color space */
33 + RBColor                 rbf_colorimetry = RBCunknown;
34 +
35 + const char              *RBCident[] = {
36 +                                "CIE-Y", "CIE-XYZ", "Spectral", "Unknown"
37 +                        };
38 +
39 +                                /* BSDF histogram */
40 + unsigned long           bsdf_hist[HISTLEN];
41 +
42 +                                /* BSDF value for boundary regions */
43 + double                  bsdf_min = 0;
44 + double                  bsdf_spec_val = 0;
45 + double                  bsdf_spec_rad = 0;
46 +
47                                  /* processed incident DSF measurements */
48   RBFNODE                 *dsf_list = NULL;
49  
# Line 35 | Line 53 | MIGRATION              *mig_list = NULL;
53                                  /* current input direction */
54   double                  theta_in_deg, phi_in_deg;
55  
56 +                                /* header line sharing callback */
57 + int                     (*sir_headshare)(char *s) = NULL;
58 +
59   /* Register new input direction */
60   int
61   new_input_direction(double new_theta, double new_phi)
62   {
42        if (!input_orient)              /* check input orientation */
43                input_orient = 1 - 2*(new_theta > 90.);
44        else if (input_orient > 0 ^ new_theta < 90.) {
45                fprintf(stderr,
46                "%s: Cannot handle input angles on both sides of surface\n",
47                                progname);
48                return(0);
49        }
63                                          /* normalize angle ranges */
64          while (new_theta < -180.)
65                  new_theta += 360.;
# Line 56 | Line 69 | new_input_direction(double new_theta, double new_phi)
69                  new_theta = -new_theta;
70                  new_phi += 180.;
71          }
59        if ((theta_in_deg = new_theta) < 1.0)
60                return(1);              /* don't rely on phi near normal */
72          while (new_phi < 0)
73                  new_phi += 360.;
74          while (new_phi >= 360.)
75                  new_phi -= 360.;
76 +                                        /* check input orientation */
77 +        if (!input_orient)
78 +                input_orient = 1 - 2*(new_theta > 90.);
79 +        else if (input_orient > 0 ^ new_theta < 90.) {
80 +                fprintf(stderr,
81 +                "%s: Cannot handle input angles on both sides of surface\n",
82 +                                progname);
83 +                return(0);
84 +        }
85 +        if ((theta_in_deg = new_theta) < 1.0)
86 +                return(1);              /* don't rely on phi near normal */
87          if (single_plane_incident > 0)  /* check input coverage */
88                  single_plane_incident = (round(new_phi) == round(phi_in_deg));
89          else if (single_plane_incident < 0)
# Line 189 | Line 211 | rotate_rbf(RBFNODE *rbf, const FVECT invec)
211          int                     pos[2];
212          int                     n;
213  
214 <        for (n = ((-.01 > phi) | (phi > .01))*rbf->nrbf; n-- > 0; ) {
214 >        for (n = (cos(phi) < 1.-FTINY)*rbf->nrbf; n-- > 0; ) {
215                  ovec_from_pos(outvec, rbf->rbfa[n].gx, rbf->rbfa[n].gy);
216                  spinvector(outvec, outvec, vnorm, phi);
217                  pos_from_vec(pos, outvec);
# Line 199 | Line 221 | rotate_rbf(RBFNODE *rbf, const FVECT invec)
221          VCOPY(rbf->invec, invec);
222   }
223  
202 /* Compute volume associated with Gaussian lobe */
203 double
204 rbf_volume(const RBFVAL *rbfp)
205 {
206        double  rad = R2ANG(rbfp->crad);
207
208        return((2.*M_PI) * rbfp->peak * rad*rad);
209 }
210
224   /* Compute outgoing vector from grid position */
225   void
226   ovec_from_pos(FVECT vec, int xpos, int ypos)
# Line 237 | Line 250 | pos_from_vec(int pos[2], const FVECT vec)
250          pos[1] = (int)(sq[1]*grid_res);
251   }
252  
253 < /* Evaluate RBF for DSF at the given normalized outgoing direction */
253 > /* Compute volume associated with Gaussian lobe */
254   double
255 < eval_rbfrep(const RBFNODE *rp, const FVECT outvec)
255 > rbf_volume(const RBFVAL *rbfp)
256   {
257 +        double  rad = R2ANG(rbfp->crad);
258 +        FVECT   odir;
259 +        double  elev, integ;
260 +                                /* infinite integral approximation */
261 +        integ = (2.*M_PI) * rbfp->peak * rad*rad;
262 +                                /* check if we're near horizon */
263 +        ovec_from_pos(odir, rbfp->gx, rbfp->gy);
264 +        elev = output_orient*odir[2];
265 +                                /* apply cut-off correction if > 1% */
266 +        if (elev < 2.8*rad) {
267 +                /* elev = asin(elev);   /* this is so crude, anyway... */
268 +                integ *= 1. - .5*exp(-.5*elev*elev/(rad*rad));
269 +        }
270 +        return(integ);
271 + }
272 +
273 + /* Evaluate BSDF at the given normalized outgoing direction in color */
274 + SDError
275 + eval_rbfcol(SDValue *sv, const RBFNODE *rp, const FVECT outvec)
276 + {
277 +        const double    rfact2 = (38./M_PI/M_PI)*(grid_res*grid_res);
278 +        int             pos[2];
279          double          res = 0;
280 +        double          usum = 0, vsum = 0;
281          const RBFVAL    *rbfp;
282          FVECT           odir;
283 <        double          sig2;
283 >        double          rad2;
284          int             n;
285 <
286 <        if (rp == NULL)
287 <                return(.0);
285 >                                /* assign default value */
286 >        sv->spec = c_dfcolor;
287 >        sv->cieY = bsdf_min;
288 >                                /* check for wrong side */
289 >        if (outvec[2] > 0 ^ output_orient > 0) {
290 >                strcpy(SDerrorDetail, "Wrong-side scattering query");
291 >                return(SDEargument);
292 >        }
293 >        if (rp == NULL)         /* return minimum if no information avail. */
294 >                return(SDEnone);
295 >                                /* optimization for fast lobe culling */
296 >        pos_from_vec(pos, outvec);
297 >                                /* sum radial basis function */
298          rbfp = rp->rbfa;
299          for (n = rp->nrbf; n--; rbfp++) {
300 +                int     d2 = (pos[0]-rbfp->gx)*(pos[0]-rbfp->gx) +
301 +                                (pos[1]-rbfp->gy)*(pos[1]-rbfp->gy);
302 +                double  val;
303 +                rad2 = R2ANG(rbfp->crad);
304 +                rad2 *= rad2;
305 +                if (d2 > rad2*rfact2)
306 +                        continue;
307                  ovec_from_pos(odir, rbfp->gx, rbfp->gy);
308 <                sig2 = R2ANG(rbfp->crad);
309 <                sig2 = (DOT(odir,outvec) - 1.) / (sig2*sig2);
310 <                if (sig2 > -19.)
311 <                        res += rbfp->peak * exp(sig2);
308 >                val = rbfp->peak * exp((DOT(odir,outvec) - 1.) / rad2);
309 >                if (rbf_colorimetry == RBCtristimulus) {
310 >                        usum += val * (rbfp->chroma & 0xff);
311 >                        vsum += val * (rbfp->chroma>>8 & 0xff);
312 >                }
313 >                res += val;
314          }
315 <        return(res);
315 >        sv->cieY = res / COSF(outvec[2]);
316 >        if (sv->cieY < bsdf_min) {      /* never return less than bsdf_min */
317 >                sv->cieY = bsdf_min;
318 >        } else if (rbf_colorimetry == RBCtristimulus) {
319 >                C_CHROMA        cres = (int)(usum/res + frandom());
320 >                cres |= (int)(vsum/res + frandom()) << 8;
321 >                c_decodeChroma(&sv->spec, cres);
322 >        }
323 >        return(SDEnone);
324   }
325  
326 + /* Evaluate BSDF at the given normalized outgoing direction in Y */
327 + double
328 + eval_rbfrep(const RBFNODE *rp, const FVECT outvec)
329 + {
330 +        SDValue sv;
331 +
332 +        if (eval_rbfcol(&sv, rp, outvec) == SDEnone)
333 +                return(sv.cieY);
334 +
335 +        return(0.0);
336 + }
337 +
338   /* Insert a new directional scattering function in our global list */
339   int
340   insert_dsf(RBFNODE *newrbf)
# Line 270 | Line 345 | insert_dsf(RBFNODE *newrbf)
345          for (rbf = dsf_list; rbf != NULL; rbf = rbf->next)
346                  if (DOT(rbf->invec, newrbf->invec) >= 1.-FTINY) {
347                          fprintf(stderr,
348 <                                "%s: Duplicate incident measurement (ignored)\n",
349 <                                        progname);
348 >                "%s: Duplicate incident measurement ignored at (%.1f,%.1f)\n",
349 >                                        progname, get_theta180(newrbf->invec),
350 >                                        get_phi360(newrbf->invec));
351                          free(newrbf);
352                          return(-1);
353                  }
# Line 359 | Line 435 | get_triangles(RBFNODE *rbfv[2], const MIGRATION *mig)
435          return((rbfv[0] != NULL) + (rbfv[1] != NULL));
436   }
437  
438 + /* Return single-lobe specular RBF for the given incident direction */
439 + RBFNODE *
440 + def_rbf_spec(const FVECT invec)
441 + {
442 +        RBFNODE         *rbf;
443 +        FVECT           ovec;
444 +        int             pos[2];
445 +
446 +        if (input_orient > 0 ^ invec[2] > 0)    /* wrong side? */
447 +                return(NULL);
448 +        if ((bsdf_spec_val <= bsdf_min) | (bsdf_spec_rad <= 0))
449 +                return(NULL);                   /* nothing set */
450 +        rbf = (RBFNODE *)malloc(sizeof(RBFNODE));
451 +        if (rbf == NULL)
452 +                return(NULL);
453 +        ovec[0] = -invec[0];
454 +        ovec[1] = -invec[1];
455 +        ovec[2] = invec[2]*(2*(input_orient==output_orient) - 1);
456 +        pos_from_vec(pos, ovec);
457 +        rbf->ord = 0;
458 +        rbf->next = NULL;
459 +        rbf->ejl = NULL;
460 +        VCOPY(rbf->invec, invec);
461 +        rbf->nrbf = 1;
462 +        rbf->rbfa[0].peak = bsdf_spec_val * COSF(ovec[2]);
463 +        rbf->rbfa[0].chroma = c_dfchroma;
464 +        rbf->rbfa[0].crad = ANG2R(bsdf_spec_rad);
465 +        rbf->rbfa[0].gx = pos[0];
466 +        rbf->rbfa[0].gy = pos[1];
467 +        rbf->vtotal = rbf_volume(rbf->rbfa);
468 +        return(rbf);
469 + }
470 +
471 + /* Advect and allocate new RBF along edge (internal call) */
472 + RBFNODE *
473 + e_advect_rbf(const MIGRATION *mig, const FVECT invec, int lobe_lim)
474 + {
475 +        double          cthresh = FTINY;
476 +        RBFNODE         *rbf;
477 +        int             n, i, j;
478 +        double          t, full_dist;
479 +                                                /* get relative position */
480 +        t = Acos(DOT(invec, mig->rbfv[0]->invec));
481 +        if (t <= .001) {                        /* near first DSF */
482 +                n = sizeof(RBFNODE) + sizeof(RBFVAL)*(mig->rbfv[0]->nrbf-1);
483 +                rbf = (RBFNODE *)malloc(n);
484 +                if (rbf == NULL)
485 +                        goto memerr;
486 +                memcpy(rbf, mig->rbfv[0], n);   /* just duplicate */
487 +                rbf->next = NULL; rbf->ejl = NULL;
488 +                return(rbf);
489 +        }
490 +        full_dist = acos(DOT(mig->rbfv[0]->invec, mig->rbfv[1]->invec));
491 +        if (t >= full_dist-.001) {              /* near second DSF */
492 +                n = sizeof(RBFNODE) + sizeof(RBFVAL)*(mig->rbfv[1]->nrbf-1);
493 +                rbf = (RBFNODE *)malloc(n);
494 +                if (rbf == NULL)
495 +                        goto memerr;
496 +                memcpy(rbf, mig->rbfv[1], n);   /* just duplicate */
497 +                rbf->next = NULL; rbf->ejl = NULL;
498 +                return(rbf);
499 +        }
500 +        t /= full_dist;
501 + tryagain:
502 +        n = 0;                                  /* count migrating particles */
503 +        for (i = 0; i < mtx_nrows(mig); i++)
504 +            for (j = 0; j < mtx_ncols(mig); j++)
505 +                n += (mtx_coef(mig,i,j) > cthresh);
506 +                                                /* are we over our limit? */
507 +        if ((lobe_lim > 0) & (n > lobe_lim)) {
508 +                cthresh = cthresh*2. + 10.*FTINY;
509 +                goto tryagain;
510 +        }
511 + #ifdef DEBUG
512 +        fprintf(stderr, "Input RBFs have %d, %d nodes -> output has %d\n",
513 +                        mig->rbfv[0]->nrbf, mig->rbfv[1]->nrbf, n);
514 + #endif
515 +        rbf = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(n-1));
516 +        if (rbf == NULL)
517 +                goto memerr;
518 +        rbf->next = NULL; rbf->ejl = NULL;
519 +        VCOPY(rbf->invec, invec);
520 +        rbf->nrbf = n;
521 +        rbf->vtotal = 1.-t + t*mig->rbfv[1]->vtotal/mig->rbfv[0]->vtotal;
522 +        n = 0;                                  /* advect RBF lobes */
523 +        for (i = 0; i < mtx_nrows(mig); i++) {
524 +            const RBFVAL        *rbf0i = &mig->rbfv[0]->rbfa[i];
525 +            const float         peak0 = rbf0i->peak;
526 +            const double        rad0 = R2ANG(rbf0i->crad);
527 +            C_COLOR             cc0;
528 +            FVECT               v0;
529 +            float               mv;
530 +            ovec_from_pos(v0, rbf0i->gx, rbf0i->gy);
531 +            c_decodeChroma(&cc0, rbf0i->chroma);
532 +            for (j = 0; j < mtx_ncols(mig); j++)
533 +                if ((mv = mtx_coef(mig,i,j)) > cthresh) {
534 +                        const RBFVAL    *rbf1j = &mig->rbfv[1]->rbfa[j];
535 +                        double          rad2;
536 +                        FVECT           v;
537 +                        int             pos[2];
538 +                        rad2 = R2ANG(rbf1j->crad);
539 +                        rad2 = rad0*rad0*(1.-t) + rad2*rad2*t;
540 +                        rbf->rbfa[n].peak = peak0 * mv * rbf->vtotal *
541 +                                                rad0*rad0/rad2;
542 +                        if (rbf_colorimetry == RBCtristimulus) {
543 +                                C_COLOR cres;
544 +                                c_decodeChroma(&cres, rbf1j->chroma);
545 +                                c_cmix(&cres, 1.-t, &cc0, t, &cres);
546 +                                rbf->rbfa[n].chroma = c_encodeChroma(&cres);
547 +                        } else
548 +                                rbf->rbfa[n].chroma = c_dfchroma;
549 +                        rbf->rbfa[n].crad = ANG2R(sqrt(rad2));
550 +                        ovec_from_pos(v, rbf1j->gx, rbf1j->gy);
551 +                        geodesic(v, v0, v, t, GEOD_REL);
552 +                        pos_from_vec(pos, v);
553 +                        rbf->rbfa[n].gx = pos[0];
554 +                        rbf->rbfa[n].gy = pos[1];
555 +                        ++n;
556 +                }
557 +        }
558 +        rbf->vtotal *= mig->rbfv[0]->vtotal;    /* turn ratio into actual */
559 +        return(rbf);
560 + memerr:
561 +        fprintf(stderr, "%s: Out of memory in e_advect_rbf()\n", progname);
562 +        exit(1);
563 +        return(NULL);   /* pro forma return */
564 + }
565 +
566   /* Clear our BSDF representation and free memory */
567   void
568   clear_bsdf_rep(void)
# Line 373 | Line 577 | clear_bsdf_rep(void)
577                  dsf_list = rbf->next;
578                  free(rbf);
579          }
580 +        bsdf_name[0] = '\0';
581 +        bsdf_manuf[0] = '\0';
582          inp_coverage = 0;
583          single_plane_incident = -1;
584          input_orient = output_orient = 0;
585 +        rbf_colorimetry = RBCunknown;
586          grid_res = GRIDRES;
587 +        memset(bsdf_hist, 0, sizeof(bsdf_hist));
588 +        bsdf_min = 0;
589 +        bsdf_spec_val = 0;
590 +        bsdf_spec_rad = 0;
591   }
592  
593   /* Write our BSDF mesh interpolant out to the given binary stream */
# Line 387 | Line 598 | save_bsdf_rep(FILE *ofp)
598          MIGRATION       *mig;
599          int             i, n;
600                                          /* finish header */
601 +        if (bsdf_name[0])
602 +                fprintf(ofp, "NAME=%s\n", bsdf_name);
603 +        if (bsdf_manuf[0])
604 +                fprintf(ofp, "MANUFACT=%s\n", bsdf_manuf);
605          fprintf(ofp, "SYMMETRY=%d\n", !single_plane_incident * inp_coverage);
606          fprintf(ofp, "IO_SIDES= %d %d\n", input_orient, output_orient);
607 +        fprintf(ofp, "COLORIMETRY=%s\n", RBCident[rbf_colorimetry]);
608          fprintf(ofp, "GRIDRES=%d\n", grid_res);
609 +        fprintf(ofp, "BSDFMIN=%g\n", bsdf_min);
610 +        if ((bsdf_spec_val > bsdf_min) & (bsdf_spec_rad > 0))
611 +                fprintf(ofp, "BSDFSPEC= %f %f\n", bsdf_spec_val, bsdf_spec_rad);
612          fputformat(BSDFREP_FMT, ofp);
613          fputc('\n', ofp);
614 +        putint(BSDFREP_MAGIC, 2, ofp);
615                                          /* write each DSF */
616          for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) {
617                  putint(rbf->ord, 4, ofp);
# Line 402 | Line 622 | save_bsdf_rep(FILE *ofp)
622                  putint(rbf->nrbf, 4, ofp);
623                  for (i = 0; i < rbf->nrbf; i++) {
624                          putflt(rbf->rbfa[i].peak, ofp);
625 +                        putint(rbf->rbfa[i].chroma, 2, ofp);
626                          putint(rbf->rbfa[i].crad, 2, ofp);
627 <                        putint(rbf->rbfa[i].gx, 1, ofp);
628 <                        putint(rbf->rbfa[i].gy, 1, ofp);
627 >                        putint(rbf->rbfa[i].gx, 2, ofp);
628 >                        putint(rbf->rbfa[i].gy, 2, ofp);
629                  }
630          }
631          putint(-1, 4, ofp);             /* terminator */
# Line 440 | Line 661 | save_bsdf_rep(FILE *ofp)
661   static int
662   headline(char *s, void *p)
663   {
664 <        char    fmt[32];
664 >        char    fmt[MAXFMTLEN];
665 >        int     i;
666  
667 +        if (isheadid(s))
668 +                return(0);
669 +        if (!strncmp(s, "NAME=", 5)) {
670 +                strcpy(bsdf_name, s+5);
671 +                bsdf_name[strlen(bsdf_name)-1] = '\0';
672 +                return(1);
673 +        }
674 +        if (!strncmp(s, "MANUFACT=", 9)) {
675 +                strcpy(bsdf_manuf, s+9);
676 +                bsdf_manuf[strlen(bsdf_manuf)-1] = '\0';
677 +                return(1);
678 +        }
679          if (!strncmp(s, "SYMMETRY=", 9)) {
680                  inp_coverage = atoi(s+9);
681                  single_plane_incident = !inp_coverage;
682 <                return(0);
682 >                return(1);
683          }
684          if (!strncmp(s, "IO_SIDES=", 9)) {
685                  sscanf(s+9, "%d %d", &input_orient, &output_orient);
686 <                return(0);
686 >                return(1);
687          }
688 +        if (!strncmp(s, "COLORIMETRY=", 12)) {
689 +                fmt[0] = '\0';
690 +                sscanf(s+12, "%s", fmt);
691 +                for (i = RBCunknown; i >= 0; i--)
692 +                        if (!strcmp(fmt, RBCident[i]))
693 +                                break;
694 +                if (i < 0)
695 +                        return(-1);
696 +                rbf_colorimetry = i;
697 +                return(1);
698 +        }
699          if (!strncmp(s, "GRIDRES=", 8)) {
700                  sscanf(s+8, "%d", &grid_res);
701 <                return(0);
701 >                return(1);
702          }
703 <        if (formatval(fmt, s) && strcmp(fmt, BSDFREP_FMT))
704 <                return(-1);
703 >        if (!strncmp(s, "BSDFMIN=", 8)) {
704 >                sscanf(s+8, "%lf", &bsdf_min);
705 >                return(1);
706 >        }
707 >        if (!strncmp(s, "BSDFSPEC=", 9)) {
708 >                sscanf(s+9, "%lf %lf", &bsdf_spec_val, &bsdf_spec_rad);
709 >                return(1);
710 >        }
711 >        if (formatval(fmt, s))
712 >                return (strcmp(fmt, BSDFREP_FMT) ? -1 : 0);
713 >        if (sir_headshare != NULL)
714 >                return ((*sir_headshare)(s));
715          return(0);
716   }
717  
# Line 471 | Line 726 | load_bsdf_rep(FILE *ifp)
726          clear_bsdf_rep();
727          if (ifp == NULL)
728                  return(0);
729 <        if (getheader(ifp, headline, NULL) < 0 || single_plane_incident < 0 |
730 <                        !input_orient | !output_orient) {
729 >        if (getheader(ifp, headline, NULL) < 0 || (single_plane_incident < 0) |
730 >                        !input_orient | !output_orient |
731 >                        (grid_res < 16) | (grid_res > 0xffff)) {
732                  fprintf(stderr, "%s: missing/bad format for BSDF interpolant\n",
733                                  progname);
734                  return(0);
735          }
736 <        rbfh.next = NULL;               /* read each DSF */
737 <        rbfh.ejl = NULL;
736 >        if (getint(2, ifp) != BSDFREP_MAGIC) {
737 >                fprintf(stderr, "%s: bad magic number for BSDF interpolant\n",
738 >                                progname);
739 >                return(0);
740 >        }
741 >        memset(&rbfh, 0, sizeof(rbfh)); /* read each DSF */
742          while ((rbfh.ord = getint(4, ifp)) >= 0) {
743                  RBFNODE         *newrbf;
744  
# Line 495 | Line 755 | load_bsdf_rep(FILE *ifp)
755                                          sizeof(RBFVAL)*(rbfh.nrbf-1));
756                  if (newrbf == NULL)
757                          goto memerr;
758 <                memcpy(newrbf, &rbfh, sizeof(RBFNODE)-sizeof(RBFVAL));
758 >                *newrbf = rbfh;
759                  for (i = 0; i < rbfh.nrbf; i++) {
760                          newrbf->rbfa[i].peak = getflt(ifp);
761 +                        newrbf->rbfa[i].chroma = getint(2, ifp) & 0xffff;
762                          newrbf->rbfa[i].crad = getint(2, ifp) & 0xffff;
763 <                        newrbf->rbfa[i].gx = getint(1, ifp) & 0xff;
764 <                        newrbf->rbfa[i].gy = getint(1, ifp) & 0xff;
763 >                        newrbf->rbfa[i].gx = getint(2, ifp) & 0xffff;
764 >                        newrbf->rbfa[i].gy = getint(2, ifp) & 0xffff;
765                  }
766                  if (feof(ifp))
767                          goto badEOF;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines