Implemented all colorfilters in TMXMapEditor, thanks to the new

MatrixComposite that really emulates the ColorMatrixColorFilter of
Android ! Hi-fidelity filter emulation !
This commit is contained in:
Zukero
2016-08-10 16:20:48 +02:00
parent 1458fb0aaa
commit c07fb4ddf3
3 changed files with 204 additions and 18 deletions

View File

@@ -44,7 +44,10 @@ public class TMXMap extends GameDataElement {
black60,
black80,
invert,
bw
bw,
redtint,
greentint,
bluetint
}
public File tmxFile = null;

View File

@@ -3,6 +3,7 @@ package com.gpl.rpg.atcontentstudio.ui.map;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
@@ -10,7 +11,6 @@ import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
@@ -19,13 +19,7 @@ import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.image.BandCombineOp;
import java.awt.image.BufferedImageOp;
import java.awt.image.ByteLookupTable;
import java.awt.image.ColorConvertOp;
import java.awt.image.LookupOp;
import java.awt.image.LookupTable;
import java.awt.image.ShortLookupTable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -108,6 +102,7 @@ import com.gpl.rpg.atcontentstudio.ui.Editor;
import com.gpl.rpg.atcontentstudio.ui.FieldUpdateListener;
import com.gpl.rpg.atcontentstudio.ui.IntegerBasedCheckBox;
import com.gpl.rpg.atcontentstudio.ui.ScrollablePanel;
import com.gpl.rpg.atcontentstudio.ui.tools.MatrixComposite;
import com.jidesoft.swing.JideBoxLayout;
import com.jidesoft.swing.JideTabbedPane;
@@ -1473,37 +1468,101 @@ public class TMXMapEditor extends Editor {
}
}
if (map.colorFilter != null) {
Composite oldComp = g2d.getComposite();
MatrixComposite newComp = null;
float f=0.0f;
switch(map.colorFilter) {
case black20:
g2d.setPaint(new Color(0f, 0f, 0f, 0.2f));
g2d.fill(clip);
f=0.8f;
newComp = new MatrixComposite(new float[]{
f, 0.00f, 0.00f, 0.0f, 0.0f,
0.00f, f, 0.00f, 0.0f, 0.0f,
0.00f, 0.00f, f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
break;
case black40:
g2d.setPaint(new Color(0f, 0f, 0f, 0.4f));
g2d.fill(clip);
f=0.6f;
newComp = new MatrixComposite(new float[]{
f, 0.00f, 0.00f, 0.0f, 0.0f,
0.00f, f, 0.00f, 0.0f, 0.0f,
0.00f, 0.00f, f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
break;
case black60:
g2d.setPaint(new Color(0f, 0f, 0f, 0.6f));
g2d.fill(clip);
f=0.4f;
newComp = new MatrixComposite(new float[]{
f, 0.00f, 0.00f, 0.0f, 0.0f,
0.00f, f, 0.00f, 0.0f, 0.0f,
0.00f, 0.00f, f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
break;
case black80:
g2d.setPaint(new Color(0f, 0f, 0f, 0.8f));
g2d.fill(clip);
f=0.2f;
newComp = new MatrixComposite(new float[]{
f, 0.00f, 0.00f, 0.0f, 0.0f,
0.00f, f, 0.00f, 0.0f, 0.0f,
0.00f, 0.00f, f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
break;
case bw:
newComp = new MatrixComposite(new float[]{
0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
break;
case invert:
newComp = new MatrixComposite(new float[]{
-1.00f, 0.00f, 0.00f, 0.0f, 255.0f,
0.00f, -1.00f, 0.00f, 0.0f, 255.0f,
0.00f, 0.00f, -1.00f, 0.0f, 255.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
break;
case redtint:
newComp = new MatrixComposite(new float[]{
1.20f, 0.20f, 0.20f, 0.0f, 25.0f,
0.00f, 0.80f, 0.00f, 0.0f, 0.0f,
0.00f, 0.00f, 0.80f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
break;
case greentint:
newComp = new MatrixComposite(new float[]{
0.85f, 0.00f, 0.00f, 0.0f, 0.0f,
0.15f, 1.15f, 0.15f, 0.0f, 15.0f,
0.00f, 0.00f, 0.85f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
break;
case bluetint:
newComp = new MatrixComposite(new float[]{
0.70f, 0.00f, 0.00f, 0.0f, 0.0f,
0.00f, 0.70f, 0.00f, 0.0f, 0.0f,
0.30f, 0.30f, 1.30f, 0.0f, 40.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f
});
break;
default:
break;
}
if (newComp != null) {
g2d.setComposite(newComp);
g2d.setPaint(new Color(1.0f, 1.0f, 1.0f, 1.0f));
g2d.fill(clip);
g2d.setComposite(oldComp);
}
}
// Draw each object map layer
// Draw each map object layer
boolean paintSelected = false;
for (tiled.core.MapLayer layer : ((TMXMap)target).tmxMap) {
if (layer instanceof tiled.core.ObjectGroup && layer.isVisible()) {

View File

@@ -0,0 +1,124 @@
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;
}
}
}