LoginSignup
5
0

More than 5 years have passed since last update.

ARMアセンブリ言語でRGBA->HSVに変換してみた件

Last updated at Posted at 2017-01-27

きっかけ

写真の色分析をするのに、RGBA形式の画像をHSV形式に変換しています。
いつもはOpenCV(c++)を使っているのですが、どうもその処理が遅いようなので、ARMアセンブリ言語の勉強を兼ねて作ってみました。
(というか、ネット上に無かったので自分で作らざるを得なかった)

前回、c++とアセンブリ言語で画像を反転させてみるをやったので
できるだろ、とか思ったら、甘かった。。。。

RGBA画像をHSVに変換するコード(c++)

Googleで検索するといくつかでてきますが、
今回はそのなかでConvert RGB to HSV and back with a micro controllerのコードを参考にしました。

理由は unsigned char型を使っていたので、アセンブリ言語に落とし込みやすかったから。

コードはこんな感じです。

最後の部分の 0.71って数字は、Hの値の範囲を0〜360から、0〜256に変換しているだけです。
(0.71 ≒ 256/360)

typedef struct {
    unsigned char *data;
    int width;
    int height;
    int bpp;
    int size;
} image_t;


void rgba2hsvx_c(image_t *rgba, image_t *hsv) {
  unsigned char *src = (unsigned char *)rgba->data;
  unsigned char *dst = (unsigned char *)hsv->data;

  unsigned char min, max, delta;
  unsigned char r,g,b,a;
  unsigned char s,v;
  signed int h;

  int x, y;

  for (y = 0; y < rgba->height; y++) {
    for (x = 0; x < rgba->width; x++) {
      r = *src++;
      g = *src++;
      b = *src++;
      a = *src++;

      min = r < g ? r : g;
      min = b < min ? b : min;

      max = r > g ? r : g;
      max = b > max ? b : max;

      v = max;
      delta = max - min;

      if (!(max == 0 || delta == 0)) {

        s = (int)(delta * 255) / max;

        if (r == max) {
          h = (60 * (g - b))  / delta;
        } else {
          if (g == max) {
            h = 120 + ((60 * (b - r)) / delta);
          } else {
            h = 240 + ((60 * (r - g)) / delta);
          }
        }

        if (h < 0)  {
          h += 360;
        }
      } else {
        h = 0;
        s = 0;
      }

      *dst++ = (unsigned int)(h * 0.71);   // In real value range is 0-359, but for rgba image, down scale to 0-255
      *dst++ = s;
      *dst++ = v;
      *dst++ = a;
    }
  }
}

アセンブリコード

めちゃくちゃ苦労しました。5日間、脳みそ使用率120%って感じw
(ARMアセンブリコードを書き始めてから、まだ2週間ですので)

