zoukankan      html  css  js  c++  java
  • 写一个自己的C++控制台字符画版BadApple!!

    字符画的原理十分简单,就是先将图片转换成文本,然后在控制台输出实现播放。

    我写的badapple程序:http://files.cnblogs.com/files/CodeMIRACLE/badapple.zip

    首先下载 badapple!影绘.mp4

    利用ffmpeg将这个视频按照每秒8帧截取成24位位图序列

    ffmpeg -i badapple.mp4 -r 8 -vcodec bmp p\%04d.bmp

    在p文件夹下生成了1754个bmp文件,我们利用下面的程序把它们转化为字符画,并整合到一个txt文件中。

      1  #include <cstdio>
      2  #include <cstring>
      3  #include <stdint.h>
      4  #include <windows.h>
      5  int32_t width,height;
      6  RGBQUAD *pixels;
      7  bool OpenBitmap(char const *filename)
      8  {
      9      FILE *file = fopen(filename, "rb");
     10      if (file)
     11      {
     12          width=0;
     13          height=0;
     14          BITMAPFILEHEADER bf;
     15          BITMAPINFOHEADER bi;
     16          fread(&bf, sizeof(bf), 1, file);
     17          fread(&bi, sizeof(bi), 1, file);
     18          if(bi.biBitCount!=24)
     19              return false;
     20          if(bi.biCompression!=BI_RGB)
     21              return false;
     22          width=bi.biWidth;
     23          height=bi.biHeight;
     24          pixels=new RGBQUAD[width*height];
     25          uint32_t rowSize = (bi.biBitCount * width + 31) / 32 * 4;
     26          uint8_t *line = new uint8_t[rowSize];
     27          for (int y = 0; y < height; y++)
     28          {
     29              fread(line, rowSize, 1, file);
     30              for (int x = 0; x < width; x++)
     31              {
     32                  uint8_t *color = line + x * 3;
     33                  RGBQUAD *pixel = &pixels[(height-y-1) * width+x];
     34                  pixel->rgbBlue  = color[0];
     35                  pixel->rgbGreen = color[1];
     36                  pixel->rgbRed   = color[2];
     37              }
     38          }
     39          delete[] line;
     40          fclose(file);
     41          return true;
     42      }
     43      return false;
     44  }
     45  RGBQUAD GetColor(int x, int y, int w, int h)
     46  {
     47      int r = 0, g = 0, b = 0;
     48      for (int i = 0; i < w; i++)
     49      {
     50          if (i + x >= width) continue;
     51          for (int j = 0; j < h; j++)
     52          {
     53              if (j + y >= height) continue;
     54              RGBQUAD const& color = pixels[(y + j) * width + (x + i)];
     55              r += color.rgbRed;
     56              g += color.rgbGreen;
     57              b += color.rgbBlue;
     58          }
     59      }
     60      return RGBQUAD{r / (w * h), g / (w * h),b / (w * h)};
     61  }
     62  char ColorToCharacter(RGBQUAD const& color)
     63  {
     64      int brightness = (color.rgbRed + color.rgbGreen + color.rgbBlue) / 3;
     65      static char const *characters = "Qdogc*;:-. ";
     66      int len = strlen(characters);
     67      int span = 0xFF / len;
     68      int cidx = brightness / span;
     69      if (cidx == len)
     70          cidx--;
     71      return characters[cidx];
     72  }
     73  void OutputAscii(const char* filename, int w, int h)
     74  {
     75      FILE *file=fopen(filename,"a+");
     76      int x = width  / w;
     77      int y = height / h;
     78      for (int i = 0; i < height; i += y)
     79      {
     80          for (int j = 0; j < width; j += x)
     81          {
     82              RGBQUAD color = GetColor(j, i, x, y);
     83              fprintf(file, "%c", ColorToCharacter(color));
     84              //printf("%c", ColorToCharacter(color));
     85          }
     86          fprintf(file, "
    ");
     87          //printf("
    ");
     88      }
     89      delete [] pixels;
     90      fclose(file);
     91  }
     92  int main()
     93  {
     94      char filename[1024];
     95      for(int i=1;i<=1754;i++)
     96      {
     97          sprintf(filename,"p/%04d.bmp",i);
     98          if(OpenBitmap(filename));
     99              OutputAscii("badapple.txt",width/6,height/12);
    100      }
    101  }
    

     接下来要做的就是播放了,读取文件然后输出在控制台中。

    为了保证和原来的视频有一样的播放速度,我们要限制其播放帧数。

     1  #include <cstdio>
     2  #include <windows.h>
     3  struct fps_limit {
     4  
     5      int previous_time;
     6      int tpf_limit;
     7      int tpf;
     8      fps_limit(int fps = 60) : previous_time(GetTickCount()), tpf(0) {
     9          limit_fps(fps);
    10      }
    11      void reset() {
    12          previous_time = GetTickCount(),
    13          tpf = 0;
    14          tpf_limit = 60;
    15      }
    16      void limit_fps(int fps) {
    17          tpf_limit = (int)(1000.0f / (float)fps);
    18      }
    19      void delay() {
    20          tpf = GetTickCount() - previous_time;
    21  
    22          if(tpf < tpf_limit)
    23              Sleep(tpf_limit - tpf - 1);
    24  
    25          previous_time = GetTickCount();
    26      }
    27  };
    28  int main()
    29  {
    30      FILE* fp=fopen("badapple.txt","r");
    31      char buf[1024];
    32      fps_limit fps(8);
    33      while(!feof(fp))
    34      {
    35          HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    36          COORD pos;
    37          pos.X = 0;
    38          pos.Y = 0;
    39          SetConsoleCursorPosition(hConsoleOutput, pos);
    40          for(int i=0;i<32;i++)
    41          {
    42              fgets(buf,1024,fp);
    43              printf("%s",buf);
    44          }
    45          fps.delay();
    46      }
    47      return 0;
    48  }
    

    到这位置badapple就写完了 

    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------

    后来看见网上很多大神写的badapple程序,他们的exe文件都特别小,只有单个exe文件。我开始考虑改进一下自己的程序。

    先是压缩数据,写了个huffman压缩,原来4.7M的badapple.txt文件,压缩了成1.3M。感觉压缩效率太低了。

    肯定是我姿势不对......

      1  #include<cstdio>
      2  #include<vector>
      3  #include<map>
      4  #include<algorithm>
      5  #include<queue>
      6  #include<windows.h>
      7  #include<string>
      8  using namespace std;
      9  int cnum[256]= {0};
     10  map<char,string> huffmap;
     11  static const unsigned char mask[8] =
     12  {
     13        0x80, /* 10000000 */
     14        0x40, /* 01000000 */
     15        0x20, /* 00100000 */
     16        0x10, /* 00010000 */
     17        0x08, /* 00001000 */
     18        0x04, /* 00000100 */
     19        0x02, /* 00000010 */
     20        0x01  /* 00000001 */
     21  };
     22  
     23  typedef struct HTNode
     24  {
     25      char c;
     26      unsigned int freq;
     27      HTNode *lchild, *rchild;
     28      HTNode(char key='', unsigned int fr=0, HTNode *l=NULL, HTNode *r=NULL):
     29          c(key),freq(fr),lchild(l),rchild(r) {};
     30  } HTNode,*pNode;
     31  
     32  struct cmp
     33  {
     34      bool operator()(pNode node1, pNode node2)
     35      {
     36          return node1->freq > node2->freq;
     37      }
     38  };
     39  
     40  priority_queue<pNode, vector<pNode>, cmp> pq;
     41  void HuffmanCode(int n)
     42  {
     43      pNode l, r;
     44      while (pq.size() > 1)
     45      {
     46          pNode z = new HTNode;
     47          l = pq.top();
     48          pq.pop();
     49          r = pq.top();
     50          pq.pop();
     51          z->lchild = l;
     52          z->rchild = r;
     53          z->freq = l->freq + r->freq;
     54          pq.push(z);
     55      }
     56  }
     57  
     58  void GetCode(pNode t, string str)
     59  {
     60      if (t == NULL)
     61          return;
     62      if (t->lchild)
     63      {
     64          str += '0';
     65          GetCode(t->lchild, str);
     66      }
     67      if (t->lchild == NULL && t->rchild == NULL)
     68      {
     69          //printf("%c's code: %s
    ",t->c,str.c_str());
     70          huffmap[t->c]=str;
     71      }
     72      str.erase(str.end()-1);
     73      if (t->rchild)
     74      {
     75          str += '1';
     76          GetCode(t->rchild, str);
     77      }
     78  }
     79  
     80  void WriteCode(FILE *fp,string& code)
     81  {
     82        char bitBuf = 0x00;
     83        int bitPos = 0;
     84        for(int i = 0; i < code.size(); ++i)
     85        {
     86              if(code[i] == '1')
     87              {
     88                    bitBuf |= mask[bitPos++];
     89              }
     90              else
     91              {
     92                    bitPos++;
     93              }
     94              if(bitPos == 8)
     95              {
     96                    fwrite(&bitBuf, 1, sizeof(bitBuf), fp);
     97                    bitBuf = 0x00;
     98                    bitPos = 0;
     99              }
    100        }
    101        if(bitPos != 0)
    102        {
    103              fwrite(&bitBuf, 1, sizeof(bitBuf), fp);
    104        }
    105  }
    106  
    107  int main()
    108  {
    109      FILE* fp=fopen("badapple.txt","r");
    110      while(!feof(fp))
    111      {
    112          int c=fgetc(fp);
    113          cnum[c]++;
    114      }
    115      int n=0;
    116      for(int i=0; i<256; i++)
    117          if(cnum[i])
    118          {
    119              n++;
    120              pNode p = new HTNode;
    121              p->c = i;
    122              p->freq = cnum[i];
    123              pq.push(p);
    124          }
    125      string str;
    126      HuffmanCode(n);
    127      GetCode(pq.top(), str);
    128      FILE *fout=fopen("badapple.dat","wb+");
    129      fseek(fp,0,SEEK_SET);
    130      str="";
    131      while(!feof(fp))
    132      {
    133          int c=fgetc(fp);
    134          str+=huffmap[c];
    135       }
    136       fclose(fp);
    137       WriteCode(fout,str);
    138       fclose(fout);
    139  }
    

     压缩率好低啊, (╯‵□′)╯︵┴─┴ 

    后来试了几种其他方法,不想再试了,直接用别人的库,后来用的zlib进行压缩。

    下面的程序把4.7M的文件,压缩到了470多KB。

     1  #include <stdlib.h>
     2  #include <string.h>
     3  #include <cstdio>
     4  #include "zlib.h"
     5  
     6  int main()
     7  {
     8      FILE* fp=fopen("badapple.txt","r");
     9      char* buf = NULL;
    10      char* src =  NULL;
    11      fseek(fp,0,SEEK_END);
    12      unsigned int flen=ftell(fp);
    13      fseek(fp,0,SEEK_SET);
    14      if((src = (char*)malloc(sizeof(char) * flen)) == NULL)
    15      {
    16          printf("no enough memory!
    ");
    17          return -1;
    18      }
    19      fread(src,flen,sizeof(char),fp);
    20      unsigned int blen=compressBound(flen);
    21      if((buf = (char*)malloc(sizeof(char) * blen)) == NULL)
    22      {
    23          printf("no enough memory!
    ");
    24          return -1;
    25      }
    26      if(compress(buf, &blen, src, flen) != Z_OK)
    27      {
    28          printf("compress failed!
    ");
    29          return -1;
    30      }
    31      fclose(fp);
    32      free(src);
    33      fp=fopen("badapple.dat","wb");
    34      fwrite(&flen,1,sizeof(int),fp);
    35      fwrite(buf,blen,sizeof(char),fp);
    36      fclose(fp);
    37      free(buf);
    38      return 0;
    39  }
    

     然后我写了一个解压程序测试一下。

     1  #include <cstdio>
     2  #include <iostream>
     3  #include <cstdlib>
     4  #include "zlib.h"
     5  using namespace std;
     6  int main()
     7  {
     8      FILE* fp=fopen("badapple.dat","rb");
     9      unsigned int slen;
    10      fread(&slen,1,sizeof(int),fp);
    11      char* buf = NULL;
    12      char* dst =  NULL;
    13      fseek(fp,0,SEEK_END);
    14      unsigned int flen=ftell(fp);
    15      fseek(fp,4,SEEK_SET);
    16      flen-=4;
    17      unsigned int blen=compressBound(slen);
    18      if((dst = (char*)malloc(sizeof(char) * slen)) == NULL)
    19      {
    20          printf("no enough memory!
    ");
    21          return -1;
    22      }
    23      if((buf = (char*)malloc(sizeof(char) * blen)) == NULL)
    24      {
    25          printf("no enough memory!
    ");
    26          return -1;
    27      }
    28      fread(buf,flen,sizeof(char),fp);
    29      if(uncompress(dst, &slen, buf, blen) != Z_OK)
    30      {
    31          printf("uncompress failed!
    ");
    32          return -1;
    33      }
    34      free(buf);
    35      fclose(fp);
    36      for(int i=0;i<slen;i++)
    37          printf("%c",dst[i]);
    38  }
    

     解压和压缩没问题了,就该把数据文件打包生成exe文件了,有了图像之后顺便也把音乐加上吧。

    先编写rc文件

    0 TYPEDATA "badapple.dat"
    1 TYPEWAV "BadApple.wav"

    调用windres生成res文件,然后和下面的程序链接生成exe文件

     1  #include <cstdio>
     2  #include <windows.h>
     3  #include "zlib.h"
     4  char* dst =  NULL;
     5  struct fps_limit {
     6  
     7      int previous_time;
     8      int tpf_limit;
     9      int tpf;
    10      fps_limit(int fps = 60) : previous_time(GetTickCount()), tpf(0) {
    11          limit_fps(fps);
    12      }
    13      void reset() {
    14          previous_time = GetTickCount(),
    15          tpf = 0;
    16          tpf_limit = 60;
    17      }
    18      void limit_fps(int fps) {
    19          tpf_limit = (int)(1000.0f / (float)fps);
    20      }
    21      void delay() {
    22          tpf = GetTickCount() - previous_time;
    23  
    24          if(tpf < tpf_limit)
    25              Sleep(tpf_limit - tpf - 1);
    26  
    27          previous_time = GetTickCount();
    28      }
    29  };
    30  void Uncompressdata()
    31  {
    32      HRSRC hrscdat =FindResource(NULL,MAKEINTRESOURCE(0),"TYPEDATA");
    33      char *a=(char *)LockResource(LoadResource(NULL,hrscdat));
    34      DWORD flen = SizeofResource(NULL, hrscdat);
    35      DWORD slen;
    36      memcpy(&slen,a,sizeof(int));
    37      char* buf = NULL;
    38      flen-=4;
    39      unsigned int blen=compressBound(slen);
    40      if((dst = (char*)malloc(sizeof(char) * (slen+1))) == NULL)
    41      {
    42          printf("no enough memory!
    ");
    43          exit(1);
    44      }
    45      if((buf = (char*)malloc(sizeof(char) * blen)) == NULL)
    46      {
    47          printf("no enough memory!
    ");
    48          exit(1);
    49      }
    50      memcpy(buf,a+4,flen*sizeof(char));
    51      if(uncompress(dst, &slen, buf, blen) != Z_OK)
    52      {
    53          printf("uncompress failed! %d
    ");
    54          exit(1);
    55      }
    56      dst[slen]='';
    57      free(buf);
    58  }
    59  int main()
    60  {
    61      printf("Loading......");
    62      HRSRC hrscwav=FindResource(NULL,MAKEINTRESOURCE(1),"TYPEWAV");
    63      Uncompressdata();
    64      printf("OK
    把控制台的高度和宽度都调大点哦
    ");
    65      system("PAUSE");
    66      system("CLS");
    67      PlaySound((LPCSTR)LockResource(LoadResource(NULL,hrscwav)),NULL,SND_MEMORY|SND_ASYNC);
    68      fps_limit fps(8);
    69      int i=0,j=0;
    70      char buf[3000];
    71      Sleep(2000);
    72      while(1)
    73      {
    74          HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    75          COORD pos;
    76          pos.X = 0;
    77          pos.Y = 0;
    78          SetConsoleCursorPosition(hConsoleOutput, pos);
    79          while(j<32*87)
    80         {
    81             if(dst[i]=='')
    82                  return 0;
    83              buf[j++]=dst[i++];
    84         }
    85          j=0;
    86          printf("%s",buf);
    87          fps.delay();
    88      }
    89  }
    

     好了,大功告成,写个这种程序还是蛮有意思的。

  • 相关阅读:
    LeetCode15 3Sum
    LeetCode10 Regular Expression Matching
    LeetCode20 Valid Parentheses
    LeetCode21 Merge Two Sorted Lists
    LeetCode13 Roman to Integer
    LeetCode12 Integer to Roman
    LeetCode11 Container With Most Water
    LeetCode19 Remove Nth Node From End of List
    LeetCode14 Longest Common Prefix
    LeetCode9 Palindrome Number
  • 原文地址:https://www.cnblogs.com/CodeMIRACLE/p/5508236.html
Copyright © 2011-2022 走看看