公式:位图字节对齐

最近工作涉及了位图4字节对齐问题,前人代码中用了下面的公式(width * bitCounts + 31) / 32 * 4,来计算图像每行的字节数,但是一直不理解公式的原理,经过网上搜索,看过别人文章,以及自己琢磨,发现原理如下:

  1. 首先来自于这样一个公式:(width * bitCounts / 8 + 3) / 4 * 4,该公式含义比上面的公式要容易理解一些,比如biWidth * biBitCount代表了对齐前每行的总位数,位图有1位、2位、4位、8位、16位、24位、32位等,大于8位的都是8的倍数,所以biWidth * biBitCount / 8是对齐前的总字节数,要4字节对齐,除以4,看余数多少,不够多少补多少。

而因为除以4的余数只能是0、1、2、3这四种情况,0就是刚好整除不需要再补,1、2、3分别需要补3、2、1个字节才能凑足4字节。那么

我们在除之前先补上3个字节会是什么情况呢,对于四种余数情形,分别是余3(3+0)、0(3+1)、1(3+2)、2(3+3),对于整数操作(width * bitCounts / 8 + 3) / 4得到的结果不会有余数,刚好达到了我们需要补足4字节的目的;

另外再考虑能不能先补上别的数字,例如1、2,根据前面余数情况分析,补1会漏掉余数为1、2的情况,补2会漏掉余数为1的情况;

再考虑补上4或者更大数字的情况,余数为0的情形补4就多了4字节,数字再往上就更加多余,所以补上最大余数刚刚合适。

  1. 上面的分析对于位宽大于等于8的位图已经正确,但小于8位的情况,width * bitCounts不一定是8的整数倍,所以我们先不要除以8,而是按照4字节等于32个bit位来计算,我们看看需要补多少位使得刚好32位对齐,那么就有公式:

(width * bitCounts + n)/ 32 * 4

跟1中的分析方法相同,n应为32的最大余数31,所以得到最终公式:(width * bitCounts + 31)/ 32 * 4。

易语言以下公式:

位与 (宽度 × 位深 ÷ 83, 位取反 (3))
左移 (右移 (宽度 × 位深 + 31, 5), 2)
.版本 2

.子程序 对齐, 整数型
.参数 待对齐数, 整数型
.参数 对齐长度, 整数型, , 1 2 4 8 16 32 64 ...

对齐长度 = 逆向位扫描 (对齐长度)
.如果真 (对齐长度 > 0)
    对齐长度 = 左移 (1, 对齐长度)1
    返回 (位与 (待对齐数 + 对齐长度, 位取反 (对齐长度)))
.如果真结束
返回 (待对齐数)

.子程序 逆向位扫描, 整数型
.参数 待扫描数, 整数型

置入代码 ({ 15, 189, 69, 8, 201, 194, 4, 0 })
' 十六进制:0F BD 45 08 C9 C2 04 00
' bsr eax, dword ptr [ebp+08h]
' leave
' retn 4
返回 (0)