void rgba2hsvx_neon(image_t *rgba, image_t *hsv) {
  unsigned char *src = (unsigned char *)rgba->data;
  unsigned char *dst = (unsigned char *)hsv->data;

  __asm__ __volatile__(

    "            vmov.i32       d0, #0\n"                    // d0 (s0, s1) = 0x0000000000000000

    "            lsr            %[total], %[total], #5\n"    // %[size] = %[size] / 32 ; The number '32' means 4bytes * 8pixels

    "            mov            r9, %[src]\n"
    "            mov            r10, %[dst]\n"

    "            mov            r0, #60\n"
    "            vmov.u32       s31, r0\n"                   // (d15) s31 = 60
    "            vcvt.f32.u32   s31, s31\n"

    "            mov            r0, #255\n"
    "            vmov.i32       s30, r0\n"                   // (d15) s30 = 255
    "            vcvt.f32.u32   s30, s30\n"

    "            mov            r0, #256\n"
    "            vmov.s32       s29, r0\n"                   // (d14) s29 = 256
    "            vcvt.f32.u32   s29, s29\n"

    "            mov            r0, #360\n"
    "            vmov.u32       s28, r0\n"                   // (d14) s28 = 360
    "            vcvt.f32.u32   s28, s28\n"

    "            mov            r0, #120\n"
    "            vmov.s32       s27, r0\n"                   // (d13) s27 = 120
    "            vcvt.f32.u32   s27, s27\n"

    "            mov            r0, #240\n"
    "            vmov.u32       s26, r0\n"                   // (d13) s26 = 240
    "            vcvt.f32.u32   s26, s26\n"

    "loop_hsv:\n"
    "            pld            [r9, #256]\n"
    "            vld4.8         {d1, d2, d3, d4}, [r9]\n"    // Load elements from %[src] and store to each D registers:
                                                             //  [RED]   d1 = [r7, r6, ... r0]
                                                             //  [GREEN] d2 = [g7, g6, ... g0]
                                                             //  [BLUE]  d3 = [b7, b6, ... b0]
                                                             //  [ALPHA] d4 = [a7, a6, ... a0]

    "            vmin.u8        d5, d1, d2\n"                // min = MIN(r, g);
    "            vmin.u8        d5, d5, d3\n"                // min = MIN(b, min);

    "            vmax.u8        d6, d1, d2\n"                // max = MAX(r, g);
    "            vmax.u8        d6, d6, d3\n"                // max = MAX(b, max);

    "            vqsub.u8       d7, d6, d5\n"                // delta = max - min

                                                             //if (!(max == 0 || delta == 0))
    "            vceq.u8        d4, d7, d0\n"                // d4 = max == 0 ? 0xFF : 0
    "            vceq.u8        d8, d6, d0\n"                // d8 = delta == 0 ? 0xFF : 0
    "            vorr.u8        d8, d4, d8\n"                // d8 = d4(0 or 0xFF) | d8(0 or 0xFF)


    "            vst4.8         {d5, d6, d7, d8}, [r10]\n"   // (min) : (max) : (delta) : (flag)

    "            mov            r0, #0\n"                    // H = 0
    "            mov            r1, #0\n"                    // S = 0
    "            mov            r2, #0\n"                    // V = 0
    "            mov            r8, #8\n"                    // r8 (i) = 8

    //------------
    //   if (!(max == 0 || delta == 0)) {
    //------------
    "calc_hsv:\n"
    "            pld            [r10, #32]\n"                // Preload 32bit data from the main memory
    "            ldrb           r3, [r10]\n"                 // flag = *r10++

    "            cmp            r3, #0xFF\n"                 // if (flag == 0xff)
    "            beq            calc_hsv_end\n"              //    goto calc_s
    //------------
    //       s = (int)(delta * 255) / max;
    //------------
    "calc_s:\n"

    "            pld            [r9, #32]\n"                // Preload 256bit data from the main memory
    "            ldrb           r5, [r9]\n"                  // red(r5) = *r9
    "            ldrb           r6, [r9, #1]\n"              // green(r6) = *(r9 + 1)
    "            ldrb           r3, [r9, #2]\n"              // blue(r3) = *(r9 + 2)

    "            vmov.u32       s2, r5\n"                    // s2 = red
    "            vmov.u32       s3, r6\n"                    // s3 = green
    "            vmov.u32       s4, r3\n"                    // s4 = blue
    "            vcvt.f32.u32   s2, s2\n"                    // convert unsigned int to floating point
    "            vcvt.f32.u32   s3, s3\n"
    "            vcvt.f32.u32   s4, s4\n"

    "            ldrb           r2, [r10, #1]\n"             // max(V) = *(r10 + 1)
    "            ldrb           r3, [r10, #2]\n"             // delta = *(r10 + 2)
    "            vmov.u32       s5, r3\n"                    // s5 = delta
    "            vmov.u32       s6, r2\n"                    // s6 = max
    "            vcvt.f32.u32   s5, s5\n"                    // unsigned int 32 bits -> float 32 bits
    "            vcvt.f32.u32   s6, s6\n"

    "            vmul.f32       s7, s5, s30\n"               // s7 = s5(delta) * s30(#255)
    "            vdiv.f32       s7, s7, s6\n"                // s7 = s7 / s6(max)
    "            vcvt.u32.f32   s7, s7\n"                    // float 32 bits -> unsigned int 32 bits
    "            vmov           r1, s7\n"                    // r1(S) = s7


    //---------------
    //    s6(offset) = 0
    //    if (r == max) {
    //      s7 = (g-b)
    //    } else {
    //      if (g == max) {
    //        s7 = (b - r)
    //        s6 = 120
    //      } else {
    //        s7 = (r - g)
    //        s6 = 240
    //      }
    //   }
    //   s7 = s7 * 60
    //   s7 = s7 / delta
    //   s7 += offset
    //   H = s7 < 0 ? s7 + 360 : s7
    //---------------
    "            cmp            r2, r5\n"                    // if ( r2(max) == r5(red) )
    "            beq            r_equal_max\n"               //   goto r_equal_max
    "            cmp            r2, r6\n"                    // if ( r2(max) == r6(green) )
    "            beq            g_equal_max\n"               //   goto g_equal_max


    "b_equal_max:\n"
    "            vsub.f32       s7, s2, s3\n"                // s7 = s2(red) - s3(green)
    "            vmov           s6, s26\n"                   // s6 = s26(#240)
    "            bal            calc_h\n"                    // goto calc_h

    "g_equal_max:\n"
    "            vsub.f32       s7, s4, s2\n"                // s7 = s4(blue) - s2(red)
    "            vmov           s6, s27\n"                   // s6 = s27(#120)
    "            bal            calc_h\n"                    // goto calc_h

    "r_equal_max:\n"
    "            vmov           s6, s0\n"                    // s6 = s0(#0)
    "            vsub.f32       s7, s3, s4\n"                // s7 = s3(green) - s4(blue)


    "calc_h:\n"
    "            vmul.f32       s7, s7, s31\n"               // s7 = s7 * s31(#60)
    "            vdiv.f32       s7, s7, s5\n"                // s7 = s7 / s5(delta)
    "            vadd.f32       s7, s7, s6\n"                // s7 += s6(offset)

    "            vaddlt.f32     s7, s28\n"                   // s7 = s7 < 0 ? s7 + s28(#360) : 0
    "            vmul.f32       s7, s7, s29\n"               // s7 = s7 * s29(#256)
    "            vdiv.f32       s7, s7, s28\n"               // s7 = s7 / s28(#360)
    "            vcvt.u32.f32   s7, s7\n"                    // float 32bit -> unsigned int 32bit
    "            vmov           r0, s7\n"                    // r0(H) = s7

    "calc_hsv_end:\n"
    "            mov            r3, #0xFF\n"
    "            strb           r0, [r10]\n"                 // H
    "            strb           r1, [r10, #1]\n"             // S
    "            strb           r2, [r10, #2]\n"             // V
    "            strb           r3, [r10, #3]\n"             // A = 0xFF
    "            add            r9, r9, #4\n"
    "            add            r10, r10, #4\n"


    "            subs           r8, r8, #1\n"                // r8--
    "            bne            calc_hsv\n"                  // if (r8 != 0) goto calc_hsv
    "            subs           %[total], %[total], #1\n"    // %[total]--
    "            bne            loop_hsv\n"                  // if (%[total] != 0) goto loop_hsv



    :
    : [src]"r"(src) , [dst]"r"(dst), [total]"r"(rgba->size)
    : "q0", "q1", "q2", "q3", "r0",
      "r1", "r2", "r3", "r5", "r6", "r4", "r8", "r9", "r10",
      "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s26", "s27",
      "s28", "s29", "s30", "s31", "memory", "cc"
  );

}

