天天看点

c/c++控制台图片浏览

最近刚写完c++的大作业,觉得挺有意思,所以就拿来和大家分享交流一下

下面是要求

一.基本要求

使用C++语言编写程序,在控制台(字符界面)上实现图片浏览。

基本功能包括:

1. 能够浏览同一个文件夹下所有的图片,而不是只能用输入名字的方式浏览。

2. 实现图片的彩色显示。

3. 界面友好,操作简便。

4. 编写一个文档,包括程序的基本模块说明,核心功能的实现方法,人员分工等。

不要求可以显示所有的图片,只需能够浏览24位色,不超过150*150的小图片。符合要求的图片可以使用photoshop或者画图板自行制作(可以用示例中自带的图片进行测试)。

这是成品

c/c++控制台图片浏览

代码如下

#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
#include <Windows.h>
#include<conio.h> 
#include <stdio.h>
#include <io.h> 
#include <time.h>
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;

}
           

继续阅读