ref: f8d65f9f1c74b101cccfdc08250e31c5e554b899
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);
memfillcolor(tmi, DBlack);
memimagedraw(tmi, tmi->r, src, addpt(tmi->r.min, vstate.offset), nil, ZP, S);
nw = tmi->width * tmi->r.max.y * sizeof(ulong); // tmi->r.max.y == Dy(tmi->r)
draw(img, img->r, display->black, nil, ZP);
loadimage(img, img->r, tmi->data->bdata, nw);
freememimage(tmi);
vstate.dirty = 0;
}