LinearGradientPaint getColor by val - java

so got a question involving linear gradients in Java, using multiple colors. I am looking to get an RGB color at any point along agradient. Creating the gradient and painting it is easy, and i can get the fractions and colors for those colors i set.....
The issue i have is that i want to get an RGB color at any point along the gradient.
to break it down, an example application would be the creation and display of a gradient in some JPanel with size of 255 (maxSize=255 (see below)). depending on the size of said JPanel (maxSize) the interpolation would be different (larger number in maxSize would result in more interpolated values).
I would like to be able to grab the RGB value at any location along the gradient,
you could almost equate it to being able to do the following...
grab the RGB value based on the location in the gradient
RGB_Values = p.getColorByGradientLocation(float locationInGradient);
or
grab the RGB value based off a specific value, somewhere between Point2D start and Point2D end
RGB_Values = p.getColorByValue(float value);
e.g setting gradient code
Point2D start = new Point2D.Float(0, 0);
Point2D end = new Point2D.Float(0, maxSize);
Color[] colors = {n number of colors};
dist[] = ((float) i / (float) colors.length); //equally distributes colors
p = new LinearGradientPaint(start, end, dist, colors, CycleMethod.NO_CYCLE);
Many Thanks

thanks to Nolo for the suggestion that help me figure out a method to do this. This is still in progress and i may find a better way to do this, but for now this works....
so you need to paint the lineargradient to a panel then paint the panel to an image (without displaying it).
BufferedImage bi = new BufferedImage(xSize, ySize, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
panel.print(g);
you need to set both the image and the panel to a width of 1, then height to match the gradient end (do 1 width for speed).
to display the gradient you can then grab just 1 pixel off each row, then use the image.getRGB(x,y) to get the pixel values in conjunction with the bit settings e.g
int rgb = im.getRGB(0, i);
r = (rgb >> 16) & 0xFF;
g = (rgb >> 8) & 0xFF;
b = (rgb & 0xFF);
Color newC = new Color(r, g, b);
if you loop through the image height using the above, you can get all the color values for a gradient you create.
:-)

Related

Draw grid on transparent Mat