結果どうなった

1920x1080の画像をRK3288 CPUで試してみました。
たぶん使用しているメモリとかの関係だと思いますが、他のデバイスでテストすると結果が変わります。

方法 時間
OpenCV(3.1) 約84ms
c++ (-O3) 約193ms
assembly (neon) 約80ms

うーん、苦労した割には大差がない。

バグ?

どうゆうわけか、OpenCVでHSVに変換したときと出力される画像が微妙に違うんですよね。
でもc++とは同じになるので、(とりあえず)良し、とはしていますが。

元画像はこれ。使用しているデバイスの中にサンプル画像としてあったものを1920x1080にリサイズして使用。

(画像をクリックすればダウンロードできます)

OpenCVで変換すると、こうなります。なんかシュール...

アセンブリコードで変換するとこうなります。

どうも鳥の胸のあたりが結果が違うんですよね。
理由はいまのところ不明です。

VFPレジスタ、Dレジスタ、Qレジスタ

やっている途中でハマったのが、レジスタのマップ。
s0 = 360とコードの前方で定義して、後半になるとぜんぜん違う値が入っていて、最初は計算が全然合わない謎の現象が発生して、随分悩まされました。

調べたところ、VFPレジスタはD/Qレジスタと同じ空間なのだと。
なぬぅ!!

