最近刚写完c++的大作业,觉得挺有意思,所以就拿来和大家分享交流一下
下面是要求
一.基本要求
使用C++语言编写程序,在控制台(字符界面)上实现图片浏览。
基本功能包括:
1. 能够浏览同一个文件夹下所有的图片,而不是只能用输入名字的方式浏览。
2. 实现图片的彩色显示。
3. 界面友好,操作简便。
4. 编写一个文档,包括程序的基本模块说明,核心功能的实现方法,人员分工等。
不要求可以显示所有的图片,只需能够浏览24位色,不超过150*150的小图片。符合要求的图片可以使用photoshop或者画图板自行制作(可以用示例中自带的图片进行测试)。
这是成品
代码如下
#include "stdafx.h"
#include
#include
#include
#include
#include
#include
#include
using namespace std;
/*
求近似颜色
按照老师给的图片,这些都是24位的bmp图片,所以从其位图数据可以得到每个像素的RGB值分量,R为red,G为green,B为blue
通过RGB可以显示的颜色是很多种的,而控制台只有16种背景颜色,下面列出了14种
所以我们要将每个像素的颜色近似成这14种颜色
你可以将R,G,B想象成X,Y,Z,建立一个空间坐标系,所以,哪两个点的距离最近那么颜色也就最接近
*/
WORD RED = BACKGROUND_RED | BACKGROUND_INTENSITY;//red: 255 green: 0 blue: 0
WORD RED_DARK = BACKGROUND_RED ;//128,0,0
WORD BLUE = BACKGROUND_BLUE | BACKGROUND_INTENSITY;//0 0 255
WORD BLUE_DARK = BACKGROUND_BLUE ;//0,0,128
WORD GREEN = BACKGROUND_GREEN | BACKGROUND_INTENSITY;//0 255 0
WORD GREEN_DARK = BACKGROUND_GREEN ;//0,128,0
WORD MAGENTA = BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY;//255 0 255
WORD MAGENTA_DARK = BACKGROUND_BLUE | BACKGROUND_RED ;//128,0,128
WORD CYAN = BACKGROUND_GREEN |BACKGROUND_BLUE |BACKGROUND_INTENSITY;//0 255 255
WORD CYAN_DARK = BACKGROUND_GREEN | BACKGROUND_BLUE ;//0,128,128
WORD YELLOW = BACKGROUND_RED |BACKGROUND_GREEN |BACKGROUND_INTENSITY;//255 255 0
WORD YELLOW_DARK = BACKGROUND_RED | BACKGROUND_GREEN ;//128,128,0
WORD BLACK = 0;//0,0 0
WORD WHITE = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY;//255 255 255
/*
建立一个Color类,里面有四个属性
int R; RGB中的红色分量
int G; RGB中的绿色分量
int B; RGB中的蓝色分量
WORD value; SetConsoleTextAttribute()中第二个参数的类型,也就是字体背景颜色
还有一个以这四个为参数的构造函数
*/
class Color {
public:
WORD Value;
int R,G,B;
Color() {
}
Color(WORD value, int r, int g, int b) {
Value = value;
R = r;
G = g;
B = b;
}
};
/*
对tm时间进行处理,得到类似如下的时间格式
2017年12月8日 星期五 22:43:19 .
getTime函数
year 从1900开始计时,所以加上1900
month 0是一月,所以加一
day 几月几号的号,不用处理
weekDay 一个星期的第几天,0是周日,以此类推.为此专门建立一个getwDay函数,传入数字,通过switch判断,返回汉字日 一 二等
hour minute second 小时分钟秒,不用处理
最后建立字符数组date,返回我们需要的时间格式
*/
class TimeUtils {
public:
static char* getTime(tm time) {
long year = time.tm_year + 1900;
long month = time.tm_mon + 1;
long day = time.tm_mday;
char weekDay[10];
strcpy(weekDay, getwDay((int)time.tm_wday));
long hour = time.tm_hour;
long minute = time.tm_min;
long second = time.tm_sec;
char date[100];
sprintf(date, "%ld年%ld月%ld日 星期%s %ld:%ld:%ld ", year, month, day, weekDay, hour, minute, second);
return date;
}
static char* getwDay(int d) {
switch (d)
{
case 0:
return "日";
break;
case 1:
return "一";
break;
case 2:
return "二";
break;
case 3:
return "三";
break;
case 4:
return "四";
break;
case 5:
return "五";
break;
case 6:
return "六";
break;
default:
break;
}
}
};
/*
建立一个Color类数组,将上面十四种颜色变成Color类都储存进去
*/
Color colors[14] = { Color(RED,255,0,0),Color(RED_DARK,128,0,0),Color(BLUE,0,0,255),Color(BLUE_DARK,0,0,128),Color(GREEN,0,255,0),
Color(GREEN_DARK,0,128,0),Color(MAGENTA,255,0,255),Color(MAGENTA_DARK,128,0,128),Color(CYAN,0,255,255),Color(CYAN_DARK,0,128,128),
Color(YELLOW,255,255,0),Color(YELLOW_DARK,128,128,0),Color(BLACK,0,0,0),Color(WHITE,255,255,255) };
/*
这个方法就是得到一个颜色的近似色
参数为一个像素的RGB值的三个分量
原理:
value代表这个像素点与数组里某个颜色的"距离"(RGB类比XYZ),将value一开始设置的尽量大点
tmp是临时储存一个"距离"
index代表colors数组的第几个颜色
pow(x,y)代表x的y次方
进行一个for循环,将传入的颜色与每个颜色对比
最后返回距离最近的颜色
*/
Color getColor(int r,int g,int b) {
int value = 1000000;
int tmp = 0;
int index = 10;
for (int i = 0; i <14; i++)
{
tmp = pow((colors[i].R - r), 2)+ pow((colors[i].G - g), 2) + pow((colors[i].B - b), 2) ;
if (tmp<=value)
{
value = tmp;
index = i;
}
}
return colors[index];
}
/*
该函数可以在控制台画出一个bmp格式文件,参数为当前文件夹图片名
首先要搞清楚24位bmp图的结构
由三部分组成
1.文件头
文件头里面的信息是我们不需要的
2.信息头
这里面有图片的格式,宽度,高度等信息
3.位图数据
图片的像素详细信息;这里面的每个值都是0-255的数字,代表某个R,G或B的值
比如,开头三个连在一起是一个像素点的RGB值,然后接着三个又是下一个像素点,以此类推
*/
void drawImage(const char* fileName) {
unsigned char *pBmpBuf;//读入图像数据的指针
int bmpWidth;//图像的宽
int bmpHeight;//图像的高
int biBitCount;//图像类型,每像素位数
BITMAPINFOHEADER head;//信息头
FILE *fp = fopen(fileName, "rb");//fp为文件指针,fopen()为打开一个文件的函数,第一个参数为文件名,第二个为读取方式,r代表read读取,b代表byte字节
fseek(fp, sizeof(BITMAPFILEHEADER), 0);//fseek()表示在一个文件内寻找;参数依次为 文件指针 跳过的长度 开始位置
fread(&head, sizeof(BITMAPINFOHEADER), 1, fp);//freed表示 从文件fp里,读取1个长度为sizeof(BITMAPINFOHEADER)数据,储存到地址&head里
bmpWidth = head.biWidth;
bmpHeight = head.biHeight;
biBitCount = head.biBitCount;
int lineByte = bmpWidth * biBitCount / 8;//每一行的字节数,因为像素RGB值分量有三个,所以这是应该是宽度*3,由于是24位图,所以/8;
pBmpBuf = new unsigned char[lineByte * bmpHeight];//定义指针指向的数据大小
fread(pBmpBuf, 1, lineByte * bmpHeight, fp);//读取数据,将图片位图数据储存到已经开辟好的临时缓存区
int i = bmpHeight*bmpWidth - bmpWidth;//i代表指针位置,由于位图数据的存储是倒过来的,所以第一行第一个应该是最后一行第一个
int count = 1;//代表指针位于当前行的位置
while (true)
{
//设置字体背景颜色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), getColor(pBmpBuf[3 * i + 2], pBmpBuf[3 * i + 1], pBmpBuf[3 * i]).Value);
//打印空格
printf(" ");
//当前行位置右移
count++;
//指针位置右移
i++;
//如果读到当前最后一行
if (count == bmpWidth)
{
//指针进入上一行的第一个
i = i - 2 * bmpWidth + 1;
//位于当前行第一个
count = 1;
//图片打印换行
printf("\n");
}
//如果读完,循环结束
if (i<0)
{
break;
}
}
//将字体背景色设置回黑色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),BLACK);
fclose(fp);//关闭文件
}
/*
该函数会遍历当前文件夹内所有bmp格式图片,并将图片的相关信息写入一个文本文档中
参数为文本文档名,如果没有该文件则会新建,有则覆盖
*/
void writeMessage(const char* fileName) {
//储存文件信息
struct _finddata_t fileinfo;
//可以将其理解为一个标识符,当你使用_findfirst时会返回一个值,如果为-1则表示找不到图片,
//而后使用_findnext函数时需要传入该变量,继续上次的查找
long hFile;
//要打开或新建的文本文档
FILE *file;
//打开指定文件,第二个参数为打开方式,write
file = fopen(fileName, "w");
//如果未找到bmp文件
if ((hFile = _findfirst("*.bmp", &fileinfo)) == -1)
return;
else {
do
{
//创建时间
char createTime[100];
//修改时间
char writeTime[100];
//赋值
strcpy(createTime, TimeUtils::getTime(*localtime(&fileinfo.time_create)));
strcpy(writeTime, TimeUtils::getTime(*localtime(&fileinfo.time_write)));
//将信息写入文本文件
fprintf(file, "文件名:%s\n创建时间:%s\n修改时间:%s\n", fileinfo.name, createTime, writeTime);
fprintf(file, "文件大小:%dKB", fileinfo.size);
fprintf(file, "\n\n");
} while (_findnext(hFile, &fileinfo) == 0);
}
//关闭文件
fclose(file);
//结束遍历
_findclose(hFile);
}
/*
先将图片信息都写入文本文件
而后开始遍历文件夹
在查找到一个图片后,先清屏,然后使用drawImage函数画出图片,使用getchar函数暂停程序
由于最外面是个参数为true的while循环,所以遍历文件夹结束后会重新开始遍历,
这样就会重新输出打印图片了
*/
int main() {
writeMessage("图片信息.txt");
struct _finddata_t fileinfo;
long hFile;
while (true)
{
if ((hFile = _findfirst("*.bmp", &fileinfo)) == -1)
return -1;
else {
do
{
system("cls");
drawImage(fileinfo.name);
getchar();
} while (_findnext(hFile, &fileinfo) == 0);
}
_findclose(hFile);
}
return 0;
}