How to get RGB values of a pixel in JavaFX - javafx

I am getting started with JavaFX and basically what I am trying to implement is a Color Picker.
At first, I thought of having a rectangle with a LinearGradient that goes through all primary/secondary colors.
Looks like what I want, but the problem is that I can not get the RGB values at a given coordinate(x,y) in this Node.
I know you can do it through the fill property of any Shape IF it is a Color.
But Is there anyway to get the RGB values of anything inside a LinearGradient/Paint ?

Does this ColorPicker JavaFX example help?
[...]
function colorAtLocation(x:Integer, y:Integer) : Color {
var bimg = iv.image.bufferedImage;
if (x < 0 or x >= bimg.getWidth() or y < 0 or y >= bimg.getHeight()) {
return null;
}
var rgb = bimg.getRGB(x, y);
var r = Bits.bitAnd(Bits.shiftRight(rgb, 16), 0xff);
var g = Bits.bitAnd(Bits.shiftRight(rgb, 8), 0xff);
var b = Bits.bitAnd(Bits.shiftRight(rgb, 0), 0xff);
Color.rgb(r, g, b)
}
function updateSelectedColor(e:MouseEvent) {
var rgb = colorAtLocation(e.x, e.y);
if (rgb != null) {
picker.selectedColor = rgb;
}
}
[...]

The ColorPicker JavaFX example starts with a png image that is loaded to an image that then populates the ImageView.
The question starts with a JavaFX Rectangle containing LinearGradient.
To get the rectangle contents into a buffered image, one can use the java.awt.Robot:
var rectangle = new java.awt.Rectangle(x,y,width,height);
var robot = new java.awt.Robot();
var bufferedImage = robot.createScreenCapture(rectangle);
where rectangle would be describe the coordinates of the JavaFX Rectangle containing the bits of interest.
The robot.createScreenCapture call has the gotcha that to do the screen capture, the screen has to be visible. There should be a better of way to populate the buffered image but I've not yet encountered it.

Related

JavaFx ImageViewer from unsigned short array