そんなことは、AndroidNDKネイティブプログラミング第2版には書いてなかった!

公式サイトの解説によると、次のように書いてあります。

VFP コプロセッサには、s0 〜 s31 の 32 本の単精度レジスタがあります。各レジスタは、単精度浮動小数点値または 32 ビット整数を保持できます。
これら 32 本のレジスタは、d0 〜 d15 の 16 本の倍精度レジスタとしても使用されます。dn は、s(2n) および s(2n+1) と同じハードウェアを使用します。

図解すると、こうらしい。

registers.png

つまり、d0レジスタに値を保存してあるのに、s3レジスタに値をうっかり保存すると、d0レジスタは影響を受けるのです。

これを念頭にいれて、コードを読んで下さい。

コードの解説

将来の自分のために、残しておきます。
まぁコメントを読めばわかる気がするけど。

定数の宣言

コード中で計算のための定数をsレジスタに定義しておきます。

うっかり上書きしないようにするために、定数をsレジスタの後ろの方に入れておきます。

step1.png

min, max, deltaの計算

NEON命令を使って、8ピクセル分をまとめて計算してしまいます。

step2.png

max == 0 && delta == 0 の判定

2つの値を比較するif文は面倒だったので、NEON命令を使って、8ピクセル分をまとめて判定しておきます。
step3.png

計算結果を一時保存

そしてこれらの値を 書き出し領域の%[dst] (r10)に一時保存します。
(※メインメモリを使わなかったら、もっと早くなるかも...)

この段階で、8ピクセル分の min, max(V), delta, flagが計算できました。

step4.png

Sの計算

書き出し領域に保存した1ピクセル分のデータを読み出して、1ピクセルずつ処理していきます。
(* ホントはこれも並列化したい)

HSVのSの計算はこの部分です。
c++に書いてある計算をVFPを使って計算しているだけです。

//------------
//       s = (int)(delta * 255) / max;
//------------
"calc_s:\n"

"            pld            [r9, #256]\n"                // Preload 256bit data from the main memory
"            ldrb           r5, [r9]\n"                  // red(r5) = *r9
"            ldrb           r6, [r9, #1]\n"              // green(r6) = *(r9 + 1)
"            ldrb           r7, [r9, #2]\n"              // blue(r7) = *(r9 + 2)

"            vmov.u32       s2, r5\n"                    // s2 = red
"            vmov.u32       s3, r6\n"                    // s3 = green
"            vmov.u32       s4, r7\n"                    // s4 = blue
"            vcvt.f32.u32   s2, s2\n"                    // convert unsigned int to floating point
"            vcvt.f32.u32   s3, s3\n"
"            vcvt.f32.u32   s4, s4\n"

"            ldrb           r2, [r10, #1]\n"             // max(V) = *r11++
"            ldrb           r3, [r10, #2]\n"             // delta = *r11++
"            vmov.u32       s5, r3\n"                    // s5 = delta
"            vmov.u32       s6, r2\n"                    // s6 = max
"            vcvt.f32.u32   s5, s5\n"                    // unsigned int 32 bits -> float 32 bits
"            vcvt.f32.u32   s6, s6\n"

"            vmul.f32       s7, s5, s30\n"               // s7 = s5(delta) * s30(#255)
"            vdiv.f32       s7, s7, s6\n"                // s7 = s7 / s6(max)
"            vcvt.u32.f32   s7, s7\n"                    // float 32 bits -> unsigned int 32 bits
"            vmov           r1, s7\n"                    // r1(S) = s7

Hの計算

HSVのHの計算はこの部分です。
(* ホントはこれも並列化したい)

