Rotate a bitmap represented by an array of bytes

依然范特西╮ 提交于 2019-12-13 09:15:37

问题


In an AVR, I'm using an array of eight bytes to store a picture displayed on an 8x8 LED matrix. The picture needs to be rotated from time to time. So, given the picture defined as:

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

I want to "rotate" this anticlockwise to get as:

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

Or this if done clockwise, :

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

How do I do this in straight C?


回答1:


Some bitwise operations can do the trick.

#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];
    }
  }
}

In the clockwise case, you get each (7-i)-th bit of the j-th original byte with (rows[j] & (1 << (7-i) ) ) >> (7-i) and then shift it to the j-th position. You collect all the bits by doing an "or" (|) with the byte itself, so it is very important to initialize the array with 0s. The anti-clockwise case is analogous, changing the indexing. I used another letter to test it, that let you know for sure if the rotation is working properly. If you need further explanation, please just ask.

If you want to look the result, I'm using the function in this SO question: Is there a printf converter to print in binary format?




回答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:


This is a simple/standard [square] matrix rotation. I worked this out by hand using graph paper and physically rotating it.

For counterclockwise (rotate left), the equation is:

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

For clockwise (rotate right), the equation is:

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

... except that we have to extract bits in the X dimension, so we need some functions that simulate the matrix access for bits.

Here's a test program with the necessary functions:

#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;
}

Here's the program output:

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:


You can do it with array indexes, but it is far easier to use the tools string.h provides (e.g. memcpy and memmove) to accomplish the rotations. For example if your array is r with sz number of elements that you want to rotate by n positions, your clockwise rotation is simply:

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;

Your counter-clockwise rotation is:

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;

There is nothing to do if (n == 0 || n == sz) and you have to choose what to do if (n > sz) (e.g. throw error, or rotate n % sz positions). It appears you are only concerned with a rotation of 1 in either direction. Putting all the pieces together, a short example would be:


Updated Rotations

#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');
}

Output

$ ./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


来源:https://stackoverflow.com/questions/35544172/rotate-a-bitmap-represented-by-an-array-of-bytes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!