旋转由字节数组表示的位图
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
提供的工具(例如 memcpy
和 memmove
)来完成旋转要容易得多。例如,如果您的数组是 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
在 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
提供的工具(例如 memcpy
和 memmove
)来完成旋转要容易得多。例如,如果您的数组是 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