I want to display an image received in a short[] of pixels from a server.
The server(C++) writes the image as an unsigned short[] of pixels (12 bit depth).
My java application gets the image by a CORBA call to this server.
Since java does not have ushort, the pixels are stored as (signed) short[].
This is the code I'm using to obtain a BufferedImage from the array:
private WritableImage loadImage(short[] pixels, int width, int height) {
int[] intPixels = new int[pixels.length];
for (int i = 0; i < pixels.length; i++) {
intPixels[i] = (int) pixels[i];
}
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
WritableRaster raster = (WritableRaster) image.getData();
raster.setPixels(0, 0, width, height, intPixels);
return SwingFXUtils.toFXImage(image, null);
}
And later:
WritableImage orgImage = convertShortArrayToImage2(image.data, image.size_x, image.size_y);
//load it into the widget
Platform.runLater(() -> {
imgViewer.setImage(orgImage);
});
I've checked that width=1280 and height=1024 and the pixels array is 1280x1024, that matches with the raster height and width.
However I'm getting an array out of bounds error in the line:
raster.setPixels(0, 0, width, height, intPixels);
I have try with ALL ImageTypes , and all of them produce the same error except for:
TYPE_USHORT_GRAY: Which I thought it would be the one, but shows an all-black image
TYPE_BYTE_GRAY: which show the image in negative(!) and with a lot of grain(?)
TYPE_BYTE_INDEXED: which likes the above what colorized in a funny way
I also have tried shifting bits when converting from shot to int, without any difference:
intPixels[i] = (int) pixels[i] & 0xffff;
So..I'm quite frustrated after looking for days a solution in the internet. Any help is very welcome
Edit. The following is an example of the images received, converted to jpg on the server side. Not sure if it is useful since I think it is made from has pixel rescaling (sqrt) :
Well, finally I solved it.
Probably not the best solution but it works and could help someone in ether....
Being the image grayscale 12 bit depth, I used BufferedImage of type TYPE_BYTE_GRAY, but I had to downsample to 8 bit scaling the array of pixels. from 0-4095 to 0-255.
I had an issue establishing the higher and lower limits of the scale. I tested with avg of the n higher/lower limits, which worked reasonably well, until someone sent me a link to a java program translating the zscale algorithm (used in DS9 tool for example) for getting the limits of the range of greyscale vlues to be displayed:
find it here
from that point I modified the previous code and it worked like a charm:
//https://github.com/Caltech-IPAC/firefly/blob/dev/src/firefly/java/edu/caltech/ipac/visualize/plot/Zscale.java
Zscale.ZscaleRetval retval = Zscale.cdl_zscale(pixels, width, height,
bitsVal, contrastVal, opt_sizeVal, len_stdlineVal, blankValueVal);
double Z1 = retval.getZ1();
double Z2 = retval.getZ2();
try {
int[] ints = new int[pixels.length];
for (int i = 0; i < pixels.length; i++) {
if (pixels[i] < Z1) {
pixels[i] = (short) Z1;
} else if (pixels[i] > Z2) {
pixels[i] = (short) Z2;
}
ints[i] = ((int) ((pixels[i] - Z1) * 255 / (Z2 - Z1)));
}
BufferedImage bImg
= new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
bImg.getRaster().setPixels(0, 0, width, height, ints);
return SwingFXUtils.toFXImage(bImg, null);
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return null;

How to use a QSGSimpleTextureNode?

I'm trying to understand how do I use a QSGSimpleTextureNode but Qt documentation is very vague. I want to render text on the scene graph, so basically what I want is to draw a texture with all the glyphs and then set that texture on a QSGSimpleTextureNode. My idea was to create the texture using standard OpenGL code and set the texture data to the data I have just created. I can't find an example to show me how to achieve this.
I would use QSGGeometryNode instead of QSGSimpleTextureNode. If I am not wrong it is not possible to set the texture coordinates in a QSGSimpleTextureNode. You could write a custom QQuickItem for the SpriteText and override the updatePaintNode:
QSGNode* SpriteText::updatePaintNode(QSGNode *old, UpdatePaintNodeData *data)
{
QSGGeometryNode* node = static_cast<QSGGeometryNode*>(old);
if (!node){
node = new QSGGeometryNode();
}
QSGGeometry *geometry = NULL;
if (!old){
geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D()
,vertexCount);
node->setFlag(QSGNode::OwnsGeometry);
node->setMaterial(material); // <-- Texture with your glyphs
node->setFlag(QSGNode::OwnsMaterial);
geometry->setDrawingMode(GL_TRIANGLES);
node->setGeometry(geometry);
} else {
geometry = node->geometry();
geometry->allocate(vertexCount);
}
if (textChanged){
//For every Glyph in Text:
//Calc x + y position for glyph in texture (between 0-1)
//Create vertexes with calculated texture coordinates and calculated x coordinate
geometry->vertexDataAsTexturedPoint2D()[index].set(...);
...
node->markDirty(QSGNode::DirtyGeometry);
}
//you could start timer here which call update() methode
return node;
}

How to animate through a rainbow of colors on a bitmap image

