ref: 9b6630e3b2aa191c282cf59345fe874305ee3638
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)
{
Point p;
Rectangle r, tr;
int 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;
}
// Allocate temporary image with the size of the dimensions
// we are supposed to write on
tr = dr;
if (Dx(dr) < Dx(si->r))
tr.max.x = tr.min.x + Dx(si->r);
if (Dy(dr) < Dy(si->r))
tr.max.y = tr.min.y + Dy(si->r);
if ((t = allocimage(display, tr, 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, Dy(tr)), 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 = tr.min;
r.max.y = tr.max.y;
i = 0;
// 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 < tr.max.x) {
// 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 space to the righ
draw(ci, ci->r, si, nil, p);
p.x += 1;
// Move the draw rectangle for the temporary image scale
// rectangles to the right
r.min.x = (int)(((float)(i))*scale) + tr.min.x;
r.max.x = (int)(((float)(i+1))*scale) + tr.min.x;
draw(t, r, ci, nil, ZP);
i+=1;
}
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(tr), 1), si->chan, 1, DNofill)) == nil) {
werrstr("allocimage: %r");
freeimage(t);
return -1;
}
// Location of the cursor relative to the source image's s
p.x = (int)(((float)dr.min.x)/scale);
p.y = (int)(((float)dr.min.y)/scale);
p = addpt(p, sp);
// Wide rectangle where the ci image will be drawn in
// the temporary image
r.min = tr.min;
r.max.x = tr.max.x;
r.max.y = r.min.y + MAX(1.0, scale);
i = 0;
while (r.min.y < dr.max.y) {
// 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 space down
draw(ci, ci->r, t, nil, p);
p.y += 1;
// Move the draw rectangle for the temporary image scale
// rectangles to the right
r.min.y = (int)(((float)(i))*scale) + dr.min.x;
r.max.y = (int)(((float)(i+1))*scale) + dr.min.x;
draw(di, r, ci, nil, ZP);
i+=1;
}
freeimage(t);
freeimage(ci);
return 1;
}