【问题标题】:Rotate a bitmap represented by an array of bytes旋转由字节数组表示的位图
【发布时间】:2016-06-03 07:41:25
【问题描述】:

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

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

我想逆时针“旋转”这个得到

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 语言中做到这一点?

【问题讨论】:

  • rows3[(i+j+8)%8] 其中 i 是您要访问的元素,j 是移位量
  • 顺时针旋转有什么变化?你是说没有变化吗?
  • rows[]rows3[] 看起来很可疑,除了一个使用 0b 前缀和另一个使用 0x 前缀。这是你的意图吗?
  • 不清楚你想要什么,但我想你想要memmove

标签: c bit-manipulation bit avr


【解决方案1】:

一些按位运算可以解决问题。

#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] &amp; (1 &lt;&lt; (7-i) ) ) &gt;&gt; (7-i) 获得第 j 个原始字节的每个 (7-i) 位,然后将其移动到第 j 个位置。您可以通过对字节本身执行“或”(|) 来收集所有位,因此使用 0 初始化数组非常重要。 逆时针的情况类似,改变索引。 我用另一个字母来测试它,让你确定旋转是否正常工作。如果您需要进一步的解释,请尽管询问。

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

【讨论】:

  • 在您之前的代码导致 LED 矩阵上出现乱涂乱画的情况下,我在凌晨时分抓狂。感谢您解决这个问题。当我在我的 Linux 机器上编译代码时,我发现 printf 转换器是独立的,因此我可以更好地观察它的执行情况。
  • 对不起。问题是前面的代码在“L”测试用例中运行正常,但后来我意识到,为了完成测试,需要一个更复杂的形状。使用“E”形帮助我调试了第一个版本的代码。
【解决方案2】:
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];
}

【讨论】:

    【解决方案3】:

    这是一个简单/标准的 [方形] 矩阵旋转。我用方格纸手工计算出来,然后物理旋转它。

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

    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
    

    【讨论】:

      【解决方案4】:

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

      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 &gt; 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
      

      【讨论】:

      • 也许匿名投票者缺乏诚信来解释他的投票并指出任何技术上的不准确之处?
      • 也许吧。但我可以告诉你,你的答案与问题无关。这个错误是可以理解的,因为原来的标题不是那么清楚,而且你误解了 OP 的“轮换”是什么意思。您必须实际阅读问题和 cmets 才能了解 OP 想要做什么。我编辑了标题以使其更清晰。
      • 你知道,这就是它的症结所在。最初,并且仍然在他的示例中,他展示了完全按照答案所做的事情。因此,如果对数组旋转一词有歧义或混淆,那是可以理解的。因为上面的代码反映了他想要的,但我没有打印 45 行示例输出以匹配他的垂直格式,而是在 5 行压缩的十六进制水平行中显示相同的内容,而不是在二进制中显示相同的数字(因为他没有旋转位开始)。总有一种可能他也没有把握住。这些数字都存储相同。
      • 你需要仔细看看这个问题。顺时针旋转时,0x01s 变为 0x80s。你的答案不是那样。
      • Shifting 之所以奏效,是因为他选择了作为示例的特定形状,这也令人困惑。只用一条线而不是“L”会更清楚,但旋转应该适用于任何图片。换班不适用于任意安排。我认为 bit manip 仍然是最简单的方法。
      猜你喜欢
      • 2018-06-29
      • 2014-10-23
      • 1970-01-01
      • 1970-01-01
      • 2010-09-26
      • 1970-01-01
      • 1970-01-01
      • 2020-08-13
      相关资源
      最近更新 更多