在 Linux 上使用 ASCII 艺术打印万圣节问候语

使用 Linux 或 FreeDOS 从 C 程序生成彩色 ASCII 艺术。
29 位读者喜欢这个。

全彩 ASCII 艺术曾经在 DOS 上非常流行,DOS 可以利用扩展的 ASCII 字符集及其绘图元素集合。您可以通过添加 ASCII 艺术作为炫酷的“欢迎”屏幕或带有更多程序信息的彩色“退出”屏幕,为您的下一个 FreeDOS 程序增加一些视觉趣味。

但是这种风格的 ASCII 艺术并不局限于 FreeDOS 应用程序。您可以在 Linux 终端模式程序中使用相同的方法。虽然 Linux 使用 ncurses 来控制屏幕,而不是 DOS 的 conio,但相关概念很好地适用于 Linux 程序。本文着眼于如何从 C 程序生成彩色 ASCII 艺术。

ASCII 艺术文件

您可以使用各种工具来绘制您的 ASCII 艺术。对于此示例,我使用了名为 TheDraw 的旧 DOS 应用程序,但您可以在 Linux 上找到现代开源 ASCII 艺术程序,例如 Moebius (Apache 许可证) 或 PabloDraw (MIT 许可证)。您使用什么工具并不重要,只要您知道保存的数据是什么样子即可。

这是一个示例 ASCII 艺术文件的一部分,保存为 C 源代码。请注意,代码片段定义了一些值:IMAGEDATA_WIDTHIMAGEDATA_DEPTH 定义了屏幕上的列数和行数。在本例中,它是一个 80x25 ASCII 艺术“图像”。IMAGEDATA_LENGTH 定义了 IMAGEDATA 数组中的条目数。ASCII 艺术屏幕中的每个字符都可以用两个字节的数据表示:要显示的字符和一个颜色属性,其中包含字符的前景色和背景色。对于 80x25 的屏幕,其中每个字符都与一个属性配对,该数组包含 4000 个条目(即 80 * 25 * 2 = 4000)。