#pragma once #define NULL ((void*)0) typedef unsigned char Uint8; typedef unsigned long Uint_L; extern Uint8 Init_Malloc(void* start_addr, Uint_L Length); extern void* My_malloc(Uint_L size); extern void My_Free(void* start_addr); //内存对齐的字节大小 #define OFFSET_BYTE 8 //无效内存标志位 #define INVALID_ADDR ~((Uint_L)0x00) //内存对齐 #define ALIGN_SIZE(size) (((size) + ((OFFSET_BYTE) - 1)) & ~((OFFSET_BYTE) - 1)) //内存块的大小 typedef struct Bitmap_Memory { Uint_L len;//分配出去的内存块长度 }Bitmap_Memory; //主控制块 typedef struct Manage_Head { Uint_L Bit_Len;//记录块的数量 void* start_memory;//记录开始地址 Uint_L index;//查找索引 Uint8 Bit_map[0];//Bit_Map图 }Manage_Head; //主控制块指针 volatile static Manage_Head* Head = NULL; /* *函数功能:从位图里寻找指定长度的元素; *形参参数: * { * Sum_Bit:位图的长度; * } * 返回值:位图的位置索引; */ Uint_L Seek_Map_Bit(Uint_L Sum_Bit) { Uint8 byte_bit = 0; Uint_L Seek_Bit = Sum_Bit; Uint_L Aim_addr_bit = 0; Uint_L start_station = Head->index, end_station = Head->Bit_Len; while(Seek_Bit) { for (Aim_addr_bit = start_station, Seek_Bit = Sum_Bit;start_station < end_station && Seek_Bit; ) { byte_bit = Head->Bit_map[start_station >> 3]; if (byte_bit & (0x01 << (start_station & 0x07))) { Seek_Bit = Sum_Bit; if (byte_bit == 0xFF) {//如果某个字节图不满足就直接跳过 Aim_addr_bit = start_station = (start_station & (~0x07)) + 8; continue; } else Aim_addr_bit = start_station + 1; } else Seek_Bit--; start_station++; } if (Head->index == 0 && Seek_Bit) break; //搜索优化,每次遍历时避免重复从头开始查找 if (Seek_Bit) { end_station = (Sum_Bit + Head->index) > Head->Bit_Len ? Head->Bit_Len : (Sum_Bit + Head->index); start_station = 0, Head->index = 0; } else Head->index += Sum_Bit; } if (!Seek_Bit) return Aim_addr_bit; return INVALID_ADDR; } /* *函数功能:标记位图的内存标志位; *形参参数: * { * start_index:位图索引的开始地址; * Sum_Bit:位图标记的长度; * flat:标志位的标识信息; * } * 返回值:无; */ void Map_Bit_Mask(Uint_L start_index,Uint_L Sum_Bit,Uint8 flag) { Uint_L start = start_index, end = start_index + Sum_Bit; if(flag) { for (;start < end;start++) { Head->Bit_map[start >> 3] |= 0x01 << (start & 0x07); } } else { for (;start < end;start++) { Head->Bit_map[start >> 3] &= ~(0x01 << (start & 0x07)); } } } /* *函数功能:用于初始化内存控制块; *形参参数: * { * start_addr:内存块的首地址; * Length:内存块的长度; * } * 返回值:如果初始化成功返回1,失败返回0; */ Uint8 Init_Malloc(void* start_addr, Uint_L Length) { Uint_L Block_len = 0, Map_len = 0; if (start_addr == NULL) return 0; Head = start_addr; //计算可使用地址的长度 Block_len = (8 * (Length - sizeof(Manage_Head)) * OFFSET_BYTE) / (8 * OFFSET_BYTE + 1); //计算位图所占用的字节空间 Map_len = Block_len / (8 * OFFSET_BYTE); //计算位图指向的对应空间 Head->Bit_Len = (((Map_len + sizeof(Manage_Head)) % OFFSET_BYTE) ? Block_len - 1 : Block_len) / OFFSET_BYTE; //计算开始的指向的开始地址 Head->start_memory = (Uint8*)start_addr + ALIGN_SIZE(Map_len + sizeof(Manage_Head)); Head->index = 0; Map_Bit_Mask(0, Head->Bit_Len,0); return 1; } /* *函数功能:用于从内存块中分配内存; *形参参数: * { * size:要分配的字节长度; * } * 返回值:分配成功时返回内存的首地址,分配失败时返回NULL; */ void* My_malloc(Uint_L size) { Uint_L Aim_index = 0; Bitmap_Memory* Aim_addr = NULL; Uint_L Sum_Len = ALIGN_SIZE(ALIGN_SIZE(sizeof(Bitmap_Memory)) + size);//计算要申请内存块的总大小 if (Head == NULL) return NULL;//判断控制块的内存信息是否为空 Aim_index = Seek_Map_Bit(Sum_Len / OFFSET_BYTE);//寻找内存块 if (Aim_index == INVALID_ADDR) return NULL;//判断是否找到了内存块的起始索引 Map_Bit_Mask(Aim_index, Sum_Len / OFFSET_BYTE,1);//标记内存块 Aim_addr = (Bitmap_Memory*)(((Uint8*)Head->start_memory) + Aim_index * OFFSET_BYTE);//内存块地址信息初始化 Aim_addr->len = Sum_Len; return (((Uint8*)Aim_addr)+ ALIGN_SIZE(sizeof(Bitmap_Memory)));//返回内存块的首地址 } /* * 函数功能:释放在My_Malloc函数分配的内存块; * 形参参数: * { * start_addr:用户内存的首地址; * } * 返回值:无; */ void My_Free(void* start_addr) { Uint_L Aim_Len = 0, Aim_index = 0; Bitmap_Memory* Aim_addr = NULL; if (start_addr == NULL) return; //偏移到内存控制块 Aim_addr = ((Uint8*)start_addr) - ALIGN_SIZE(sizeof(Bitmap_Memory)); Aim_Len = Aim_addr->len;//获取内存块的大小 Aim_index = (((Uint8*)Aim_addr) - Head->start_memory)/ OFFSET_BYTE;//获取内存块的参数信息 Map_Bit_Mask(Aim_index, Aim_Len / OFFSET_BYTE, 0);//标记内存块 } 公式来源: x=管理的内存块数量(不是字节长度) k=对齐字节量 x/(8*k)=位图本身占用的字节数 N=控制结构体长度 L=总内存长度 x+x/(8*k)+N=L
最新发布
10-11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值