//---------------
//    s6(offset) = 0
//    if (r == max) {
//      s7 = (g-b)
//    } else {
//      if (g == max) {
//        s7 = (b - r)
//        s6 = 120
//      } else {
//        s7 = (r - g)
//        s6 = 240
//      }
//   }
//   s7 = s7 * 60
//   s7 = s7 / delta
//   s7 += offset
//   H = s7 < 0 ? s7 + 360 : s7
//---------------
"            cmp            r2, r5\n"                    // if ( r2(max) == r5(red) )
"            beq            r_equal_max\n"               //   goto r_equal_max
"            cmp            r2, r6\n"                    // if ( r2(max) == r6(green) )
"            beq            g_equal_max\n"               //   goto g_equal_max


"b_equal_max:\n"
"            vsub.f32       s7, s2, s3\n"                // s7 = s2(red) - s3(green)
"            vmov           s6, s26\n"                   // s6 = s26(#240)
"            bal            calc_h\n"                    // goto calc_h

"g_equal_max:\n"
"            vsub.f32       s7, s4, s2\n"                // s7 = s4(blue) - s2(red)
"            vmov           s6, s27\n"                   // s6 = s27(#120)
"            bal            calc_h\n"                    // goto calc_h

"r_equal_max:\n"
"            vmov           s6, s0\n"                    // s6 = s0(#0)
"            vsub.f32       s7, s3, s4\n"                // s7 = s3(green) - s4(blue)


"calc_h:\n"
"            vmul.f32       s7, s7, s31\n"               // s7 = s7 * s31(#60)
"            vdiv.f32       s7, s7, s5\n"                // s7 = s7 / s5(delta)
"            vadd.f32       s7, s7, s6\n"                // s7 += s6(offset)

"            vaddlt.f32     s7, s28\n"                   // s7 = s7 < 0 ? s7 + s28(#360) : 0
"            vmul.f32       s7, s7, s29\n"               // s7 = s7 * s29(#256)
"            vdiv.f32       s7, s7, s28\n"               // s7 = s7 / s28(#360)
"            vcvt.u32.f32   s7, s7\n"                    // float 32bit -> unsigned int 32bit
"            vmov           r0, s7\n"                    // r0(H) = s7

最後に保存

あとは書き出し領域に保存して、次の8ピクセルの処理に移ります。

"calc_hsv_end:\n"
"            mov            r3, #0xFF\n"
"            strb           r0, [r10]\n"                 // H
"            strb           r1, [r10, #1]\n"             // S
"            strb           r2, [r10, #2]\n"             // V
"            strb           r3, [r10, #3]\n"             // A = 0xFF
"            add            r9, r9, #4\n"
"            add            r10, r10, #4\n"


"            subs           r8, r8, #1\n"                // r8--
"            bne            calc_hsv\n"                  // if (r8 != 0) goto calc_hsv
"            subs           %[total], %[total], #1\n"                // sub--
"            bne            loop_hsv\n"                  // if (r11 != 0) goto calc_hsv

まとめ

記事を書くのに疲れたので、一旦まとめます。

このコードを書くのに、5日間フルで使いましたね。
朝も昼も夜も、寝ている間でさえもうなされながら、考えてました。

で、結果が、OpenCVと対して変わらない!!!! ... orz

でもSの計算を並列化できれば、たぶん早くなるはず。と思っているので、誰かヘルプしてください

追記1. Sの計算は並列化できない(たぶん)

ARM32ビットCPUの場合、除算を擬似的に行っているらしく、計算に32ビット幅を要します。

そして一回の命令で一つの値しか計算できないので、除算の部分はどうしても並列化はできないっぽい。

追記2. 3週間バトルしてみてその後...

最終的にはこんなコードにしました。でもOpenCVの方がちょっとだけ早い。。。のでギブアップ

