/*
* This file is part of the Haven & Hearth game client.
* Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and
* Björn Johannessen <johannessen.bjorn@gmail.com>
*
* Redistribution and/or modification of this file is subject to the
* terms of the GNU Lesser General Public License, version 3, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Other parts of this source tree adhere to other copying
* rights. Please see the file `COPYING' in the root directory of the
* source tree for details.
*
* A copy the GNU Lesser General Public License is distributed along
* with the source tree of which this file is a part in the file
* `doc/LPGL-3'. If it is missing for any reason, please see the Free
* Software Foundation's website at <http://www.fsf.org/>, or write
* to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package haven;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.*;
public class PUtils {
public static Coord imgsz(BufferedImage img) {
return(new Coord(img.getWidth(), img.getHeight()));
}
public static Coord imgsz(Raster img) {
return(new Coord(img.getWidth(), img.getHeight()));
}
public static WritableRaster byteraster(Coord sz, int bands) {
return(Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, sz.x, sz.y, bands, null));
}
public static WritableRaster alpharaster(Coord sz) {
return(byteraster(sz, 1));
}
public static WritableRaster imgraster(Coord sz) {
return(byteraster(sz, 4));
}
public static WritableRaster copy(Raster src) {
int w = src.getWidth(), h = src.getHeight(), b = src.getNumBands();
WritableRaster ret = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, w, h, b, null);
int[] buf = new int[w * h];
for(int i = 0; i < b; i++)
ret.setSamples(0, 0, w, h, i, src.getSamples(0, 0, w, h, i, buf));
return(ret);
}
public static BufferedImage rasterimg(WritableRaster img) {
return(new BufferedImage(TexI.glcm, img, false, null));
}
public static WritableRaster imggrow(WritableRaster img, int rad) {
int h = img.getHeight(), w = img.getWidth();
int[] buf = new int[w * h];
int o = 0;
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
int m = 0;
int u = Math.max(0, y - rad), b = Math.min(h - 1, y + rad);
int l = Math.max(0, x - rad), r = Math.min(w - 1, x + rad);
for(int y2 = u; y2 <= b; y2++) {
for(int x2 = l; x2 <= r; x2++) {
m = Math.max(m, img.getSample(x2, y2, 0));
}
}
buf[o++] = m;
}
}
img.setSamples(0, 0, w, h, 0, buf);
return(img);
}
public static WritableRaster imgblur(WritableRaster img, int rad, double var) {
int h = img.getHeight(), w = img.getWidth();
double[] gk = new double[(rad * 2) + 1];
for(int i = 0; i <= rad; i++)
gk[rad + i] = gk[rad - i] = Math.exp(-0.5 * Math.pow(i / var, 2.0));
double s = 0;
for(double cw : gk) s += cw;
s = 1.0 / s;
for(int i = 0; i <= rad * 2; i++)
gk[i] *= s;
int[] buf = new int[w * h];
for(int band = 0; band < img.getNumBands(); band++) {
int o;
o = 0;
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
double v = 0;
int l = Math.max(0, x - rad), r = Math.min(w - 1, x + rad);
for(int x2 = l, ks = l - (x - rad); x2 <= r; x2++, ks++)
v += img.getSample(x2, y, band) * gk[ks];
buf[o++] = (int)v;
}
}
img.setSamples(0, 0, w, h, band, buf);
o = 0;
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
double v = 0;
int u = Math.max(0, y - rad), b = Math.min(h - 1, y + rad);
for(int y2 = u, ks = u - (y - rad); y2 <= b; y2++, ks++)
v += img.getSample(x, y2, band) * gk[ks];
buf[o++] = (int)v;
}
}
img.setSamples(0, 0, w, h, band, buf);
}
return(img);
}
public static WritableRaster alphadraw(WritableRaster dst, Raster alpha, Coord ul, Color col) {
int r = col.getRed(), g = col.getGreen(), b = col.getBlue(), ba = col.getAlpha();
int w = alpha.getWidth(), h = alpha.getHeight();
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
int a = (alpha.getSample(x, y, 0) * ba) / 255;
int dx = x + ul.x, dy = y + ul.y;
dst.setSample(dx, dy, 0, ((r * a) + (dst.getSample(dx, dy, 0) * (255 - a))) / 255);
dst.setSample(dx, dy, 1, ((g * a) + (dst.getSample(dx, dy, 1) * (255 - a))) / 255);
dst.setSample(dx, dy, 2, ((b * a) + (dst.getSample(dx, dy, 2) * (255 - a))) / 255);
dst.setSample(dx, dy, 3, Math.max((ba * a) / 255, dst.getSample(dx, dy, 3)));
}
}
return(dst);
}
public static WritableRaster blit(WritableRaster dst, Raster src, Coord off) {
int w = src.getWidth(), h = src.getHeight(), b = src.getNumBands();
for(int y = 0; y < h; y++) {
int dy = y + off.y;
for(int x = 0; x < w; x++) {
int dx = x + off.x;
for(int i = 0; i < b; i++)
dst.setSample(dx, dy, i, src.getSample(x, y, i));
}
}
return(dst);
}
public static WritableRaster gayblit(WritableRaster dst, int dband, Coord doff, Raster src, int sband, Coord soff) {
if(doff.x < 0) {
soff = soff.add(-doff.x, 0);
doff = doff.add(-doff.x, 0);
}
if(doff.y < 0) {
soff = soff.add(0, -doff.x);
doff = doff.add(0, -doff.x);
}
int w = Math.min(src.getWidth() - soff.x, dst.getWidth() - doff.x), h = Math.min(src.getHeight() - soff.y, dst.getHeight() - doff.y);
for(int y = 0; y < h; y++) {
int sy = y + soff.y, dy = y + doff.y;
for(int x = 0; x < w; x++) {
int sx = x + soff.x, dx = x + doff.x;
dst.setSample(dx, dy, dband, (dst.getSample(dx, dy, dband) * src.getSample(sx, sy, sband)) / 255);
}
}
return(dst);
}
public static WritableRaster alphablit(WritableRaster dst, Raster src, Coord off) {
int w = src.getWidth(), h = src.getHeight();
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
int a = src.getSample(x, y, 3);
int dx = x + off.x, dy = y + off.y;
dst.setSample(dx, dy, 0, ((src.getSample(x, y, 0) * a) + (dst.getSample(dx, dy, 0) * (255 - a))) / 255);
dst.setSample(dx, dy, 1, ((src.getSample(x, y, 1) * a) + (dst.getSample(dx, dy, 1) * (255 - a))) / 255);
dst.setSample(dx, dy, 2, ((src.getSample(x, y, 2) * a) + (dst.getSample(dx, dy, 2) * (255 - a))) / 255);
dst.setSample(dx, dy, 3, Math.max(src.getSample(x, y, 3), dst.getSample(dx, dy, 3)));
}
}
return(dst);
}
public static WritableRaster colmul(WritableRaster img, Color col) {
int w = img.getWidth(), h = img.getHeight();
int[] bm = {col.getRed(), col.getGreen(), col.getBlue(), col.getAlpha()};
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
for(int b = 0; b < 4; b++)
img.setSample(x, y, b, (img.getSample(x, y, b) * bm[b]) / 255);
}
}
return(img);
}
public static WritableRaster copyband(WritableRaster dst, int dband, Coord doff, Raster src, int sband, Coord soff, Coord sz) {
dst.setSamples(doff.x, doff.y, sz.x, sz.y, dband, src.getSamples(soff.x, soff.y, sz.x, sz.y, sband, (int[])null));
return(dst);
}
public static WritableRaster copyband(WritableRaster dst, int dband, Coord doff, Raster src, int sband) {
return(copyband(dst, dband, doff, src, sband, Coord.z, imgsz(src)));
}
public static WritableRaster copyband(WritableRaster dst, int dband, Raster src, int sband) {
return(copyband(dst, dband, Coord.z, src, sband));
}
public static WritableRaster blurmask(Raster img, int grad, int brad, Color col) {
Coord marg = new Coord(grad + brad, grad + brad), sz = imgsz(img).add(marg.mul(2));
return(alphadraw(imgraster(sz), imgblur(imggrow(copyband(alpharaster(sz), 0, marg, img, 3), grad), brad, brad), Coord.z, col));
}
public static WritableRaster blurmask2(Raster img, int grad, int brad, Color col) {
return(alphablit(blurmask(img, grad, brad, col), img, new Coord(grad + brad, grad + brad)));
}
public static WritableRaster glowmask(Raster img) {
Coord sz = imgsz(img);
int nb = img.getNumBands();
WritableRaster ret = alpharaster(sz);
float[] hsv = new float[3];
float max = 0;
for(int y = 0; y < sz.y; y++) {
for(int x = 0; x < sz.x; x++) {
Color.RGBtoHSB(img.getSample(x, y, 0), img.getSample(x, y, 1), img.getSample(x, y, 2), hsv);
float a = (nb > 3)?(img.getSample(x, y, 3) / 255f):1f;
float val = ((1f - hsv[1]) * hsv[2]) * a;
max = Math.max(max, val);
}
}
float imax = 1f / max;
for(int y = 0; y < sz.y; y++) {
for(int x = 0; x < sz.x; x++) {
Color.RGBtoHSB(img.getSample(x, y, 0), img.getSample(x, y, 1), img.getSample(x, y, 2), hsv);
float a = (nb > 3)?(img.getSample(x, y, 3) / 255f):1f;
float val = ((1f - hsv[1]) * hsv[2]) * a;
ret.setSample(x, y, 0, Math.min(Math.max((int)(Math.sqrt(val * imax) * 255), 0), 255));
}
}
return(ret);
}
public static BufferedImage glowmask(Raster img, int grad, Color col) {
Coord sz = imgsz(img), off = new Coord(grad, grad);
WritableRaster buf = imgraster(sz.add(off.mul(2)));
for(int i = 0; i < grad; i++) {
alphadraw(buf, img, off, col);
imgblur(buf, 2, 2);
}
return(rasterimg(buf));
}
public static class BlurFurn extends Text.Imager {
public final int grad, brad;
public final Color col;
public BlurFurn(Text.Furnace bk, int grad, int brad, Color col) {
super(bk);
this.grad = grad;
this.brad = brad;
this.col = col;
}
public BufferedImage proc(Text text) {
return(rasterimg(blurmask2(text.img.getRaster(), grad, brad, col)));
}
}
public static void dumpband(Raster img, int band) {
int w = img.getWidth(), h = img.getHeight();
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
System.err.print((char)('a' + ((img.getSample(x, y, band) * ('z' - 'a')) / 255)));
}
System.err.println();
}
}
public static BufferedImage monochromize(BufferedImage img, Color col) {
Coord sz = Utils.imgsz(img);
BufferedImage ret = TexI.mkbuf(sz);
Raster src = img.getRaster();
WritableRaster dst = ret.getRaster();
boolean hasalpha = (src.getNumBands() == 4);
for(int y = 0; y < sz.y; y++) {
for(int x = 0; x < sz.x; x++) {
int r = src.getSample(x, y, 0),
g = src.getSample(x, y, 1),
b = src.getSample(x, y, 2);
int a = hasalpha?src.getSample(x, y, 3):255;
int max = Math.max(r, Math.max(g, b)),
min = Math.min(r, Math.min(g, b));
int val = (max + min) / 2;
dst.setSample(x, y, 0, (col.getRed() * val) / 255);
dst.setSample(x, y, 1, (col.getGreen() * val) / 255);
dst.setSample(x, y, 2, (col.getBlue() * val) / 255);
dst.setSample(x, y, 3, (col.getAlpha() * a) / 255);
}
}
return(ret);
}
public static interface Convolution {
public double cval(double td);
public double support();
}
public static final Convolution box = new Convolution() {
public double cval(double td) {
return(((td >= -0.5) && (td < 0.5))?1.0:0.0);
}
public double support() {return(0.5);}
};
public static class Hanning implements Convolution {
private final double sz;
public Hanning(double sz) {this.sz = sz;}
public double cval(double td) {
if(td == 0)
return(1.0);
else if((td < -sz) || (td > sz))
return(0.0);
double tdp = td * Math.PI;
return((Math.sin(tdp) / tdp) * (0.5 + (0.5 * Math.cos(tdp / sz))));
}
public double support() {return(sz);}
}
public static class Hamming implements Convolution {
private final double sz;
public Hamming(double sz) {this.sz = sz;}
public double cval(double td) {
if(td == 0)
return(1.0);
else if((td < -sz) || (td > sz))
return(0.0);
double tdp = td * Math.PI;
return((Math.sin(tdp) / tdp) * (0.54 + (0.46 * Math.cos(tdp / sz))));
}
public double support() {return(sz);}
}
public static class Lanczos implements Convolution {
private final double sz;
public Lanczos(double sz) {this.sz = sz;}
public double cval(double td) {
if(td == 0)
return(1.0);
else if((td < -sz) || (td > sz))
return(0.0);
double tdp = td * Math.PI;
double wtdp = tdp / sz;
return((Math.sin(tdp) / tdp) * (Math.sin(wtdp) / wtdp));
}
public double support() {return(sz);}
}
public static BufferedImage convolvedown(BufferedImage img, Coord tsz, Convolution filter) {
Raster in = img.getRaster();
int w = in.getWidth(), h = in.getHeight(), nb = in.getNumBands();
double xf = (double)w / (double)tsz.x, ixf = 1.0 / xf;
double yf = (double)h / (double)tsz.y, iyf = 1.0 / yf;
double[] ca = new double[nb];
WritableRaster buf = byteraster(new Coord(tsz.x, h), nb);
double support = filter.support();
{
double[] cf = new double[tsz.x * (int)Math.ceil(2 * support * xf + 2)];
int[] cl = new int[tsz.x];
int[] cr = new int[tsz.x];
for(int x = 0, ci = 0; x < tsz.x; x++) {
int si = ci;
double wa = 0.0;
cl[x] = Math.max((int)Math.floor((x + 0.5 - support) * xf), 0);
cr[x] = Math.min((int)Math.ceil((x + 0.5 + support) * xf), w - 1);
for(int sx = cl[x]; sx <= cr[x]; sx++) {
double tx = ((sx + 0.5) * ixf) - x - 0.5;
double fw = filter.cval(tx);
wa += fw;
cf[ci++] = fw;
}
wa = 1.0 / wa;
for(; si < ci; si++)
cf[si] *= wa;
}
for(int y = 0; y < h; y++) {
for(int x = 0, ci = 0; x < tsz.x; x++) {
for(int b = 0; b < nb; b++)
ca[b] = 0.0;
for(int sx = cl[x]; sx <= cr[x]; sx++) {
double fw = cf[ci++];
for(int b = 0; b < nb; b++)
ca[b] += in.getSample(sx, y, b) * fw;
}
for(int b = 0; b < nb; b++)
buf.setSample(x, y, b, Utils.clip((int)ca[b], 0, 255));
}
}
}
WritableRaster res = byteraster(tsz, nb);
{
double[] cf = new double[tsz.y * (int)Math.ceil(2 * support * yf + 2)];
int[] cu = new int[tsz.y];
int[] cd = new int[tsz.y];
for(int y = 0, ci = 0; y < tsz.y; y++) {
int si = ci;
double wa = 0.0;
cu[y] = Math.max((int)Math.floor((y + 0.5 - support) * yf), 0);
cd[y] = Math.min((int)Math.ceil((y + 0.5 + support) * yf), h - 1);
for(int sy = cu[y]; sy <= cd[y]; sy++) {
double ty = ((sy + 0.5) * iyf) - y - 0.5;
double fw = filter.cval(ty);
wa += fw;
cf[ci++] = fw;
}
wa = 1.0 / wa;
for(; si < ci; si++)
cf[si] *= wa;
}
for(int x = 0; x < tsz.x; x++) {
for(int y = 0, ci = 0; y < tsz.y; y++) {
for(int b = 0; b < nb; b++)
ca[b] = 0.0;
for(int sy = cu[y]; sy <= cd[y]; sy++) {
double fw = cf[ci++];
for(int b = 0; b < nb; b++)
ca[b] += buf.getSample(x, sy, b) * fw;
}
for(int b = 0; b < nb; b++)
res.setSample(x, y, b, Utils.clip((int)ca[b], 0, 255));
}
}
}
return(new BufferedImage(img.getColorModel(), res, false, null));
}
public static void main(String[] args) throws Exception {
Convolution[] filters = {
box,
new Hanning(1),
new Hanning(2),
new Hamming(1),
new Lanczos(2),
new Lanczos(3),
};
//BufferedImage in = Resource.loadimg("gfx/invobjs/herbs/crowberry");
BufferedImage in = javax.imageio.ImageIO.read(new java.io.File("/tmp/e.jpg"));
Coord tsz = new Coord(300, 300);
for(int i = 0; i < filters.length; i++) {
long start = System.nanoTime();
BufferedImage out = convolvedown(in, tsz, filters[i]);
System.err.println(System.nanoTime() - start);
javax.imageio.ImageIO.write(out, "PNG", new java.io.File("/tmp/barda" + i + ".png"));
}
}
}