旋转由字节数组表示的位图

Rotate a bitmap represented by an array of bytes

在 AVR 中,我使用一个八字节数组来存储在 8x8 LED 矩阵上显示的图片。图片需要不时旋转。所以,给定的图片定义为:

uint8_t rows[8] = {
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b11111111
};

我想 "rotate" 这个逆时针方向得到 为:

uint8_t rows2[8] = {
    0b11111111,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001
};

或者顺时针旋转,:

uint8_t rows3[8] = {
    0b10000000,
    0b10000000,
    0b10000000,
    0b10000000,
    0b10000000,
    0b10000000,
    0b10000000,
    0b11111111
};

我如何在纯 C 中执行此操作?

一些按位运算可以达到目的。

#include <inttypes.h>

int main(){

  uint8_t rows[8] = {
      0b11111111,
      0b00000001,
      0b00000001,
      0b00111111,
      0b00000001,
      0b00000001,
      0b00000001,
      0b11111111
  };


  uint8_t rows2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
  uint8_t rows3[8] = {0, 0, 0, 0, 0, 0, 0, 0};

  int i, j;
  // rotate clockwise
  for(i=0; i<8; ++i){
    for(j=0; j<8; ++j){
      rows3[i] = (  ( (rows[j] & (1 << (7-i) ) ) >> (7-i) ) << j ) | rows3[i];
    }
  }

  // rotate anti-clockwise
  for(i=0; i<8; ++i){
    for(j=0; j<8; ++j){
      rows2[i] = (  ( (rows[j] & (1 << i ) ) >> i ) << (7-j) ) | rows2[i];
    }
  }
}

在顺时针的情况下,你用(rows[j] & (1 << (7-i) ) ) >> (7-i)得到第j个原始字节的每个第(7-i)位,然后将它移到第j个位置。您通过对字节本身执行 "or" (|) 来收集所有位,因此用 0 初始化数组非常重要。 逆时针情况类似,改变索引。 我用另一封信来测试它,让你确定旋转是否正常工作。如果您需要进一步的解释,请直接询问。

如果你想查看结果,我正在使用这个 SO 问题中的函数:Is there a printf converter to print in binary format?

uint8_t rows[8] = {
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b11111111
};

uint8_t temp[8];

void anti()
{
    int i, j;

    for(i = 0; i < 8; ++i) temp[i] = 0;

    for(i = 0; i < 8; ++i)
        for(j = 0; j < 8; ++j)
             if(rows[j] & 1<<i) temp[i] |= 1<<(7-j); 

    for(i = 0; i < 8; ++i) row[i] = temp[i];
}

您可以使用数组索引来完成,但使用 string.h 提供的工具(例如 memcpymemmove)来完成旋转要容易得多。例如,如果您的数组是 r,其中 sz 个元素要旋转 n 个位置,则 顺时针 旋转很简单:

uint8_t i = 0, tmp[n];
...
memcpy  (tmp, &r[sz-n], n);
memmove (&r[n], r, sz-n);
memcpy  (r, tmp, n);
for (; i < sz; i++)
    if (i != n-1 && r[i] != 0x80)
        r[i] ^= 0x81;

你的逆时针旋转是:

memcpy  (tmp, r, n);
memmove (r, &r[n], sz-n);
memcpy  (&r[sz-n], tmp, n);
for (; i < sz; i++)
    if (i != sz-n+1 && r[i] != 0xff)
        r[i] ^= 0x81;

如果 (n == 0 || n == sz) 则无事可做,如果 (n > sz) 您必须选择要做什么(例如抛出错误,或旋转 n % sz 位置)。看来您只关心任一方向的 1 旋转。将所有部分放在一起,一个简短的例子是:


更新轮换

#include <stdio.h>
#include <string.h>
#include <inttypes.h>

void rotcw (uint8_t *r, uint8_t sz, uint8_t n);
void rotccw (uint8_t *r, uint8_t sz, uint8_t n);
void prnrows (uint8_t *r, uint8_t sz);

int main (void) {

    uint8_t rows[] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xff};
    uint8_t sz = sizeof rows / sizeof *rows;

    printf ("\n original array  :");
    prnrows (rows, sz);

    rotcw (rows, sz, 1);
    printf ("\n rotate cw by 1  :");
    prnrows (rows, sz);

    rotccw (rows, sz, 1);
    printf ("\n rotate ccw by 1 :");
    prnrows (rows, sz);

    return 0;
}

void rotcw (uint8_t *r, uint8_t sz, uint8_t n)
{
    if (n == sz ) return;  /* nothing to do */
    if (n > sz)   n %= sz; /* rotate 'n % sz' positions */

    uint8_t i = 0, tmp[n];

    memcpy  (tmp, &r[sz-n], n);
    memmove (&r[n], r, sz-n);
    memcpy  (r, tmp, n);
    for (; i < sz; i++)
        if (i != n-1 && r[i] != 0x80)
            r[i] ^= 0x81;
}

