ref: 2135d47a94469871de61d2efe2e9dbf8993c583d
dir: /resize.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include "dat.h"
#include "fns.h"
// Inspired by /sys/src/games/doom/i_video.c:/^convproc
// and /sys/src/cmd/page.c:/^zoomdraw
// Thanks sigrid!
int
resizeimage(Image * di, Rectangle dr, float scale, Image * si, Point sp)
{
float d, f;
Point p;
Rectangle r;
int sh, i;
Image * t, * ci;
if (scale < 0) {
werrstr("scale < 0");
return -1;
} else if (scale == 1) {
draw(di, dr, si, nil, addpt(sp, dr.min));
return 1;
}
sh = Dy(si->r);
// Allocate temporary image with the size of the dimensions
// we are supposed to write on
if ((t = allocimage(display, dr, si->chan, 1, DNofill)) == nil) {
werrstr("allocimage: %r");
return -1;
}
// Allocate the cursor image. Think of it as a line that scans an
// image and copies the pixels underneath it to another image
//
// For this first scan, we will be using a vertical line of width 1, and height
// sh (so the source image's height)
if ((ci = allocimage(display, Rect(0, 0, 1, sh), si->chan, 1, DNofill)) == nil) {
werrstr("allocimage: %r");
freeimage(t);
return -1;
}
// Location of the cursor relative to the source image's sp
p.x = (int)(((float)dr.min.x)/scale);
p.y = (int)(((float)dr.min.y)/scale);
p = addpt(p, sp);
// Tall rectangle where the ci image will be drawn in
// the temporary image
r.min = dr.min;
r.max.y = dr.max.y;
r.max.x = r.min.x + MAX(1.0, 1/scale); // It wouldn't make much sense to have
// a horizontal width of less than 1
print("t->r = %R\n", t->r);
print("ci->r = %R\n", ci->r);
i = r.max.x - r.min.x;
d = p.x;
f = r.min.x;
// Scan the source image with a vertical cursor image and apply it to the
// temporary image. We want to do this until we try to scan outside the
// source image, or we try to write outside of the target image.
while (r.min.x < dr.max.x || p.x < (si->r.max.x/scale)) {
// If we are expanding an image, one scan on the source image will result on
// multiple draws on the target image. Conversively, if we are shrinking an
// image, for every line on the target image will we will skip over multiple
// lines on the source image.
print("f = %f, p = %P, r = %R\n", scale, p, r);
// Move the ci one 1/scale space to the right
draw(ci, ci->r, si, nil, p);
d += 1/scale;
p.x = d;
// Move the draw rectangle for the temporary image scale
// rectangles to the right
draw(t, r, ci, nil, ZP);
f += scale;
r.min.x = f;
r.max.x = r.min.x + i;
}
freeimage(ci);
// Now let's do the same, but this time we will use a horizontal
// cursor image and instead of reading from the source image into the temporary
// image, we will read from the temporary image to the destination image.
//
// This is because we already expanded the image horizontally, how we just have
// to do it vertically
// Make our horizontal scan image
if ((ci = allocimage(display, Rect(0, 0, Dx(dr), 1), si->chan, 1, DNofill)) == nil) {
werrstr("allocimage: %r");
freeimage(t);
return -1;
}
// Location of the cursor relative to the source image's sp
p.y = dr.min.y;
p.x = (int)(((float)dr.min.x)/scale);
p = addpt(p, sp);
// Wide rectangle where the ci image will be drawn in
// the temporary image
r.min = dr.min;
r.max.x = dr.max.x;
r.max.y = r.min.y + MAX(1.0, scale);
i = r.max.y - r.min.y;
d = p.y;
draw(di, t->r, t, nil, ZP);
while (0) {
// If we are expanding an image, one scan on the source image will result on
// multiple draws on the target image. Conversively, if we are shrinking an
// image, for every line on the target image will we will skip over multiple
// lines on the source image.
// Move the ci one 1/scale space to the right
draw(ci, ci->r, t, nil, p);
d += 1/scale;
p.y = d;
// Move the draw rectangle for the temporary image scale
// rectangles to the right
draw(di, r, ci, nil, ZP);
r.max.y += i;
r.min.y += i;
print("f = %f, p = %P, r = %R\n", scale, p, r);
}
freeimage(t);
freeimage(ci);
return 1;
}