color && bit shifting.

edited December 2015 in Programming Questions

having to work with getting argb values i have firstly used the bit shifting method, which runs very well; yet, as i do like to understand exacly what is happening i made some experiences after having read again the color reference.

a) i see that println(color) returns weird number "usually" negative. OK, but if you add the alpha value these numbers are not negative. And you can see easily what is behind them using binary(yourcolor,32);

b) i see in the same ref that bit shifting is the more quick way to extract ARGB values. Is it true??? - I have tried 2 ways and the first one is more quick, on my computer of course!!! - Try and tell me...

c) in the same ref the example extract alpha using bitwise op. Why ?

        char[] redV;
         String resultR;

         char[] greenV;
         String resultG;

         char[] blueV;
         String resultB;

         color d;

        void setup(){

          size(100,100);
          redV = new char[8];
          greenV = new char[8];
          blueV = new char[8];
          d = color(0, 204, 255,6);
          //println(d);
          //returns positive value

///first way i have found

          calcValues(d);//comment or uncomment to compare

second way with bit shifting operators

          //calcValuesShift(d);//comment or uncomment to compare
        }




        public void calcValues(int x){

          int alph = (x>>24);

         String e = (binary(x,32));

        for (int i= 8; i<16; i++){
          char extractRed = e.charAt(i);
          redV[i-8] = extractRed;

           resultR = String.valueOf(redV);

        }

        for (int i= 16; i<24; i++){
          char extractGreen = e.charAt(i);
          greenV[i-16] = extractGreen;

           resultG = String.valueOf(greenV);


        }

        for (int i= 24; i<32; i++){
          char extractBlue = e.charAt(i);
          blueV[i-24] = extractBlue;

           resultB = String.valueOf(blueV);
           //println(resultB);

        }

         int finalRed = Integer.parseInt(resultR,2);
          ///println("finalRed=====" + finalRed);

          int finalGreen = Integer.parseInt(resultG,2);
          //println("finalGreen=====" + finalGreen);

          int finalBlue = Integer.parseInt(resultB,2);
          //println("finalBlue=====" + finalBlue);



          background(finalRed, finalGreen, finalBlue, alph);

        println(millis());
        exit();



        }

        public void calcValuesShift(int x){


        int a = x >> 24 & 0xFF;
        int r = x >> 16 & 0xFF;
        int g = x >> 8 & 0xFF;
        int b = x & 0xFF;
        background(r,g,b,a);

        println(millis());
        exit();

        }

