Bitwise Shifting in Swift Different From Java

I am trying to shift RGB to a ColorInt like I did in Java.

Java: The code below returns '-16777216' for a black color.

int a = 255;
int r = 0;
int g = 0;
int b = 0;

int hcol = 0;
hcol |= (a & 0xFF) << 24;
hcol |= (r & 0xFF) << 16;
hcol |= (g & 0xFF) << 8;
hcol |= b & 0xFF;
System.out.println(hcol);

Swift: From what I know this should be the same exact code as the Java example. But it is returning only positive numbers and is '0' for black where '16777216'(not a negative) is white.

let a = 255
let r = 0;
let g = 0;
let b = 0;

var colInt = 0;
//colInt |= (a & 0xFF) << 24; (Adding this makes the variable even bigger)
colInt |= (r & 0xFF) << 16;
colInt |= (g & 0xFF) << 8;
colInt |= (b & 0xFF);

print("\(colInt)");

As you can see in the Swift example I don't have the alpha color. If I add that it just makes the colInt an even higher number than the max amount of colors in the RGB spectrum.

int is a 32-bit type in Java, and shifting into its sign bit is defined to wrap around as a two's-complement number. Neither of these is true in Swift. But if you build the mask as an Int32 and use &<< to do the shift, you should get exactly the same negative number in the end.

2 Likes

Basically what John said, but no need to use &<<, which masks the shift count, rather than wrapping the result. Since all your shift counts are constants, it doesn't make any difference to mask them.

var colInt: Int32 = 0
colInt |= Int32(a & 0xff) << 24
colInt |= Int32(r & 0xff) << 16
colInt |= Int32(g & 0xff) << 8
colInt |= Int32(b & 0xff)
2 Likes

Oh, do we not care about overflow in the result when shifting? My mistake, then.

1 Like

Thank you for the reply. I have been trying to figure this out for a couple days now!

Thank you! That totally worked. I have been coding in Java for a few years now and just started in Swift recently. Let me tell you one thing, it is a lot to get used to.

It went back and forth a few times in the early days of the language/stdlib, but shifts are now formally defined as bitwise operations, rather than by equivalence to multiplication by a power of two, so they "can't overflow".

3 Likes

We would be remiss not to also point out that if you're working with colors, there's a giant pile of APIs on every platform to handle colors more-or-less correctly while accounting for display color spaces, gamma, etc, that eight bits per channel isn't really enough to do it right anymore, and that you usually shouldn't be rolling your own color handling unless you absolutely have to.

If this is just a fun hobby project, or you have a genuine need to do your own thing, carry on, have fun.

3 Likes

I was probably there when that was decided, so I should remember these things, but I really don't. :)

on Linux as well?

On Linux there are several different giant piles to choose from!

3 Likes

I searched GitHub for a bit and the libs I found for Linux only do the basics without any of the fancy correctness Steve was alluding to. Do you have an example?

GTK, Qt, and lower-level drawing toolkits like Cairo all have their own mechanisms for managing color.

1 Like

Ah, I see your point. But is wrapping this in Swift your suggestion to newcomers to the language? I mean that is again "rolling your own" kinda

I mean, I wouldn't suggest a newbie go try to write a Qt wrapper as their first project, but I think Steve's point was that if you're developing for a particular platform, to favor that platform's existing mechanisms for color management instead of rolling your own. On the other hand, learning the math yourself is also a good introductory project if you're just getting started too.

1 Like

I understand your point and thanks for the pointers to the libraries.

I think for server-side Swift though it doesn't make sense to use Qt or GTK (they are more targeting X-Window systems right? Sry, have only used Qt, so I might be wrong) but a wrapper around Cairo could be a nice thing :thinking:

But that's a different topic I guess (useful cross platform types that are just outside the realm of Foundation) and I don't want to derail the thread.

Thx Joe!

Yeah, if your goal is to do server-side image rendering, using Cairo or another established graphics library would be my recommendation (if there were adequate Swift bindings, of course).

Wrapping a C[++] APIs can be a pain, but it's a whole lot easier than implementing your own color management stack. I wrote some of the computation routines that lie at the bottom of that stack on Apple's platforms, and it is an on-going multi-year project with multiple teams involved.

"Learning the math yourself" is good, but actually doing it correctly turns out to be fiendishly difficult with lots of exciting corner cases that aren't clearly documented anywhere. You talk to color experts and they basically say "well, I think X, but really we'll need to look at sample images and see which option looks right."

3 Likes

Hehe and it turns out even Cairo doesn't do color profiles/spaces it seems :joy:

And kudos to the people who are implementing this on Apple platforms. It's easy to miss all the nice things we get basically for free only now that we also need them on Linux :slight_smile:

I haven't looked at color on Linux much, but my vague recollection is that imagemagick has a pretty good color stack; that might be one good place to look.

1 Like