ref: 13590f759547f4ef544b251f2966b4ed2d82038c
dir: /sample.c/
#include <u.h> #include <libc.h> #include <draw.h> #include <memdraw.h> #include <event.h> #include "blie.h" /* direct copy from /sys/src/cmd/iconv.c:/^writeuncompressed */ static void writeuncompressed(int fd, Memimage *m) { char chanstr[32]; int bpl, y, j; uchar *buf; if(chantostr(chanstr, m->chan) == nil) sysfatal("can't convert channel descriptor: %r"); fprint(fd, "%11s %11d %11d %11d %11d ", chanstr, m->r.min.x, m->r.min.y, m->r.max.x, m->r.max.y); bpl = bytesperline(m->r, m->depth); buf = malloc(bpl); if(buf == nil) sysfatal("malloc failed: %r"); for(y=m->r.min.y; y<m->r.max.y; y++){ j = unloadmemimage(m, Rect(m->r.min.x, y, m->r.max.x, y+1), buf, bpl); if(j != bpl) sysfatal("image unload failed: %r"); if(write(fd, buf, bpl) != bpl) sysfatal("write failed: %r"); } free(buf); } typedef struct Vec2 Vec2; struct Vec2 { double x; double y; }; /* add components to vector */ static Vec2 addvec(Vec2 a, double x, double y) { a.x += x; a.y += y; return a; } /* get UV for p in [0;np] */ static Vec2 getuv(Point p, Point np) { Vec2 r; if (p.x > np.x) p.x = np.x; else if (p.x < 0) p.x = 0; if (p.y > np.y) p.y = np.y; else if (p.y < 0) p.y = 0; r.x = (double)p.x / np.x; r.y = (double)p.y / np.y; return r; } /* clamp(v, 0., 1.) */ static void saturate(Vec2 *uv) { if (uv->x < 0.) uv->x = 0.; else if (uv->x > 1.) uv->x = 1.; if (uv->y < 0.) uv->y = 0.; else if (uv->y > 1.) uv->y = 1.; } /* sample reference point from uv (nearest neighbor, top-left pixel) */ static uchar* samplevalue(Memimage *i, Vec2 uv) { Point p; saturate(&uv); p.x = uv.x * i->r.max.x; p.y = uv.y * i->r.max.y; return byteaddr(i, p); } /* distance of uv from reference point (barycentric coordinates) */ static Vec2 ptdist(Memimage *i, Vec2 uv, Vec2 px) { Point p; Vec2 a, b, r; p.x = uv.x * i->r.max.x; p.y = uv.y * i->r.max.y; a.x = (double)p.x / i->r.max.x; a.y = (double)p.y / i->r.max.y; b.x = a.x + px.x; b.y = a.y + px.y; r.x = (uv.x - p.x) * px.x * (-1); r.y = (uv.y - p.y) * px.y * (-1); return r; } static double lerp(double A, double B, double a) { return A * a + B * (1.-a); } /* bilinear interpolation */ static uchar pxavg(uchar v1, uchar v2, uchar v3, uchar v4, Vec2 n) { double a1, a2, a3, a4; a1 = (double)v1/256; a2 = (double)v2/256; a3 = (double)v3/256; a4 = (double)v4/256; return lerp( lerp(a1, a2, n.x), lerp(a3, a4, n.x), n.y) * 256; } /* performance can be improved: * * only scaling what's needed, which means calculating * the rectangle we want, extracting that to a * separate image for resampling. * * However, this would mean that we have to resample when * panning. */ static Memimage* resample(Memimage *src, int nx, int ny, int quality) { Memimage *tm; int x, y, bpp; Vec2 uv, ndist, px; uchar *f1, *f2, *f3, *f4, *to; bpp = src->depth/8; tm = allocmemimage(Rect(0, 0, nx, ny), src->chan); // memfillcolor(tm, DBlack); px.x = (double)1 / src->r.max.x; px.y = (double)1 / src->r.max.y; for (y = 0; y < ny; y++) for (x = 0; x < nx; x++) { to = byteaddr(tm, Pt(x, y)); uv = getuv(Pt(x, y), tm->r.max); f1 = samplevalue(src, uv); if (!quality) { switch (bpp) { case 4: to[3] = f1[3]; case 3: to[2] = f1[2]; to[1] = f1[1]; case 1: to[0] = f1[0]; } continue; } ndist = ptdist(src, uv, px); f2 = samplevalue(src, addvec(uv, px.x, 0.)); f3 = samplevalue(src, addvec(uv, 0., px.y)); f4 = samplevalue(src, addvec(uv, px.x, px.y)); #define AVG(n) (pxavg(f1[n], f2[n], f3[n], f4[n], ndist)) switch (bpp) { case 4: to[3] = AVG(3); case 3: to[2] = AVG(2); to[1] = AVG(1); case 1: to[0] = AVG(0); } #undef AVG } return tm; } Memimage *lastsampled = nil; static int needssample(Memimage *i, double zoom) { return !(Dx(i->r)*zoom == Dx(i->r) && Dy(i->r)*zoom == Dy(i->r)); } void sampleview(Image *img, Memimage *src, int quality) { Memimage *tmi; Rectangle r; int nw; if (!vstate.dirty) return; if (!src) return; r.min = ZP; r.max = Pt(Dx(img->r), Dy(img->r)); if (quality > vstate.maxquality) quality = vstate.maxquality; if (vstate.dirty & Dzoom) { freememimage(lastsampled); lastsampled = nil; } if (!lastsampled && needssample(src, vstate.zoom)) { lastsampled = resample(src, Dx(src->r)*vstate.zoom, Dy(src->r)*vstate.zoom, quality); } if (lastsampled) src = lastsampled; tmi = allocmemimage(r, img->chan); vdata.mtransparent->clipr = tmi->r; memimagedraw(tmi, tmi->r, vdata.mtransparent, ZP, nil, ZP, S); memimagedraw(tmi, tmi->r, src, addpt(tmi->r.min, vstate.offset), nil, ZP, SoverD); nw = tmi->width * tmi->r.max.y * sizeof(ulong); // tmi->r.max.y == Dy(tmi->r) draw(img, img->r, vdata.transparent, nil, ZP); loadimage(img, img->r, tmi->data->bdata, nw); freememimage(tmi); vstate.dirty = 0; }