Path: utzoo!attcan!uunet!tut.cis.ohio-state.edu!ucbvax!pasteur!miro.Berkeley.EDU!ph From: ph@miro.Berkeley.EDU (Paul Heckbert) Newsgroups: comp.graphics Subject: source code for filtered image zoom, part 1/3 Message-ID: <16203@pasteur.Berkeley.EDU> Date: 11 Aug 89 02:43:08 GMT Sender: news@pasteur.Berkeley.EDU Reply-To: ph@miro.Berkeley.EDU (Paul Heckbert) Organization: University of California at Berkeley Lines: 1192 # to unpack, cut here and run the following shell archive through sh # contents: README Makefile zoom/zoom.1 zoom/zoom.c zoom/Makefile # mkdir libsys libpic zoom echo extracting README sed 's/^X//' <<'EOF13085' >README XThis is C source code for a program to do filtered zoom (resize) of 8 Xand 24-bit raster images. The program supports arbitrary floating Xpoint scale and translation with independent control of x and y scales, Xupsampling (interpolation) or downsampling (decimation), 1-channel Xblack and white or 3-channel color pictures, overlapping source and Xdestination windows within the same frame buffer, and separable Xfiltering with a large choice of filters. It can also be used for Xin-place separable filtering of images. X XThis directory contains: X libsys: X one header file X libpic: X Subroutine package for device-independent picture I/O. X Modify this to add picture I/O capability for your own frame buffers X and file formats to the pic package and thereby to the zoom program. X Read the man page pic.3 to learn how. X zoom: X Source for the main program zoom, and a set of useful filter routines. X This code is graphics device independent, so you shouldn't have X to modify it. X XRun "make install" and it should create subdirectories include, lib, and bin, Xleaving the zoom program in bin/zoom. Note that you shouldn't expect this Xto compile as is, as it's currently configured to link in the XSilicon Graphics iris library. Again, see the notes under X"CREATING A DEVICE LIBRARY" in libpic/pic.3 X XPlease email me any enhancements, such as new device libraries for libpic; XI'll collect and redistribute. (I already have libpic device libraries for Xmemory frame buffers, various MIT, NYIT, Pixar, Xerox PARC, and UC Berkeley Xpicture file formats, Sunraster format, Utah raster toolkit RLE format, Xa fairly standard dump format, and hp98721 and iris frame buffers, Xwhich I may distribute in the future.) X XPaul Heckbert 10 Aug 1989 XUC Berkeley Xph@miro.berkeley.edu EOF13085 echo extracting Makefile sed 's/^X//' <<'EOF13086' >Makefile Xinstall: X -mkdir include lib bin X cd libsys; make install X cd libpic; make install X cd zoom; make install X Xclean: X -rm -fr include lib bin X cd libsys; make clean X cd libpic; make clean X cd zoom; make clean EOF13086 echo extracting zoom/zoom.1 sed 's/^X//' <<'EOF13087' >zoom/zoom.1 X.\" $Header$ X.\" a few macros X.de Cs \" code start X.DS X.ft B X.ta 9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n X.. X.de Ce \" code end X.ft R X.DE X.. X.de Ss \" subroutine definition start X.nf X.ft B X.ta 9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n X.. X.de Sd \" subroutine documentation X.ft R X.fi X.in +4n X.. X.de Se \" subroutine definition&documentation end X.in -4n X.. X.de DS X.nf X.in +4n X.sp .5v X.. X.de DE X.sp .5v X.in -4n X.fi X.. X.TH ZOOM 1 "10 August 1989" X.SH NAME Xzoom \- filtered image scale and translate X.SH SYNOPSIS X.DS XUsage: zoom [options] X-src %s source filename X-dst %s dest filename X-s %d%d%d%d source box (x0 y0 xsize ysize) X-d %d%d%d%d dest box X-sw %d%d%d%d window (x0 y0 x1 y1) X-dw %d%d%d%d dest window X-map %f%f%f%f scale and translate: sx sy tx ty (src to dst by default) X-square use square mapping (don't stretch pixels) X-intscale use integer scale factors X-filt %s[%s filter name in x and y (default=triangle) X-supp %f[%f filter support radius X-blur %f[%f blur factor: >1 is blurry, <1 is sharp X-window %s[%s window an IIR filter (default=blackman) X-mono monochrome mode (1 channel) X-dev print list of known picture devices/file formats XWhere %d denotes integer, %f denotes float, %s denotes string, Xand '[' marks optional following args X.DE X.SH DESCRIPTION X\fIzoom\fP zooms a raster image Xfrom one picture file or frame buffer to another. XThe "zoom" operation, also known as "resize", Xconsists of a scale and translation. XThe program supports arbitrary floating point scale and translation Xwith independent control of x and y scales, Xupsampling (interpolation) or downsampling (decimation), X1-channel black and white or 3-channel color pictures, Xoverlapping source and destination windows within the same frame buffer, Xand separable filtering with a large choice of filters. XIt can also be used for in-place separable filtering of images. XThe algorithm is not limited to integer or rational scale factors; Xit can scale by any floating point number. XThe program uses the \fIpic\fP package for all picture I/O, Xso the \fIzoom\fP source code is device-independent, Xand the program can read and write any of the picture file formats and Xframe buffers known by \fIpic\fP. XThe program is optimized to be nearly as fast as special-purpose code Xfor important special cases such as point sampling. XThe memory used by the algorithm is modest: it is proportional to the Xpicture width times the filter width, Xnot proportional to picture area. X.PP XTo run \fIzoom\fP, the user specifies the source and destination file Xor frame buffer names, specifies the mapping (zoom transformation) Xand filter. XMost of these options have reasonable defaults. XThe program will read and write the full-screen area of the source and Xdestination pictures by default; Xthe user can specify subrectangles to read and write if desired. XRectangles can be defined in either of two ways: as a "box", Xwhich consists of \fIxmin, ymin, xsize, ysize\fP, Xor as a "window", which consists of \fIxmin, ymin, xmax, ymax\fP. XBy default, the source window is zoomed into the destination window. XAlternatively, a mapping can be specified directly with the \fB-map\fP option. XA mapping can be constrained to have equal scale in x and y, or to have Xinteger scale factors, using the \fB-square\fP and \fB-intscale\fP options, Xrespectively. X.PP XFilters are selected with the \fB-filt\fP option. XIf given one filter name, \fB-filt\fP will use that filter in both x and y; Xif given two names, different filters can be used in the two dimensions. XThe command \fBzoom -filt '?'\fP prints Xthe list of filters currently known to \fIzoom\fP. XThat list is: X.DS XNAME SUPPORT Xpoint 0 Xbox 0.5 Xtriangle 1 Xquadratic 1.5 Xcubic 2 Xcatrom 2 Xmitchell 2 Xgaussian 1.25 Xsinc 4 Xbessel 3.24 X.DE XThe option \fB-filt point\fP gives fast pixel replication, X\fB-filt triangle\fP (the default) gives bilinear interpolation, Xwhich is suitable for most purposes, Xand \fB-filt mitchell\fP gives slower, very high quality results. XThe other filters are provided for experimentation. XThe time required by a filter is proportional to its support. X.PP XMost of the filters known to \fIzoom\fP are FIR (finite impulse response), Xwith an intrinsic support (width), Xbut some of them (gaussian, sinc, bessel) are IIR (infinite impulse response), Xand must be truncated at some arbitrary support. XThis can be done with the \fB-supp\fP option. XAgain, the defaults are reasonable. XThe IIR filters can be windowed (brought down to zero) with various window Xfunctions listed below: X.Cs Xhanning Xhamming Xblackman Xkaiser X.Ce XThe sinc and bessel filters are blackman-windowed by default. XFilters can be scaled artificially to blur or sharpen them with the \fB-blur\fP Xoption. X.SH EXAMPLES X.Ss Xzoom -src mandrill.dump X.Sd XZoom the mandrill from picture file \fBmandrill.dump\fP Xinto the default destination device (whatever that is), Xmapping the file's rectangle into the device's rectangle, Xwith a triangle filter. X.Se X X.Ss Xzoom -src mandrill.dump -filt point -square -intscale X.Sd XZoom the mandrill to the default device's full screen, Xbut maintain the picture's aspect ratio, and zoom it up by an integer factor Xwith point sampling (pixel replication) X.Se X X.Ss Xzoom -src mandrill.dump -dst iris -d 50 75 100 100 X.Sd XZoom the mandrill into an iris window at position (50,70) with xsize=ysize=100. X.Se X X.Ss Xzoom -src hp -dst hp -s 100 100 640 512 -d 0 0 1280 1024 -filt mitchell X.Sd XZoom from hp to itself with overlapping source and destination windows, Xusing a mitchell filter. X.Se X X.Ss Xzoom -src hp -dst hp -s 0 0 500 500 -d 0 0 500 500 -filt cubic -blur 2 X.Sd XBlur (low pass filter) an image in-place. X.Se X.SH SEE ALSO Xpic(3), X\fIDiscrete Time Signal Processing\fP, Alan Oppenheim, Ronald Schafer, 1989. X.SH AUTHOR XPaul Heckbert, UC Berkeley, August 1989. Xph@miro.berkeley.edu EOF13087 echo extracting zoom/zoom.c sed 's/^X//' <<'EOF13088' >zoom/zoom.c X/* X * zoom: subroutines for in-place one-pass filtered image zooming X * X * Zooms (resizes) one image into another with independent control of X * scale, translation, and filtering in x and y. Magnifies or minifies. X * Filters with an arbitrary separable finite impulse response filter. X * (A filter h(x,y) is called separable if h(x,y)=f(x)*g(y)). X * See the filt package regarding filtering. X * The code is graphics device independent, using only generic scanline X * i/o operations. X * X * The program makes one pass over the image using a moving window of X * scanlines, so memory usage is proportional to imagewidth*filterheight, X * not imagewidth*imageheight, as you'd get if the entire image were X * buffered. The separability of the filter is also exploited, to get X * a cpu time proportional to npixels*(filterwidth+filterheight), X * not npixels*filterwidth*filterheight as with direct 2-D convolution. X * X * The source and destination images can be identical, with overlapping X * windows, in which case the algorithm finds the magic split point and X * uses the appropriate scanning directions to avoid feedback (recursion) X * in the filtering. X * X * Paul Heckbert 12 Sept 1988 X * UC Berkeley X * ph@miro.berkeley.edu X * X * Copyright (c) 1989 Paul S. Heckbert X * This source may be used for peaceful, nonprofit purposes only, unless X * under licence from the author. This notice should remain in the source. X */ X X/* X * routines in this source file: X * X * PUBLIC: X * zoom most common entry point; window-to-window zoom X * zoom_continuous fancier: explicit, continuous scale&tran control X * X * SORTA PRIVATE: X * zoom_unfiltered_mono point-sampled zooming, called by zoom_continuous X * zoom_unfiltered_rgb point-sampled zooming, called by zoom_continuous X * zoom_filtered filtered zooming, called by zoom_continuous X * zoom_filtered_xy zoom, filtering x before y, called by zoom_filtered X * zoom_filtered_yx zoom, filtering y before x, called by zoom_filtered X * X * make_weighttab make lookup table of filter coefficients X * make_map_table in-place tricks; order scanlines to avoid feedback X */ X Xstatic char rcsid[] = "$Header: zoom.c,v 3.0 88/10/10 13:52:07 ph Locked $"; X X#include X X#include X#include X#include "filt.h" X#include "scanline.h" X#include "zoom.h" X X#define EPSILON 1e-7 /* error tolerance */ X#define UNDEF PIC_UNDEFINED X X#define WEIGHTBITS 14 /* # bits in filter coefficients */ X#define FINALSHIFT (2*WEIGHTBITS-CHANBITS) /* shift after x&y filter passes */ X#define WEIGHTONE (1<x0..a->x1] X * and y in [a->y0..a->y1], (ranges are inclusive). Likewise for b. X * apic and bpic may be equal and a and b may overlap, no sweat. X */ X Xzoom(apic, a, bpic, b, xfilt, yfilt) XPic *apic, *bpic; /* source and dest pictures */ XWindow_box *a, *b; /* source and dest windows */ XFilt *xfilt, *yfilt; /* filters for x and y */ X{ X Mapping map; X X if (b->x0==UNDEF) { X printf("zoom: undefined window for dest %s: ", X pic_get_name(bpic)); X window_print("", &b); X printf("\n"); X exit(1); X } X window_box_set_size(a); X window_box_set_size(b); X if (a->nx<=0 || a->ny<=0) return; X map.sx = (double)b->nx/a->nx; X map.sy = (double)b->ny/a->ny; X map.tx = b->x0-.5 - map.sx*(a->x0-.5); X map.ty = b->y0-.5 - map.sy*(a->y0-.5); X zoom_continuous(apic, a, bpic, b, &map, xfilt, yfilt); X} X X/* X * zoom_opt: window-to-window zoom with options. X * Like zoom() but has options to make mapping square (preserve pixel shape) X * and round to the next lowest integer scale factor. X */ X Xzoom_opt(apic, a, bpic, b, xfilt, yfilt, square, intscale) XPic *apic, *bpic; /* source and dest pictures */ XWindow_box *a, *b; /* source and dest windows */ XFilt *xfilt, *yfilt; /* filters for x and y */ Xint square, intscale; /* square mapping? integer scales? */ X{ X Mapping map; X X if (b->x0==UNDEF) { X printf("zoom_opt: undefined window for dest %s: ", X pic_get_name(bpic)); X window_print("", &b); X printf("\n"); X exit(1); X } X window_box_set_size(a); X window_box_set_size(b); X if (a->nx<=0 || a->ny<=0) return; X map.sx = (double)b->nx/a->nx; X map.sy = (double)b->ny/a->ny; X if (square) { X if (map.sx>map.sy) { /* use the smaller scale for both sx and sy */ X map.sx = map.sy; X b->nx = ceil(a->nx*map.sx); X } X else { X map.sy = map.sx; X b->ny = ceil(a->ny*map.sy); X } X } X if (intscale) { X if (map.sx>1.) { X map.sx = floor(map.sx); X b->nx = ceil(a->nx*map.sx); X } X if (map.sy>1.) { X map.sy = floor(map.sy); X b->ny = ceil(a->ny*map.sy); X } X } X window_box_set_max(b); X map.tx = b->x0-.5 - map.sx*(a->x0-.5); X map.ty = b->y0-.5 - map.sy*(a->y0-.5); X zoom_continuous(apic, a, bpic, b, &map, xfilt, yfilt); X} X X/* X * zoom_continuous: zoom with explicit, continuous scale&tran control. X * Zoom according to the mapping defined in m, X * reading from window awin of apic and writing to window bwin of bpic. X * Like zoom but allows continuous, subpixel scales and translates. X * Meaning of m is explained above. Pixels in the dest window bwin outside X * of the support of the mapped source window will be left unchanged. X * Scales in m should be positive. X */ X Xzoom_continuous(apic, awin, bpic, bwin, m, xfilt, yfilt) XPic *apic, *bpic; XWindow_box *awin, *bwin; XMapping *m; XFilt *xfilt, *yfilt; X{ X int xy; X Window_box a, b, bc, t; X Filtpar ax, ay; X X /* we consider 3 channel and 4 channel to be the same */ X if ((pic_get_nchan(apic)==1) != (pic_get_nchan(bpic)==1)) { X fprintf(stderr, "zoom_continuous: source has %d channels, dest has %d channels\n", X pic_get_nchan(apic), pic_get_nchan(bpic)); X return; X } X if (m->sx<=0. || m->sy<=0.) { X fprintf(stderr, "zoom_continuous: negative scales not allowed\n"); X return; X } X X a = *awin; X b = *bwin; X window_box_set_size(&a); X window_box_set_size(&b); X X /* X * find scale of filter in a space (source space) X * when minifying, ascale=1/scale, but when magnifying, ascale=1 X */ X ax.scale = xfilt->blur*MAX(1., 1./m->sx); X ay.scale = yfilt->blur*MAX(1., 1./m->sy); X /* X * find support radius of scaled filter X * if ax.supp and ay.supp are both <=.5 then we've got point sampling. X * Point sampling is essentially a special filter whose width is fixed X * at one source pixel. X */ X ax.supp = MAX(.5, ax.scale*xfilt->supp); X ay.supp = MAX(.5, ay.scale*yfilt->supp); X ax.wid = ceil(2.*ax.supp); X ay.wid = ceil(2.*ay.supp); X X /* clip source and dest windows against their respective pictures */ X window_box_intersect(&a, (Window_box *)pic_get_window(apic, &t), &a); X if (pic_get_window(bpic, &t)->x0 != UNDEF) X window_box_intersect(&b, &t, &b); X X /* find valid dest window (transformed source + support margin) */ X bc.x0 = ceil(m->sx*(a.x0-ax.supp)+m->tx+EPSILON); X bc.y0 = ceil(m->sy*(a.y0-ay.supp)+m->ty+EPSILON); X bc.x1 = floor(m->sx*(a.x1+ax.supp)+m->tx-EPSILON); X bc.y1 = floor(m->sy*(a.y1+ay.supp)+m->ty-EPSILON); X window_box_set_size(&bc); X X if (b.x0bc.x1 || b.y0bc.y1) { X /* requested dest window lies outside the valid dest, so clip dest */ X window_box_print(" clipping odst=", &b); X window_box_print(" to ", &bc); X printf("\n"); X window_box_intersect(&b, &bc, &b); X } X pic_set_window(bpic, &b); X X /* compute offsets for MAP (these will be .5 if zoom() routine was called)*/ X m->ux = b.x0-m->sx*(a.x0-.5)-m->tx; X m->uy = b.y0-m->sy*(a.y0-.5)-m->ty; X X printf("src=%s:%s", pic_get_dev(apic), pic_get_name(apic)); X window_box_print("", &a); X printf("\ndst=%s:%s", pic_get_dev(bpic), pic_get_name(bpic)); X window_box_print("", &b); X printf("\n"); X X if (a.nx<=0 || a.ny<=0 || b.nx<=0 || b.ny<=0) return; X X /* check for high-level simplifications of filter */ X xfilt = simplify_filter("x", zoom_coerce, xfilt, &ax, m->sx, m->ux); X yfilt = simplify_filter("y", zoom_coerce, yfilt, &ay, m->sy, m->uy); X X /* X * decide which filtering order (xy or yx) is faster for this mapping by X * counting convolution multiplies X */ X xy = zoom_xy!=UNDEF ? zoom_xy : X b.nx*(a.ny*ax.wid+b.ny*ay.wid) < b.ny*(b.nx*ax.wid+a.nx*ay.wid); X X /* choose the appropriate filtering routine */ X if (str_eq(xfilt->name, "point") && str_eq(yfilt->name, "point")) X zoom_unfiltered(apic, &a, bpic, &b, m); X else if (xy) X zoom_filtered_xy(apic, &a, bpic, &b, m, xfilt, &ax, yfilt, &ay); X else X zoom_filtered_yx(apic, &a, bpic, &b, m, xfilt, &ax, yfilt, &ay); X} X X/* X * filter_simplify: check if our discrete sampling of an arbitrary continuous X * filter, parameterized by the filter spacing (a->scale), its radius (a->supp), X * and the scale and offset of the coordinate mapping (s and u), causes the X * filter to reduce to point sampling. X * X * It reduces if support is less than 1 pixel or X * if integer scale and translation, and filter is cardinal X */ X Xstatic Filt *simplify_filter(dim, coerce, filter, a, s, u) Xchar *dim; Xint coerce; XFilt *filter; XFiltpar *a; Xdouble s, u; /* scale and offset */ X{ X if (coerce && (a->supp<=.5 || X filter->cardinal && INTEGER(1./a->scale) && INTEGER(1./(s*a->scale)) X && INTEGER((u/s-.5)/a->scale))) { X if (!str_eq(filter->name, "point")) X printf("coercing %sfilter=%s to point\n", dim, filter->name); X filter = filt_find("point"); X a->scale = 1.; X a->supp = .5; X a->wid = 1; X } X return filter; X} X X/*----------------------------------------------------------------------*/ X X/* zoom_unfiltered: do unfiltered zoom (point sampling) */ X Xzoom_unfiltered(apic, a, bpic, b, m) XPic *apic, *bpic; XWindow_box *a, *b; XMapping *m; X{ X int overlap; X X /* do source and dest windows overlap? */ X overlap = apic==bpic && window_box_overlap(a, b); X X printf("-map %g %g %g %g\n", m->sx, m->sy, m->tx, m->ty); X /* X * note: We have only x-resample before y-resample versions of the X * unfiltered zoom case; we could optimize further by creating X * y-resample before x-resample versions. X */ X if (pic_get_nchan(apic)==1) X zoom_unfiltered_mono(apic, a, bpic, b, m, overlap); X else X zoom_unfiltered_rgb (apic, a, bpic, b, m, overlap); X} X X/* zoom_unfiltered_mono: monochrome unfiltered zoom */ X Xzoom_unfiltered_mono(apic, a, bpic, b, m, overlap) XPic *apic, *bpic; XWindow_box *a, *b; XMapping *m; Xint overlap; X{ X int byi, by, ay, ayold, bx, ax; X Pixel1 *abuf, *bbuf, *bp; /* source and dest scanline buffers */ X Pixel1 **xmap, **xp; /* mapping from abuf to bbuf (optimization) */ X short *ymap; /* order of dst y coords that avoids feedback */ X char *linewritten; /* has scanline y been written? (debugging) */ X X ALLOC(abuf, Pixel1, a->nx); X ALLOC(bbuf, Pixel1, b->nx); X ALLOC(xmap, Pixel1 *, b->nx); X ALLOC(ymap, short, b->ny); X ALLOC_ZERO(linewritten, char, MAX(a->y1, b->y1)+1); X X /* if overlapping src & dst, find magic y ordering that avoids feedback */ X make_map_table(m->sy, m->ty, .5, a->y0, b->y0, b->ny, overlap, ymap); X X for (bx=0; bxnx; bx++) { X ax = MAP(bx, m->sx, m->ux); X xmap[bx] = &abuf[ax]; X } X X ayold = -1; /* impossible value for ay */ X for (byi=0; byiny; byi++) { X by = ymap[byi]; X ay = MAP(by, m->sy, m->uy); X /* scan line a.y0+ay goes to b.y0+by */ X if (ay!=ayold) { /* new scan line; read it in */ X ayold = ay; X if (overlap && linewritten[a->y0+ay]) X fprintf(stderr, "FEEDBACK ON LINE %d\n", a->y0+ay); X /* read scan line into abuf */ X pic_read_row(apic, a->y0+ay, a->x0, a->nx, abuf); X /* resample in x */ X for (bp=bbuf, xp=xmap, bx=0; bxnx; bx++) X *bp++ = **xp++; X } X pic_write_row(bpic, b->y0+by, b->x0, b->nx, bbuf); X linewritten[b->y0+by] = 1; X } X free(abuf); X free(bbuf); X free(xmap); X free(ymap); X} X X/* zoom_unfiltered_rgb: color unfiltered zoom */ X Xzoom_unfiltered_rgb(apic, a, bpic, b, m, overlap) XPic *apic, *bpic; XWindow_box *a, *b; XMapping *m; Xint overlap; X{ X int byi, by, ay, ayold, bx, ax; X Pixel1_rgba *abuf, *bbuf, *bp; /* source and dest scanline buffers */ X Pixel1_rgba **xmap, **xp; /* mapping from abuf to bbuf */ X short *ymap; /* order of dst y coords that avoids feedback */ X char *linewritten; /* has scanline y been written? (debugging) */ X X ALLOC(abuf, Pixel1_rgba, a->nx); X ALLOC(bbuf, Pixel1_rgba, b->nx); X ALLOC(xmap, Pixel1_rgba *, b->nx); X ALLOC(ymap, short, b->ny); X ALLOC_ZERO(linewritten, char, MAX(a->y1, b->y1)+1); X X /* if overlapping src & dst, find magic y ordering that avoids feedback */ X make_map_table(m->sy, m->ty, .5, a->y0, b->y0, b->ny, overlap, ymap); X X for (bx=0; bxnx; bx++) { X ax = MAP(bx, m->sx, m->ux); X xmap[bx] = &abuf[ax]; X } X X ayold = -1; /* impossible value for ay */ X for (byi=0; byiny; byi++) { X by = ymap[byi]; X ay = MAP(by, m->sy, m->uy); X /* scan line a.y0+ay goes to b.y0+by */ X if (ay!=ayold) { /* new scan line; read it in */ X ayold = ay; X if (overlap && linewritten[a->y0+ay]) X fprintf(stderr, "FEEDBACK ON LINE %d\n", a->y0+ay); X /* read scan line into abuf */ X pic_read_row_rgba(apic, a->y0+ay, a->x0, a->nx, abuf); X /* resample in x */ X for (bp=bbuf, xp=xmap, bx=0; bxnx; bx++) X *bp++ = **xp++; X } X pic_write_row_rgba(bpic, b->y0+by, b->x0, b->nx, bbuf); X linewritten[b->y0+by] = 1; X } X free(abuf); X free(bbuf); X free(xmap); X free(ymap); X} X X/*----------------------------------------------------------------------*/ X X/* X * zoom_filtered_xy: filtered zoom, xfilt before yfilt X * X * note: when calling make_weighttab, we can trim leading and trailing X * zeros from the x weight buffers as an optimization, X * but not for y weight buffers since the split formula is anticipating X * a constant amount of buffering of source scanlines; X * trimming zeros in yweight could cause feedback. X */ X X Xzoom_filtered_xy(apic, a, bpic, b, m, xfilt, ax, yfilt, ay) XPic *apic, *bpic; XWindow_box *a, *b; XMapping *m; XFilt *xfilt, *yfilt; /* x and y filters */ XFiltpar *ax, *ay; /* extra x and y filter parameters */ X{ X int ayf, bx, byi, by, overlap, nchan; X X /*PIXELTYPE NBITS RAISON D'ETRE */ X Scanline abbuf; /* PIXEL1 8 src&dst scanline bufs */ X Scanline accum; /* PIXEL4 28 accumulator buf for yfilt */ X Scanline *linebuf, *tbuf; /* PIXEL2 14 circular buf of active lines */ X X short *ymap; /* order of dst y coords that avoids feedback */ X Weighttab *xweights; /* sampled filter at each dst x pos; for xfilt*/ X short *xweightbuf, *xwp; /* big block of memory addressed by xweights */ X Weighttab yweight; /* a single sampled filter for current y pos */ X char *linewritten; /* has scanline y been written? (debugging) */ X X nchan = pic_get_nchan(apic)==1 ? PIXEL_MONO : PIXEL_RGB; X scanline_alloc(&abbuf, PIXEL1|nchan, MAX(a->nx, b->nx)); X scanline_alloc(&accum, PIXEL4|nchan, b->nx); X ALLOC(linebuf, Scanline, ay->wid); X /* construct circular buffer of ay->wid intermediate scanlines */ X for (ayf=0; ayfwid; ayf++) { X scanline_alloc(&linebuf[ayf], PIXEL2|nchan, b->nx); X linebuf[ayf].y = -1; /* mark scanline as unread */ X } X X ALLOC(ymap, short, b->ny); X ALLOC(xweights, Weighttab, b->nx); X ALLOC(xweightbuf, short, b->nx*ax->wid); X ALLOC(yweight.weight, short, ay->wid); X ALLOC_ZERO(linewritten, char, MAX(a->y1, b->y1)+1); X X /* do source and dest windows overlap? */ X overlap = apic==bpic && window_box_overlap(a, b); X X printf("-xy -map %g %g %g %g\n", m->sx, m->sy, m->tx, m->ty); X printf("X: filt=%s support=%g, scale=%g scaledsupport=%g width=%d\n", X xfilt->name, xfilt->supp, ax->scale, ax->supp, ax->wid); X printf("Y: filt=%s support=%g, scale=%g scaledsupport=%g width=%d\n", X yfilt->name, yfilt->supp, ay->scale, ay->supp, ay->wid); X X /* X * prepare a weighttab (a sampled filter for source pixels) for X * each dest x position X */ X for (xwp=xweightbuf, bx=0; bxnx; bx++, xwp+=ax->wid) { X xweights[bx].weight = xwp; X make_weighttab(b->x0+bx, MAP(bx, m->sx, m->ux), X xfilt, ax, a->nx, zoom_trimzeros, &xweights[bx]); X } X X /* if overlapping src & dst, find magic y ordering that avoids feedback */ X make_map_table(m->sy, m->ty, ay->supp, a->y0, b->y0, b->ny, overlap, X ymap); X X for (byi=0; byiny; byi++) { /* loop over dest scanlines */ X by = ymap[byi]; X if (zoom_debug) printf("by=%d: ", b->y0+by); X /* prepare a weighttab for dest y position by */ X make_weighttab(b->y0+by, MAP(by, m->sy, m->uy), X yfilt, ay, a->ny, 0, &yweight); X if (zoom_debug) printf("ay=%d-%d, reading: ", X a->y0+yweight.i0, a->y0+yweight.i1-1); X scanline_zero(&accum); X X /* loop over source scanlines that influence this dest scanline */ X for (ayf=yweight.i0; ayfwid]; X if (tbuf->y != ayf) { /* scanline needs to be read in */ X if (zoom_debug) printf("%d ", a->y0+ayf); X if (overlap && linewritten[a->y0+ayf]) X fprintf(stderr, "FEEDBACK ON LINE %d\n", a->y0+ayf); X tbuf->y = ayf; X /* read source scanline into abbuf */ X abbuf.len = a->nx; X scanline_read(apic, a->x0, a->y0+ayf, &abbuf); X /* and filter it into the appropriate line of linebuf (xfilt) */ X scanline_filter(CHANBITS, xweights, &abbuf, tbuf); X } X /* add weighted tbuf into accum (these do yfilt) */ X scanline_accum(yweight.weight[ayf-yweight.i0], tbuf, &accum); X } X X /* shift accum down into abbuf and write out dest scanline */ X abbuf.len = b->nx; X scanline_shift(FINALSHIFT, &accum, &abbuf); X scanline_write(bpic, b->x0, b->y0+by, &abbuf); X linewritten[b->y0+by] = 1; X if (zoom_debug) printf("\n"); X } X X scanline_free(&abbuf); X scanline_free(&accum); X for (ayf=0; ayfwid; ayf++) X scanline_free(&linebuf[ayf]); X free(ymap); X free(linebuf); X free(xweights); X free(xweightbuf); X free(yweight.weight); X free(linewritten); X statistics(); X} X X/* zoom_filtered_yx: filtered zoom, yfilt before xfilt */ X Xzoom_filtered_yx(apic, a, bpic, b, m, xfilt, ax, yfilt, ay) XPic *apic, *bpic; XWindow_box *a, *b; XMapping *m; XFilt *xfilt, *yfilt; /* x and y filters */ XFiltpar *ax, *ay; /* extra x and y filter parameters */ X{ X int ayf, bx, byi, by, overlap, nchan; X X /*PIXELTYPE NBITS RAISON D'ETRE */ X Scanline bbuf; /* PIXEL1 8 dst scanline buf */ X Scanline accum; /* PIXEL4 22 accumulator buf for yfilt */ X Scanline *linebuf, *tbuf; /* PIXEL1 8 circular buf of active lines */ X X short *ymap; /* order of dst y coords that avoids feedback */ X Weighttab *xweights; /* sampled filter at each dst x pos; for xfilt*/ X short *xweightbuf, *xwp; /* big block of memory addressed by xweights */ X Weighttab yweight; /* a single sampled filter for current y pos */ X char *linewritten; /* has scanline y been written? (debugging) */ X X nchan = pic_get_nchan(apic)==1 ? PIXEL_MONO : PIXEL_RGB; X scanline_alloc(&bbuf, PIXEL1|nchan, b->nx); X scanline_alloc(&accum, PIXEL4|nchan, a->nx); X ALLOC(linebuf, Scanline, ay->wid); X /* construct circular buffer of ay->wid intermediate scanlines */ X for (ayf=0; ayfwid; ayf++) { X scanline_alloc(&linebuf[ayf], PIXEL1|nchan, a->nx); X linebuf[ayf].y = -1; /* mark scanline as unread */ X } X X ALLOC(ymap, short, b->ny); X ALLOC(xweights, Weighttab, b->nx); X ALLOC(xweightbuf, short, b->nx*ax->wid); X ALLOC(yweight.weight, short, ay->wid); X ALLOC_ZERO(linewritten, char, MAX(a->y1, b->y1)+1); X X /* do source and dest windows overlap? */ X overlap = apic==bpic && window_box_overlap(a, b); X X printf("-yx -map %g %g %g %g\n", m->sx, m->sy, m->tx, m->ty); X printf("X: filt=%s supp=%g scale=%g scaledsupp=%g wid=%d\n", X xfilt->name, xfilt->supp, ax->scale, ax->supp, ax->wid); X printf("Y: filt=%s supp=%g scale=%g scaledsupp=%g wid=%d\n", X yfilt->name, yfilt->supp, ay->scale, ay->supp, ay->wid); X X /* X * prepare a weighttab (a sampled filter for source pixels) for X * each dest x position X */ X for (xwp=xweightbuf, bx=0; bxnx; bx++, xwp+=ax->wid) { X xweights[bx].weight = xwp; X make_weighttab(b->x0+bx, MAP(bx, m->sx, m->ux), X xfilt, ax, a->nx, zoom_trimzeros, &xweights[bx]); X } X X /* if overlapping src & dst, find magic y ordering that avoids feedback */ X make_map_table(m->sy, m->ty, ay->supp, a->y0, b->y0, b->ny, overlap, X ymap); X X for (byi=0; byiny; byi++) { /* loop over dest scanlines */ X by = ymap[byi]; X if (zoom_debug) printf("by=%d: ", b->y0+by); X /* prepare a weighttab for dest y position by */ X make_weighttab(b->y0+by, MAP(by, m->sy, m->uy), X yfilt, ay, a->ny, 0, &yweight); X if (zoom_debug) printf("ay=%d-%d, reading: ", X a->y0+yweight.i0, a->y0+yweight.i1-1); X scanline_zero(&accum); X X /* loop over source scanlines that influence this dest scanline */ X for (ayf=yweight.i0; ayfwid]; X if (tbuf->y != ayf) { /* scanline needs to be read in */ X if (zoom_debug) printf("%d ", a->y0+ayf); X if (overlap && linewritten[a->y0+ayf]) X fprintf(stderr, "FEEDBACK ON LINE %d\n", a->y0+ayf); X tbuf->y = ayf; X /* read source scanline into linebuf */ X scanline_read(apic, a->x0, a->y0+ayf, tbuf); X } X /* add weighted tbuf into accum (these do yfilt) */ X scanline_accum(yweight.weight[ayf-yweight.i0], tbuf, &accum); X } X X /* and filter it into the appropriate line of linebuf (xfilt) */ X scanline_filter(FINALSHIFT, xweights, &accum, &bbuf); X /* and write out dest scanline in bbuf */ X scanline_write(bpic, b->x0, b->y0+by, &bbuf); X linewritten[b->y0+by] = 1; X if (zoom_debug) printf("\n"); X } X X scanline_free(&bbuf); X scanline_free(&accum); X for (ayf=0; ayfwid; ayf++) X scanline_free(&linebuf[ayf]); X free(ymap); X free(linebuf); X free(xweights); X free(xweightbuf); X free(yweight.weight); X free(linewritten); X statistics(); X} X X/* X * make_weighttab: sample the continuous filter, scaled by ap->scale and X * positioned at continuous source coordinate cen, for source coordinates in X * the range [0..len-1], writing the weights into wtab. X * Scale the weights so they sum to WEIGHTONE, and trim leading and trailing X * zeros if trimzeros!=0. X * b is the dest coordinate (for diagnostics). X */ X Xstatic make_weighttab(b, cen, filter, ap, len, trimzeros, wtab) Xint b, len; Xdouble cen; XFilt *filter; XFiltpar *ap; Xint trimzeros; XWeighttab *wtab; X{ X int i0, i1, i, sum, t, stillzero, lastnonzero, nz; X short *wp; X double den, sc, tr; X X /* find the source coord range of this positioned filter: [i0..i1-1] */ X i0 = cen-ap->supp+.5; X i1 = cen+ap->supp+.5; X if (i0<0) i0 = 0; X if (i1>len) i1 = len; X if (i0>=i1) { X fprintf(stderr, "make_weighttab: null filter at %d\n", b); X exit(1); X } X /* the range of source samples to buffer: */ X wtab->i0 = i0; X wtab->i1 = i1; X X /* find scale factor sc to normalize the filter */ X for (den=0, i=i0; iscale); X /* set sc so that sum of sc*func() is approximately WEIGHTONE */ X sc = den==0. ? WEIGHTONE : WEIGHTONE/den; X if (zoom_debug>1) printf(" b=%d cen=%g scale=%g [%d..%d) sc=%g: ", X b, cen, ap->scale, i0, i1, sc); X X /* compute the discrete, sampled filter coefficients */ X stillzero = trimzeros; X for (sum=0, wp=wtab->weight, i=i0; iscale); X X if (trMAXSHORT) { X fprintf(stderr, "tr=%g at %d\n", tr, b); X exit(1); X } X t = floor(tr+.5); X if (stillzero && t==0) i0++; /* find first nonzero */ X else { X stillzero = 0; X *wp++ = t; /* add weight to table */ X sum += t; X if (t!=0) lastnonzero = i; /* find last nonzero */ X } X } X ntot += wtab->i1-wtab->i0; X if (sum==0) { X nz = wtab->i1-wtab->i0; X /* fprintf(stderr, "sum=0 at %d\n", b); */ X wtab->i0 = wtab->i0+wtab->i1 >> 1; X wtab->i1 = wtab->i0+1; X wtab->weight[0] = WEIGHTONE; X } X else { X if (trimzeros) { /* skip leading and trailing zeros */ X /* set wtab->i0 and ->i1 to the nonzero support of the filter */ X nz = wtab->i1-wtab->i0-(lastnonzero-i0+1); X wtab->i0 = i0; X wtab->i1 = i1 = lastnonzero+1; X } X else /* keep leading and trailing zeros */ X nz = 0; X if (sum!=WEIGHTONE) { X /* X * Fudge the center slightly to make sum=WEIGHTONE exactly. X * Is this the best way to normalize a discretely sampled X * continuous filter? X */ X i = cen+.5; X if (i=i1) i = i1-1; X t = WEIGHTONE-sum; X if (zoom_debug>1) printf("[%d]+=%d ", i, t); X wtab->weight[i-i0] += t; /* fudge center sample */ X } X } X if (nz>nzmax) nzmax = nz; X nzero += nz; X if (zoom_debug>1) { X printf("\t"); X for (wp=wtab->weight, i=i0; i1.) { /* magnify */ X /* X * choose integer nearest midpoint of valid interval: X * x < split <= x+s/(s-1) X * where x=(tran+scale*asupp)/(1-scale)-b0 X */ X split = floor((tran+scale*asupp-.5)/(1.-scale)-b0+1.); X } X else { /* minify */ X /* X * only one valid split pt in this case: X * x <= split < x+1 X * so we take extra care (x as above) X */ X split = ceil((tran+scale*asupp)/(1.-scale)-b0); X /* X * The above formula is perfect for exact arithmetic, X * but hardware roundoff can cause split to be one too big. X * Check for roundoff by simulating precisely the calculation X * of i0 in make_weighttab. X */ X u = b0-scale*(a0-.5)-tran; /* recalculate offset */ X z = MAP(split-1, scale, u); /* cen at b=split-1 */ X z = z-asupp+.5; X i0 = z; /* i0 at b=split-1 */ X if (a0+i0>=b0+split) /* feedback at b=split-1? */ X split--; /* correct for roundoff */ X } X if (split<0) split = 0; X else if (split>bn) split = bn; X printf("split at y=%d\n", b0+split); X } X X if (scale>=1.) { /* magnify: scan in toward split */ X for (b=0; b=split; b--) *map++ = b; X } X else { /* minify: scan out away from split */ X for (b=split-1; b>=0; b--) *map++ = b; X for (b=split; bzoom/Makefile X# $Header: Makefile,v 3.0 88/10/10 13:52:15 ph Locked $ X XDEST = .. XL = $(DEST)/lib XBIN = $(DEST)/bin XCOPTS = -g XIPATH = -I$(DEST)/include XCFLAGS = $(IPATH) $(COPTS) XOBJ = zoom_main.o zoom.o scanline.o filt.o XSRC = zoom_main.c zoom.c scanline.c filt.c XEXTRALIB = -lgl_s X# to pick up iris graphics library, use "make EXTRALIB=-lgl_s ..." X Xzoom: $(OBJ) $L/libpic.a X cc $(COPTS) -o zoom $(OBJ) $L/libpic.a $(EXTRALIB) -lm X Xzoom_main.o: X cc $(CFLAGS) -c zoom_main.c \ X -DDEFAULT_FILE='"iris"' X# "note: we'll pick up pic_list from libpic.a(pic_all.o)" X Xzoom.lint: GHOST X lint $(IPATH) $(SRC) >zoom.lint X Xinstall: zoom X mv zoom $(BIN) X XGHOST: X Xzoom_main.o: zoom.h filt.h Xzoom.o: zoom.h scanline.h filt.h Xscanline.o: scanline.h Xfilt.o: filt.h X Xclean: X -rm -f zoom *.o *.lint EOF13089 exit