I would like to create a Mat with a grid on a transparent background that I can lay on top of other Mats. I struggle with the transparent part and to laying on top
Mat image = imread("pic.jpg");
Mat grid = new Mat(image.size(), CV_8UC4, new Scalar(0, 0, 0, 0);
for (//times)
// draw grid with: line(grid, ... )
grid.copyTo(image);
First of all the grid Mat is not transparent at all it is black. Isn't scalar constructed like this?
new Scalar(Blue, Green, Red, Alpha)
Also how do I overlay an image with another one? This is just overwriting.
Here is sample program written in C++ but it should be very analogue in java:
cv::Mat input = cv::imread("../inputData/Lenna.png");
cv::Mat inputBGRA;
cv::cvtColor(input, inputBGRA, CV_BGR2BGRA);
cv::Mat gridSolid = cv::Mat(input.size(), inputBGRA.type(), cv::Scalar(0,0,0,0));
cv::Mat gridMask = cv::Mat(input.size(), CV_8UC1, cv::Scalar(0));
cv::Mat gridAlpha = cv::Mat(input.size(), inputBGRA.type(), cv::Scalar(0,0,0,0));
cv::line(gridSolid, cv::Point(0,0), cv::Point(512,512), cv::Scalar(0,255,0,255), 10);
cv::line(gridSolid, cv::Point(0,512), cv::Point(512,0), cv::Scalar(0,255,0,255), 10);
cv::line(gridMask, cv::Point(0,0), cv::Point(512,512), cv::Scalar(255), 10); // single channel
cv::line(gridMask, cv::Point(0,512), cv::Point(512,0), cv::Scalar(255), 10); // single channel
// copy and use the mask. copying eliminates the original values where the mask is set
cv::Mat outputCopy = inputBGRA.clone();
gridSolid.copyTo(outputCopy,gridMask);
// here set the scalar alpha value to less than 255
// both lines use different alpha values
cv::line(gridAlpha, cv::Point(0,0), cv::Point(512,512), cv::Scalar(0,255,0,120), 10);
cv::line(gridAlpha, cv::Point(0,512), cv::Point(512,0), cv::Scalar(0,255,0,180), 10);
cv::Mat outputWeightSum = inputBGRA.clone();
//cv::addWeighted(inputBGRA, 0.5, gridAlpha, 0.5, 0, outputWeightSum);
// manually add weighted sum PER ALPHA VALUE:
for(int y=0; y<outputWeightSum.rows; ++y)
for(int x=0; x<outputWeightSum.cols; ++x)
{
// the bigger the alpha value, the less of the original image is kept at that pixel
cv::Vec4b imgPix = outputWeightSum.at<cv::Vec4b>(y,x);
cv::Vec4b gridPix = gridAlpha.at<cv::Vec4b>(y,x);
// use alpha channel vor blending
float blendpart = (float)gridPix[3]/(float)255;
// set pixel value to blended value
outputWeightSum.at<cv::Vec4b>(y,x) = blendpart * gridPix + (1.0f-blendpart) * imgPix;
}
in fact you dont need the alpha channel in this example but if you have more complex "grids" with differing alpha values, it might be nice.
I get these results:
method: copy:
method: blend with alpha channel:

Convert rgb color to x,y direction and back

Imagine round color pallet like this.
I am building a game(Android) in which you can draw line and when you drawing it you're creating vector between each two points. I need to transform this direction to color on the pallet. So when you draw, the color of the line will depend on your swipe direction.
I need to transform color to direction too. Can't figure out relation between 3 rgb params and x,y direction.
Of course I could limit number of colors to, for example 8. Then create array and every cell will represent direction of different color. But I wonder if it possible to use all of the colors without allocating tons of memory or using unnecessary if\else's?
Update:
Thanks to domi's advice I was able to do what I wanted. Thanx man!
That's how I converted vector to color:
double angle = Math.atan2(vector_x, vector_y) * 57.2957795;
double final_angle = angle<0? 360 + angle:angle;
int myRGBColor = Color.HSVToColor(new float[]{(float) final_angle, saturation, brightness} );
And that's how I converted color to vector:
int sample = bmp.getPixel( (int)X, (int)Y);
//int a = (sample >> 24) & 255;
int r = (sample >> 16) & 255;
int g = (sample >> 8) & 255;
int b = sample & 255;
float[] hsv = new float[3];
android.graphics.Color.RGBToHSV(r, g, b, hsv);
float hue = hsv[0];
vector_x = Math.toDegrees( Math.sin( Math.toRadians(hue) ) );
vector_y = Math.toDegrees( Math.cos( Math.toRadians(hue) ) );
Everyone is welcome to play my game.
If you have a vector you can use the degree to calculate the color with the HSL system.
If you go on the round color pallet you can see that Saturation (S) is always 100 and Luminance (L) is always 50 then you just have to use the degree (0-360) to have the HUE and have a complete HSL color (degree, 100, 50).
Then you can transforme RGB to HSL and HSL to RGB

Color detector in Java

I have list of colors in HEX format (for example #000000) and I would like to detect color type (blue, red, green etc.) and then change color type to another color type. Is this possible and are there any frameworks/libraries for this task?
Example:
I have color #EB1369 (red) then I convert it to blue and it becomes for example #1313EB (blue).
Here's a function that will let you shift colors around the hue circle. You should read the wikipedia page on the HSB (or HSV) color system to really understand what is going on: http://en.wikipedia.org/wiki/HSV_color_space
/** Converts an input color given as a String such as "ab451e" to
* the HSB color space. Shifts its hue from the given angle in degrees.
* Then returns the new color in the same format it was given.
*
* For example shift("ff0000", 180); returns "80ff00" (green is the opposite of red).*/
public static String shift(String rgbS, int angle) {
// Convert String to integer value
int value = Integer.parseInt(rgbS, 16);
// Separate red green and blue
int r = value >> 16;
int g = (value >> 8) & 0xff;
int b = value & 0xff;
// Convert to hsb
float[] hsb = Color.RGBtoHSB(r, g, b, null);
// Convert angle to floating point between 0 and 1.0
float angleF = (float)(angle/360.0);
// Shift the hue using the angle.
float newAngle = hsb[0] + angleF;
if(newAngle > 1.0)
newAngle = newAngle - 1.0f;
hsb[0] = newAngle;
// Convert back to RGB, removing the alpha component
int rgb = Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]);
rgb = rgb & 0xffffff;
// Build a new String
return Integer.toHexString(rgb);
}
Detecting colors can be complex, it depends on the result you really expect.
If what you want is simply an approximation (red, green, blue, yellow, etc.) then you can look at the hue circle of the HSB color-space, choose a hue value for each color you want to define, and then map the color you get in input to the closest one you chose.
You can also rely on things like named HTML colors: http://www.w3schools.com/html/html_colornames.asp . Take this list, create a mapping in your program, then all you have to do is map the color you get to the closest one in your map, and return its name. Be wary though: computing the distance between two colors can be tricky (especially in RGB) and naive approaches (such as channel-by-channel difference) can give surprisingly bad results. Colorimetry is a complex topic, and you will find good methods on this page: http://en.wikipedia.org/wiki/Color_difference
Try convert RGB values to HSV (HSB exactly) - it is format for colors which is more comfortable for human. After conversion, all u need to do is change H V (probably) and convert it back to RGB.
I guess that you like to convert RGB color to HSB. YOu can do this wuth:
java.awt.Color.RGBtoHSB(...)
then you can easily determine whetther H value fits in your definition of blue, and modify it to whatever you like. After this, you can easily convert it back to RGB via:
java.awt.Color.getHSBColor(...)
And ifg you do not like jawa.awt.color just multiply color vector by transofrmation matrix.
Each HEX Color has three parts in it, red, green and blue the # identifies a HEX color, the following two letters are the amount of red; the next two are green and the next two are blue. i.e: RGB
The two letters can have a maximum hexidecimal value of FF which is 255, and a minimum of 00 which is zero.
So you can argue like this, I want a color with 2 red parts, 7 green parts, and zero blue parts, which will give you #020700
That is why #FFFFFF is white (all the colors together) and #000000 is black (no colors at all)
With this logic you can modify the color in any way you want; The Color class can also help a lot.

Faster way to set a (PNG) bitmap color instead of pixel by pixel

I have some png files that I am applying a color to. The color changes depending on a user selection. I change the color via 3 RGB values set from another method. The png files are a random shape with full transparency outside the shape. I don't want to modify the transparency, only the RGB value. Currently, I'm setting the RGB values pixel by pixel (see code below).
I've come to realize this is incredibly slow and possibly just not efficient enough do in an application. Is there a better way I could do this?
Here is what I am currently doing. You can see that the pixel array is enormous for an image that takes up a decent part of the screen:
public void foo(Component component, ComponentColor compColor, int userColor) {
int h = component.getImages().getHeight();
int w = component.getImages().getWidth();
mBitmap = component.getImages().createScaledBitmap(component.getImages(), w, h, true);
int[] pixels = new int[h * w];
//Get all the pixels from the image
mBitmap[index].getPixels(pixels, 0, w, 0, 0, w, h);
//Modify the pixel array to the color the user selected
pixels = changeColor(compColor, pixels);
//Set the image to use the new pixel array
mBitmap[index].setPixels(pixels, 0, w, 0, 0, w, h);
}
public int[] changeColor(ComponentColor compColor, int[] pixels) {
int red = compColor.getRed();
int green = compColor.getGreen();
int blue = compColor.getBlue();
int alpha;
for (int i=0; i < pixels.length; i++) {
alpha = Color.alpha(pixels[i]);
if (alpha != 0) {
pixels[i] = Color.argb(alpha, red, green, blue);
}
}
return pixels;
}
Have you looked at the functions available in Bitmap? Something like extractAlpha sounds like it might be useful. You an also look at the way functions like that are implemented in Android to see how you could adapt it to your particular case, if it doesn't exactly meet your needs.
The answer that worked for me was a write up Square did here Transparent jpegs
They provide a faster code snippet for doing this exact thing. I tried extractAlpha and it didn't work but Square's solution did. Just modify their solution to instead modify the color bit and not the alpha bit.
i.e.
pixels[x] = (pixels[x] & 0xFF000000) | (color & 0x00FFFFFF);

changing rgb color values to represent a value

I am trying to make a color change on a canvas in java to respresent how strong or weak the values respresented by color are relative to each other.
The rgb colors have to be the same color just different shades, like white to grey to black and every shade of grey in between. How can I change the rgb values considering that the values I am representing vary a lot, from -9999999 to positive 9999999.
I think you should take a look at HSL/HSV instead of RGB.
While RGB is elementary in nature in that it expresses colors in terms of the primaries, it does not allow you to make "understandable" changes to the R, G or B values to arrive at "similar" colors. With a HSL/HSV model, you will be able to make changes to Brightness/Lightness/Value (L/V) to arrive at colors with varying amounts of gray, or make changes to Hue (H) to obtain similar colors across the spectrum. You can start at full brightness (White) and create darker tones of gray by decreasing the value of L/V and eventually reach the color of no brightness (Black).
A very mild introduction to color theory, for developers is available here.
As to your question, you should express your colors in terms of HSL, with increasing values of Saturation to have a range of colors starting from white to black. Of course, if you want gray tones in between white and black without any other color, you should keep the hue to a minimum.
A short example on how to get a range of colors follows. For brevity, I've populated the colors into an array, but that is not required since you might want to use the color rightaway (besides considering memory requirements).
private Color[] produceColorRange(int steps)
{
float value = 1.0f; //Starting with full brightness
Color[] colors = new Color[steps];
for(int ctr = 0; ctr < steps; ctr++)
{
value = value - (1.0f/steps); //tend to darkness
int rgb = Color.HSBtoRGB(0.7f, 0.0f, value); //create a darker color
//Hue is Blue, not noticeable
//because Saturation is 0
Color color = new Color(rgb);
colors[ctr] = color;
}
return colors;
}
If you use the above method and paint a JFrame, you will be able to get a result similar to the one below (except that I've modified the hue and saturation to get my color range).
Note that if you want a simpler way of getting a color range, initialize a Color object with Color.WHITE and invoke color.darker(). Of course, you will not be able to control the increment.
Yes, scale your values to fit your domain. That depends on how your RGB values are stored. Usually, 8 bits are used for each. Since grey has R = G = B, you want to scale values in range (-9999999,9999999) to (0, 255).
Consider x in the first interval. Since the first range covers also negative numbers, first, do a shift.
x = x + 9999999
Now x is in the interval (0, 19999998). And the next step is to scale it down to (0, 255). Since the colour values grow linearly in that interval, all you have to do is this:
x = x * 255 / 19999998
Now x is in the interval (0, 255) just like you want.
Generally, if your inital values are in an interval (a, b) and want to transform it into (0, c), apply this formula: (Note that a can be negative)
x = (x - a) * c / (b - a)
So if you R, G, B values are 16 bits long, c will be 2**16 = 65536 and the formula:
x = (x + 9999999) * 65536 / 19999998
Hope that helps.
I'm not completely sure I understand your question, but, if I do:
Why not just scale the RGB values to the values in your range (from -9999999 to positive 9999999)? Moreover, set R, G, and B all to the same value so that you're using shades of gray to represent the value.
Like this:
private final int MIN = -9999999;
private final int MAX = 9999999;
public Color getScaledColor(int val) {
int gray = (int) Math.round((double) (val - MIN) / (double) (MAX - MIN)
* 255.0);
Color color = new Color(gray, gray, gray);
return color;
}
Note that this solution will not give unique colors for all the values in the range you specified. But also keep in mind that the human eye can only distinguish between so many shades (and 2 * 9999999 + 1 is probably more than the number of shades than it can distinguish between).
The HSL Color class implements the formulas provided in the Wikipedia link on HSL/HSV provide above.

Categories

Resources