Solved

How do you change the fore color of an image?

Posted on 2014-10-02
7
187 Views
Last Modified: 2014-10-22
I've got an image "mask" (eg: the outline of an image all in white, with appropriate transparency).   I need to be able to draw this to an image, but change the color (say, to a shade of blue, but without messing up the transparency).  

The only methods I've found require you to iterate through each pixel in the image.  That isn't an option.  Is there another, cleaner method for simply changing the shade of color of an image?
0
Comment
Question by:Javin007
  • 4
  • 3
7 Comments
 
LVL 35

Expert Comment

by:mccarl
Comment Utility
Is there another, cleaner method for simply changing the shade of color of an image?
Assuming that you are using BufferedImage objects, then yes, you can use BufferedImageOp's to do this kind of processing. One implementation of BufferedImageOp is the RescaleOp which should do exactly what you want. Check the following...
BufferedImage image = ....           // Get your image somehow, and assuming that your image has 4 channels, RGBA, and all RGB channels have a value of 1 (meaning pure white) and Alpha channel varies for our transparency
RescaleOp rescale = new RescaleOp({ 0f, 0f, 1f, 1f }, { 0f, 0f, 0f, 0f }, null);

// Now there are a number of ways to "apply" the rescale op

// 1. Change the image in place, ie. overwrite the original in-memory image
rescale.filter(image, image);

// 2. Apply the rescale as you draw the image to the output/another image, ie. keeps the original in-memory image intact so that you could draw it out using a different RescaleOp that produces a different colour, etc
Graphics2D g2d = .....    // Your output, either a visible component or another image, etc
g2d.drawImage(image, rescale, x, y);

// 3. You could use BufferedImageFilter if that's what you are using...

Open in new window


The values passed to the RescaleOp constructor determine the color that will be produced. In this example, the first array of floats and the "scaling factors" for each channel of the source image. I am zeroing out the Red and Green channels, leaving the Blue channel as is which will change the white mask to a blue one, and the Alpha channel is left as is too so that your transparency is maintained. The second array of floats are "offsets" that get added to the original source channels but we don't need to do that, hence they're all zero.

So you would just have to fiddle with the first 3 floats in the first array to change the resultant colour.
0
 
LVL 4

Author Comment

by:Javin007
Comment Utility
Ack!  So sorry, McCarl.  This is the second time you've helped me and I've given no response.  :/  I don't think I'm getting notifications that people have responded anymore.  Let me play with what you've got here, and I'll get back to you.
0
 
LVL 4

Author Comment

by:Javin007
Comment Utility
Okay read over what you said but haven't messed with it in code yet.  Looks simple enough.  

Now my question is, what level of accuracy do you get here?  I'm hoping to use this as a method of making it simple to "pick" items with a mouse in a 2D game.  So every object on-screen will also have its "mask" drawn to an off-screen buffer where their RGB value is their unique identifier.  When a user clicks the screen, I will simply check the color of the corresponding pixel on the off-screen buffer to see which object was clicked.  (Unless you have a sexier method, that's all I've got.)  

So if this is using floats, how accurate can I rely on the value being?  Since a "1" on the RGB scale would have a difference of 0.00390625, and I know that floating point numbers can sometimes get wonky, is there a risk of a specific color not actually being the exact color I'm looking for after it's been drawn to a buffer?

And you wouldn't happen to know the "getPixelColor" equivalent off the top of your head?

Thanks for all your help!
0
Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 35

Accepted Solution

by:
mccarl earned 500 total points
Comment Utility
Firstly, I don't think there will be any problem with the precision of the float data type with what you are trying to do.

Secondly, the way you are implementing this MAY work but there would be a number of considerations to make.

Understand that every pixel in your square mask image will have that colour, ie. say you have a mask image of a circle, the image itself is square, and ALL the pixels have colour say (123, 234, 135) it is just that the pixels inside the circle have an alpha (transparency value) of 1.0f all fully opaque, and the pixels outside the circle have an alpha of 0.0f, fully transparent, with some pixels on the edge of the circle somewhere in between to give the antialiased effect. If the user clicked some where just outside the circle (but inside the square bounding box of the image) you would still get your colour (or "identifier") of this object. You could cater for this by only registering a "hit" if the alpha component retrieved is greater than a threshold, say 0.1f or whatever works for you
Do any of these objects overlap? Either in terms of their square image bounding boxes or of the actual shapes represented by the masks? If so, then the Composite rule that is used when you "draw" each mask to your off screen image will determine exactly what the resultant off screen image looks like, and if any of these objects overlap, the resultant colour of the pixels that overlap will be blended in some way and so their use as "identifiers" would probably be lost

With the limited amount of info about exactly what you want to do, my "sexier" ;) way to do it would probably be something like....

Have a data structure (say a List) that stores "objects" and they are stored in their "z-order", ie the objects that are logically "on top" of other objects are stored first in the list. The data structure of each object would be something like this... a) the unique identifier of the object (or other object properties), b) the x and y co-ordinates of where the mask image bounding box is located on the screen and c) the actual mask image itself.

Now, when you get a click at a specific point x, y on the screen, you start iterating through your list of "objects" in normal order (so you will see the "top" most object first). For each object, you check if the clicked point (x,y) is greater than the x,y of where the object is located, but less than x+width, y+ height of the object, ie. you are just checking if the clicked point lies somewhere on the objects image bounding box. If not, you can skip this object and move on to the next. But if the clicked point is within the bounding box, then you can subtract the x,y of the objects bounding box from the clicked points x,y (which gives you the co-ordinates within the image of where it was clicked) and then you can get the alpha component of your mask at that location. If the alpha > some threshold, then this is the object that was selected, otherwise go on to the next one in the list.

No, it might not seem as sexy as what you are proposing, but I think it would have less issues, ie. it doesn't require mapping a unique identifier to a colour and therefore, it doesn't have any issues if colour get blended when drawing you mapping image. It also, might seem like it would add a lot to the processing required, but I think that you might find that the performance hit is negligible. Most object in the list don't get considered because they don't pass the first test, which is not much computation, then only a few objects need to have their masks "tested" to see if the click is really on the object or not.

And you wouldn't happen to know the "getPixelColor" equivalent off the top of your head?
I'm not 100% sure what you are asking, but is this the answer...
http://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferedImage.html#getRGB(int,%20int)


Hope all that helps, if there is anything unclear about the above, just let us know.
0
 
LVL 4

Author Comment

by:Javin007
Comment Utility
Basically, imagine a scene with multiple components, but those components may not be squares, circles, or predictable shapes.  (This is specifically for a graphical representation of data, but let's use a video game analogy.)  

Suppose you have the ground rendered first.  You set this to color "0" then render it in the "PixelColorPicker" background.  Then you draw a "sprite" on top of that ground, and you render that as color "1".  That sprite has a sword, so say you render that separately as color "2".  And so on and so forth.  

So if a player were to click the sprite, you could tell if they were aiming for the sprite's sword (say, to knock it out of their hand) or the body itself.  So long as the sprite's alphas are only 0 and 1, then there shouldn't be any problem with blending throwing off your color.  (I did something like this for a 3D game once, rendering to a "picker" buffer with full emissive, and without anti-aliasing, and it worked well.)  The assigning of the color isn't difficult, as the color of the object is simply the object's ID.  With RGB at 256, this gives me a possible 16,777,216 object IDs (and if I hit even a tenth of that number, I'm doing something wrong.)  Thus, when I pull the pixel color, and convert it to the "long" color, I immediately have the ID of the object clicked, and vice versa (the object's ID determines its color).  

Your idea of first checking by bounding box, then going "deeper" might actually be the "right" answer.  (My method requires doubling your texture memory - due to the need for the mask - as well as having an additional screen buffer to render to.  When the user clicks the screen, you have to first render everything to the color buffer, then pick a pixel.  Your method may, in fact, be much faster.)

Unfortunately, I've been slammed, and haven't had the opportunity to see which works best.  Hopefully I can find a moment today to give it a test.  

Thanks for all your help!
0
 
LVL 4

Author Closing Comment

by:Javin007
Comment Utility
The more I thought about it, the more this seems to be the right answer.  My method would always end up using more RAM, as well as complicating things by needing a completely separate render method.  For 2D purposes, I believe this is the superior method, as it also prevents false "hits".  Thanks!
0
 
LVL 35

Expert Comment

by:mccarl
Comment Utility
Glad to help! :)
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

By the end of 1980s, object oriented programming using languages like C++, Simula69 and ObjectPascal gained momentum. It looked like programmers finally found the perfect language. C++ successfully combined the object oriented principles of Simula w…
Introduction Java can be integrated with native programs using an interface called JNI(Java Native Interface). Native programs are programs which can directly run on the processor. JNI is simply a naming and calling convention so that the JVM (Java…
Viewers learn about the “while” loop and how to utilize it correctly in Java. Additionally, viewers begin exploring how to include conditional statements within a while loop and avoid an endless loop. Define While Loop: Basic Example: Explanatio…
The viewer will learn how to implement Singleton Design Pattern in Java.

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

5 Experts available now in Live!

Get 1:1 Help Now