7

32 ビット プロセッサで実行されている R、G、B ビット データを含む 3 つのバッファがあります。

次の方法で 3 バイトを結合する必要があります。

R[0] = 0b r1r2r3r4r5r6r7r8
G[0] = 0b g1g2g3g4g5g6g7g8
B[0] = 0b b1b2b3b4b5b6b7b8

int32_t Out = 0b r1g1b1r2g2b2r3g3 b3r4g4b4r5g5b5r6 g6b6r7g7b7r8g8b8 xxxxxxxx

ここで、xxxxxxxx は、バッファー内の次の各バイトに続きます。

それらを組み合わせる最適な方法を探しています。私のアプローチは間違いなく効率的ではありません。

これが私のアプローチです

static void rgbcombineline(uint8_t line)
{
    uint32_t i, bit;
    uint8_t bitMask, rByte, gByte, bByte;
    uint32_t ByteExp, rgbByte;
    uint8_t *strPtr = (uint8_t*)&ByteExp;

    for (i = 0; i < (LCDpixelsCol / 8); i++)
    {
        rByte = rDispbuff[line][i];
        gByte = gDispbuff[line][i];
        bByte = bDispbuff[line][i];

        bitMask = 0b00000001;
        ByteExp = 0;
        for(bit = 0; bit < 8; bit++)
        {
            rgbByte = 0;
            rgbByte |= ((rByte & bitMask) >> bit) << 2;
            rgbByte |= ((gByte & bitMask) >> bit) << 1;
            rgbByte |= ((bByte & bitMask) >> bit);
            ByteExp |= (rgbByte << 3*bit);
            bitMask <<= 1;
        }
        TempLinebuff[((i*3)+0) +2] = *(strPtr + 2);
        TempLinebuff[((i*3)+1) +2] = *(strPtr + 1);
        TempLinebuff[((i*3)+2) +2] = *(strPtr + 0);
    }
}
4

4 に答える 4

7

1024 バイトを節約できる場合は、1 つの 256 要素のルックアップ テーブルで目的の結果を得ることができます。

uint32_t lookup[256] = {
    0, 1, 8, 9, 64, 65, ...
    /* map abcdefgh to a00b00c00d00e00f00g00h */
};

uint32_t result = (lookup[rByte] << 2) | (lookup[gByte] << 1) | lookup[bByte];

これは、3 回のルックアップ、2 回のシフト、および 2 回orの操作のみを使用するため、許容できるスピードアップが得られるはずです。

より多くのスペースがある場合は、3 つのルックアップ テーブルを使用してシフトを排除することもできます (ただし、これによりキャッシュ パフォーマンスが低下する可能性があるため、常にプロファイルして確認してください!)

于 2016-03-28T06:16:18.907 に答える
3

6 ビットのビットストリップ値を含むサイズ 64 のテーブルを使用して、r、g、b からそれぞれ 2 ビットをフェッチし、テーブルを使用してルックアップを高速化できます。サイズ 512 または 4096 のルックアップを使用すると、より効率的です。

/* Converts bits abcdefghijkl to adgjbehkcfil */
static const uint32_t bitStripLookUp[4096] = {
  /* Hard coded values, can be generate with some script */
  ...
};

...

rByte = rDispbuff[line][i];  // rByte, gByte, bByte should be unit32
gByte = gDispbuff[line][i];
bByte = bDispbuff[line][i];

uMSB = ((rByte << 4) & 0x0F00) | (gByte & 0x00F0) | ((bByte >> 4) & 0x000F);  // r7r6r5r4g7g6g5g4b7b6b5b4
uLSB = ((rByte << 8) & 0x0F00) | ((gByte << 4) & 0x00F0) | (bByte & 0x000F);  // r3r2r1r0g3g2g1g0b3b2b1b0
stuffed_value = (bitStripLookUp[uMSB] << 12) | bitStripLookUp[uLSB];
于 2016-03-28T05:55:11.947 に答える
1

ビット演算子によるインターリーブ

inline unsigned interleave(unsigned n)
{
    n = ((n << 18) | (n << 9) | n) & 0007007007; // 000000111 000000111 000000111
    n =  ((n << 6) | (n << 3) | n) & 0444444444; // 100100100 100100100 100100100
    return n;
}

unsigned r = interleave(rByte);
unsigned g = interleave(gByte);
unsigned b = interleave(bByte);

unsigned rgb = r | (g >> 1) | (b >> 2);

TempLinebuff[((i*3)+0) +2] = rgb >> 16;
TempLinebuff[((i*3)+1) +2] = rgb >>  8;
TempLinebuff[((i*3)+2) +2] = rgb;

ルックアップ テーブル ソリューション

#define EXPANDBIT(x, n) (((x) & (1 << (n))) << (3*(n))))
#define EXPAND8BIT(a) (EXPANDBIT(a, 0) | EXPANDBIT(a, 1) | EXPANDBIT(a, 2) | EXPANDBIT(a, 3) | \
                       EXPANDBIT(a, 4) | EXPANDBIT(a, 5) | EXPANDBIT(a, 6) | EXPANDBIT(a, 7))
#define EXPAND16(A) EXPAND8BIT(16*(A)+ 0), EXPAND8BIT(16*(A)+ 1), EXPAND8BIT(16*(A)+ 2), EXPAND8BIT(16*(A)+ 3), \
                    EXPAND8BIT(16*(A)+ 4), EXPAND8BIT(16*(A)+ 5), EXPAND8BIT(16*(A)+ 6), EXPAND8BIT(16*(A)+ 7), \
                    EXPAND8BIT(16*(A)+ 8), EXPAND8BIT(16*(A)+ 9), EXPAND8BIT(16*(A)+10), EXPAND8BIT(16*(A)+11), \
                    EXPAND8BIT(16*(A)+12), EXPAND8BIT(16*(A)+13), EXPAND8BIT(16*(A)+14), EXPAND8BIT(16*(A)+15)