#define IMAGEDATA_WIDTH 80
#define IMAGEDATA_DEPTH 25
#define IMAGEDATA_LENGTH 4000
unsigned char IMAGEDATA [] = {
    '.', 0x08,  ' ', 0x08,  ' ', 0x08,  ' ', 0x08,  ' ', 0x08,  ' ', 0x08,
    ' ', 0x08,  ' ', 0x08,  '.', 0x0F,  ' ', 0x08,  ' ', 0x08,  ' ', 0x08,
    ' ', 0x08,  ' ', 0x08,  ' ', 0x08,  ' ', 0x08,  ' ', 0x08,  '.', 0x0F,
    ' ', 0x08,  ' ', 0x08,  ' ', 0x08,  ' ', 0x08,  ' ', 0x08,  ' ', 0x08,
    ' ', 0x08,  ' ', 0x08,  ' ', 0x08,  ' ', 0x08,  ' ', 0x08,  ' ', 0x08,

等等,其余的数组也是如此。

要将此 ASCII 艺术显示到屏幕上,您需要编写一个小程序来读取数组并使用正确的颜色打印每个字符。

设置颜色属性

此 ASCII 艺术文件中的颜色属性在一个字节中定义了背景色和前景色,用十六进制值表示,例如 0x080x6E。事实证明,十六进制是表达这种颜色“对”的紧凑方式。

字符模式系统,例如 Linux 上的 ncurses 或 DOS 上的 conio 只能显示十六种颜色。那是十六种可能的文本颜色和八种背景颜色。在二进制中计数十六个值(从 0 到 15)只需要四位

  • 1111 在二进制中是 16

方便的是,十六进制可以使用单个字符表示 0 到 15:0、1、2、3、4、5、6、7、8、9、A、B、C、D、E 和 F。因此,十六进制值 F 是数字 15,或二进制中的 1111

使用颜色对,您可以在一个八位字节中编码背景色和前景色。文本颜色为四位(十六进制为 0 到 15 或 0 到 F),背景色为三位(十六进制为 0 到 7 或 0 到 E)。字节中剩余的位在此处未使用,因此我们可以忽略它。

要将颜色对或属性转换为程序可以使用的颜色值,您需要使用位掩码来仅指定用于文本颜色或背景色的位。在 FreeDOS 上使用 OpenWatcom C 编译器,您可以编写此函数以从颜色属性适当设置颜色

void
textattr(int newattr)
{
  _settextcolor(newattr & 15);         /* 0000xxxx */
  _setbkcolor((newattr >> 4) & 7);     /* 0xxx0000 */
}

_settextcolor 函数仅设置文本颜色,_setbkcolor 函数设置背景颜色。两者都在 graph.h 中定义。请注意,由于颜色属性在一个字节值中同时包含背景色和前景色,因此 textattr 函数使用 &(二进制 AND)来设置位掩码,该位掩码仅隔离属性中的最后四位。这就是颜色对存储前景色值 0 到 15 的位置。

要获取背景色,该函数首先执行位移,以将位“推”到右侧。这会将“高”位放入“低”位范围,因此任何类似 0xxx0000 的位都会变为 00000xxx。我们可以使用另一个带有 7(二进制 0111)的位掩码来选出背景色值。

显示 ASCII 艺术

IMAGEDATA 数组包含整个 ASCII 艺术屏幕以及每个字符的颜色值。要将 ASCII 艺术显示到屏幕上,您的程序需要扫描数组,设置颜色属性,然后一次显示一个字符的屏幕。

让我们在屏幕底部留出空间,以便为用户显示单独的消息或提示。这意味着我不希望显示 80 列 ASCII 屏幕的所有 25 行,而只想显示前 24 行。

  /* print one line less than the 80x25 that's in there:
     80 x 24 x 2 = 3840 */

  for (pos = 0; pos < 3840; pos += 2) {
...
  }

for 循环内部,我们需要设置颜色,然后打印字符。OpenWatcom C 编译器提供了一个函数 _outtext,用于使用当前颜色值显示文本。但是,这需要传递一个字符串,并且如果我们需要一次处理每个字符,以防一行上的每个字符都需要不同的颜色,则效率会很低。

相反,OpenWatcom 有一个类似的函数 _outmem,允许您指示要显示多少个字符。对于一次一个字符,我们可以提供一个指向 IMAGEDATA 数组中字符值的指针,并告诉 _outtext 仅显示一个字符。这将使用当前颜色属性显示字符,这正是我们需要的。

  for (pos = 0; pos < 3840; pos += 2) {
    ch = &IMAGEDATA[pos];              /* pointer assignment */
    attr = IMAGEDATA[pos + 1];
 
    textattr(attr);
    _outmem(ch, 1);
  }

这个更新后的 for 循环通过分配一个指向 IMAGEDATA 数组中的指针来设置字符 ch。接下来,循环设置文本属性,然后使用 _outmem 显示字符。

将它们放在一起

借助 textattr 函数和用于处理数组的 for 循环,我们可以编写一个完整的程序来显示 ASCII 艺术文件的内容。对于此示例,将 ASCII 艺术另存为 imgdata.inc,并使用 #include 语句将其包含在源文件中。

#include <stdio.h>
#include <conio.h>
#include <graph.h>

#include "imgdata.inc"

void
textattr(int newattr)
{
  _settextcolor(newattr & 15);         /* 0000xxxx */
  _setbkcolor((newattr >> 4) & 7);     /* 0xxx0000 */
}

int
main()
{
  char *ch;
  int attr;
  int pos;

  if (_setvideomode(_TEXTC80) == 0) {
    fputs("Error setting video mode", stderr);
    return 1;
  }

  /* draw the array */

  _settextposition(1, 1);              /* top left */

  /* print one line less than the 80x25 that's in there:
     80 x 24 x 2 = 3840 */

  for (pos = 0; pos < 3840; pos += 2) {
    ch = &IMAGEDATA[pos];              /* pointer assignment */
    attr = IMAGEDATA[pos + 1];

    textattr(attr);
    _outmem(ch, 1);
  }

  /* done */

  _settextposition(25, 1);             /* bottom left */

  textattr(0x0f);
  _outtext("Press any key to quit");

  getch();

  textattr(0x00);
  return 0;
}

在 FreeDOS 上使用 OpenWatcom C 编译器编译程序,您将获得一个新程序,该程序显示此节日消息

Happy Halloween message in ASCII art

万圣节快乐 (CC-BY-SA 4.0)

祝大家万圣节快乐!

在此处下载 INC 代码文件。

在此处下载 C 代码文件。

接下来阅读什么
photo of Jim Hall
Jim Hall 是一位开源软件倡导者和开发人员,以 GNOME 中的可用性测试以及作为 FreeDOS 的创始人和项目协调员而闻名。

2 条评论

在 BBS 和 MS-DOS 的时代,有一个令人难以置信的工具用于绘制 ASCII 艺术屏幕,称为 TheDraw ( https://en.wikipedia.org/wiki/TheDraw )。

使用这样的工具,您可以将您的作品保存到文本文件中(其中包括用于光标定位和颜色更改的嵌入式 ANSI 终端序列)。在合适的终端设备(ANSI 转义序列支持和 IBM 代码页 437 字体)上运行的应用程序(在任何操作系统上)可以通过简单地将文件转储到控制台来显示艺术作品。

对于动画,以字符方式转储文件,并在字节之间添加延迟,以模拟 1200 或 2400 波特调制解调器的典型数据速率。

嗨 David - 很高兴你喜欢这篇文章!我同意,TheDraw 是一个很棒的 DOS 程序,用于绘制 ASCII 艺术。正如您所提到的,您可以输出文件以全彩色查看它。在 FreeDOS 上使用 TYPE,或在 Linux 上使用 'cat'。您需要在 FreeDOS 上加载 ANSI 驱动程序,例如开源 NANSI.SYS 驱动程序。Linux 终端是经过修改的彩色 ANSI 终端,因此应默认显示 ANSI 转义符。

为 DOS 编写此程序的另一种方法是将数组直接复制到视频内存。但我认为展示如何使用 _setbkcolor 和 _settextcolor “逐字符”显示将有助于其他人制作自己的彩色文本程序。:-)

Creative Commons License本作品已根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.