views:

150

answers:

4

This is probably easy, but I'm trying to convert from a source which provides colors in RGB strings to an output in BGR strings in Java. I've been busting my brain and time on shifting and Long.decode and Long.toHexString.

Feel free to also throw alpha values in there (RGBA -> ABGR), though I think I can extend the principles.

I can assume that the hex is in the form specified in the long and int decode:

0x HexDigits  
0X HexDigits 
# HexDigits
+3  A: 

For 24bit colors (8 bits to each of R,G,B):

String rgbSource = getRGBSource(); //your function to get a string version of it
int in = Integer.decode(rgbSource);
int red = (in >> 16) & 0xFF;
int green = (in >> 8) & 0xFF;
int blue = (in >> 0) & 0xFF;
int out = (blue << 16) | (green << 8) | (red << 0);
Mike
good answer! This one worked if I switched the color variable names to be in order, red green blue.
Adam
Oops. I've fixed it now. :)
Mike
This method also works when adding an optional alpha if the pattern is continued. You can use the following from my answer to auto add an opaque alpha if one is missing: long rgba = (rgbSource.length() < 8 ? Long.decode(rgbSource+ "ff") : Long.decode(rgbSource)); //decodes a number of hex formats and sets alpha
Adam
+2  A: 

int abgr = Integer.reverseBytes(rgba);


Including supporting code, with the assumption that it is safe to decide whether alpha needs adding based on the string length (consider "0xFFFFFF".length() for example):

String rgb = getRGB();

//decodes a number of hex formats and sets alpha
int rgba = rgb.length() < 8 ? 
           Long.decode(rgb + "ff").intValue() : 
           Long.decode(rgb       ).intValue();

int abgr = Integer.reverseBytes(rgba);

Here's a one line method:

public static String reverseRGB(String rgba) {
    return String.format("%08X",Integer.reverseBytes(Long.decode(rgba.length() < 8 ? rgba + "ff" : rgba).intValue()));
}
Stephen Denne
looks appealing, but int in = Integer.decode(preferenceValue);String abgrColor = Integer.toHexString(Integer.reverseBytes(in));had #00ff0d -> dff0000
Adam
@Adam that's why I included "a" in the variable names. If you add "ff" on the end using your code, then #00ff0dff becomes ff0dff00
Stephen Denne
I guess I am confused. I thought that in order to alpha long would be needed because #ffffffff (opaque white) is larger than int max. When I tried this same idea with longs the result was even further off. Were you using unsigned ints?
Adam
@Adam, Java doesn't have unsigned ints, but it does have methods in Integer that work with 32 bits, ignoring the special status of the high bit as a sign bit. The reverseBytes method uses >>> 24 to perform a logical shift right of the high bytes, saving the need to AND it with 0xFF.
Stephen Denne
@Stephen How do you store the rbga #ffffffff in an int (4 294 967 295 > 2 147 483 647)? Sorry for my ignorance on the issue. Do you convert the int back into a long for Long.toHexString?
Adam
@Adam both Integer and Long's decode functions require the hex digits portion to represent positive numbers, with a minus sign to indicate negatives, so I recommend using Long's decode function. Then Long.intValue() returns the lower 32 bits, which interpreted as a two's complement signed integer is -1.
Stephen Denne
@Adam javadocs on Integer.toHexString say that integer is treated as an unsigned integer, so no need to use Long's, though it wouldn't make any difference. Of more concern is that both toHexString methods only return sufficient hex characters to represent the number. You'd probably be better off with String.format("%08X",abgr);
Stephen Denne
@Stephen I appreciate all your help with this, but this method still isn't working for me. Note that missing leading zeroes is fine in my case, but I like your format (+1).long number = (rgba .length() < 8 ? Long.decode(rgba + "ff") : Long.decode(rgba ));String abgrColor = Long.toHexString(Integer.reverseBytes((int)number));Results in #d90d8e -> ffffffff
Adam
@Adam `Long.toHexString` returns `ffffffffff8e0dd9`
Stephen Denne
@Stephen Ah, I see what is happeneing. I was then putting that value into a kml doc where only the first 8 characters were read. Thanks for your help on this!
Adam
A: 

Here is how I got it to work, but I really hope there is a better way because this is awful. Please come up with a cleaner, more efficient way to do this, so I can give you rep.

long number = (rgb.length() < 8 ? Long.decode(rgb+ "ff") : Long.decode(rgb)); //decodes a number of hex formats and sets alpha
String abgrColor = (new StringBuilder())
.append((Long.toHexString((number) & 0xFF).length()==2? Long.toHexString((number) & 0xFF): "0"+Long.toHexString((number) & 0xFF)))
.append((Long.toHexString((number>>8) & 0xFF).length()==2? Long.toHexString((number>>8) & 0xFF): "0"+Long.toHexString((number>>8) & 0xFF)))
.append((Long.toHexString((number>>16) & 0xFF).length()==2? Long.toHexString((number>>16) & 0xFF): "0"+Long.toHexString((number>>16) & 0xFF)))
.append((Long.toHexString((number>>24) & 0xFF).length()==2? Long.toHexString((number>>24) & 0xFF): "0"+Long.toHexString((number>>24) & 0xFF)))
.toString();
Adam
+1  A: 

If the input is a 6 character rgb string:

String bgr = rgb.substring(4,6) + rgb.substring(2,4) + rgb.substring(0,2);

If the input is an 8 character rgba string:

String abgr = rgba.substring(6,8) + rgba.substring(4,6) + rgba.substring(2,4) + rgba.substring(0,2);

If the input is an int with 8 bit channels:

String bgr = String.format( "%02X%02X%02X" , rgb & 0x00FF , (rgb>>8) & 0x00FF , (rgb>>16) & 0x00FF );
String abgr = String.format( "%02X%02X%02X%02X" , rgba & 0x00FF , (rgba>>8) & 0x00FF , (rgba>>16) & 0x00FF , (rgba>>24) & 0x00FF );
// or
String bgr = String.format( "%06X" , Integer.reverseBytes( rgb ) >> 8 );
String abgr = String.format( "%08X" , Integer.reverseBytes( rgba ) );
drawnonward
This answer is also good, but it doesn't have the robustness of using a "decode" function when dealing with "#rrggbb" or "0xrrggbb"
Adam