I have a bitmap (bitmapData) that I'd like it to change colors over time. I've seen examples of applying a color transform and I couldn't get it to work. Also, with those examples they only apply one color. I would like to animate through the ROYGBIV range. Note: I am dynamically loading images. I don't want to tint the image I want the color in the image to change if that makes sense.
To manipulate/tint colors you would use the colorTransform function
To illustrate:
In the example below I embed the image and reference it, if you are using adobe flash you would 'name' your asset and reference it in you code.
import flash.geom.ColorTransform;
import flash.events.Event;
import flash.display.Bitmap;
[Embed(source="img/logo.png")] private var logoCls:Class;
private var bitmapLogo:Bitmap = new logoCls();
public function Test()
{
addEventListener(Event.ADDED_TO_STAGE, init);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
addChild(bitmapLogo);
}
// r, g, b colors
var r:int = 256; // start at Red
var g:int = 0;
var b:int = 0;
var increment:int = 16; // how quickly to change the color
var colorCycle:int = 0;
var mult:Number = 0.25; // How heavily TINTED you want the image
private function onEnterFrame(e:Event):void
{
var ct:ColorTransform = new ColorTransform (1-mult,1-mult,1-mult,1,r*mult,g*mult,b*mult,0);
bitmapLogo.transform.colorTransform = ct;
incrementRainbowColors();
}
private function incrementRainbowColors():void
{
if (colorCycle == 0) // -> yellow
if ((g+=increment) >= 256) colorCycle=1;
else if (colorCycle == 1) // -> green
if ((r-=increment) <= 0) colorCycle=2;
else if (colorCycle == 2) // -> cyan
if ((b+=increment) >=256) colorCycle=3;
else if (colorCycle == 3) // -> blue
if ((g-=increment) <= 0) colorCycle = 4;
else if (colorCycle == 4) // -> magenta
if ((r+=increment) >=256) colorCycle = 5;
else if (colorCycle == 5) // -> red
if ((b-=increment)<=0) colorCycle = 0;
}
}
You said you were working with bitmapData, so this answer is assuming you can / want to directly manipulate it.
You can use bitmapData.setPixel(row, column, color); to change a pixel's color. row and column are 0-based and represent exactly which pixel to modify - think of it like grid paper if you have never dealt with this before. The color parameter is actually a uint - unsigned integer. You can use a hex value like 0x000000 to set the pixel to black and 0xFFFFFF to white.
To alternate colors, you can just loop through different values for uint. There are 16777215 different possible values for colors and I honestly don't know much about hex colors - I just google the color I want.
But, here's a little tip if you're mathematically inclined:
Each spot in a hex number corresponds to 16 to the power of that spot * the actual number in that spot, in decimal.
I probably worded that wrong, so here is a visual example:
456789 in hex is (4 * 16^5) + (5 * 16^4) + (6 * 16^3) + (7 * 16^2) + (8 * 16^1) + (9 * 16^0) in decimal which equals 4548489.
The first 2 spots (from the left) correspond to Red, then Blue, then Green. so FF0000 is red, 00FF00 is green and 0000FF is blue. You can (I think) supply the decimal value for color in the setPixelfunction, so you can do (or find online) some conversions to get the color you want / loop through colors in a rainbow style pattern!
I hope this random crash course on Hex was helpful :P

.net Drawing.Graphics.FromImage() returns blank black image

I'm trying to rescale uploaded jpeg in asp.net
So I go:
Image original = Image.FromStream(myPostedFile.InputStream);
int w=original.Width, h=original.Height;
using(Graphics g = Graphics.FromImage(original))
{
g.ScaleTransform(0.5f, 0.5f); ... // e.g.
using (Bitmap done = new Bitmap(w, h, g))
{
done.Save( Server.MapPath(saveas), ImageFormat.Jpeg );
//saves blank black, though with correct width and height
}
}
this saves a virgin black jpeg whatever file i give it.
Though if i take input image stream immediately into done bitmap, it does recompress and save it fine, like:
Image original = Image.FromStream(myPostedFile.InputStream);
using (Bitmap done = new Bitmap(original))
{
done.Save( Server.MapPath(saveas), ImageFormat.Jpeg );
}
Do i have to make some magic with g?
upd:
i tried:
Image original = Image.FromStream(fstream);
int w=original.Width, h=original.Height;
using(Bitmap b = new Bitmap(original)) //also tried new Bitmap(w,h)
using (Graphics g = Graphics.FromImage(b))
{
g.DrawImage(original, 0, 0, w, h); //also tried g.DrawImage(b, 0, 0, w, h)
using (Bitmap done = new Bitmap(w, h, g))
{
done.Save( Server.MapPath(saveas), ImageFormat.Jpeg );
}
}
same story - pure black of correct dimensions
Since you didn't fill the area with background of image you're reading from inputStream,you can only get a blank image that way.
Instead of using scaling the image,you can use Fill background into a resized area.
Check this out:
Image img = Image.FromFile(Server.MapPath("a.png"));
int w = img.Width;
int h = img.Height;
//Create an empty bitmap with scaled size,here half
Bitmap bmp = new Bitmap(w / 2, h / 2);
//Create graphics object to draw
Graphics g = Graphics.FromImage(bmp);
//You can also use SmoothingMode,CompositingMode and CompositingQuality
//of Graphics object to preserve saving options for new image.
//Create drawing area with a rectangle
Rectangle drect = new Rectangle(0, 0, bmp.Width, bmp.Height);
//Draw image into your rectangle area
g.DrawImage(img, drect);
//Save your new image
bmp.Save(Server.MapPath("a2.jpg"), ImageFormat.Jpeg);
Hope this helps
Myra
Try this:
- get the Image from your stream
- create a new Bitmap of the correct size
- get the Graphics object from the new bitmap, not the original one
- call g.DrawImage(original, 0, 0, done.Width, done.Height)
Edit:
The problem is this section:
using (Bitmap done = new Bitmap(w, h, g))
{
done.Save( Server.MapPath(saveas), ImageFormat.Jpeg );
}
You're creating a black bitmap, with the resolution specified by g. You're not actually creating a bitmap with any image data coming from g. In fact, I don't think the Graphics object actually stores image data that you can really pass around, it just allows you to manipulate some object that stores the image data.
Try replacing that with b.Save(...)

