mirror of
https://github.com/AndorsTrailRelease/ATCS.git
synced 2025-10-27 18:44:03 +01:00
MatrixComposite that really emulates the ColorMatrixColorFilter of Android ! Hi-fidelity filter emulation !
125 lines
4.9 KiB
Java
125 lines
4.9 KiB
Java
package com.gpl.rpg.atcontentstudio.ui.tools;
|
|
|
|
import java.awt.Composite;
|
|
import java.awt.CompositeContext;
|
|
import java.awt.RenderingHints;
|
|
import java.awt.image.ColorModel;
|
|
import java.awt.image.DataBuffer;
|
|
import java.awt.image.Raster;
|
|
import java.awt.image.WritableRaster;
|
|
|
|
/**
|
|
* This Composite emulates the behaviour of Android's ColorMatrixColorFilter,
|
|
* except that you have to use this one a posteriori instead of a filtering paint.
|
|
*
|
|
* It applies a ColorMatrix to the destination pixels, regardless of potential "source" pixels.
|
|
* Once created and activated through Graphics2D.setComposite(), just paint anything over the pixels you want "filtered".
|
|
*
|
|
* Works on a per-pixel basis, no sampling of surrounding pixels, or anything.
|
|
*
|
|
* @author pochat
|
|
*
|
|
*/
|
|
public class MatrixComposite implements Composite {
|
|
|
|
|
|
final float[] matrix = new float[20];
|
|
|
|
|
|
/**
|
|
* Dismisses the source pixels. Just paint it black, or white, it only affects the dest RGB with the following formulae.
|
|
*
|
|
* R' = a*R + b*G + c*B + d*A + e;
|
|
* G' = f*R + g*G + h*B + i*A + j;
|
|
* B' = k*R + l*G + m*B + n*A + o;
|
|
* A' = p*R + q*G + r*B + s*A + t;
|
|
*
|
|
* @param matrix a flat float[20] array, giving the a..t values;
|
|
*/
|
|
public MatrixComposite(float[] matrix) {
|
|
if (matrix.length != this.matrix.length) {
|
|
throw new Error("MatrixComposite matrix must be of length "+this.matrix.length);
|
|
}
|
|
System.arraycopy(matrix, 0, this.matrix, 0, this.matrix.length);
|
|
}
|
|
|
|
@Override
|
|
public CompositeContext createContext(ColorModel srcColorModel,
|
|
ColorModel dstColorModel, RenderingHints hints) {
|
|
return new MatrixCompositeContext(this);
|
|
}
|
|
|
|
class MatrixCompositeContext implements CompositeContext {
|
|
|
|
MatrixComposite composite;
|
|
|
|
public MatrixCompositeContext(MatrixComposite composite) {
|
|
this.composite = composite;
|
|
}
|
|
|
|
@Override
|
|
public void dispose() {
|
|
}
|
|
|
|
@Override
|
|
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
|
|
if (src.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
|
|
dstIn.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
|
|
dstOut.getSampleModel().getDataType() != DataBuffer.TYPE_INT) {
|
|
throw new IllegalStateException(
|
|
"Source and destination must store pixels as INT.");
|
|
}
|
|
|
|
int width = Math.min(src.getWidth(), dstIn.getWidth());
|
|
int height = Math.min(src.getHeight(), dstIn.getHeight());
|
|
|
|
float alpha = 1.0f;
|
|
|
|
int[] srcPixel = new int[4];
|
|
int[] dstPixel = new int[4];
|
|
int[] srcPixels = new int[width];
|
|
int[] dstPixels = new int[width];
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
src.getDataElements(0, y, width, 1, srcPixels);
|
|
dstIn.getDataElements(0, y, width, 1, dstPixels);
|
|
for (int x = 0; x < width; x++) {
|
|
// pixels are stored as INT_ARGB
|
|
// our arrays are [R, G, B, A]
|
|
int pixel = srcPixels[x];
|
|
srcPixel[0] = (pixel >> 16) & 0xFF;
|
|
srcPixel[1] = (pixel >> 8) & 0xFF;
|
|
srcPixel[2] = (pixel ) & 0xFF;
|
|
srcPixel[3] = (pixel >> 24) & 0xFF;
|
|
|
|
pixel = dstPixels[x];
|
|
dstPixel[0] = (pixel >> 16) & 0xFF;
|
|
dstPixel[1] = (pixel >> 8) & 0xFF;
|
|
dstPixel[2] = (pixel ) & 0xFF;
|
|
dstPixel[3] = (pixel >> 24) & 0xFF;
|
|
|
|
int[] result = applyMatrix(matrix, dstPixel);
|
|
|
|
// mixes the result with the opacity
|
|
dstPixels[x] = ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 |
|
|
((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 |
|
|
((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8 |
|
|
(int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
|
|
}
|
|
dstOut.setDataElements(0, y, width, 1, dstPixels);
|
|
}
|
|
}
|
|
|
|
private int[] applyMatrix(float[] matrix, int[] dstPixel) {
|
|
int[] result = new int[4];
|
|
result[0] = Math.max(0, Math.min(255, (int) (matrix[ 0] * dstPixel[0] + matrix[ 1] * dstPixel[1] + matrix[ 2] * dstPixel[2] + matrix[ 3] * dstPixel[3] + matrix[ 4]) ));
|
|
result[1] = Math.max(0, Math.min(255, (int) (matrix[ 5] * dstPixel[0] + matrix[ 6] * dstPixel[1] + matrix[ 7] * dstPixel[2] + matrix[ 8] * dstPixel[3] + matrix[ 9]) ));
|
|
result[2] = Math.max(0, Math.min(255, (int) (matrix[10] * dstPixel[0] + matrix[11] * dstPixel[1] + matrix[12] * dstPixel[2] + matrix[13] * dstPixel[3] + matrix[14]) ));
|
|
result[3] = Math.max(0, Math.min(255, (int) (matrix[15] * dstPixel[0] + matrix[16] * dstPixel[1] + matrix[17] * dstPixel[2] + matrix[18] * dstPixel[3] + matrix[19]) ));
|
|
return result;
|
|
}
|
|
|
|
}
|
|
|
|
}
|