Linux中國

在 Linux 上用 ASCII 藝術列印萬聖節問候語

利用擴展 ASCII 字符集和它的繪畫元素集合的全彩 ASCII 藝術在 DOS 上曾經相當流行。你可以在你的下一個 FreeDOS 程序中加入 ASCII 藝術,作為一個很酷的「歡迎」屏幕,或者作為一個提供了更多程序信息的彩色「退出」屏幕,來增加一點視覺上的樂趣。

但是,這種 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 這樣的字元模式系統 只能顯示 16 種顏色。這就是十六種可能的文本顏色和八種背景顏色。用二進位計算十六個值(從 0 到 15)只需要四個二進位位。

1111 是二進位的 15

而且方便的是,十六進位可以用一個字元表示 0 到 15:0123456789ABCDEF。所以十六進位的值 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 函數使用 &(二進位的「與」運算)來設置一個位掩碼,只隔離了屬性中的最後四個位。這就是顏色對存儲前景顏色的值 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&apos;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 編譯器編譯該程序,你會得到一個顯示這個節日信息的新程序。

ASCII藝術中的萬聖節信息

萬聖節快樂(Jim Hall, CC-BY-SA 4.0)

萬聖節快樂,各位!

via: https://opensource.com/article/21/10/ascii-linux-halloween

作者:Jim Hall 選題:lujun9972 譯者:wxy 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國