//
// Copyright 2001 Ken Perlin package render; /** * Multi Image Pyramid (MIP) processing */ public class ImagePyramid { private String notice = "Copyright 2001 Ken Perlin. All rights reserved."; /** Creates a multi-image maps from an image buffer * @param base the original image * @see ImageBuffer */ public ImagePyramid(ImageBuffer base) { pyramid[0] = new ImageBuffer(base.width + 1, base.height + 1); for (int y = 0; y < base.height; y++) for (int x = 0; x < base.width; x++) pyramid[0].pix[x + y * pyramid[0].width] = 0x00ffffff & base.pix[x + y * base.width]; for (int level = 1; level < pyramid.length; level++) { ImageBuffer parent = pyramid[level - 1]; int w = parent.getWidth() - 1; int h = parent.getHeight() - 1; if (w <= 1 || h <= 1) break; ImageBuffer tmp = new ImageBuffer(w, h / 2 + 1); for (int x = 0; x <= w; x++) for (int y = 0; y < h; y += 2) { int A = parent.get(x, y - 1), B = parent.get(x, y), C = parent.get(x, y + 1), D = parent.get(x, y + 2); tmp.set(x, y >> 1, (unpack(A, 0) + 3 * (unpack(B, 0) + unpack(C, 0)) + unpack(D, 0) >> 3) << 16 | (unpack(A, 1) + 3 * (unpack(B, 1) + unpack(C, 1)) + unpack(D, 1) >> 3) << 8 | (unpack(A, 2) + 3 * (unpack(B, 2) + unpack(C, 2)) + unpack(D, 2) >> 3)); } pyramid[level] = new ImageBuffer(w / 2 + 1, h / 2 + 1); for (int x = 0; x <= w; x += 2) for (int y = 0; y <= h / 2; y++) { int A = tmp.get(x - 1, y), B = tmp.get(x, y), C = tmp.get(x + 1, y), D = tmp.get(x + 2, y); pyramid[level].set(x >> 1, y, (unpack(A, 0) + 3 * (unpack(B, 0) + unpack(C, 0)) + unpack(D, 0) >> 3) << 16 | (unpack(A, 1) + 3 * (unpack(B, 1) + unpack(C, 1)) + unpack(D, 1) >> 3) << 8 | (unpack(A, 2) + 3 * (unpack(B, 2) + unpack(C, 2)) + unpack(D, 2) >> 3)); } } } /** Returns a sample at a particular fractional location and size * @param u the horizontal component * @param v the vertical component * @param s the size of the pixel */ public int get(double u, double v, double s) { u = Math.max(0, Math.min(1, u)); v = Math.max(0, Math.min(1, v)); if (s <= 0) { ImageBuffer p0 = pyramid[0]; return p0.get((int) (u * (p0.getWidth() - 1)), (int) (v * (p0.getHeight() - 1))) | 0xff000000; } int level = 0, targetWidth = (int) (2 / s); for (; pyramid[level + 2] != null; level++) if (pyramid[level].getWidth() - 1 <= targetWidth) break; ImageBuffer p0 = pyramid[level], p1 = pyramid[level + 1]; int w0 = p0.getWidth(), h0 = p0.getHeight(), w1 = p1.getWidth(), h1 = p1.getHeight(); double X0 = (w0 - 1) * u - .5, Y0 = (h0 - 1) * v - .5, X1 = (w1 - 1) * u - .5, Y1 = (h1 - 1) * v - .5; int x0 = (int) X0, dx0 = f2i(X0 - x0), y0 = (int) Y0, dy0 = f2i(Y0 - y0), i0 = x0 + w0 * y0, x1 = (int) X1, dx1 = f2i(X1 - x1), y1 = (int) Y1, dy1 = f2i(Y1 - y1), i1 = x1 + w1 * y1; return lerpRGB(f2i(w0 * s - 1), lerpRGB(dy0, lerpRGB(dx0, p0.pix[i0], p0.pix[i0 + 1]), lerpRGB(dx0, p0.pix[i0 + w0], p0.pix[i0 + w0 + 1])), lerpRGB(dy1, lerpRGB(dx1, p1.pix[i1], p1.pix[i1 + 1]), lerpRGB(dx1, p1.pix[i1 + w1], p1.pix[i1 + w1 + 1]))) | 0xff000000; } private int f2i(double t) { return Math.max(0, Math.min(255, (int) (256 * t))); } private int lerpRGB(int t, int a, int b) { return lerp(t, a & 0xff0000, b & 0xff0000) & 0xff0000 | lerp(t, a & 0x00ff00, b & 0x00ff00) & 0x00ff00 | lerp(t, a & 0x0000ff, b & 0x0000ff); } private int lerp(int t, int a, int b) { return a + (t * (b - a) >> 8); } /** returns the width of an image at the given level * @param level the level within the pyramid */ public int getWidth(int level) { return pyramid[level].getWidth(); } /** returns the height of an image at the given level * @param level the level within the pyramid */ public int getHeight(int level) { return pyramid[level].getHeight(); } /** returns a pixel of an image at the given location in the specified level * @param level the level within the pyramid * @param x, y location */ public int get(int level, int x, int y) { return pyramid[level].get(x, y); } private int unpack(int packed, int i) { switch (i) { case 0 : return (packed >> 16) & 255; case 1 : return (packed >> 8) & 255; default : return (packed) & 255; } } private ImageBuffer pyramid[] = new ImageBuffer[12]; }