bdsup2sub.bitmap.FilterOp Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2009, Morten Nobel-Joergensen / Volker Oth (0xdeadbeef) / Miklos Juhasz (mjuhasz)
*
* This program 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 3 of the License, or
* any later version.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
package bdsup2sub.bitmap;
import bdsup2sub.bitmap.Bitmap;
import bdsup2sub.bitmap.Palette;
import com.mortennobel.imagescaling.ResampleFilter;
import com.mortennobel.imagescaling.ResampleFilters;
/**
* Subsampling scaling algorithm with various filters.
*
* Based on the ResampleOp class from the Java Image Scaling Library.
* by Morten Nobel-Joergensen which again is based on "Java Image Util".
*
* @author Morten Nobel-Joergensen / 0xdeadbeef / mjuhasz
*/
class FilterOp {
private int srcWidth;
private int srcHeight;
private final int dstWidth;
private final int dstHeight;
private byte r[];
private byte g[];
private byte b[];
private byte a[];
private SubSamplingData horizontalSubsamplingData;
private SubSamplingData verticalSubsamplingData;
private final ResampleFilter filter;
public FilterOp(ResampleFilter filter, int dstWidth, int dstHeight) {
this.filter = filter;
this.dstWidth = dstWidth;
this.dstHeight = dstHeight;
}
public int[] filter(Bitmap bitmap, Palette palette) {
this.srcWidth = bitmap.getWidth();
this.srcHeight = bitmap.getHeight();
r = palette.getR();
g = palette.getG();
b = palette.getB();
a = palette.getAlpha();
horizontalSubsamplingData = createSubSampling(srcWidth, dstWidth);
verticalSubsamplingData = createSubSampling(srcHeight, dstHeight);
int[] workPixels = new int[srcHeight * dstWidth];
filterHorizontally(bitmap.getInternalBuffer(), workPixels);
int[] outPixels = new int[dstHeight * dstWidth];
filterVertically(workPixels, outPixels);
return outPixels;
}
private SubSamplingData createSubSampling(int srcSize, int dstSize) {
float scalingFactor = (float)(dstSize - 1) / (float)(srcSize - 1);
int[] arrN = new int[dstSize];
int numContributors;
float[] arrWeight;
int[] arrPixel;
float fwidth = filter.getSamplingRadius();
if (scalingFactor < 1.0f) {
// scale down -> subsampling
float width = fwidth / scalingFactor;
numContributors= (int)(width * 2.0f + 2); // Heinz: added 1 to be safe with the ceilling
arrWeight = new float[dstSize * numContributors];
arrPixel = new int[dstSize * numContributors];
float fNormFac = (float)(1f / (Math.ceil(width) / fwidth));
for (int i = 0; i < dstSize; i++) {
arrN[i]= 0;
int subindex = i * numContributors;
float center = i / scalingFactor;
int left = (int)Math.floor(center - width);
int right = (int)Math.ceil(center + width);
for (int j=left; j <= right; j++) {
float weight = filter.apply((center - j) * fNormFac);
if (weight == 0.0f) {
continue;
}
int n;
if (j < 0) {
n = -j;
} else if (j >= srcSize) {
n = srcSize - j + srcSize - 1;
} else {
n = j;
}
int k = arrN[i];
arrN[i]+= 1;
if (n < 0 || n >= srcSize) {
weight = 0.0f; // Flag that cell should not be used
}
arrPixel[subindex + k] = n;
arrWeight[subindex + k] = weight;
}
// normalize the filter's weight's so the sum equals to 1.0, very important for avoiding box type of artifacts
int max= arrN[i];
float tot= 0;
for (int k = 0; k < max; k++) {
tot+= arrWeight[subindex + k];
}
if (tot != 0f) { // 0 should never happen except bug in filter
for (int k = 0; k < max; k++) {
arrWeight[subindex + k] /= tot;
}
}
}
} else {
// scale up -> super-sampling
numContributors = (int)(fwidth * 2.0f + 1);
arrWeight = new float[dstSize * numContributors];
arrPixel = new int[dstSize * numContributors];
//
for (int i = 0; i < dstSize; i++) {
arrN[i] = 0;
int subindex = i * numContributors;
float center = i / scalingFactor;
int left = (int)Math.floor(center - fwidth);
int right = (int)Math.ceil(center + fwidth);
for (int j = left; j <= right; j++) {
float weight = filter.apply(center - j);
if (weight == 0.0f) {
continue;
}
int n;
if (j < 0) {
n = -j;
} else if (j >= srcSize) {
n = srcSize - j + srcSize - 1;
} else {
n = j;
}
int k = arrN[i];
arrN[i] += 1;
if (n < 0 || n >= srcSize) {
weight = 0.0f; // Flag that cell should not be used
}
arrPixel[subindex + k] = n;
arrWeight[subindex + k] = weight;
}
// normalize the filter's weights so the sum equals to 1.0, very important for avoiding box type of artifacts
int max = arrN[i];
float tot = 0;
for (int k = 0; k < max; k++) {
tot += arrWeight[subindex + k];
}
assert tot != 0:"should never happen except bug in filter";
if (tot != 0f) {
for (int k = 0; k < max; k++) {
arrWeight[subindex + k] /= tot;
}
}
}
}
return new SubSamplingData(arrN, arrPixel, arrWeight, numContributors);
}
/**
* Apply filter to sample vertically from temporary buffer to target buffer
* @param src Integer array holding result from filtering horizontally
* @param trg Integer array for target bitmap
*/
private void filterVertically(int[] src, int[] trg) {
for (int x = 0; x < dstWidth; x++) {
for (int y = dstHeight-1; y >= 0 ; y--) {
int yTimesNumContributors = y * verticalSubsamplingData.matrixWidth;
int max = verticalSubsamplingData.sampleCount[y];
int ofsY = dstWidth * y;
float red = 0;
float green = 0;
float blue = 0;
float alpha = 0;
int index = yTimesNumContributors;
for (int j = max-1; j >= 0 ; j--) {
int color = src[x + dstWidth * verticalSubsamplingData.pixelPositions[index]];
float w = verticalSubsamplingData.weightFactors[index];
alpha += ((color >> 24)&0xff) * w;
red += ((color >> 16)&0xff) * w;
green += ((color >> 8)&0xff) * w;
blue += (color &0xff) * w;
index++;
}
int ri = (int)(red);
if (ri < 0) {
ri = 0;
} else if (ri > 255) {
ri = 255;
}
int gi = (int)(green);
if (gi < 0) {
gi = 0;
} else if (gi > 255) {
gi = 255;
}
int bi = (int)(blue);
if (bi < 0) {
bi = 0;
} else if (bi > 255) {
bi = 255;
}
int ai = (int)(alpha);
if (ai < 0) {
ai = 0;
} else if (ai > 255) {
ai = 255;
}
trg[x + ofsY] = ( (ai<<24) | (ri<<16) | (gi << 8) | bi);
}
}
}
/**
* Apply filter to sample horizontally from src to Work
* @param src Byte array holding source image data
* @param trg Integer array to store temporary result from filtering horizontally
*/
private void filterHorizontally(byte[] src, int[] trg) {
for (int k = 0; k < srcHeight; k++) {
int destOfsY = dstWidth * k;
int srcOfsY = srcWidth * k;
for (int i = dstWidth-1; i >= 0 ; i--) {
float red = 0;
float green = 0;
float blue = 0;
float alpha = 0;
int max = horizontalSubsamplingData.sampleCount[i];
int index = i * horizontalSubsamplingData.matrixWidth;
for (int j = max-1; j >= 0; j--) {
int ofsX = horizontalSubsamplingData.pixelPositions[index];
int palIdx = src[srcOfsY+ofsX] & 0xff;
float w = horizontalSubsamplingData.weightFactors[index];
red += (r[palIdx] & 0xff) * w;
green += (g[palIdx] & 0xff) * w;
blue += (b[palIdx] & 0xff) * w;
alpha += (a[palIdx] & 0xff) * w;
index++;
}
int ri = (int)(red);
if (ri < 0) {
ri = 0;
} else if (ri > 255) {
ri = 255;
}
int gi = (int)(green);
if (gi < 0) {
gi = 0;
} else if (gi > 255) {
gi = 255;
}
int bi = (int)(blue);
if (bi < 0) {
bi = 0;
} else if (bi > 255) {
bi = 255;
}
int ai = (int)(alpha);
if (ai < 0) {
ai = 0;
} else if (ai > 255) {
ai = 255;
}
trg[i + destOfsY] = ( (ai<<24) | (ri<<16) | (gi << 8) | bi);
}
}
}
private class SubSamplingData {
/** Number of samples */
private final int[] sampleCount;
/** 2D matrix of pixel positions */
private final int[] pixelPositions;
/** 2D matrix of weight factors */
private final float[] weightFactors;
/** Width of 2D matrices pixelPos and weight */
private final int matrixWidth;
/**
* Private storage class to hold precalculated values for subsampling or supersampling
* @param sampleCount Number of samples contributing to the pixel
* @param pixelPositions 2D matrix of pixel positions
* @param weightFactors 2D matrix of weight factors
* @param matrixWidth Width of 2D matrices pixelPos and weight
*/
private SubSamplingData(int[] sampleCount, int[] pixelPositions, float[] weightFactors, int matrixWidth) {
this.sampleCount = sampleCount;
this.pixelPositions = pixelPositions;
this.weightFactors = weightFactors;
this.matrixWidth = matrixWidth;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy