Link to home
Start Free TrialLog in
Avatar of Javin007
Javin007Flag for United States of America

asked on

How do you change the fore color of an image?

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?
Avatar of mccarl
mccarl
Flag of Australia image

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.
Avatar of Javin007

ASKER

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.
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!
ASKER CERTIFIED SOLUTION
Avatar of mccarl
mccarl
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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!
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!
Glad to help! :)