javafx - How to use ColorAdjust to set a target Color? -
situation
i have image circle filled radial gradient ranges white black/transparency. serves particle , needs colorized during lifecycle.
problem
i'm using coloradjust apply different colors image. problem colors aren't way want them be. if use green target color, pink ball.
question
how calculate coloradjust's hue value match given target color? or there better way colorize images? can't use shape because using imageview way faster using shape.
code
here's code. problem hue difference current color:
import javafx.application.application; import javafx.scene.node; import javafx.scene.scene; import javafx.scene.snapshotparameters; import javafx.scene.effect.coloradjust; import javafx.scene.image.image; import javafx.scene.image.imageview; import javafx.scene.image.writableimage; import javafx.scene.layout.hbox; import javafx.scene.paint.color; import javafx.scene.paint.cyclemethod; import javafx.scene.paint.radialgradient; import javafx.scene.paint.stop; import javafx.scene.shape.circle; import javafx.stage.stage; public class main extends application { @override public void start(stage primarystage) { hbox root = new hbox(); // create alpha masked image image image = createimage(createalphamaskedball(200)); // create original imageview imageview original = new imageview( image); // create imageview color adjustment imageview modified = new imageview( image); // coloradjust effect coloradjust coloradjust = new coloradjust(); // set saturation 1, otherwise hue won't have effect coloradjust.setsaturation(1); // define target color color targetcolor = color.green; // calculate hue: map [0,360] [-1,1]; todo: here's problem double hue = map( targetcolor.gethue(), 0, 360, -1, 1); coloradjust.sethue(hue); // apply color adjustment modified.seteffect(coloradjust); root.getchildren().addall( original, modified); scene scene = new scene(root,1024,500, color.black); primarystage.setscene(scene); primarystage.show(); } /** * snapshot image out of node, consider transparency. * @param node * @return */ public static image createimage( node node) { writableimage wi; snapshotparameters parameters = new snapshotparameters(); parameters.setfill(color.transparent); int imagewidth = (int) node.getboundsinlocal().getwidth(); int imageheight = (int) node.getboundsinlocal().getheight(); wi = new writableimage( imagewidth, imageheight); node.snapshot(parameters, wi); return wi; } /** * create alpha masked ball gradient colors white black/transparent. used e. g. particles. * * @param radius * @return */ public static node createalphamaskedball( double radius) { circle ball = new circle(radius); radialgradient gradient1 = new radialgradient(0, .1, 0, 0, radius, false, cyclemethod.no_cycle, new stop(0, color.white.derivecolor(1,1,1,1)), new stop(1, color.black.derivecolor(1,1,1,0))); ball.setfill(gradient1); return ball; } public static double map(double value, double start, double stop, double targetstart, double targetstop) { return targetstart + (targetstop - targetstart) * ((value - start) / (stop - start)); } public static void main(string[] args) { launch(args); } }
and screenshot:
thank help!
edit:
solution
working extended version rgb sliders , josé's solution applied, ones who'd toy around it:
import javafx.application.application; import javafx.beans.value.changelistener; import javafx.beans.value.observablevalue; import javafx.event.actionevent; import javafx.event.eventhandler; import javafx.geometry.insets; import javafx.scene.node; import javafx.scene.scene; import javafx.scene.snapshotparameters; import javafx.scene.control.combobox; import javafx.scene.control.contentdisplay; import javafx.scene.control.label; import javafx.scene.control.listcell; import javafx.scene.control.listview; import javafx.scene.control.slider; import javafx.scene.effect.coloradjust; import javafx.scene.image.image; import javafx.scene.image.imageview; import javafx.scene.image.writableimage; import javafx.scene.layout.borderpane; import javafx.scene.layout.gridpane; import javafx.scene.layout.hbox; import javafx.scene.paint.color; import javafx.scene.paint.cyclemethod; import javafx.scene.paint.radialgradient; import javafx.scene.paint.stop; import javafx.scene.shape.circle; import javafx.scene.shape.rectangle; import javafx.stage.stage; import javafx.util.callback; /** * modify coloradjust given target color matched. */ public class main extends application { color[] presetcolors = new color[] {color.red, color.green, color.blue, color.yellow, color.magenta, color.cyan, color.dodgerblue, color.lightgrey, color.darkgray, color.black, color.white, color.brown}; color targetcolor; rectangle referencecolorrectangle; // display target color reference (rgb) coloradjust coloradjust; slider redslider; slider greenslider; slider blueslider; combobox<color> colorcombobox; @override public void start(stage primarystage) { borderpane root = new borderpane(); // content // ------------------------- hbox content = new hbox(); content.setstyle("-fx-background-color:black"); // create alpha masked image image image = createimage(createalphamaskedball(100)); // create original imageview imageview original = new imageview( image); // create imageview color adjustment imageview modified = new imageview( image); // coloradjust effect coloradjust = new coloradjust(); modified.seteffect(coloradjust); content.getchildren().addall( original, modified); // toolbar // ------------------------- gridpane toolbar = new gridpane(); // presets: show colors rectangles in combobox list, hex color in combobox selection colorcombobox = new combobox<>(); colorcombobox.getitems().addall( presetcolors); colorcombobox.setonaction(new eventhandler<actionevent>() { @override public void handle(actionevent event) { updatesliders(); } }); colorcombobox.setcellfactory(new callback<listview<color>, listcell<color>>() { @override public listcell<color> call(listview<color> p) { return new listcell<color>() { private final rectangle rectangle; { setcontentdisplay(contentdisplay.graphic_only); rectangle = new rectangle(100, 10); } @override protected void updateitem(color item, boolean empty) { super.updateitem(item, empty); if (item == null || empty) { setgraphic(null); } else { rectangle.setfill(item); setgraphic(rectangle); } } }; } }); // sliders, value initialized later redslider = createslider( 0,255, 0); greenslider = createslider( 0,255, 0); blueslider = createslider( 0,255, 0); // reference rectangle in rgb referencecolorrectangle = new rectangle( 0, 0, 100, 100); referencecolorrectangle.setstroke(color.black); // listener: new target color sliders , apply changelistener<number> listener = new changelistener<number>() { @override public void changed(observablevalue<? extends number> observable, number oldvalue, number newvalue) { updatecolor(); } }; redslider.valueproperty().addlistener(listener); greenslider.valueproperty().addlistener(listener); blueslider.valueproperty().addlistener(listener); // add nodes gridpane toolbar.addrow(0, new label( "preset"), colorcombobox); toolbar.addrow(1, new label( "red"), redslider); toolbar.addrow(2, new label( "green"), greenslider); toolbar.addrow(3, new label( "blue"), blueslider); toolbar.add(referencecolorrectangle, 2, 0, 1, 4); // margin gridpane nodes for( node node: toolbar.getchildren()) { gridpane.setmargin(node, new insets(5,5,5,5)); } // layout root.settop(toolbar); root.setcenter(content); // create scene scene scene = new scene(root,800,400, color.black); primarystage.setscene(scene); primarystage.show(); // set height of combobox list colorcombobox.lookup(".list-view").setstyle("-fx-pref-height: 200"); // select 1st color , implicitly initialize sliders , colors colorcombobox.getselectionmodel().selectfirst(); } private void updatecolor() { // create target color targetcolor = color.rgb( (int) redslider.getvalue(), (int) greenslider.getvalue(), (int) blueslider.getvalue()); // update reference referencecolorrectangle.setfill(targetcolor); // update coloradjust // see http://stackoverflow.com/questions/31587092/how-to-use-coloradjust-to-set-a-target-color double hue = map( (targetcolor.gethue() + 180) % 360, 0, 360, -1, 1); coloradjust.sethue(hue); // use saturation double saturation = targetcolor.getsaturation(); coloradjust.setsaturation(saturation); // use white in masked ball creation => inverse brightness double brightness = map( targetcolor.getbrightness(), 0, 1, -1, 0); coloradjust.setbrightness(brightness); // system.out.println("target color: " + targetcolor + ", hue 0..360: " + targetcolor.gethue() + ", hue 0..1: " + hue); } private void updatesliders() { color referencecolor = colorcombobox.getvalue(); redslider.setvalue( map( referencecolor.getred(), 0, 1, 0, 255)); greenslider.setvalue( map( referencecolor.getgreen(), 0, 1, 0, 255)); blueslider.setvalue( map( referencecolor.getblue(), 0, 1, 0, 255)); } private slider createslider( double min, double max, double value) { slider slider = new slider( min, max, value); slider.setprefwidth(600); slider.setshowticklabels(true); slider.setshowtickmarks(true); return slider; } /** * snapshot image out of node, consider transparency. * @param node * @return */ public static image createimage( node node) { writableimage wi; snapshotparameters parameters = new snapshotparameters(); parameters.setfill(color.transparent); int imagewidth = (int) node.getboundsinlocal().getwidth(); int imageheight = (int) node.getboundsinlocal().getheight(); wi = new writableimage( imagewidth, imageheight); node.snapshot(parameters, wi); return wi; } /** * create alpha masked ball gradient colors white black/transparent. used e. g. particles. * * @param radius * @return */ public static node createalphamaskedball( double radius) { circle ball = new circle(radius); radialgradient gradient1 = new radialgradient(0, 0, 0, 0, radius, false, cyclemethod.no_cycle, new stop(0, color.white.derivecolor(1,1,1,1)), new stop(1, color.white.derivecolor(1,1,1,0))); ball.setfill(gradient1); return ball; } public static double map(double value, double start, double stop, double targetstart, double targetstop) { return targetstart + (targetstop - targetstart) * ((value - start) / (stop - start)); } public static void main(string[] args) { launch(args); } }
or short version of solution:
double hue = map( (targetcolor.gethue() + 180) % 360, 0, 360, -1, 1); coloradjust.sethue(hue); // use saturation double saturation = targetcolor.getsaturation(); coloradjust.setsaturation(saturation); // use white in masked ball creation => inverse brightness double brightness = map( targetcolor.getbrightness(), 0, 1, -1, 0); coloradjust.setbrightness(brightness);
if have @ color on center of circle (for pure white, 100% opacity), result of applying coloradust
effect, have magenta #ff00ffff
.
the color applying has hue value of 120 (color.green.gethue()
), , if create color:
color color = color.hsb(120,1,1); system.out.println(color);
it print #00ff00ff
, green (100% opacity), , precisely opposite color in terms of r,g,b.
based on this, other way around: if want green result, need set inital color magenta, hue value of 300:
targetcolor = color.web("#ff00ffff");
if have on color wheel:
you notice diametrically opposite colors, separated 180º.
so general rule can add 180º hue color want display.
double hue = map( targetcolor.gethue()+180, 0, 360, -1, 1);
in test i've added slider check it:
Comments
Post a Comment