void rgba2hsvx_neon(image_t *rgba, image_t *hsv) {
  volatile unsigned char *src = (unsigned char *)rgba->data;
  volatile unsigned char *dst = (unsigned char *)hsv->data;

  __asm__ __volatile__(
    "           mov             r0, %[src]\n"
    "           pld             [r0]\n"

    "           mov             r1, %[dst]\n"
    "           mov             r7, %[size]\n"
    "           lsr             r7, r7, #5\n"           // size = size >> 5

    "           vmov.u32        d25, #255\n"             //d25 = 255.0f
    "           vcvt.f32.u32    d25, d25\n"

    "           vmov.u32        q15, #0xff\n"            //q15(d30, d31) = 0xFF
    "           vmov.u32        d29, #120\n"             //d29 = 120
    "           vmov.u32        d28, #240\n"             //d28 = 240

    "           vmov.u32        d27, #60\n"              //d27 = 60.0f

    "           vadd.u32        d15, d29, d28\n"         //d15 = 360.0f
    "           vcvt.f32.u32    d15, d15\n"

    "           vmov.u32        d14, #256\n"             //d14 = 0.71f
    "           vcvt.f32.u32    d14, d14\n"
    "           vdiv.f32        s28, s28, s30\n"
    "           vmov            s29, s28\n"

    "           vmov.u8         d23, #0xFF\n"


    "loop_hsv:\n"
    "           vld4.8          {d0-d3}, [r0]\n"         // r,g,b,a

    //-----------------
    // d4 = max
    // d5 = delta
    //-----------------
    "           vmin.u8         d5, d0, d1\n"            // min = MIN(r, g);
    "           vmin.u8         d5, d5, d2\n"            // min = MIN(b, min);

    "           vmax.u8         d22, d0, d1\n"           // max = MAX(r, g);
    "           vmax.u8         d22, d22, d2\n"          // max = MAX(b, max);
    "           vqsub.u8        d5, d22, d5\n"           // delta = max - min


    //--------------------------------
    // zero clear of the store space
    //--------------------------------
    "           mov             r4, #4\n"
    "           vmov.u64        q10, #0\n"               // For the final result of H and S values
    "           vmov            d4, d22\n"

    "calc_s:\n"
    //------------------------------------------------
    // Shift the store space for H and S (d20, d21)
    //------------------------------------------------
    "           vshl.u32        q10, #8\n"               // q10(d20,d21) = q10 << 8

    //----------------------------------------
    // Load values from d3(max), d4(delta)
    //----------------------------------------
    "           vand.u32        q4, q2, q15\n"           // d8(max) = d4(max) & d30(#0xff), d9(delta) = d5(delta) & d31(#0xff)

    //----------------
    // calculate H
    //----------------
    "           vsub.u64        d11, d11, d11\n"         // d11 = 0
    "           vmov.u32        d18, d31\n"              // d18 = d31(0xFF)

    //--------------------------------
    // if (red == max) {
    //   op1 = green
    //   op2 = blue
    // }
    //--------------------------------
    "           vand.u32        d17, d31, d0\n"          // d17 = red & 0xff
    "           vceq.u32        d7, d17, d8\n"           // d7 = red == max ? 0xFF : 0
    "           vsub.u32        d18, d18, d7\n"          // d18 = d18 - d7
    "           vand.u32        d7, d7, d31\n"           // d7 = d7 & 0xff
    "           vand.u32        d10, d7, d1\n"           // d10(op1) = d7 & green
    "           vand.u32        d12, d7, d2\n"           // d12(op2) = d7 & blue


    //--------------------------------
    // if (green == max) {
    //   op1 = blue
    //   op2 = red
    //   offset = 120
    // }
    //--------------------------------
    "           vand.u32        d17, d31, d1\n"          // d17 = green & 0xff
    "           vceq.u32        d7, d17, d8\n"           // d7 = green == max ? 0xFF : 0
    "           vsub.u32        d18, d18, d7\n"          // d18 = d18 - d7
    "           vand.u32        d7, d7, d31\n"           // d7 = d7 & 0xff
    "           vand.u32        d7, d7, d18\n"           // d7 = d7 & d18
    "           vand.u32        d17, d7, d2\n"           // d17 = d7 & blue
    "           vorr.u32        d10, d10, d17\n"         // d10(op1) = d10 | d17
    "           vand.u32        d17, d7, d0\n"           // d17 = d7 & red
    "           vorr.u32        d12, d12, d17\n"         // d12(op2) = d12 | d17
    "           vand.u32        d11, d7, d29\n"          // d11(offset) = d7 & d29(#120)

    //--------------------------------
    // if (blue == max) {
    //   op1 = red
    //   op2 = green
    //   offset = 240
    // }
    //--------------------------------
    "           vand.u32        d17, d31, d2\n"          // d17 = blue & 0xff
    "           vceq.u32        d7, d17, d8\n"           // d7 = blue == max ? 0xFF : 0
    "           vand.u32        d7, d7, d31\n"           // d7 = d7 & 0xff
    "           vand.u32        d7, d7, d18\n"           // d7 = d7 & d18
    "           vand.u32        d17, d7, d0\n"           // d17 = d7 & red
    "           vorr.u32        d10, d10, d17\n"         // d10(op1) = d10 | d19
    "           vand.u32        d17, d7, d1\n"           // d17 = d7 & green
    "           vorr.u32        d12, d12, d17\n"         // d12(op2) = d12 | d17
    "           vand.u32        d17, d7, d28\n"          // d17 = d7 & d28(#240)
    "           vorr.u32        d11, d11, d17\n"         // d11(offset) = d11 | d17


    //--------------------------------
    // h = ((op1 - op2) * 60) / delta
    //--------------------------------
    "           vand.u32        d10, d10, d31\n"         // d10(op1) = d10 & d31(#0xFF)
    "           vand.u32        d12, d12, d31\n"         // d12(op2) = d12 & d31(#0xFF)
    "           vsub.s32        d10, d10, d12\n"         // d10 = d10(op1) - d11(op2)
    "           vmul.s32        d10, d10, d27\n"         // d10 = d10 * d27(#60)
    "           vcvt.f32.u32    q5, q5\n"                // unsigned int -> float
    "           vcvt.f32.u32    q4, q4\n"                // (d8, d9, d10, d11)
    "           vdiv.f32        s20, s20, s18\n"         // s20 = s20 / s18(delta)
    "           vdiv.f32        s21, s21, s19\n"         // s21 = s21 / s19(delta)
    "           vadd.f32        d10, d10, d11\n"         // d10 += d11(offset)

    //-------------------------
    // h += h < 0 ? 360.0f : 0
    //-------------------------
    "           vclt.f32        d11, d10, #0\n"          // d11 = d10 < 0 ? 0xFF : 0
    "           vand.f32        d11, d15\n"              // d11 = d11 & d15(#360.0f)
    "           vadd.f32        d10, d11\n"              // d10 = d10 + d11
    //---------------------------------------
    // downscale H value from 0-359 to 0-255
    //---------------------------------------
    "           vmul.f32        d10, d10, d14\n"         // d10 = d10 * 0.71

    //------------------------
    // s = delta * 255 / max
    //------------------------
    "           vmul.f32        d11, d9, d25\n"          // d11 = d9 * d25(#255.0f)
    "           vdiv.f32        s22, s22, s16\n"         // s22 = s22 / s16(max)
    "           vdiv.f32        s23, s23, s17\n"         // s23 = s23 / s17(max)

    "           vcvt.u32.f32    q5, q5\n"                // float -> unsigned int (d10, d11)

    "           vorr.u64        q10, q5, q10\n"          // H = H | d10, S = S | d11

    "           vshr.u64        q0, q0, #8\n"            // q0(red, green) = q0 >> 8
    "           vshr.u64        d2, d2, #8\n"            // d2(blue) = q1 >> 8
    "           vshr.u64        q2, q2, #8\n"            // q2(delta, max) = q2 >> 8

    "           subs            r4, r4, #1\n"
    "           bne             calc_s\n"

    "           pldw            [r1]\n"
    "           vrev32.8        q10, q10\n"
    "           vst4.8          {q10-q11}, [r1]\n"       // r,g,b,a


    "           add             r0, r0, #32\n"
    "           add             r1, r1, #32\n"
    "           pld             [r0]\n"

    "           subs            r7, r7, #1\n"
    "           bne             loop_hsv\n"



    :
    : [src]"r"(src), [dst]"r"(dst), [size]"r"(rgba->size)
    : "memory", "cc", "r0",  "r1", "r4", "r5", "r6", "r7"
  );

}
5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0