Answers

  • @gotToLoop===

    a) is (ref) bit shifting useful or useless (as i think ) for alpha???

    b) why (ref) negative numbers??

    c) what is the most quick ( i dont say "elegant") way to get ARGB=== as for that have you tried the 2 codes??? - what result???

  • edited December 2015

    a) You can use the slowest alpha() instead: https://Processing.org/reference/alpha_.html

    b) When the left-most bit (a.K.a MSb or as sign bit) is 1, value is negative:

    c) If you wanna split an int or color type as a 4 byte array, I believe the code from the link at my 1st reply is pretty fast.

  • Your call to background () fills every visible pixel with colour. Including that in the code segment you're profiling is likely to obscure the results.

  • edited December 2015

    @koogs===

    have you answered to a) have you answered to b) have you answered to c)

    i think NO

    have you tried the code what return millis() that s problem (and i can agree with what you said. but show me how== snipet)

  • @goToLoop=== a) if i understand what you mean so it's alpha value which fixes the + or -; so i tried that:

        for (int i = 0; i<255; i++){
          color d = color(i, 204, 255,i);
          println("couleur weird====" + d + "      i==========" + i + "   binary" + binary(d, 32));
    

    and seen that till i = 127 (for alpha) number is always +; after that allways -; why that?

    b) i see in the reference::

        color argb = color(204, 204, 51, 255);
        int a = (argb >> 24) & 0xFF;
    

    i ask: what is the need of the bitwise operator in this case (32 bits, right shift 24)?

  • Answer ✓

    For the question about perfromance of both functions:

    For profiling you should remove all code that is not needed for the calculations. And i would suggest to repeat the functions to get some kind of average executions time, since that can vary a lot, depending on what your computer is doing in the background.

    Here is an example, i only changed a little and have both functions execute multiple times. For me this results in a great difference in favor of the bitshifting approach.

    char[] redV, greenV, blueV;
    String resultR, resultG, resultB;
    int d, a, r, g, b, time;
    
    void setup() {
      redV = new char[8];
      greenV = new char[8];
      blueV = new char[8];
      d = color(0, 204, 255, 60);
    
      int n = 100000;
    
      time = millis();
      for (int i=0; i< n; i++) {
        calcValues(d);//comment or uncomment to compare
      }
      println("time for "+n+" executions of calcValues(): "+(millis()-time));
      fill(r, g, b, a);
      rect(0, 0, width/2, height);
    
      time= millis();
      for (int i=0; i< n; i++) {
        calcValuesShift(d);//comment or uncomment to compare
      }
      println("time for "+n+" executions of calcValuesShift(): "+(millis()-time));
    
      fill(r, g, b, a);
      rect(width/2, 0, width/2, height);
    }
    
    
    public void calcValues(int x) {
    
      a = (x>>24);
    
      String e = (binary(x, 32));
    
      for (int i= 8; i<16; i++) {
        char extractRed = e.charAt(i);
        redV[i-8] = extractRed;
        resultR = String.valueOf(redV);
      }
    
      for (int i= 16; i<24; i++) {
        char extractGreen = e.charAt(i);
        greenV[i-16] = extractGreen;
        resultG = String.valueOf(greenV);
      }
    
      for (int i= 24; i<32; i++) {
        char extractBlue = e.charAt(i);
        blueV[i-24] = extractBlue;
        resultB = String.valueOf(blueV);
      }
    
      r = Integer.parseInt(resultR, 2);
      g = Integer.parseInt(resultG, 2);
      b = Integer.parseInt(resultB, 2);
    }
    
    public void calcValuesShift(int x) {
      a = x >> 24 & 0xFF;
      r = x >> 16 & 0xFF;
      g = x >> 8 & 0xFF;
      b = x & 0xFF;
    }
    
  • edited December 2015

    int a = (argb >> 24) & 0xFF;

    what is the need of the bitwise operator in this case (32 bits, right shift 24)?

    both >> and & are bitwise operators, i assume you mean the mask

    0xff000000
    in binary
    11111111000000000000000000000000
    >> 24 (because of sign extension)
    11111111111111111111111111111111
    >> 24 & 0xff
    00000000000000000000000011111111
    

    (although you could use >>> which shifts a 0 into the MSB - https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

    people my age may recognise this as the difference between SRA and SRL in z80 assembly language... and ASR / LSR in 68000)

    and no, i didn't run your code because a) it was 10 on a saturday evening and i was checking the forum in bed on my phone and b) it was obviously nonsense 8) trying to measure one tiny mathematical function in the midst of all that other code is like trying to hear a pin drop inside a jet engine.

    benja's profiling is a lot better.

    (i'd've taken the function call out of the big loop - add it to each of the functions you're checking instead - 100,000 function calls is non-trivial. i'd've also disregarded the first few runs just in case there were any startup artefacts. would've used random numbers, seeded identically for each function - using low numbers may skew the results. and would've done 10 runs of each to give a hint of the distribution. that said, when i've done this in the past it is always very noisy because, as benja points out, the rest of the system is doing something else)

  • edited December 2015

    Just re-posting my own algorithm from the link below here for easier reference:
    https://forum.Processing.org/two/discussion/13411/how-to-write-a-large-int-as-as-bytes

    // forum.Processing.org/two/discussion/13411/how-to-write-a-large-int-as-as-bytes
    // GoToLoop (2015-Nov-05)
     
    final byte[] output = new byte[4];
    int input = 115200;
     
    void setup() {
      println(input, binary(input));
      to_aRGB_array(input, output);
      println(output);
      exit();
    }
     
    static final byte[] to_aRGB_array(color c, byte... target) {
      byte a = (byte) (c >>> 030);
      byte r = (byte) (c >>  020 & 0xff);
      byte g = (byte) (c >>  010 & 0xff);
      byte b = (byte) (c         & 0xff);
     
      if (target == null || target.length < 4)  return new byte[] {a, r, g, b};
     
      target[0] = a;
      target[1] = r;
      target[2] = g;
      target[3] = b;
     
      return target;
    }
    
  • edited February 2016 Answer ✓

    And for completeness' sake, an extra utility function for converting byte[] back to aRGB color: :bz

    /**
     * aRGB_Shifting_Conversions (v2.02)
     * GoToLoop (2015-Dec-06)
     *
     * forum.Processing.org/two/discussion/13792/color-bit-shifting
     * forum.Processing.org/two/discussion/13411/how-to-write-a-large-int-as-as-bytes
     * forum.Processing.org/two/discussion/14814/how-to-translate-byte-to-pimage
     */
    
    final byte[] output = new byte[4];
    int input = 115200;
    
    void setup() {
      println(input, hex(input), binary(input));
    
      to_aRGB_array(input, output);
      println(output);
      println();
      println(int(output));
    
      input = from_aRGB_array(output);
      println(input, hex(input), binary(input));
    
      exit();
    }
    
    static final byte[] to_aRGB_array(final color c, final byte... target) {
      final byte a = (byte) (c >>> 030);
      final byte r = (byte) (c >>  020 & 0xff);
      final byte g = (byte) (c >>  010 & 0xff);
      final byte b = (byte) (c         & 0xff);
    
      if (target == null || target.length < 4)  return new byte[] {a, r, g, b};
    
      target[0] = a;
      target[1] = r;
      target[2] = g;
      target[3] = b;
    
      return target;
    }
    
    static final color from_aRGB_array(final byte... source) {
      if (source == null)  return 0;
    
      color c = 0, s = 040;
      for (final byte b : source)  c |= (b & 0xff) << (s -= 010);
      return c;
    }
    
  • @benja:: you are absolutely right; i tested the twos: winner is bit shifting!!! - what is strange is that the difference grows (if you change the value for execution number) in a non proportional way!!!

    @koogs:: ok: it is probably impossible to measure the difference without making what added Benja (the rest of the code is exactly what i provided), that is to say a for loop, which , as you say, would be better out of the "big loop".

    The 2 other points (negative numbers and use of mask 0xff) were not "in absolute" but / at the reference doc: this reference says that println with some color returns negative numbers and it seemed to me that it depends of the alpha value (because here are the first 8 bits):: for the 127 first alpha values (from zero to 127) it returns + numbers, then 128/255 it returns negative numbers... As for bit shifting the same reference uses 0xFF for extracting alpha and it seemed to me that it was useless, right shifting 24; of course one must use that for r,g,b in the same case...

    @goToLoop:: now i can organize some comparative test with 3 codes!!! - i ll give you the "palmares"...

    thanks to all!!!

  • edited December 2015

    @akenaton, just an important notice. Since values past 127 are negative as byte datatype,
    we need to apply & 0xff before using them in fill() & stroke():

    fill(output[1] & 0xff, output[2] & 0xff, output[3] & 0xff, output[0] & 0xff);

    In order to steer away from that byte negative annoyance, you can modify my 2 utility functions to use a more robust whole datatype like short, char or int.

    Or you can simply convert the whole byte[] to either char[] or int[] w/ char() & int():

    1. https://Processing.org/reference/charconvert_.html
    2. https://Processing.org/reference/intconvert_.html

    char[] argb = char( to_aRGB_array(#F0A080) );
    fill(argb[1], argb[2], argb[3], argb[0]); // no & 0xff needed now!
    
  • @GoToLoop: that is exactly what i was now discovering, trying your code, seeing these negative values and deciding to convert byte[] to int[]!!! (i didn't think to add & 0xff...) Many thanks.

  • @GotoLoop== @koogs== @benja==

    testing the 3 codes result is WInner = classical bit Shifting Second is GoToLoop ( a lot milliseconds after!!!) I am the looser!!! ( far away....) but i have learned a lot of things about color... thanks! :o3

  • edited December 2015

    In my defense for 2nd place, both of my functions are static.
    Therefore they don't depend on the state of sketch's fields and can be used elsewhere. ~O)
    It stores everything in a byte[] rather than accessing some external global variables. :D

    P.S.: Another feature is that once compiled, neither of my functions crash ever! \m/

  • edited December 2015

    "z80 assembly language.."

    I started on that :) It was a gateway drug :)

Sign In or Register to comment.