shithub: pain

ref: 9811bf0e2479014d7b2ca71dd59c8557ed70780f
dir: /resize.c/

View raw version
#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;
}