void rotccw (uint8_t *r, uint8_t sz, uint8_t n)
{
    if (n == sz ) return;  /* nothing to do */
    if (n > sz)   n %= sz; /* rotate 'n % sz' positions */

    uint8_t i = 0, tmp[n];

    memcpy  (tmp, r, n);
    memmove (r, &r[n], sz-n);
    memcpy  (&r[sz-n], tmp, n);
    for (; i < sz; i++)
        if (i != sz-n+1 && r[i] != 0xff)
            r[i] ^= 0x81;
}

void prnrows (uint8_t *r, uint8_t sz)
{
    uint8_t i;

    for (i = 0; i < sz; i++)
        printf (" 0x%02" PRIx8, r[i]);
    putchar ('\n');
}

输出

$ ./bin/bytes_rotate

 original array  : 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0xff

 rotate cw by 1  : 0xff 0x80 0x80 0x80 0x80 0x80 0x80 0x80

 rotate ccw by 1 : 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0xff

这是一个simple/standard[方形]矩阵旋转。我用方格纸手工解决了这个问题,并对其进行了物理旋转。

对于逆时针(向左旋转),方程为:

out[7-x][y] = inp[y][x];

顺时针(向右旋转),等式为:

out[x][7-y] = inp[y][x];

...除了我们要提取X维度的位,所以我们需要一些模拟位矩阵访问的函数。

这是一个具有必要功能的测试程序:

#include <stdio.h>

typedef unsigned char byte;

typedef void (*rot_p)(byte *dst,const byte *src);

#define MSK(_shf)       (1 << (7 - (_shf)))

byte result[8];

// original matrix
byte rows[8] = {
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b11111111
};

// counterclockwise (rotate left)
byte rows2[8] = {
    0b11111111,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001,
    0b00000001
};

// clockwise (rotate right)
byte rows3[8] = {
    0b10000000,
    0b10000000,
    0b10000000,
    0b10000000,
    0b10000000,
    0b10000000,
    0b10000000,
    0b11111111
};

// bitget -- get bit from matrix
byte
bitget(const byte *rows,int y,int x)
{
    byte val;

    rows += y;
    val = *rows;
    val &= MSK(x);

    return val;
}

// bitget -- set bit in matrix
void
bitset(byte *rows,int y,int x,byte val)
{
    byte msk;

    rows += y;

    msk = MSK(x);

    if (val)
        *rows |= msk;
    else
        *rows &= ~msk;
}

// rotright -- rotate matrix right (clockwise)
void
rotright(byte *dst,const byte *src)
{
    int x;
    int y;
    byte val;

    for (y = 0;  y < 8;  ++y) {
        for (x = 0;  x < 8;  ++x) {
            val = bitget(src,y,x);
            bitset(dst,x,7 - y,val);
        }
    }
}

// rotleft -- rotate matrix left (counterclockwise)
void
rotleft(byte *dst,const byte *src)
{
    int x;
    int y;
    byte val;

    for (y = 0;  y < 8;  ++y) {
        for (x = 0;  x < 8;  ++x) {
            val = bitget(src,y,x);
            bitset(dst,7 - x,y,val);
        }
    }
}

// mtxshow -- print matrix
void
mtxshow(const byte *mtx,const char *sym,const char *tag)
{
    int y;
    int x;
    byte val;

    printf("%s/%s:\n",sym,tag);
    for (y = 0;  y < 8;  ++y) {
        printf("  ");
        for (x = 0;  x < 8;  ++x) {
            val = bitget(mtx,y,x);
            val = val ? '1' : '0';
            fputc(val,stdout);
        }
        fputc('\n',stdout);
    }
}

// test -- perform test
void
test(const byte *exp,rot_p fnc,const char *tag)
{

    printf("\n");

    mtxshow(exp,tag,"expected");
    fnc(result,rows);
    mtxshow(result,tag,"actual");
}

int
main(void)
{

    mtxshow(rows,"rows","orig");

    test(rows2,rotleft,"rotleft");
    test(rows3,rotright,"rotright");

    return 0;
}

程序输出如下:

rows/orig:
  00000001
  00000001
  00000001
  00000001
  00000001
  00000001
  00000001
  11111111

rotleft/expected:
  11111111
  00000001
  00000001
  00000001
  00000001
  00000001
  00000001
  00000001
rotleft/actual:
  11111111
  00000001
  00000001
  00000001
  00000001
  00000001
  00000001
  00000001

rotright/expected:
  10000000
  10000000
  10000000
  10000000
  10000000
  10000000
  10000000
  11111111
rotright/actual:
  10000000
  10000000
  10000000
  10000000
  10000000
  10000000
  10000000
  11111111