mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
add mutable filters into internal color blindness test
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package com.intellij.ide.ui;
|
||||
|
||||
import com.intellij.util.Matrix;
|
||||
import com.intellij.util.Vector;
|
||||
|
||||
/**
|
||||
* @author Sergey.Malenkov
|
||||
*/
|
||||
final class ColorBlindnessMatrix {
|
||||
private static final Matrix CORRECTION = Matrix.createIdentity(3);
|
||||
|
||||
private static final Matrix RGB_LMS; // a matrix to convert a RGB color to the LMS space
|
||||
private static final Matrix LMS_RGB; // a matrix to convert a LMS color to the RGB space
|
||||
|
||||
private static final Vector WHITE_BLUE;
|
||||
private static final Vector WHITE_RED;
|
||||
|
||||
static {
|
||||
// a matrix to convert a RGB color to the XYZ space
|
||||
Matrix RGB_XYZ = Matrix.create(3,
|
||||
0.4124, 0.2126, 0.0193,
|
||||
0.3576, 0.7152, 0.1192,
|
||||
0.1805, 0.0722, 0.9505);
|
||||
|
||||
// a matrix to convert an XYZ color to the LMS space
|
||||
Matrix XYZ_LMS = Matrix.create(3,
|
||||
0.7328, -0.7036, 0.0030,
|
||||
0.4296, 1.6975, 0.0136,
|
||||
-0.1624, 0.0061, 0.9834);
|
||||
|
||||
// create direct conversion from RGB to LMS and vice versa
|
||||
RGB_LMS = RGB_XYZ.multiply(XYZ_LMS);
|
||||
LMS_RGB = RGB_LMS.inverse();
|
||||
|
||||
// To simulate color blindness we remove the data lost by the absence of a cone.
|
||||
// This cannot be done by just zeroing out the corresponding LMS component,
|
||||
// because it would create a color outside of the RGB gammut. Instead,
|
||||
// we project the color along the axis of the missing component
|
||||
// onto a plane within the RGB gammut:
|
||||
// - since the projection happens along the axis of the missing component,
|
||||
// a color blind viewer perceives the projected color the same.
|
||||
// - We use the plane defined by 3 points in LMS space: black, white and
|
||||
// blue and red for protanopia/deuteranopia and tritanopia respectively.
|
||||
Vector red = RGB_LMS.getRow(0); // LMS space red
|
||||
Vector blue = RGB_LMS.getRow(2); // LMS space blue
|
||||
Vector white = Vector.create(1, 1, 1).multiply(RGB_LMS); // LMS space white
|
||||
|
||||
// To find the planes we solve the a*L + b*M + c*S = 0 equation
|
||||
// for the LMS values of the three known points. This equation is trivially solved,
|
||||
// and has for solution the following cross-products:
|
||||
WHITE_BLUE = cross(white, blue); // protanopia/deuteranopia
|
||||
WHITE_RED = cross(white, red); // tritanopia
|
||||
}
|
||||
|
||||
private static Vector cross(Vector left, Vector right) {
|
||||
return Vector.create(
|
||||
left.get(1) * right.get(2) - left.get(2) * right.get(1),
|
||||
left.get(2) * right.get(0) - left.get(0) * right.get(2),
|
||||
left.get(0) * right.get(1) - left.get(1) * right.get(0));
|
||||
}
|
||||
|
||||
private static Matrix calculate(Matrix simulation, Matrix correction) {
|
||||
// We will calculate the error between the color and the color
|
||||
// viewed by a color blind user and "spread" this error onto the healthy cones.
|
||||
// The correction matrix perform this last step and have been chosen arbitrarily.
|
||||
Matrix matrix = simulation.multiply(RGB_LMS);
|
||||
if (correction == null) correction = CORRECTION;
|
||||
return LMS_RGB.multiply(matrix.plus(correction.multiply(RGB_LMS.minus(matrix))));
|
||||
}
|
||||
|
||||
static final class Protanopia {
|
||||
private static final double V1 = -WHITE_BLUE.get(1) / WHITE_BLUE.get(0);
|
||||
private static final double V2 = -WHITE_BLUE.get(2) / WHITE_BLUE.get(0);
|
||||
private static final Matrix SIMULATION = Matrix.create(3, 0, 0, 0, V1, 1, 0, V2, 0, 1);
|
||||
private static final Matrix CORRECTION = Matrix.create(3, 1, .7, .7, 0, 1, 0, 0, 0, 1);
|
||||
|
||||
static final Matrix MATRIX = calculate(CORRECTION);
|
||||
|
||||
static Matrix calculate(Matrix correction) {
|
||||
return ColorBlindnessMatrix.calculate(SIMULATION, correction);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Deuteranopia {
|
||||
private static final double V1 = -WHITE_BLUE.get(0) / WHITE_BLUE.get(1);
|
||||
private static final double V2 = -WHITE_BLUE.get(2) / WHITE_BLUE.get(1);
|
||||
private static final Matrix SIMULATION = Matrix.create(3, 1, V1, 0, 0, 0, 0, 0, V2, 1);
|
||||
private static final Matrix CORRECTION = Matrix.create(3, 1, 0, 0, .7, 1, .7, 0, 0, 1);
|
||||
|
||||
static final Matrix MATRIX = calculate(CORRECTION);
|
||||
|
||||
static Matrix calculate(Matrix correction) {
|
||||
return ColorBlindnessMatrix.calculate(SIMULATION, correction);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Tritanopia {
|
||||
private static final double V1 = -WHITE_RED.get(0) / WHITE_RED.get(2);
|
||||
private static final double V2 = -WHITE_RED.get(1) / WHITE_RED.get(2);
|
||||
private static final Matrix SIMULATION = Matrix.create(3, 1, 0, V1, 0, 1, V2, 0, 0, 0);
|
||||
private static final Matrix CORRECTION = Matrix.create(3, 1, 0, 0, 0, 1, 0, .7, .7, 1);
|
||||
|
||||
static final Matrix MATRIX = calculate(CORRECTION);
|
||||
|
||||
static Matrix calculate(Matrix correction) {
|
||||
return ColorBlindnessMatrix.calculate(SIMULATION, correction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.intellij.ide.ui;
|
||||
|
||||
/**
|
||||
* @author Sergey.Malenkov
|
||||
*/
|
||||
class ColorConverter {
|
||||
public int convert(int red, int green, int blue, int alpha) {
|
||||
return (fix(red) << 16) | (fix(green) << 8) | fix(blue) | (fix(alpha) << 24);
|
||||
}
|
||||
|
||||
public int convert(int argb) {
|
||||
return convert(0xFF & (argb >> 16), 0xFF & (argb >> 8), 0xFF & argb, 0xFF & (argb >> 24));
|
||||
}
|
||||
|
||||
private static int fix(int value) {
|
||||
return value < 0 ? 0 : value > 255 ? 255 : value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.intellij.ide.ui;
|
||||
|
||||
import com.intellij.util.Matrix;
|
||||
import com.intellij.util.Vector;
|
||||
|
||||
/**
|
||||
* @author Sergey.Malenkov
|
||||
*/
|
||||
final class MatrixConverter extends ColorConverter {
|
||||
private final Double myWeight;
|
||||
private final Matrix myMatrix;
|
||||
|
||||
public MatrixConverter(Matrix matrix) {
|
||||
this(null, matrix);
|
||||
}
|
||||
|
||||
public MatrixConverter(Double weight, Matrix matrix) {
|
||||
if (weight != null && !(0 < weight && weight < 1)) throw new IllegalArgumentException("unsupported weight");
|
||||
int rows = matrix.getRows();
|
||||
if (rows != 3 && rows != 4) throw new IllegalArgumentException("unsupported rows");
|
||||
int columns = matrix.getColumns();
|
||||
if (columns != 3 && columns != 4) throw new IllegalArgumentException("unsupported columns");
|
||||
myWeight = weight;
|
||||
myMatrix = matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convert(int red, int green, int blue, int alpha) {
|
||||
Vector vector = myMatrix.getRows() == 4
|
||||
? Vector.create(red, green, blue, alpha)
|
||||
: Vector.create(red, green, blue);
|
||||
|
||||
Vector result = vector.multiply(myMatrix);
|
||||
if (myWeight != null) {
|
||||
if (vector.getSize() != result.getSize()) {
|
||||
vector = result.getSize() == 4
|
||||
? Vector.create(red, green, blue, alpha)
|
||||
: Vector.create(red, green, blue);
|
||||
}
|
||||
vector = vector.multiply(1 - myWeight);
|
||||
result = result.multiply(myWeight).plus(vector);
|
||||
}
|
||||
red = (int)result.get(0);
|
||||
green = (int)result.get(1);
|
||||
blue = (int)result.get(2);
|
||||
if (result.getSize() == 4) {
|
||||
alpha = (int)result.get(3);
|
||||
}
|
||||
return super.convert(red, green, blue, alpha);
|
||||
}
|
||||
}
|
||||
@@ -15,86 +15,10 @@
|
||||
*/
|
||||
package com.intellij.ide.ui;
|
||||
|
||||
import com.intellij.util.Matrix;
|
||||
import com.intellij.util.Vector;
|
||||
|
||||
import java.awt.image.ImageFilter;
|
||||
import java.awt.image.RGBImageFilter;
|
||||
|
||||
final class MatrixFilter extends WeightFilter {
|
||||
private static final Matrix RGB_LMS; // a matrix to convert a RGB color to the LMS space
|
||||
private static final Matrix LMS_RGB; // a matrix to convert a LMS color to the RGB space
|
||||
|
||||
private static final Matrix PROTANOPIA_SIMULATION;
|
||||
private static final Matrix PROTANOPIA_CORRECTION;
|
||||
|
||||
private static final Matrix DEUTERANOPIA_SIMULATION;
|
||||
private static final Matrix DEUTERANOPIA_CORRECTION;
|
||||
|
||||
private static final Matrix TRITANOPIA_SIMULATION;
|
||||
private static final Matrix TRITANOPIA_CORRECTION;
|
||||
|
||||
static {
|
||||
Matrix RGB_XYZ = Matrix.create(3, // a matrix to convert a RGB color to the XYZ space
|
||||
0.4124, 0.2126, 0.0193,
|
||||
0.3576, 0.7152, 0.1192,
|
||||
0.1805, 0.0722, 0.9505);
|
||||
Matrix XYZ_LMS = Matrix.create(3, // a matrix to convert an XYZ color to the LMS space
|
||||
0.7328, -0.7036, 0.0030,
|
||||
0.4296, 1.6975, 0.0136,
|
||||
-0.1624, 0.0061, 0.9834);
|
||||
|
||||
// create direct conversion from RGB to LMS and vice versa
|
||||
|
||||
RGB_LMS = RGB_XYZ.multiply(XYZ_LMS);
|
||||
LMS_RGB = RGB_LMS.inverse();
|
||||
|
||||
// To simulate color blindness we remove the data lost by the absence of a cone.
|
||||
// This cannot be done by just zeroing out the corresponding LMS component,
|
||||
// because it would create a color outside of the RGB gammut. Instead,
|
||||
// we project the color along the axis of the missing component
|
||||
// onto a plane within the RGB gammut:
|
||||
// - since the projection happens along the axis of the missing component,
|
||||
// a color blind viewer perceives the projected color the same.
|
||||
// - We use the plane defined by 3 points in LMS space: black, white and
|
||||
// blue and red for protanopia/deuteranopia and tritanopia respectively.
|
||||
|
||||
Vector red = RGB_LMS.getRow(0); // LMS space red
|
||||
Vector blue = RGB_LMS.getRow(2); // LMS space blue
|
||||
Vector white = Vector.create(1, 1, 1).multiply(RGB_LMS); // LMS space white
|
||||
|
||||
// To find the planes we solve the a*L + b*M + c*S = 0 equation
|
||||
// for the LMS values of the three known points. This equation is trivially solved,
|
||||
// and has for solution the following cross-products:
|
||||
|
||||
Vector p0 = cross(white, blue); // protanopia/deuteranopia
|
||||
Vector p1 = cross(white, red); // tritanopia
|
||||
|
||||
// The following 3 matrices perform the projection of a LMS color
|
||||
// onto the given plane along the selected axis
|
||||
|
||||
PROTANOPIA_SIMULATION = Matrix.create(3,
|
||||
0, 0, 0,
|
||||
-p0.get(1) / p0.get(0), 1, 0,
|
||||
-p0.get(2) / p0.get(0), 0, 1);
|
||||
DEUTERANOPIA_SIMULATION = Matrix.create(3,
|
||||
1, -p0.get(0) / p0.get(1), 0,
|
||||
0, 0, 0,
|
||||
0, -p0.get(2) / p0.get(1), 1);
|
||||
TRITANOPIA_SIMULATION = Matrix.create(3,
|
||||
1, 0, -p1.get(0) / p1.get(2),
|
||||
0, 1, -p1.get(1) / p1.get(2),
|
||||
0, 0, 0);
|
||||
|
||||
// We will calculate the error between the color and the color
|
||||
// viewed by a color blind user and "spread" this error onto the healthy cones.
|
||||
// The matrices below perform this last step and have been chosen arbitrarily.
|
||||
// The amount of correction can be adjusted here.
|
||||
|
||||
PROTANOPIA_CORRECTION = Matrix.create(3, 1, .7, .7, 0, 1, 0, 0, 0, 1);
|
||||
DEUTERANOPIA_CORRECTION = Matrix.create(3, 1, 0, 0, .7, 1, .7, 0, 0, 1);
|
||||
TRITANOPIA_CORRECTION = Matrix.create(3, 1, 0, 0, 0, 1, 0, .7, .7, 1);
|
||||
}
|
||||
|
||||
final class MatrixFilter extends RGBImageFilter {
|
||||
public static ImageFilter get(ColorBlindness blindness) {
|
||||
if (blindness == ColorBlindness.protanopia) return protanopia;
|
||||
if (blindness == ColorBlindness.deuteranopia) return deuteranopia;
|
||||
@@ -102,45 +26,37 @@ final class MatrixFilter extends WeightFilter {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static final ImageFilter protanopia = forProtanopia(null, true);
|
||||
public static final ImageFilter deuteranopia = forDeuteranopia(null, true);
|
||||
public static final ImageFilter tritanopia = forTritanopia(null, true);
|
||||
public static final ImageFilter protanopia = forProtanopia(null);
|
||||
public static final ImageFilter deuteranopia = forDeuteranopia(null);
|
||||
public static final ImageFilter tritanopia = forTritanopia(null);
|
||||
|
||||
public static ImageFilter forProtanopia(Double weight, boolean fix) {
|
||||
return new MatrixFilter("Protanopia", weight, PROTANOPIA_SIMULATION, fix ? PROTANOPIA_CORRECTION : null);
|
||||
public static ImageFilter forProtanopia(Double weight) {
|
||||
return new MatrixFilter("Protanopia (correction matrix)", new MatrixConverter(weight, ColorBlindnessMatrix.Protanopia.MATRIX));
|
||||
}
|
||||
|
||||
public static ImageFilter forDeuteranopia(Double weight, boolean fix) {
|
||||
return new MatrixFilter("Deuteranopia", weight, DEUTERANOPIA_SIMULATION, fix ? DEUTERANOPIA_CORRECTION : null);
|
||||
public static ImageFilter forDeuteranopia(Double weight) {
|
||||
return new MatrixFilter("Deuteranopia (correction matrix)", new MatrixConverter(weight, ColorBlindnessMatrix.Deuteranopia.MATRIX));
|
||||
}
|
||||
|
||||
public static ImageFilter forTritanopia(Double weight, boolean fix) {
|
||||
return new MatrixFilter("Tritanopia", weight, TRITANOPIA_SIMULATION, fix ? TRITANOPIA_CORRECTION : null);
|
||||
public static ImageFilter forTritanopia(Double weight) {
|
||||
return new MatrixFilter("Tritanopia (correction matrix)", new MatrixConverter(weight, ColorBlindnessMatrix.Tritanopia.MATRIX));
|
||||
}
|
||||
|
||||
private final Matrix myMatrix;
|
||||
private final String myName;
|
||||
private final ColorConverter myConverter;
|
||||
|
||||
private MatrixFilter(String name, Double weight, Matrix simulation, Matrix correction) {
|
||||
super(name + (correction == null ? " (simulation matrix)" : " (correction matrix)"), weight);
|
||||
Matrix matrix = simulation.multiply(RGB_LMS);
|
||||
if (correction != null) {
|
||||
Matrix minus = RGB_LMS.minus(matrix);
|
||||
Matrix multiply = correction.multiply(minus);
|
||||
matrix = matrix.plus(multiply);
|
||||
}
|
||||
myMatrix = LMS_RGB.multiply(matrix);
|
||||
private MatrixFilter(String name, ColorConverter converter) {
|
||||
myName = name;
|
||||
myConverter = converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
int toRGB(int srcR, int srcG, int srcB) {
|
||||
Vector vector = Vector.create(srcR, srcG, srcB).multiply(myMatrix);
|
||||
return toRGB(srcR, srcG, srcB, fix(vector.get(0)), fix(vector.get(1)), fix(vector.get(2)));
|
||||
public String toString() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
private static Vector cross(Vector left, Vector right) {
|
||||
return Vector.create(
|
||||
left.get(1) * right.get(2) - left.get(2) * right.get(1),
|
||||
left.get(2) * right.get(0) - left.get(0) * right.get(2),
|
||||
left.get(0) * right.get(1) - left.get(1) * right.get(0));
|
||||
@Override
|
||||
public int filterRGB(int x, int y, int rgb) {
|
||||
return myConverter.convert(rgb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.ui.components.panels.VerticalLayout;
|
||||
import com.intellij.util.Matrix;
|
||||
import com.intellij.util.ui.ImageUtil;
|
||||
import com.intellij.util.ui.JBDimension;
|
||||
import com.intellij.util.ui.JBInsets;
|
||||
@@ -27,11 +29,12 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ImageFilter;
|
||||
import java.awt.image.RGBImageFilter;
|
||||
|
||||
/**
|
||||
* @author Sergey.Malenkov
|
||||
@@ -45,6 +48,35 @@ public class ColorBlindnessInternalAction extends DumbAwareAction {
|
||||
private static final class ColorDialog extends DialogWrapper {
|
||||
private final ColorView myView = new ColorView();
|
||||
private final JComboBox myCombo = new ComboBox<>(FilterItem.ALL);
|
||||
private final JSlider myFirstSlider = createSlider();
|
||||
private final JSlider mySecondSlider = createSlider();
|
||||
|
||||
private static JSlider createSlider() {
|
||||
JSlider slider = new JSlider(0, 100);
|
||||
slider.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
|
||||
slider.setMajorTickSpacing(10);
|
||||
slider.setMinorTickSpacing(1);
|
||||
slider.setPaintTicks(true);
|
||||
slider.setPaintLabels(true);
|
||||
slider.setVisible(false);
|
||||
return slider;
|
||||
}
|
||||
|
||||
private static void showSlider(JSlider slider, ChangeListener listener) {
|
||||
slider.setValue(70);
|
||||
slider.setVisible(true);
|
||||
slider.addChangeListener(listener);
|
||||
}
|
||||
|
||||
private static void hideSlider(JSlider slider, ChangeListener listener) {
|
||||
slider.removeChangeListener(listener);
|
||||
slider.setVisible(false);
|
||||
}
|
||||
|
||||
private void updateFilter(MutableFilter filter) {
|
||||
filter.update(myFirstSlider.getValue() / 100.0, mySecondSlider.getValue() / 100.0);
|
||||
myView.set(filter);
|
||||
}
|
||||
|
||||
private ColorDialog(AnActionEvent event) {
|
||||
super(event.getProject());
|
||||
@@ -64,11 +96,38 @@ public class ColorBlindnessInternalAction extends DumbAwareAction {
|
||||
myView.setMinimumSize(new JBDimension(360, 200));
|
||||
myView.setPreferredSize(new JBDimension(720, 400));
|
||||
|
||||
myCombo.addItemListener(myView);
|
||||
ChangeListener listener = event -> {
|
||||
if (myView.myFilter instanceof MutableFilter) {
|
||||
updateFilter((MutableFilter)myView.myFilter);
|
||||
}
|
||||
};
|
||||
|
||||
myCombo.addItemListener(event -> {
|
||||
if (ItemEvent.SELECTED == event.getStateChange()) {
|
||||
Object object = event.getItem();
|
||||
if (object instanceof FilterItem) {
|
||||
FilterItem item = (FilterItem)object;
|
||||
if (item.myFilter instanceof MutableFilter) {
|
||||
showSlider(myFirstSlider, listener);
|
||||
showSlider(mySecondSlider, listener);
|
||||
updateFilter((MutableFilter)item.myFilter);
|
||||
}
|
||||
else {
|
||||
hideSlider(myFirstSlider, listener);
|
||||
hideSlider(mySecondSlider, listener);
|
||||
myView.set(item.myFilter);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
JPanel control = new JPanel(new VerticalLayout(10));
|
||||
control.add(VerticalLayout.TOP, myCombo);
|
||||
control.add(VerticalLayout.TOP, myFirstSlider);
|
||||
control.add(VerticalLayout.TOP, mySecondSlider);
|
||||
|
||||
JPanel panel = new JPanel(new BorderLayout(10, 10));
|
||||
panel.add(BorderLayout.CENTER, myView);
|
||||
panel.add(BorderLayout.SOUTH, myCombo);
|
||||
panel.add(BorderLayout.SOUTH, control);
|
||||
return panel;
|
||||
}
|
||||
|
||||
@@ -95,21 +154,39 @@ public class ColorBlindnessInternalAction extends DumbAwareAction {
|
||||
new FilterItem(null),
|
||||
new FilterItem(DaltonizationFilter.protanopia),
|
||||
new FilterItem(MatrixFilter.protanopia),
|
||||
new FilterItem(new MutableFilter("Protanopia (mutable)") {
|
||||
@Override
|
||||
ColorConverter getConverter(double first, double second) {
|
||||
Matrix matrix = Matrix.create(3, 1, first, second, 0, 1, 0, 0, 0, 1);
|
||||
return new MatrixConverter(ColorBlindnessMatrix.Protanopia.calculate(matrix));
|
||||
}
|
||||
}),
|
||||
new FilterItem(DaltonizationFilter.deuteranopia),
|
||||
new FilterItem(MatrixFilter.deuteranopia),
|
||||
new FilterItem(new MutableFilter("Deuteranopia (mutable)") {
|
||||
@Override
|
||||
ColorConverter getConverter(double first, double second) {
|
||||
Matrix matrix = Matrix.create(3, 1, 0, 0, first, 1, second, 0, 0, 1);
|
||||
return new MatrixConverter(ColorBlindnessMatrix.Deuteranopia.calculate(matrix));
|
||||
}
|
||||
}),
|
||||
new FilterItem(DaltonizationFilter.tritanopia),
|
||||
new FilterItem(MatrixFilter.tritanopia),
|
||||
new FilterItem(new MutableFilter("Tritanopia (mutable)") {
|
||||
@Override
|
||||
ColorConverter getConverter(double first, double second) {
|
||||
Matrix matrix = Matrix.create(3, 1, 0, 0, 0, 1, 0, first, second, 1);
|
||||
return new MatrixConverter(ColorBlindnessMatrix.Tritanopia.calculate(matrix));
|
||||
}
|
||||
}),
|
||||
new FilterItem(SimulationFilter.protanopia),
|
||||
new FilterItem(MatrixFilter.forProtanopia(null, false)),
|
||||
new FilterItem(SimulationFilter.deuteranopia),
|
||||
new FilterItem(MatrixFilter.forDeuteranopia(null, false)),
|
||||
new FilterItem(SimulationFilter.tritanopia),
|
||||
new FilterItem(MatrixFilter.forTritanopia(null, false)),
|
||||
new FilterItem(SimulationFilter.achromatopsia),
|
||||
};
|
||||
}
|
||||
|
||||
private static final class ColorView extends JComponent implements ItemListener {
|
||||
private static final class ColorView extends JComponent {
|
||||
private ImageFilter myFilter;
|
||||
private Image myImage;
|
||||
|
||||
@@ -138,17 +215,36 @@ public class ColorBlindnessInternalAction extends DumbAwareAction {
|
||||
g.drawImage(myImage, bounds.x, bounds.y, bounds.width, bounds.height, this);
|
||||
}
|
||||
|
||||
void set(ImageFilter filter) {
|
||||
myImage = null;
|
||||
myFilter = filter;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static class MutableFilter extends RGBImageFilter {
|
||||
private final String myName;
|
||||
private ColorConverter myConverter;
|
||||
|
||||
private MutableFilter(String name) {
|
||||
myName = name;
|
||||
update(.7, .7);
|
||||
}
|
||||
|
||||
private void update(double first, double second) {
|
||||
myConverter = getConverter(first, second);
|
||||
}
|
||||
|
||||
abstract ColorConverter getConverter(double first, double second);
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent event) {
|
||||
if (ItemEvent.SELECTED == event.getStateChange()) {
|
||||
Object object = event.getItem();
|
||||
if (object instanceof FilterItem) {
|
||||
FilterItem item = (FilterItem)object;
|
||||
myFilter = item.myFilter;
|
||||
myImage = null;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
public String toString() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int filterRGB(int x, int y, int rgb) {
|
||||
return myConverter.convert(rgb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user