/*
JWildfire - an image and animation processor written in Java
Copyright (C) 1995-2011 Andreas Maschke
This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This software 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this software;
if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jwildfire.image;
import org.jwildfire.base.Tools;
import org.jwildfire.base.mathlib.GfxMathLib;
import org.jwildfire.base.mathlib.MathLib;
public class SimpleHDRImage implements WFImage, Cloneable {
private Pixel toolPixel = new Pixel();
private int imageWidth = -1;
private int imageHeight = -1;
private float rBuffer[], gBuffer[], bBuffer[];
public SimpleHDRImage(int pWidth, int pHeight) {
int size = (int) pWidth * (int) pHeight;
if (size > 0) {
rBuffer = new float[size];
gBuffer = new float[size];
bBuffer = new float[size];
}
imageWidth = pWidth;
imageHeight = pHeight;
}
public SimpleHDRImage() {
}
@Override
public int getImageWidth() {
return imageWidth;
}
@Override
public int getImageHeight() {
return imageHeight;
}
@Override
public double getAspect() {
return imageHeight != 0 ? (double) imageWidth / (double) imageHeight : 0.0;
}
@Override
public SimpleHDRImage clone() {
SimpleHDRImage res = new SimpleHDRImage(getImageWidth(), getImageHeight());
res.imageWidth = getImageWidth();
res.imageHeight = getImageHeight();
if (res.rBuffer != null) {
System.arraycopy(rBuffer, 0, res.rBuffer, 0, res.rBuffer.length);
System.arraycopy(gBuffer, 0, res.gBuffer, 0, res.gBuffer.length);
System.arraycopy(bBuffer, 0, res.bBuffer, 0, res.bBuffer.length);
}
return res;
}
public int getRGBEValue(int pX, int pY) {
try {
int offset = pY * imageWidth + pX;
return convertRGBToRGBE(rBuffer[offset], gBuffer[offset], bBuffer[offset]);
}
catch (Exception ex) {
throw new RuntimeException("(" + pX + ", " + pY + ") is out of bounds (0.."
+ (imageWidth - 1) + ", 0.." + (imageHeight - 1) + ")", ex);
}
}
protected static int convertRGBToRGBE(float pR, float pG, float pB) {
float mVal = max(pR, pG, pB);
if (mVal < MathLib.EPSILON) {
return 0;
}
float mantissa = mVal;
int exponent = 0;
if (mVal > 1.0f) {
while (mantissa > 1.0f) {
mantissa *= 0.5f;
exponent++;
}
}
else if (mVal <= 0.5f) {
while (mantissa <= 0.5f) {
mantissa *= 2.0f;
exponent--;
}
}
mVal = (mantissa * 255.0f) / mVal;
int res = (exponent + 128);
res |= ((int) (pR * mVal) << 24);
res |= ((int) (pG * mVal) << 16);
res |= ((int) (pB * mVal) << 8);
return res;
}
private static float max(float pA, float pB, float pC) {
if (pA < pB) {
pA = pB;
}
if (pA < pC) {
pA = pC;
}
return pA;
}
public void fillBackground(int pRed, int pGreen, int pBlue) {
float r = (float) pRed / 255.0f;
float g = (float) pGreen / 255.0f;
float b = (float) pBlue / 255.0f;
int size = imageWidth * imageHeight;
for (int i = 0; i < size; i++) {
rBuffer[i] = r;
gBuffer[i] = g;
bBuffer[i] = b;
}
}
private int getOffset(int pX, int pY) {
return pY * imageWidth + pX;
}
public void setRGB(int pX, int pY, float pR, float pG, float pB) {
int offset = getOffset(pX, pY);
rBuffer[offset] = pR;
gBuffer[offset] = pG;
bBuffer[offset] = pB;
}
public void getRGBValues(float pRGB[], int pX, int pY) {
int offset = getOffset(pX, pY);
pRGB[0] = rBuffer[offset];
pRGB[1] = gBuffer[offset];
pRGB[2] = bBuffer[offset];
}
public static float calcLum(float pR, float pG, float pB) {
return 0.299f * pR + 0.588f * pG + 0.113f * pB;
}
public float getLum(int pX, int pY) {
int offset = getOffset(pX, pY);
return calcLum(rBuffer[offset], gBuffer[offset], bBuffer[offset]);
}
public float getRValue(int pX, int pY) {
return rBuffer[getOffset(pX, pY)];
}
public float getGValue(int pX, int pY) {
return gBuffer[getOffset(pX, pY)];
}
public float getBValue(int pX, int pY) {
return bBuffer[getOffset(pX, pY)];
}
public void sampleDown(int pOversample) {
if (pOversample == 1) {
return;
}
if (pOversample < 1 || (pOversample > 1 && (imageWidth % pOversample != 0 || imageHeight % pOversample != 0))) {
throw new IllegalArgumentException("oversample " + pOversample);
}
int newWidth = imageWidth / pOversample;
int newHeight = imageHeight / pOversample;
int newSize = newWidth * newHeight;
double div = pOversample * pOversample;
float rNew[] = new float[newSize];
float gNew[] = new float[newSize];
float bNew[] = new float[newSize];
for (int rowOld = 0, rowNew = 0; rowOld < imageHeight; rowOld += pOversample, rowNew++) {
for (int colOld = 0, colNew = 0; colOld < imageWidth; colOld += pOversample, colNew++) {
double r = 0.0, g = 0.0, b = 0.0;
for (int i = rowOld; i < rowOld + pOversample; i++) {
for (int j = colOld; j < colOld + pOversample; j++) {
int off = getOffset(j, i);
r += rBuffer[off];
g += gBuffer[off];
b += bBuffer[off];
}
}
int off = rowNew * newWidth + colNew;
rNew[off] = (float) (r / div);
gNew[off] = (float) (g / div);
bNew[off] = (float) (b / div);
}
}
imageWidth = newWidth;
imageHeight = newHeight;
rBuffer = rNew;
gBuffer = gNew;
bBuffer = bNew;
}
public void assignImage(SimpleHDRImage pHDRImg) {
imageWidth = pHDRImg.imageWidth;
imageHeight = pHDRImg.imageHeight;
rBuffer = pHDRImg.rBuffer;
gBuffer = pHDRImg.gBuffer;
bBuffer = pHDRImg.bBuffer;
}
private static final float[] EXPONENT = new float[256];
static {
EXPONENT[0] = 0;
for (int i = 1; i < 256; i++) {
float f = 1.0f;
int e = i - (128 + 8);
if (e > 0)
for (int j = 0; j < e; j++)
f *= 2.0f;
else
for (int j = 0; j < -e; j++)
f *= 0.5f;
EXPONENT[i] = f;
}
}
public void setRGBEValue(int pX, int pY, int pR, int pG, int pB, int pE) {
float e = EXPONENT[pE];
int off = getOffset(pX, pY);
rBuffer[off] = e * (pR + 0.5f);
gBuffer[off] = e * (pG + 0.5f);
bBuffer[off] = e * (pB + 0.5f);
}
public void getMinMaxLum(float[] pLum) {
float minLum = Float.MAX_VALUE;
float maxLum = 0.0f;
for (int i = 0; i < imageHeight; i++) {
for (int j = 0; j < imageWidth; j++) {
float lum = getLum(j, i);
if (lum < minLum) {
minLum = lum;
}
if (lum > maxLum) {
maxLum = lum;
}
}
}
pLum[0] = minLum;
pLum[1] = maxLum;
}
public double getLumIgnoreBounds(int pX, int pY) {
if (pX >= 0 && pX < imageWidth && pY >= 0 && pY < imageHeight) {
return getLum(pX, pY);
}
else {
return 0;
}
}
public void fillBackground(SimpleImage pImage) {
if (pImage.getImageWidth() == imageWidth && pImage.getImageHeight() == imageHeight) {
for (int i = 0; i < imageHeight; i++) {
for (int j = 0; j < imageWidth; j++) {
toolPixel.setARGBValue(pImage.getARGBValue(j, i));
setRGB(j, i, toolPixel.r, toolPixel.g, toolPixel.b);
}
}
}
else {
for (int i = 0; i < imageHeight; i++) {
for (int j = 0; j < imageWidth; j++) {
double xCoord = (double) j * (double) (pImage.getImageWidth() - 1) / (double) (imageWidth - 1);
double yCoord = (double) i * (double) (pImage.getImageHeight() - 1) / (double) (imageHeight - 1);
toolPixel.setARGBValue(pImage.getARGBValueIgnoreBounds((int) xCoord, (int) yCoord));
int luR = toolPixel.r;
int luG = toolPixel.g;
int luB = toolPixel.b;
toolPixel.setARGBValue(pImage.getARGBValueIgnoreBounds(((int) xCoord) + 1, (int) yCoord));
int ruR = toolPixel.r;
int ruG = toolPixel.g;
int ruB = toolPixel.b;
toolPixel.setARGBValue(pImage.getARGBValueIgnoreBounds((int) xCoord, ((int) yCoord) + 1));
int lbR = toolPixel.r;
int lbG = toolPixel.g;
int lbB = toolPixel.b;
toolPixel.setARGBValue(pImage.getARGBValueIgnoreBounds(((int) xCoord) + 1, ((int) yCoord) + 1));
int rbR = toolPixel.r;
int rbG = toolPixel.g;
int rbB = toolPixel.b;
double x = MathLib.frac(xCoord);
double y = MathLib.frac(yCoord);
float r = (float) Tools.roundColor(GfxMathLib.blerp(luR, ruR, lbR, rbR, x, y)) / 255.0f;
float g = (float) Tools.roundColor(GfxMathLib.blerp(luG, ruG, lbG, rbG, x, y)) / 255.f;
float b = (float) Tools.roundColor(GfxMathLib.blerp(luB, ruB, lbB, rbB, x, y)) / 255.f;
setRGB(j, i, r, g, b);
}
}
}
}
}