Flex/Actionscript White to Transparent

I am trying to write something in my Flex 3 application with actionscript that will take an image and when a user clicks a button, it will strip out all the white(ish) pixels and convert them to transparent, I say white(ish) because I have tried exactly white, but I get a lot of artifacts around the edges. I have gotten somewhat close using the following code:
targetBitmapData.threshold(sourceBitmapData, sourceBitmapData.rect, new Point(0,0), ">=", 0xFFf7f0f2, 0x00FFFFFF, 0xFFFFFFFF, true);
However, it also makes red or yellows disappear. Why is it doing this? I'm not exactly sure how to make this work. Is there another function that is better suited for my needs?
A friend and I were trying to do this a while back for a project, and found writing an inline method that does this in ActionScript to be incredibly slow. You have to scan each pixel and do a computation against it, but doing it with PixelBender proved to be lightning fast (if you can use Flash 10, otherwise your stuck with slow AS).
The pixel bender code looks like:
input image4 src;
output float4 dst;
// How close of a match you want
parameter float threshold
<
minValue: 0.0;
maxValue: 1.0;
defaultValue: 0.4;
>;
// Color you are matching against.
parameter float3 color
<
defaultValue: float3(1.0, 1.0, 1.0);
>;
void evaluatePixel()
{
float4 current = sampleNearest(src, outCoord());
dst = float4((distance(current.rgb, color) < threshold) ? 0.0 : current);
}
If you need to do it in AS you can use something like:
function threshold(source:BitmapData, dest:BitmapData, color:uint, threshold:Number) {
dest.lock();
var x:uint, y:uint;
for (y = 0; y < source.height; y++) {
for (x = 0; x < source.width; x++) {
var c1:uint = source.getPixel(x, y);
var c2:uint = color;
var rx:uint = Math.abs(((c1 & 0xff0000) >> 16) - ((c2 & 0xff0000) >> 16));
var gx:uint = Math.abs(((c1 & 0xff00) >> 8) - ((c2 & 0xff00) >> 8));
var bx:uint = Math.abs((c1 & 0xff) - (c2 & 0xff));
var dist = Math.sqrt(rx*rx + gx*gx + bx*bx);
if (dist <= threshold)
dest.setPixel(x, y, 0x00ffffff);
else
dest.setPixel(x, y, c1);
}
}
dest.unlock();
}
You can actually do it without pixelbender and real-time thanks to the inbuilt threshold function :
// Creates a new transparent BitmapData (in case the source is opaque)
var dest:BitmapData = new BitmapData(source.width,source.height,true,0x00000000);
// Copies the source pixels onto it
dest.draw(source);
// Replaces all the pixels greater than 0xf1f1f1 by transparent pixels
dest.threshold(source, source.rect, new Point(), ">", 0xfff1f1f1,0x00000000);
// And here you go ...
addChild(new Bitmap(dest));
it looks like the above code would make a range of colors transparent.
pseudo-code:
for each pixel in targetBitmapData
if pixel's color is >= #FFF7F0F2
change color to #00FFFFFF
something like this will never be perfect, because you will lose any light colors
i would find an online color picker that you can use to see exactly what colors will be altered
The answer in 1 of pixel bender code:
dst = float4((distance(current.rgb, color) < threshold) ? 0.0 : current);
should be:
dst = (distance(current.rgb, color) < threshold) ? float4(0.0) : current;
or
if (distance(current.rgb, color) < threshold)
dst = float4(0.0);
else
dst = float4(current);

Resources