年轻人耗子尾汁
马大师效果预览
马老师经典名言:年轻人不讲武德,我劝你耗子尾汁!
这是一个非常有意思的水博客,年轻人不讲武德。先看看效果:
进入正题(详细步骤)
思路分析
视频
众所周知,视频都是由一帧一帧的图片构成的,在播放的时候,每一帧图片的快速切换就形成了我们看到的动画。这是我提取的图片帧:
字符
所以,我们C语言输出的马老师也是一样的道理,只不过快速切换的不是图片帧,而是由字符构成的字符帧:
具体实现
那我们从视频得图片帧,最后得到字符帧呢?
两个软件:Premiere 和 Ascgen2。
第一步:Premiere
Premiere是一个强大的剪辑软件,这里我们是用来提取视频中的图片帧:
先将视频导入Pr里面,然后选中视频按Ctrl+M,导出格式选择PNG,勾选导出为序列:
然后我们就得到图片帧了。
第二步:Ascgen2,得到字符视频
然后我们打开Ascgen2,按Ctrl+B,打开批量转换:
将图片帧的目录加入进来,然后自己设置一下转换后字符矩阵的大小,字符帧就完成了:
代码实现
资源准备好了,开始动手写代码。
字符帧最后是用的字符数组存的,这样编译后就形成了一个exe整体,不需要再读取外部数据了,所以我把字符帧放进了一个 rescore.h 头文件里。
不过我们现在得到了字符文本,它们是很多个文件,我们怎么把它合并成一个头文件?手动复制?累死!
首先我们在字符文本目录下新建一个 cmd.dat 文件,内容如下:
然后我们双击cmd.dat,随后我们就得到了一个 path.txt文件,这个文件存储了当前文件夹下,所有文件的名字 ,也就是 所有字符帧文件的名字 :
代码目录结构:
其中 rescore.h 头文件是creatRescore.cpp生成出来的。
字符帧 和 path.txt文件都在 ==RESCORE \ TXT == 目录下。
main.exe是我们最后生成的效果。
creatRescore.cpp代码:
#include <stdio.h>
#include <windows.h>
#include <mmsystem.h>
#include <string.h>
#pragma comment(lib, "winmm.lib")
#define FPS 10.92
#define TXT_PATH "RESCORE\\TXT\\"
#define FILE_NAME "path.txt"
#define COUNT (1818) //一共有多少帧
#define SIZE 115584 //一帧的大小(B)
void gotoxy(short i, short j)
{
COORD position = {j, i};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position);
CONSOLE_CURSOR_INFO cursor_info = {1, 0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
bool isError(FILE *fptr)
{
if (fptr == NULL)
return true;
return false;
}
void fun()
{
char path[200] = TXT_PATH;
FILE *fptr, *pptr, *rescore;
rescore = fopen("rescore.h", "wb+"); //生成我们的资源头文件
int cell, i;
pptr = fopen(TXT_PATH FILE_NAME, "r"); //打开我们存储字符图片名的文件
if (isError(pptr))
{
puts("path not found.");
puts(TXT_PATH FILE_NAME);
puts("Error");
getchar();
return;
}
//写入头文件的预处理部分
fprintf(rescore, "#define SIZE (%d)\n", SIZE);
fprintf(rescore, "#define COUNT (%d)\n", COUNT);
fprintf(rescore, "#define FPS %lf\n", FPS);
fputs("char rescore[COUNT][SIZE+1]{\n", rescore);
//读取字符帧
for (i = 0; i < COUNT; i++)
{
gotoxy(0,0);
printf("%d\n",i);
fgets(path + strlen(TXT_PATH), 200, pptr); //每次循环读取一张字符帧的名字
path[strlen(path) - 1] = '\0';
fptr = fopen(path, "r"); //打开字符帧文件
if (isError(fptr))
{
puts(path);
puts("Error");
getchar();
return;
}
//将字符帧写入头文件
fputs("{\n\"", rescore);
int c=0;
while ((cell = fgetc(fptr)) != EOF)
{
if (cell == '\n')
fputs("\\n\"\n\"", rescore);
else
fputc(cell, rescore);
c++;
}
printf("一帧的大小:%d\n",c);
fseek(rescore, -1, SEEK_CUR);
fputs("},\n", rescore);
fclose(fptr);
}
fseek(rescore, -1, SEEK_END), fputs("};", rescore);
fclose(rescore);//关闭头文件
fclose(pptr);//关闭路径文件
getchar();
}
int main(void)
{
fun();
}
main.cpp代码:
#include <stdio.h>
#include <windows.h>
#include "rescore.h"
void gotoxy(short i, short j)
{
COORD position = {j, i};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position);
CONSOLE_CURSOR_INFO cursor_info = {1, 0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
void init(int width, int height)
{
//----------------------设置控制台字体大小---------------------------------
HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台输出句柄
CONSOLE_FONT_INFOEX cfi;
cfi.cbSize = sizeof cfi;
cfi.nFont = 0;
cfi.dwFontSize.X = 2; //字宽
cfi.dwFontSize.Y = 2; //字高
cfi.FontFamily = FF_DONTCARE;
cfi.FontWeight = FW_NORMAL; //粗细
wcscpy_s(cfi.FaceName, L"Raster"); //设置字体,此处设为点阵字体
SetCurrentConsoleFontEx(handle_out, FALSE, &cfi);
//----------------------设置控制台窗口大小--------------------------------------
char str[20];
sprintf(str, "mode %d,%d", width, height);
system(str);
//----------------------设置控制台缓冲区大小------------------------------------
CONSOLE_SCREEN_BUFFER_INFO scbi; //定义一个窗口缓冲区信息结构体
COORD size = {(short)width, (short)height}; //定义一个坐标结构体
GetConsoleScreenBufferInfo(handle_out, &scbi); //获得窗口缓冲区信息
SetConsoleScreenBufferSize(handle_out, size); // 重新设置缓冲区大小
}
int main(void)
{
MessageBox(NULL,"年轻人不讲武德","马保国",0);
int h, w;
//scanf("%d%d",&h,&w);
printf("输入窗口大小:");
int x,y;
scanf("%d%d",&x,&y);
init(x, y);
int i;
// mciSendString(TEXT("play " WMA_PATH WMA_NAME), NULL, 0, NULL);
for (i = 0; i < COUNT; i++) //输出字符帧
{
gotoxy(0, 0);
puts((const char *)rescore[i]);
Sleep(1000 / FPS);
}
getchar();
}
我们先编译运行 creatRecsore.cpp ,得到 rescore.h 头文件。
然后编译运行 main.cpp ,就得到了我们 最终的程序main.exe 。
最终效果:
最后因为字符比较多,编译出来的exe有200多兆。。。
我这里一帧是由 115584个ascll码 组成,一共1818帧,算一下那就是:
115584B * 1818 = 210131712B = 205206.75KB = 200.4MB.