const uint32_t LUT[256] = {
    EXPAND16( 0), EXPAND16( 1), EXPAND16( 2), EXPAND16( 3),
    EXPAND16( 4), EXPAND16( 5), EXPAND16( 6), EXPAND16( 7),
    EXPAND16( 8), EXPAND16( 9), EXPAND16(10), EXPAND16(11),
    EXPAND16(12), EXPAND16(13), EXPAND16(14), EXPAND16(15)
};
    
output = LUT[rByte] | LUT[gByte] << 1 | LUT[bByte] << 2;

必要に応じて、ルックアップ テーブルのサイズを大きくすることができます。


BMI2を使用するx86 では、組み込み関数を介してアクセスできるPDEP命令によるハードウェア サポートがあります_pdep_u32。ソリューションははるかに簡単になりました

output = _pdep_u32(rByte, 044444444U << 8)
       | _pdep_u32(gByte, 022222222U << 8)
       | _pdep_u32(bByte, 011111111U << 8);

別の方法は

このパッキング手法で乗算とマスクを使用したインターリーブ

これは、ハードウェア ビット デポジット命令を使用せず、高速乗算器を使用するアーキテクチャ用です。

uint32_t expand8bits(uint8_t b)
{
    uint64_t MAGIC = 0x8040201008040201;
    uint64_t MASK  = 0x8080808080808080;
    uint64_t expanded8bits = htobe64((MAGIC*b) & MASK);
    uint64_t result = expanded8bits*0x2108421 & 0x9249000000009000;
    // no need to shift if you want to get the bits in the high part
    return ((result | (result << 30)) & (044444444ULL << 8)) >> 32;
}

uint32_t stripeBits(uint8_t rByte, uint8_t gByte, uint8_t bByte)
{
    return expand8bits(rByte) | (expand8bits(gByte) >> 1) | (expand8bits(bByte) >> 2);
}

仕組みはこんな感じ

  • 最初のステップでは、入力ビットabcdefgha0000000 b0000000 c0000000 d0000000 e0000000 f0000000 g0000000 h0000000に展開し、格納します。expand8bits
  • 次に、次のステップで乗算とマスキングを行うことにより、離れたビットを近づけます。その後、a00b00c00d00e00f000000000000000000000000000000g00h000000000000resultが含まれ、単一の値にマージする準備が整います

ビットを近づけるマジックナンバーはこんな感じで計算

  a0000000b0000000c0000000d0000000e0000000f0000000g0000000h0000000
×                                       10000100001000010000100001 (0x2108421)
  ────────────────────────────────────────────────────────────────
  a0000000b0000000c0000000d0000000e0000000f0000000g0000000h0000000
  000b0000000c0000000d0000000e0000000f0000000g0000000h0000000
+ 000000c0000000d0000000e0000000f0000000g0000000h0000000
  0c0000000d0000000e0000000f0000000g0000000h0000000
  0000d0000000e0000000f0000000g0000000h0000000
  0000000e0000000f0000000g0000000h0000000
  ────────────────────────────────────────────────────────────────
  ac0bd0cebd0ce0dfce0df0egdf0eg0fheg0fh0g0fh0g00h0g00h0000h0000000
& 1001001001001001000000000000000000000000000000001001000000000000 (0x9249000000009000)
  ────────────────────────────────────────────────────────────────
  a00b00c00d00e00f00000000000000000000000000000000g00h000000000000

あるいは、このような32 ビット マジック ナンバーの乗算のみexpand8bitsを使用して実装することもできます。

uint32_t expand8bits(uint8_t b)
{
    const uint8_t  RMASK_1458   = 0b10011001;
    const uint32_t MAGIC_1458   = 0b00000001000001010000010000000000U;
    const uint32_t MAGIC_2367   = 0b00000000010100000101000000000000U;
    const uint32_t MASK_BIT1458 = 0b10000000010010000000010000000000U;
    const uint32_t MASK_BIT2367 = 0b00010010000000010010000000000000U;
    
    return (((b &  RMASK_1458) * MAGIC_1458) & MASK_BIT1458)
         | (((b & ~RMASK_1458) * MAGIC_2367) & MASK_BIT2367);
}

ここでは、8 ビットの数値を 2 つの 4 ビット部分に分割します。1 つはビット 1、4、5、8 で、残りはビット 2、3、6、7 です。マジック ナンバーは次のようになります。

                          a00de00h                                0bc00fg0
× 00000001000001010000010000000000      × 00000000010100000101000000000000
  ────────────────────────────────        ────────────────────────────────
                a00de00h                              0bc00fg0
+         a00de00h                      +           0bc00fg0
        a00de00h                              0bc00fg0
  a00de00h                                  0bc00fg0
  ────────────────────────────────        ────────────────────────────────
  a00de0ahadedehah0de00h0000000000        000bcbcfgfgbcbcfgfg0000000000000
& 10000000010010000000010000000000      & 00010010000000010010000000000000
  ────────────────────────────────        ────────────────────────────────
  a00000000d00e00000000h0000000000        000b00c00000000f00g0000000000000

見る

于 2016-03-28T09:39:26.443 に答える