izoom.c
上传用户:xk288cn
上传日期:2007-05-28
资源大小:4876k
文件大小:16k
源码类别:

GIS编程

开发平台:

Visual C++

  1. /**
  2.  ** izoom- 
  3.  ** Magnify or minify a picture with or without filtering.  The 
  4.  ** filtered method is one pass, uses 2-d convolution, and is optimized 
  5.  ** by integer arithmetic and precomputation of filter coeffs.
  6.  **
  7.  **      Paul Haeberli - 1988
  8.  **/
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include <math.h>
  13. #include "izoom.h"
  14. #ifdef _WIN32
  15. #pragma warning (disable:4244)          /* disable bogus conversion warnings */
  16. #endif
  17. typedef struct filtinteg {
  18.   float rad, min, max;
  19.   float *tab;
  20. } filtinteg;
  21. float flerp(float f0, float f1, float p);
  22. #define GRIDTOFLOAT(pos,n) (((pos)+0.5)/(n))
  23. #define FLOATTOGRID(pos,n) ((pos)*(n))
  24. #define SHIFT  12
  25. #define ONE  (1<<SHIFT)
  26. #define EPSILON 0.0001
  27. #define FILTERRAD (blurfactor*shape->rad)
  28. #define FILTTABSIZE 250
  29. static void makexmap(short *abuf, short *xmap[], int anx, int bnx);
  30. static void setintrow(int *buf, int val, int n);
  31. static void xscalebuf(short *xmap[], short *bbuf, int bnx);
  32. static void addrow(int *iptr, short *sptr, int w, int n);
  33. static void divrow(int *iptr, short *sptr, int tot, int n);
  34. static FILTER *makefilt(short *abuf, int anx, int bnx, int *maxn);
  35. static void freefilt(FILTER * filt, int n);
  36. static void applyxfilt(short *bbuf, FILTER * xfilt, int bnx);
  37. float filterinteg(float bmin, float bmax, float blurf);
  38. static void mitchellinit(float b, float c);
  39. static void clamprow(short *iptr, short *optr, int n);
  40. float filt_box(float x);
  41. float filt_triangle(float x);
  42. float filt_quadratic(float x);
  43. float filt_mitchell(float x);
  44. float filt_gaussian(float x);
  45. static int (*xfiltfunc) (short *, int);
  46. static float blurfactor;
  47. int izoomdebug;
  48. static filtinteg *shapeBOX;
  49. static filtinteg *shapeTRIANGLE;
  50. static filtinteg *shapeQUADRATIC;
  51. static filtinteg *shapeMITCHELL;
  52. static filtinteg *shapeGAUSSIAN;
  53. static filtinteg *shape;
  54. static filtinteg *
  55. integrate(float (*filtfunc) (float), float rad)
  56. {
  57.   int i;
  58.   float del, x, min, max;
  59.   double tot;
  60.   filtinteg *filt;
  61.   min = -rad;
  62.   max = rad;
  63.   del = 2 * rad;
  64.   tot = 0.0;
  65.   filt = (filtinteg *) malloc(sizeof(filtinteg));
  66.   filt->rad = rad;
  67.   filt->min = min;
  68.   filt->max = max;
  69.   filt->tab = (float *) malloc(FILTTABSIZE * sizeof(float));
  70.   for (i = 0; i < FILTTABSIZE; i++) {
  71.     x = min + (del * i / (FILTTABSIZE - 1.0));
  72.     tot = tot + filtfunc(x);
  73.     filt->tab[i] = tot;
  74.   }
  75.   for (i = 0; i < FILTTABSIZE; i++)
  76.     filt->tab[i] /= tot;
  77.   return filt;
  78. }
  79. float 
  80. filterinteg(float bmin, float bmax, float blurf)
  81. {
  82.   int i1, i2;
  83.   float f1, f2;
  84.   float *tab;
  85.   float mult;
  86.   bmin /= blurf;
  87.   bmax /= blurf;
  88.   tab = shape->tab;
  89.   mult = (FILTTABSIZE - 1.0) / (2.0 * shape->rad);
  90.   f1 = ((bmin - shape->min) * mult);
  91.   i1 = floor(f1);
  92.   f1 = f1 - i1;
  93.   if (i1 < 0)
  94.     f1 = 0.0;
  95.   else if (i1 >= (FILTTABSIZE - 1))
  96.     f1 = 1.0;
  97.   else
  98.     f1 = flerp(tab[i1], tab[i1 + 1], f1);
  99.   f2 = ((bmax - shape->min) * mult);
  100.   i2 = floor(f2);
  101.   f2 = f2 - i2;
  102.   if (i2 < 0)
  103.     f2 = 0.0;
  104.   else if (i2 >= (FILTTABSIZE - 1))
  105.     f2 = 1.0;
  106.   else
  107.     f2 = flerp(tab[i2], tab[i2 + 1], f2);
  108.   return f2 - f1;
  109. }
  110. void
  111. setfiltertype(int filttype)
  112. {
  113.   switch (filttype) {
  114.   case IMPULSE:
  115.     shape = 0;
  116.     break;
  117.   case BOX:
  118.     if (!shapeBOX)
  119.       shapeBOX = integrate(filt_box, 0.5);
  120.     shape = shapeBOX;
  121.     break;
  122.   case TRIANGLE:
  123.     if (!shapeTRIANGLE)
  124.       shapeTRIANGLE = integrate(filt_triangle, 1.0);
  125.     shape = shapeTRIANGLE;
  126.     break;
  127.   case QUADRATIC:
  128.     if (!shapeQUADRATIC)
  129.       shapeQUADRATIC = integrate(filt_quadratic, 1.5);
  130.     shape = shapeQUADRATIC;
  131.     break;
  132.   case MITCHELL:
  133.     if (!shapeMITCHELL)
  134.       shapeMITCHELL = integrate(filt_mitchell, 2.0);
  135.     shape = shapeMITCHELL;
  136.     break;
  137.   case GAUSSIAN:
  138.     if (!shapeGAUSSIAN)
  139.       shapeGAUSSIAN = integrate(filt_gaussian, 1.5);
  140.     shape = shapeGAUSSIAN;
  141.     break;
  142.   }
  143. }
  144. void
  145. copyimage(getfunc_t getfunc, getfunc_t putfunc, int nx, int ny)
  146. {
  147.   int y;
  148.   short *abuf;
  149.   abuf = (short *) malloc(nx * sizeof(short));
  150.   for (y = 0; y < ny; y++) {
  151.     getfunc(abuf, y);
  152.     putfunc(abuf, y);
  153.   }
  154.   free(abuf);
  155. }
  156. /* general zoom follows */
  157. zoom *
  158. newzoom(getfunc_t getfunc, int anx, int any, int bnx, int bny, int filttype, float blur)
  159. {
  160.   zoom *z;
  161.   int i;
  162.   setfiltertype(filttype);
  163.   z = (zoom *) malloc(sizeof(zoom));
  164.   z->getfunc = getfunc;
  165.   z->abuf = (short *) malloc(anx * sizeof(short));
  166.   z->bbuf = (short *) malloc(bnx * sizeof(short));
  167.   z->anx = anx;
  168.   z->any = any;
  169.   z->bnx = bnx;
  170.   z->bny = bny;
  171.   z->curay = -1;
  172.   z->y = 0;
  173.   z->type = filttype;
  174.   if (filttype == IMPULSE) {
  175.     if (z->anx != z->bnx) {
  176.       z->xmap = (short **) malloc(z->bnx * sizeof(short *));
  177.       makexmap(z->abuf, z->xmap, z->anx, z->bnx);
  178.     }
  179.   } else {
  180.     blurfactor = blur;
  181.     if (filttype == MITCHELL)
  182.       z->clamp = 1;
  183.     else
  184.       z->clamp = 0;
  185.     z->tbuf = (short *) malloc(bnx * sizeof(short));
  186.     z->xfilt = makefilt(z->abuf, anx, bnx, &z->nrows);
  187.     z->yfilt = makefilt(0, any, bny, &z->nrows);
  188.     z->filtrows = (short **) malloc(z->nrows * sizeof(short *));
  189.     for (i = 0; i < z->nrows; i++)
  190.       z->filtrows[i] = (short *) malloc(z->bnx * sizeof(short));
  191.     z->accrow = (int *) malloc(z->bnx * sizeof(int));
  192.     z->ay = 0;
  193.   }
  194.   return z;
  195. }
  196. void
  197. getzoomrow(zoom * z, short *buf, int y)
  198. {
  199.   float fy;
  200.   int ay;
  201.   FILTER *f;
  202.   int i, max;
  203.   short *row;
  204.   if (y == 0) {
  205.     z->curay = -1;
  206.     z->y = 0;
  207.     z->ay = 0;
  208.   }
  209.   if (z->type == IMPULSE) {
  210.     fy = GRIDTOFLOAT(z->y, z->bny);
  211.     ay = FLOATTOGRID(fy, z->any);
  212.     if (z->anx == z->bnx) {
  213.       if (z->curay != ay) {
  214.         z->getfunc(z->abuf, ay);
  215.         z->curay = ay;
  216.         if (xfiltfunc)
  217.           xfiltfunc(z->abuf, z->bnx);
  218.       }
  219.       memcpy(buf, z->abuf, z->bnx * sizeof(short));
  220.     } else {
  221.       if (z->curay != ay) {
  222.         z->getfunc(z->abuf, ay);
  223.         xscalebuf(z->xmap, z->bbuf, z->bnx);
  224.         z->curay = ay;
  225.         if (xfiltfunc)
  226.           xfiltfunc(z->bbuf, z->bnx);
  227.       }
  228.       memcpy(buf, z->bbuf, z->bnx * sizeof(short));
  229.     }
  230.   } else if (z->any == 1 && z->bny == 1) {
  231.     z->getfunc(z->abuf, z->ay++);
  232.     applyxfilt(z->filtrows[0], z->xfilt, z->bnx);
  233.     if (xfiltfunc)
  234.       xfiltfunc(z->filtrows[0], z->bnx);
  235.     if (z->clamp) {
  236.       clamprow(z->filtrows[0], z->tbuf, z->bnx);
  237.       memcpy(buf, z->tbuf, z->bnx * sizeof(short));
  238.     } else {
  239.       memcpy(buf, z->filtrows[0], z->bnx * sizeof(short));
  240.     }
  241.   } else {
  242.     f = z->yfilt + z->y;
  243.     max = (int) (sizeof(f->dat) / sizeof(short) + (f->n - 1));
  244.     while (z->ay <= max) {
  245.       z->getfunc(z->abuf, z->ay++);
  246.       row = z->filtrows[0];
  247.       for (i = 0; i < (z->nrows - 1); i++)
  248.         z->filtrows[i] = z->filtrows[i + 1];
  249.       z->filtrows[z->nrows - 1] = row;
  250.       applyxfilt(z->filtrows[z->nrows - 1], z->xfilt, z->bnx);
  251.       if (xfiltfunc)
  252.         xfiltfunc(z->filtrows[z->nrows - 1], z->bnx);
  253.     }
  254.     if (f->n == 1) {
  255.       if (z->clamp) {
  256.         clamprow(z->filtrows[z->nrows - 1], z->tbuf, z->bnx);
  257.         memcpy(buf, z->tbuf, z->bnx * sizeof(short));
  258.       } else {
  259.         memcpy(buf, z->filtrows[z->nrows - 1], z->bnx * sizeof(short));
  260.       }
  261.     } else {
  262.       setintrow(z->accrow, f->halftotw, z->bnx);
  263.       for (i = 0; i < f->n; i++)
  264.         addrow(z->accrow, z->filtrows[i + (z->nrows - 1) - (f->n - 1)],
  265.           f->w[i], z->bnx);
  266.       divrow(z->accrow, z->bbuf, f->totw, z->bnx);
  267.       if (z->clamp) {
  268.         clamprow(z->bbuf, z->tbuf, z->bnx);
  269.         memcpy(buf, z->tbuf, z->bnx * sizeof(short));
  270.       } else {
  271.         memcpy(buf, z->bbuf, z->bnx * sizeof(short));
  272.       }
  273.     }
  274.   }
  275.   z->y++;
  276. }
  277. static void
  278. setintrow(int *buf, int val, int n)
  279. {
  280.   while (n >= 8) {
  281.     buf[0] = val;
  282.     buf[1] = val;
  283.     buf[2] = val;
  284.     buf[3] = val;
  285.     buf[4] = val;
  286.     buf[5] = val;
  287.     buf[6] = val;
  288.     buf[7] = val;
  289.     buf += 8;
  290.     n -= 8;
  291.   }
  292.   while (n--)
  293.     *buf++ = val;
  294. }
  295. void
  296. freezoom(zoom * z)
  297. {
  298.   int i;
  299.   if (z->type == IMPULSE) {
  300.     if (z->anx != z->bnx)
  301.       free(z->xmap);
  302.   } else {
  303.     freefilt(z->xfilt, z->bnx);
  304.     freefilt(z->yfilt, z->bny);
  305.     free(z->tbuf);
  306.     for (i = 0; i < z->nrows; i++)
  307.       free(z->filtrows[i]);
  308.     free(z->filtrows);
  309.     free(z->accrow);
  310.   }
  311.   free(z->abuf);
  312.   free(z->bbuf);
  313.   free(z);
  314. }
  315. void
  316. filterzoom(getfunc_t getfunc, getfunc_t putfunc, int anx, int any, int bnx, int bny, int filttype, float blur)
  317. {
  318.   zoom *z;
  319.   int y;
  320.   short *buf;
  321.   buf = (short *) malloc(bnx * sizeof(short));
  322.   z = newzoom(getfunc, anx, any, bnx, bny, filttype, blur);
  323.   for (y = 0; y < bny; y++) {
  324.     getzoomrow(z, buf, y);
  325.     putfunc(buf, y);
  326.   }
  327.   freezoom(z);
  328.   free(buf);
  329. }
  330. /* impulse zoom utilities */
  331. static void
  332. makexmap(short *abuf, short *xmap[], int anx, int bnx)
  333. {
  334.   int x, ax;
  335.   float fx;
  336.   for (x = 0; x < bnx; x++) {
  337.     fx = GRIDTOFLOAT(x, bnx);
  338.     ax = FLOATTOGRID(fx, anx);
  339.     xmap[x] = abuf + ax;
  340.   }
  341. }
  342. static void
  343. xscalebuf(short *xmap[], short *bbuf, int bnx)
  344. {
  345.   while (bnx >= 8) {
  346.     bbuf[0] = *(xmap[0]);
  347.     bbuf[1] = *(xmap[1]);
  348.     bbuf[2] = *(xmap[2]);
  349.     bbuf[3] = *(xmap[3]);
  350.     bbuf[4] = *(xmap[4]);
  351.     bbuf[5] = *(xmap[5]);
  352.     bbuf[6] = *(xmap[6]);
  353.     bbuf[7] = *(xmap[7]);
  354.     bbuf += 8;
  355.     xmap += 8;
  356.     bnx -= 8;
  357.   }
  358.   while (bnx--)
  359.     *bbuf++ = *(*xmap++);
  360. }
  361. void
  362. zoomxfilt(int (*filtfunc) (short *, int))
  363. {
  364.   xfiltfunc = filtfunc;
  365. }
  366. /* filter zoom utilities */
  367. static void
  368. addrow(int *iptr, short *sptr, int w, int n)
  369. {
  370.   while (n >= 8) {
  371.     iptr[0] += (w * sptr[0]);
  372.     iptr[1] += (w * sptr[1]);
  373.     iptr[2] += (w * sptr[2]);
  374.     iptr[3] += (w * sptr[3]);
  375.     iptr[4] += (w * sptr[4]);
  376.     iptr[5] += (w * sptr[5]);
  377.     iptr[6] += (w * sptr[6]);
  378.     iptr[7] += (w * sptr[7]);
  379.     iptr += 8;
  380.     sptr += 8;
  381.     n -= 8;
  382.   }
  383.   while (n--)
  384.     *iptr++ += (w * *sptr++);
  385. }
  386. static void
  387. divrow(int *iptr, short *sptr, int tot, int n)
  388. {
  389.   while (n >= 8) {
  390.     sptr[0] = iptr[0] / tot;
  391.     sptr[1] = iptr[1] / tot;
  392.     sptr[2] = iptr[2] / tot;
  393.     sptr[3] = iptr[3] / tot;
  394.     sptr[4] = iptr[4] / tot;
  395.     sptr[5] = iptr[5] / tot;
  396.     sptr[6] = iptr[6] / tot;
  397.     sptr[7] = iptr[7] / tot;
  398.     sptr += 8;
  399.     iptr += 8;
  400.     n -= 8;
  401.   }
  402.   while (n--)
  403.     *sptr++ = (*iptr++) / tot;
  404. }
  405. static FILTER *
  406. makefilt(short *abuf, int anx, int bnx, int *maxn)
  407. {
  408.   FILTER *f, *filter;
  409.   int x, n;
  410.   float bmin, bmax, bcent, brad;
  411.   float fmin, fmax, acent, arad;
  412.   int amin, amax;
  413.   float coverscale;
  414.   if (izoomdebug)
  415.     fprintf(stderr, "makefiltn");
  416.   f = filter = (FILTER *) malloc(bnx * sizeof(FILTER));
  417.   *maxn = 0;
  418.   if (bnx < anx) {
  419.     coverscale = ((float) anx / bnx * ONE) / 2.0;
  420.     brad = FILTERRAD / bnx;
  421.     for (x = 0; x < bnx; x++) {
  422.       bcent = ((float) x + 0.5) / bnx;
  423.       amin = floor((bcent - brad) * anx + EPSILON);
  424.       amax = floor((bcent + brad) * anx - EPSILON);
  425.       if (amin < 0)
  426.         amin = 0;
  427.       if (amax >= anx)
  428.         amax = anx - 1;
  429.       f->n = 1 + amax - amin;
  430.       f->dat = abuf + amin;
  431.       f->w = (short *) malloc(f->n * sizeof(short));
  432.       f->totw = 0;
  433.       if (izoomdebug)
  434.         fprintf(stderr, "| ");
  435.       for (n = 0; n < f->n; n++) {
  436.         bmin = bnx * ((((float) amin + n) / anx) - bcent);
  437.         bmax = bnx * ((((float) amin + n + 1) / anx) - bcent);
  438.         f->w[n] = floor((coverscale * filterinteg(bmin, bmax, blurfactor)) + 0.5);
  439.         if (izoomdebug)
  440.           fprintf(stderr, "%d ", f->w[n]);
  441.         f->totw += f->w[n];
  442.       }
  443.       f->halftotw = f->totw / 2;
  444.       if (f->n > *maxn)
  445.         *maxn = f->n;
  446.       f++;
  447.     }
  448.   } else {
  449.     coverscale = ((float) bnx / anx * ONE) / 2.0;
  450.     arad = FILTERRAD / anx;
  451.     for (x = 0; x < bnx; x++) {
  452.       bmin = ((float) x) / bnx;
  453.       bmax = ((float) x + 1.0) / bnx;
  454.       amin = floor((bmin - arad) * anx + (0.5 + EPSILON));
  455.       amax = floor((bmax + arad) * anx - (0.5 + EPSILON));
  456.       if (amin < 0)
  457.         amin = 0;
  458.       if (amax >= anx)
  459.         amax = anx - 1;
  460.       f->n = 1 + amax - amin;
  461.       f->dat = abuf + amin;
  462.       f->w = (short *) malloc(f->n * sizeof(short));
  463.       f->totw = 0;
  464.       if (izoomdebug)
  465.         fprintf(stderr, "| ");
  466.       for (n = 0; n < f->n; n++) {
  467.         acent = (amin + n + 0.5) / anx;
  468.         fmin = anx * (bmin - acent);
  469.         fmax = anx * (bmax - acent);
  470.         f->w[n] = floor((coverscale * filterinteg(fmin, fmax, blurfactor)) + 0.5);
  471.         if (izoomdebug)
  472.           fprintf(stderr, "%d ", f->w[n]);
  473.         f->totw += f->w[n];
  474.       }
  475.       f->halftotw = f->totw / 2;
  476.       if (f->n > *maxn)
  477.         *maxn = f->n;
  478.       f++;
  479.     }
  480.   }
  481.   if (izoomdebug)
  482.     fprintf(stderr, "|n");
  483.   return filter;
  484. }
  485. static void
  486. freefilt(FILTER * filt, int n)
  487. {
  488.   FILTER *f;
  489.   f = filt;
  490.   while (n--) {
  491.     free(f->w);
  492.     f++;
  493.   }
  494.   free(filt);
  495. }
  496. static void
  497. applyxfilt(short *bbuf, FILTER * xfilt, int bnx)
  498. {
  499.   short *w;
  500.   short *dptr;
  501.   int n, val;
  502.   while (bnx--) {
  503.     if ((n = xfilt->n) == 1) {
  504.       *bbuf++ = *xfilt->dat;
  505.     } else {
  506.       w = xfilt->w;
  507.       dptr = xfilt->dat;
  508.       val = xfilt->halftotw;
  509.       n = xfilt->n;
  510.       while (n--)
  511.         val += *w++ * *dptr++;
  512.       *bbuf++ = val / xfilt->totw;
  513.     }
  514.     xfilt++;
  515.   }
  516. }
  517. /* filter shape functions follow */
  518. float 
  519. filt_box(float x)
  520. {
  521.   if (x < -0.5)
  522.     return 0.0;
  523.   if (x < 0.5)
  524.     return 1.0;
  525.   return 0.0;
  526. }
  527. float 
  528. filt_triangle(float x)
  529. {
  530.   if (x < -1.0)
  531.     return 0.0;
  532.   if (x < 0.0)
  533.     return 1.0 + x;
  534.   if (x < 1.0)
  535.     return 1.0 - x;
  536.   return 0.0;
  537. }
  538. float 
  539. filt_quadratic(float x)
  540. {
  541.   if (x < -1.5)
  542.     return 0.0;
  543.   if (x < -0.5)
  544.     return 0.5 * (x + 1.5) * (x + 1.5);
  545.   if (x < 0.5)
  546.     return 0.75 - (x * x);
  547.   if (x < 1.5)
  548.     return 0.5 * (x - 1.5) * (x - 1.5);
  549.   return 0.0;
  550. }
  551. static float p0, p2, p3, q0, q1, q2, q3;
  552. /* see Mitchell&Netravali, "Reconstruction Filters in Computer Graphics",
  553.    SIGGRAPH 88.  Mitchell code provided by Paul Heckbert.  */
  554. float 
  555. filt_mitchell(float x)
  556. {                       /* Mitchell & Netravali's two-param cubic */
  557.   static int mitfirsted;
  558.   if (!mitfirsted) {
  559.     mitchellinit(1.0f / 3.0f, 1.0f / 3.0f);
  560.     mitfirsted = 1;
  561.   }
  562.   if (x < -2.0)
  563.     return 0.0;
  564.   if (x < -1.0)
  565.     return (q0 - x * (q1 - x * (q2 - x * q3)));
  566.   if (x < 0.0)
  567.     return (p0 + x * x * (p2 - x * p3));
  568.   if (x < 1.0)
  569.     return (p0 + x * x * (p2 + x * p3));
  570.   if (x < 2.0)
  571.     return (q0 + x * (q1 + x * (q2 + x * q3)));
  572.   return 0.0;
  573. }
  574. static void
  575. mitchellinit(float b, float c)
  576. {
  577.   p0 = (6.0 - 2.0 * b) / 6.0;
  578.   p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0;
  579.   p3 = (12.0 - 9.0 * b - 6.0 * c) / 6.0;
  580.   q0 = (8.0 * b + 24.0 * c) / 6.0;
  581.   q1 = (-12.0 * b - 48.0 * c) / 6.0;
  582.   q2 = (6.0 * b + 30.0 * c) / 6.0;
  583.   q3 = (-b - 6.0 * c) / 6.0;
  584. }
  585. #define NARROWNESS 1.5
  586. float 
  587. filt_gaussian(float x)
  588. {
  589.   x = x * NARROWNESS;
  590.   return (1.0 / exp(x * x) - 1.0 / exp(1.5 * NARROWNESS * 1.5 * NARROWNESS));
  591. }
  592. float 
  593. flerp(float f0, float f1, float p)
  594. {
  595.   return ((f0 * (1.0 - p)) + (f1 * p));
  596. }
  597. #define DOCLAMP(iptr,optr) *(optr) = ((*(iptr)<0) ? 0 : (*(iptr)>255) ? 255 : *(iptr))
  598. static void
  599. clamprow(short *iptr, short *optr, int n)
  600. {
  601.   while (n >= 8) {
  602.     DOCLAMP(iptr + 0, optr + 0);
  603.     DOCLAMP(iptr + 1, optr + 1);
  604.     DOCLAMP(iptr + 2, optr + 2);
  605.     DOCLAMP(iptr + 3, optr + 3);
  606.     DOCLAMP(iptr + 4, optr + 4);
  607.     DOCLAMP(iptr + 5, optr + 5);
  608.     DOCLAMP(iptr + 6, optr + 6);
  609.     DOCLAMP(iptr + 7, optr + 7);
  610.     iptr += 8;
  611.     optr += 8;
  612.     n -= 8;
  613.   }
  614.   while (n--) {
  615.     DOCLAMP(iptr, optr);
  616.     iptr++;
  617.     optr++;
  618.   }
  619. }