程序员面试金典: 9.5位操作 5.8通过位操作进行两点连线

#include <iostream>
#include <stdio.h>
#include <vector>
#include <string>
#include <bitset>

using namespace std;

/*
问题:有个单色屏幕存储在一个一维字节数组中,使得8个连续像素可以存放在一个字节里。屏幕宽度为w,且w可以被8整除(即一个字节不会分布在
      两行上),屏幕宽度可由数组长度及屏幕宽度推算得出。请实现一个函数drawHorizontalLine(byte[] screen, int width, int x1 ,int x2,
	  int y),绘制从点(x1,y)到(x2,y)的水平线。
分析:绘制水平线本质上是找到水平线上的起点和重点,然后连接,起点和重点都已经确认,这个应该没什么难度,由于是一维数组,
	  首先明确在屏幕中:x代表水平方向,也就是实际意义上的竖轴(列),y代表垂直方向,代表了矩阵中实际意义的横轴(行)
	  假设给定的像素位置为(x,y),那么它实际在一维数组的位置p=x+w*y
	  所以起点(x1,y)在是screen[x1+w*y]中的字节,(x2,y)对应screen[x2+w*y]字节,
	  找到两个字节后,调用drawLine函数即可绘制
书上解法:我没理解绘线的本质实际上将线上的所有像素的颜色都设置为同一个颜色。那么暴力破解解法就是遍历从(x1,y)到(x2,y)上所有
          经过的像素,设置颜色,时间复杂度为O(x2-x1)
		  注意:给定的x是像素的横坐标而不是字节,一个像素占据一个bit
关键:
1 绘线的本质实际上将线上的所有像素的颜色都设置为同一个颜色
2
确定比特x1对应的字节x1Byte = x1 / 8,以及起始比特位x1Bit = x1 % 8
确定比特x2对应的字节x2Byte = x2 / 8, 以及结束比特位x2Bit = x2 % 8
如果x1Bit不为0,说明x1Byte不是完整的字节,令起始字节startByte = x1Byte + 1;否则,令startByte = x1Byte
如果x2Bit不为7,说明x2Byte不是完整的字节,令结束字节endByte = x2Byte - 1;否则,令endByte = x2Byte
将startByte~endByte中字节全部设置为0xff(即白色)
设置左边不完整字节(左边起始字节)掩码,用于后续与运算,必须将不完整字节前面x1Bit位排除,即前面x1Bit位为0,后面8-x1Bit位为1,
     所以左边掩码leftMask = 0xff >> x1Bit
设置右边不完整字节(右边最后字节)掩码,必须将x2Bit位包含,即前面x2Biit(包含x2Bit位本身)位为1,后面8-x2Bit位为0,
     所以右边掩码rightMask = ~ ( 0xff >> (x2Bit + 1) )
还需要判断x1与x2对应的字节是否在同一个字节中,因为如果在同一个字节中,需要对左边掩码和右边掩码进行与操作才能得到真实的掩码
     mask = leftMask & rightMask
	 对应字节在screen中位置pos = w * y/8 + x1Byte 
	 对应的字节为 screen[ pos ] |= mask
	 易错,容易漏掉
如果不在同一个字节,则对左边不完整字节,掩码处理
     posLeft = w * y/8 + x1Byte
	 screen[ posLeft ] |= leftMask
右边不完整字节取掩码
     posRight = w * y/8 + x2Byte
	 screen[ posRight ] |= leftMask     
结束

3 screen.at(pos) |= leftMask;//注意是或运算,通过1来设置颜色,其余保持字节原先的颜色
 如果用与,那么掩码中0的部分会使得像素颜色发生变化,而我们真正需要的是掩码中的1,
 掩码中的0应该不起作用,所以用或运算
*/

//这里的宽度width,对应的是比特
void drawHorizontalLine(vector< bitset<8> >& screen, int width, int x1 ,int x2, int y)
{
	if(screen.empty())
	{
		return;
	}
	//判断x1和x2大小,使得x1为较小值
	if(x1 > x2)
	{
		int temp = x1;
		x1 = x2;
		x2 = x1;
	}
	//计算起始和结束字节位置
	int x1Byte = x1 / 8;
	int x1Bit = x1 % 8;
	int startByte = x1Byte;
	if(x1Bit != 0)
	{
		startByte++;
	}
	int x2Byte = x2 / 8;
	int x2Bit = x2 % 8;
	int endByte = x2Byte;
	if(x2Bit != 0)
	{
		endByte--;
	}
	int leftMask = 0xff >> x1Bit;
	int rightMask = ~ ( 0xff >> (x2Bit + 1) );

	//对起始到结束部分完整字节设置为白色
	for(int i = startByte ; i <= endByte ; i++)
	{
		//宽度是比特,需要转化为字节
		int pos = width/8 * y + i;
		screen.at(pos) = 0xff;
	}

	//判断两个x1和x2是否位于同一个字节中,易漏,注意不要
	if( x1Byte == x2Byte )
	{
		int mask = leftMask & rightMask;
		int pos = width/8 * y + x1Byte;
		screen.at(pos) |= mask;
	}
	else
	{
		//判断左边起始字节是否是不完整字节,如果是不完整字节,就对左边起始字节进行掩码运算
		if(x1Bit != 0)
		{
			int pos = width/8 * y + x1Byte;
			screen.at(pos) |= leftMask;//注意是或运算,通过1来设置颜色,其余保持字节原先的颜色
		}

		//对右边结束字节进行掩码运算
		if(x2Bit != 7)
		{
			int pos = width/8 * y + x2Byte;
			screen.at(pos) |= rightMask;
		}
	}
}

int main(int argc, char* argv[])
{
	getchar();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值