aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2022-04-24 13:12:59 +0900
committernsfisis <nsfisis@gmail.com>2022-04-24 13:12:59 +0900
commit83754f16833766420cdca1f8527177dbc4b6ace1 (patch)
treee713bf6e0d6d9ce74e9b361b2aeebb6f4f45c51b
parent7f4f96b94f1111fbbe71dc99c4bed506a4b4b406 (diff)
downloadterm-banner-83754f16833766420cdca1f8527177dbc4b6ace1.tar.gz
term-banner-83754f16833766420cdca1f8527177dbc4b6ace1.tar.zst
term-banner-83754f16833766420cdca1f8527177dbc4b6ace1.zip
implementv1.0.0
-rw-r--r--.gitignore1
-rw-r--r--LICENSE21
-rw-r--r--Makefile24
-rw-r--r--README.md55
-rw-r--r--assets/misaki.txt261
-rw-r--r--assets/misaki_gothic_2nd_4x8.pngbin0 -> 1073 bytes
-rw-r--r--assets/misaki_mincho.pngbin0 -> 54886 bytes
-rw-r--r--go.mod17
-rw-r--r--go.sum19
-rw-r--r--main.go392
-rw-r--r--screenshot.pngbin0 -> 27573 bytes
11 files changed, 790 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7673e14
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/term-banner
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..cc69ec3
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright 2022 nsfisis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d4abc9b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,24 @@
+BIN := term-banner
+
+all: build
+
+.PHONY: run
+run: build
+ ./$(BIN)
+
+.PHONY: build
+build: $(BIN)
+
+$(BIN): main.go
+ go build -o $(BIN)
+
+.PHONY: fmt
+fmt:
+ go fmt
+
+.PHONY: clean
+clean:
+ go clean
+
+test: build
+ ./$(BIN) 'Hello, World!' 'こんにちは、' '世界!'
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8b2a990
--- /dev/null
+++ b/README.md
@@ -0,0 +1,55 @@
+# term-banner
+
+Show a banner in your terminal.
+
+## Supported characters
+
+* All of ASCII characters
+* Some of Japanese Kanji and symbols (JIS第一・第二水準)
+
+Special thanks to Num Kadoma, the author of the awesome font!
+
+
+# Usage
+
+Pass a messsage as command line arguments:
+
+```
+$ ./term-banner 'Hello, World!'
+```
+
+Multiple arguments are displayed in multiple lines.
+
+```
+$ ./term-banner foo bar baz
+```
+
+
+# Requirements
+
+* Go toolchains
+
+
+# Build
+
+1. Clone this repository.
+2. `make build`
+
+
+# Screenshot
+
+```
+$ make test
+```
+
+![Screenshot](./screenshot.png)
+
+
+# License
+
+This program uses [美咲フォント 2021-05-05a 版](https://littlelimit.net/misaki.htm) for rendering Japanese text.
+Note that the license and copyright holder of the font is different from the program's.
+
+See [assets/misaki.txt](./assets/misaki.txt) for license of font.
+
+See [LICENSE](./LICENSE) for license of the program.
diff --git a/assets/misaki.txt b/assets/misaki.txt
new file mode 100644
index 0000000..3a1ae30
--- /dev/null
+++ b/assets/misaki.txt
@@ -0,0 +1,261 @@
+===============================================================================
+
+ 8×8 ドット日本語フォント「美咲フォント」
+ (2021-05-05a 版)
+
+ Copyright(C) 2002-2021 Num Kadoma
+
+===============================================================================
+
+-------------------------------------------------------------------------------
+ 概要
+-------------------------------------------------------------------------------
+ 美咲フォントは 8×8 ドットの日本語ビットマップフォントです。
+ JIS第一・第二水準をサポートしています。
+ 一部の記号を除いた全ての文字は 7×7 ドットの範囲に収まっているため、
+ 文字同士を隣接させても行間・字間が確保できます。
+
+ フォント名は制作のきっかけとなった美咲 礼威氏からいただきました。
+
+
+-------------------------------------------------------------------------------
+ バリエーション
+-------------------------------------------------------------------------------
+・「美咲明朝」は、美咲ゴシックの非漢字部を明朝風の字形に差し替えたものです。
+・「美咲ゴシック第2」は美咲ゴシックをベースに、半角文字は縦 7 px、
+ 全角仮名は横 7 px をいっぱいに使う字形に差し替えたものです。
+
+ 各機種用のアーカイブに、差替え済みのフォントファイルを同梱してあります。
+
+
+-------------------------------------------------------------------------------
+ ライセンス
+-------------------------------------------------------------------------------
+ These fonts are free softwares.
+ Unlimited permission is granted to use, copy, and distribute it, with or without modification, either commercially and noncommercially.
+ THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
+
+ これらのフォントはフリー(自由な)ソフトウエアです。
+ あらゆる改変の有無に関わらず、また商業的な利用であっても、自由にご利用、複製、再配布することができますが、全て無保証とさせていただきます。
+
+
+-------------------------------------------------------------------------------
+ 制作履歴
+-------------------------------------------------------------------------------
+■2021-05-05a
+・(PNG) 左へのずれを修正: ¨¥¢§
+
+■2021-05-05
+・文字を追加
+ 共通: ␣
+・字形を修正・変更
+ 共通: №
+ ゴシック第2: プヴ
+・(TTF) ビットマップを埋込み
+
+■2019-10-19
+・ドキュメントの文字コードを UTF-8N、改行コードを LF に統一
+・(TTF, BDF) 記号を追加:
+  ▁▂▃▄▅▆▇█▏▎▍▌▋▊▉▔▕╭╮╰╯♠♥♦♣╱╲╳♤♡♢♧
+
+■2019-08-18
+・各ファイルのパーミッションから、不要な実行ビットを除去 (フォントの変更はなし)
+
+■2019-06-03
+・新フォント「美咲ゴシック第2」を追加。英語名「MisakiGothic2nd」
+・「令和」の合字を追加
+・各種字形の修正
+・(E500) ファイル名のつけ方を変更して配布を再開
+・(BDF) 一部仕様を変更して配布を再開
+  エンコーディングは ISO 10646-1 のみとし、全ての文字を 1 ファイルに収録
+  XLFD 変更
+  BITMAP を文字の上下左右端の空白を削った形とし、BBX で位置を指定する形に修正
+
+■2019-02-03a
+・制作履歴における、変更した文字の抜け (ゴシックの「と」「ね」「ゐ」) を修正
+
+■2019-02-03
+・ゴシック・明朝共通の変更: &,.‘’()〔〕[]{}〈〉【】°′″№0索二
+  半角 (「&」字形変更)
+  記号類 (主に括弧類の位置調整)
+  数字 (ゼロにスラッシュを追加)
+  漢字 (「索」「二」の字形変更)
+・ゴシックのみの変更: 69CGOQΟОСЭぐぜとどねばぶゐガグゾダバヴ
+  数字 (「6」「9」の先端を伸ばす形に変更)
+  アルファベット (オーなどの丸み変更)
+  仮名 (主に濁点/半濁点つき文字の調整)
+・明朝のみの変更: ぐでとどなぶウグケサザソゾダドワヱヴЁ
+  仮名 (各種字形変更)
+  キリル (「Ё」の字形変更)
+
+■2015-04-10
+・誤字になっていた「喪」を修正
+・(TTF) ウェイトを「Regular」に変更
+  一部の記号を追加、変更
+  バージョン番号を「yyyy.mmdd」の形に変更
+  その他、パラメータを見直し
+  埋込みビットマップ版の同梱を取り止め
+
+■2012-06-03
+・正式公開初版
+
+■2012-03-31
+・各種ファイル名、ドキュメントの見直し
+・BDF 版以外のアーカイブ形式を zip に変更
+・(TTF) アウトライン版の正式採用、Mac におそらく対応
+・(FONTX) 4X8.FNT の 00h~1Fh に DOS/V 罫線を追加
+
+■2011-03-08
+・(TTF-Outline) 生成手順の改善に伴い periodβ11 作成・公開
+
+■2010-03-07
+・(BDF) FOUNDRY, POINT_SIZE, AVERAGE_WIDTH, SWIDTH, XLFD, 各ファイル名の変更
+
+■2006-10-01
+・(TTF-Outline) 試作版配布開始
+
+■2006-04-01
+・(PNG) 配布開始
+
+■2006-03-26
+・(E500) 13 区追加版・軽量版 (第一水準のみ) 同梱開始
+  フォントファイルの命名規則変更
+・(BDF) 美咲明朝の配布形態をゴシックとの差分から実ファイル同梱へ
+  BITMAP の記述方法を修正 (16 進 4 桁から 2 桁へ)
+・(FONTX) 配布再開
+
+■2005-07-20
+・アーカイブの命名規則を変更
+・(TTF, BDF) 著作権表記を変更
+・(BDF) 1 バイト文字フォントのファイル名をそれぞれ 4x8.bdf, 4x8_8859.bdf に
+
+■2004-09-12
+・(TTF, BDF) 13 区の文字追加
+
+■2004-06-24
+・フォントの名称変更
+・(美咲フォントを美咲ゴシック、ファミリーの総称を美咲フォントに)
+
+■2004-05-09
+・美咲明朝の同梱開始
+
+■2004-04-19
+・半濁点の処理を変更
+
+■2004-04-11
+・3 フォント同時編集ツール「"SCRNJPN.FNT" Editor」完成
+
+■2004-02-09
+・濁点の処理を変更
+
+■2003-09-21
+・制作履歴の日付ミスを修正 (美咲氏 Thanx!)
+
+■2003-06-04
+・ライセンスに関する記述を変更
+
+■2003-05-04
+・(BDF) 3x7_8859.bdf の CHARS の値を修正
+・(BDF) 3x7_8859.bdf のファイル名が 3X7_8859.bdf になっていたのを修正
+
+■2003-03-26
+・マニュアルをフォントの説明と各形式の説明に分離
+
+■2003-01-02
+・各部のフォント間での統一化作業を本格的に開始
+
+■2002-06-10
+・JIS X 0208-1983 での文字の変更、JIS X 0208-1990 での文字の追加に対応
+
+■2002-06-06
+・未定義文字のフォントを 3×3 の ■ に
+
+■2002-06-01
+・第二水準完成
+
+■2002-05-22
+・制作再開
+
+■2002-05-21
+・フォント作成専用ツール「'SCRNJPN.FNT' Editor」完成
+
+■2002-05-11
+・E650 暴走により第二水準中の 600 文字近くが消滅→制作中断
+
+■2002-04-初
+・第一水準完成
+
+■2002-03-中
+・制作開始
+
+
+-------------------------------------------------------------------------------
+ バージョン情報
+-------------------------------------------------------------------------------
+■2021-05-05
+・美咲フォント 2021-05-05 版 (E500/BDF/TTF/PNG)
+
+■2019-10-19
+・美咲フォント 2019-10-19 版 (E500/BDF/TTF/PNG)
+
+■2019-08-18
+・美咲フォント 2019-06-03a 版 (E500/BDF/TTF/PNG)
+
+■2019-06-03
+・美咲フォント 2019-06-03 版 (E500/BDF/TTF/PNG)
+
+■2019-02-03
+・美咲フォント 2019-02-03 版 (TTF/PNG)
+
+■2015-04-10
+・美咲フォント 2015-04-10 版 (TTF/PNG)
+
+■2012-06-03
+・美咲フォント 2012-06-03 版 (E500/BDF/TTF/Ruputer/FONTX/PNG)
+
+■2012-03-31
+・美咲フォント periodβ12 (E500/BDF/TTF/Ruputer/FONTX/PNG)
+
+■2010-03-07
+・美咲フォント periodβ11a (BDF)
+
+■2008-06-03
+・美咲フォント periodβ11 (E500/BDF/TTF/Ruputer/FONTX/PNG/TTF-Outline)
+
+■2006-03-26
+・美咲フォント periodβ10 (E500/BDF/TTF/Ruputer/FONTX/PNG/TTF-Outline)
+
+■2005-07-20
+・美咲フォント periodβ9 (E500/BDF/TTF/Ruputer)
+
+■2004-05-09
+・美咲フォント periodβ8 (E500/BDF/TTF/Ruputer)
+
+■2003-09-21
+・美咲フォント periodβ7 (E500/BDF/TTF/Ruputer)
+
+■2003-06-04
+・美咲フォント periodβ6 (E500/BDF/TTF/Ruputer)
+
+■2003-05-04
+・美咲フォント periodβ5 (E500/BDF/TTF/Ruputer)
+
+■2003-03-26
+・美咲フォント periodβ4 (E500/BDF/TTF/Ruputer/FONTX)
+
+■2002-12-14
+・美咲フォント periodβ3 (E500)
+
+■2002-06-18
+・美咲フォント periodβ2 (E500)
+
+■2002-06-03
+・美咲フォント periodβ1 (E500)
+
+
+-------------------------------------------------------------------------------
+ 連絡先
+-------------------------------------------------------------------------------
+ 門真 なむ (Num Kadoma)
+ ・Twitter: @num_kadoma
+ ・Website: http://littlelimit.net/
diff --git a/assets/misaki_gothic_2nd_4x8.png b/assets/misaki_gothic_2nd_4x8.png
new file mode 100644
index 0000000..edcfc97
--- /dev/null
+++ b/assets/misaki_gothic_2nd_4x8.png
Binary files differ
diff --git a/assets/misaki_mincho.png b/assets/misaki_mincho.png
new file mode 100644
index 0000000..874b91e
--- /dev/null
+++ b/assets/misaki_mincho.png
Binary files differ
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..34366a7
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,17 @@
+module github.com/nsfisis/term-banner
+
+go 1.18
+
+require (
+ github.com/gdamore/tcell/v2 v2.5.0
+ golang.org/x/text v0.3.7
+)
+
+require (
+ github.com/gdamore/encoding v1.0.0 // indirect
+ github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
+ github.com/mattn/go-runewidth v0.0.13 // indirect
+ github.com/rivo/uniseg v0.2.0 // indirect
+ golang.org/x/sys v0.0.0-20220318055525-2edf467146b5 // indirect
+ golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..6e7f618
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,19 @@
+github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
+github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
+github.com/gdamore/tcell/v2 v2.5.0 h1:/LA5f/wqTP5mWT79czngibKVVx5wOgdFTIXPQ68fMO8=
+github.com/gdamore/tcell/v2 v2.5.0/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
+github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
+github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
+github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
+github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220318055525-2edf467146b5 h1:saXMvIOKvRFwbOMicHXr0B1uwoxq9dGmLe5ExMES6c4=
+golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
+golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..1a14810
--- /dev/null
+++ b/main.go
@@ -0,0 +1,392 @@
+package main
+
+import (
+ "embed"
+ "image/png"
+ "log"
+ "os"
+
+ "github.com/gdamore/tcell/v2"
+ "golang.org/x/text/encoding"
+ "golang.org/x/text/encoding/japanese"
+)
+
+const (
+ // Bit width of half-width characters.
+ bitsHW = 32
+ // Glyph width of half-width characters.
+ glyphWidthHW = 4
+ // Glyph height of half-width characters.
+ glyphHeightHW = 8
+ // Bit width of full-width characters.
+ bitsFW = 64
+ // Glyph width of full-width characters.
+ glyphWidthFW = 8
+ // Glyph height of full-width characters.
+ glyphHeightFW = 8
+
+ fw1FirstByteStart = 0x81
+ fw1FirstByteEnd = 0x9F
+ fw2FirstByteStart = 0xE0
+ fw2FirstByteEnd = 0xEF
+ fwSecondByteStart = 0x40
+ fwSecondByteEnd = 0x9F
+)
+
+//go:embed assets/*.png
+var fontFiles embed.FS
+
+// One glyph for half-width characters.
+type GlyphHW uint32
+
+// One glyph for full-width characters.
+type GlyphFW uint64
+
+type Font struct {
+ // Half-width glyphs. It is keyed by a raw character code.
+ glyphsHW *[256]GlyphHW
+ // Full-width glyphs.
+ glyphsFW1 *[31][189]GlyphFW
+ // Full-width glyphs.
+ glyphsFW2 *[16][189]GlyphFW
+}
+
+type CharClass uint8
+
+const (
+ charClassHW = iota
+ charClassFW1
+ charClassFW2
+)
+
+// Get character class.
+func getCharClass(b byte) CharClass {
+ if fw1FirstByteStart <= b && b <= fw1FirstByteEnd {
+ return charClassFW1
+ } else if fw2FirstByteStart <= b && b <= fw2FirstByteEnd {
+ return charClassFW2
+ } else {
+ return charClassHW
+ }
+}
+
+func glyphHWToglyphFW(gHW GlyphHW) GlyphFW {
+ gFW := GlyphFW(0)
+ for i := 0; i < bitsHW; i++ {
+ if gHW&(1<<i) != 0 {
+ j := i/4*8 + i%4
+ gFW |= 1 << j
+ }
+ }
+ return gFW
+}
+
+func utf8ToShiftJISReplacingUnsupported(in string) (string, error) {
+ e := encoding.ReplaceUnsupported(japanese.ShiftJIS.NewEncoder())
+ return e.String(in)
+}
+
+type Banner []string
+
+func NewBanner(lines []string) (Banner, error) {
+ b := make(Banner, len(lines))
+ for i, line := range lines {
+ lineShiftJIS, err := utf8ToShiftJISReplacingUnsupported(line)
+ if err != nil {
+ return nil, err
+ }
+ b[i] = lineShiftJIS
+ }
+ return b, nil
+}
+
+type Renderer struct {
+ scr tcell.Screen
+ squareWidth int
+ squareHeight int
+ bgStyle tcell.Style
+ fgStyle tcell.Style
+}
+
+func NewRenderer(bgStyle, fgStyle tcell.Style) (*Renderer, error) {
+ scr, err := tcell.NewScreen()
+ if err != nil {
+ return nil, err
+ }
+ err = scr.Init()
+ if err != nil {
+ return nil, err
+ }
+
+ r := &Renderer{scr, 0, 0, bgStyle, fgStyle}
+ return r, nil
+}
+
+func (r *Renderer) ScreenSize() (int, int) {
+ return r.scr.Size()
+}
+
+func (r *Renderer) SetSquareSize(w, h int) {
+ r.squareWidth = w
+ r.squareHeight = h
+}
+
+func (r *Renderer) Fini() {
+ r.scr.Fini()
+}
+
+func (r *Renderer) Show() {
+ r.scr.Show()
+}
+
+func (r *Renderer) PollEvent() tcell.Event {
+ return r.scr.PollEvent()
+}
+
+func (r *Renderer) Sync() {
+ r.scr.Sync()
+}
+
+func (r *Renderer) ClearScreen() {
+ r.scr.SetStyle(r.bgStyle)
+ r.scr.Clear()
+}
+
+func (r *Renderer) DrawSquare(sx, sy int) {
+ w, h := r.squareWidth, r.squareHeight
+ for dx := 0; dx < w; dx++ {
+ for dy := 0; dy < h; dy++ {
+ r.scr.SetContent(sx*w+dx, sy*h+dy, ' ', nil, r.fgStyle)
+ }
+ }
+}
+
+func drawGlyph(r *Renderer, g GlyphFW, sx, sy int) {
+ for i := 0; i < bitsFW; i++ {
+ filled := g&(1<<i) != 0
+ if !filled {
+ continue
+ }
+ dx := i % glyphWidthFW
+ dy := i / glyphWidthFW
+ r.DrawSquare(sx+dx, sy+dy)
+ }
+}
+
+func calcGridWidth(s string) int {
+ w := 0
+ for i := 0; i < len(s); i++ {
+ switch getCharClass(s[i]) {
+ case charClassHW:
+ w += 1
+ case charClassFW1, charClassFW2:
+ i++
+ w += 2
+ }
+ }
+
+ return w * glyphWidthHW
+}
+
+func calcSquareSizeAndOffset(r *Renderer, banner Banner) (int, int, []int, int) {
+ scrW, scrH := r.ScreenSize()
+
+ gridWidthMax := 0
+ gridWidths := make([]int, len(banner))
+ for i, line := range banner {
+ gridWidths[i] = calcGridWidth(line)
+ if gridWidthMax < gridWidths[i] {
+ gridWidthMax = gridWidths[i]
+ }
+ }
+ gridHeight := glyphHeightFW * len(banner)
+
+ squareW := scrW / gridWidthMax
+ squareH := scrH / gridHeight
+ if squareW > squareH*8 {
+ squareW = squareH*8
+ }
+ if squareH > squareW {
+ squareH = squareW
+ }
+
+ xOffsets := make([]int, len(banner))
+ for i, gridWidth := range gridWidths {
+ xOffsets[i] = (scrW/squareW - gridWidth) / 2
+ }
+ yOffset := (scrH/squareH - gridHeight) / 2
+
+ return squareW, squareH, xOffsets, yOffset
+}
+
+func drawOneLine(r *Renderer, s string, xOffset, yOffset int, font *Font) {
+ for i := 0; i < len(s); i++ {
+ b := s[i]
+ x := xOffset + i*glyphWidthHW
+ y := yOffset
+ var g GlyphFW
+ switch getCharClass(b) {
+ case charClassHW:
+ g = glyphHWToglyphFW(font.glyphsHW[b])
+ case charClassFW1:
+ b2 := s[i+1]
+ g = font.glyphsFW1[b-fw1FirstByteStart][b2-fwSecondByteStart]
+ i++
+ case charClassFW2:
+ b2 := s[i+1]
+ g = font.glyphsFW1[b-fw2FirstByteStart][b2-fwSecondByteStart]
+ i++
+ }
+ drawGlyph(r, g, x, y)
+ }
+}
+
+func drawBanner(r *Renderer, banner Banner, font *Font) {
+ r.ClearScreen()
+
+ sw, sh, xOffsets, yOffset := calcSquareSizeAndOffset(r, banner)
+ r.SetSquareSize(sw, sh)
+
+ for i, line := range banner {
+ drawOneLine(r, line, xOffsets[i], yOffset+i*glyphHeightFW, font)
+ }
+}
+
+func parseGlyphsHW(filePath string) (*[256]GlyphHW, error) {
+ fp, err := fontFiles.Open(filePath)
+ if err != nil {
+ return nil, err
+ }
+ defer fp.Close()
+
+ img, err := png.Decode(fp)
+ if err != nil {
+ return nil, err
+ }
+
+ gs := [256]GlyphHW{}
+ for dy := 0; dy < 16; dy++ {
+ for dx := 0; dx < 16; dx++ {
+ glyph := GlyphHW(0)
+ for i := 0; i < bitsHW; i++ {
+ x := dx*glyphWidthHW + i%glyphWidthHW
+ y := dy*glyphHeightHW + i/glyphWidthHW
+ r, g, b, _ := img.At(x, y).RGBA()
+ if r == 0 && b == 0 && g == 0 {
+ glyph |= 1 << i
+ }
+ }
+ c := dy*16 + dx
+ gs[c] = glyph
+ }
+ }
+ return &gs, nil
+}
+
+func parseGlyphsFW(filePath string) (*[31][189]GlyphFW, *[16][189]GlyphFW, error) {
+ fp, err := fontFiles.Open(filePath)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer fp.Close()
+
+ img, err := png.Decode(fp)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ gs1 := [31][189]GlyphFW{}
+ for dy := 0; dy < 62; dy++ {
+ for dx := 0; dx < 94; dx++ {
+ glyph := GlyphFW(0)
+ for i := 0; i < bitsFW; i++ {
+ x := dx*glyphWidthFW + i%glyphWidthFW
+ y := dy*glyphHeightFW + i/glyphWidthFW
+ r, g, b, _ := img.At(x, y).RGBA()
+ if r == 0 && b == 0 && g == 0 {
+ glyph |= 1 << i
+ }
+ }
+ c1 := dy / 2
+ c2 := dx + (fwSecondByteEnd-fwSecondByteStart)*(dy%2)
+ gs1[c1][c2] = glyph
+ }
+ }
+
+ yOffset := 31 * glyphHeightFW
+ gs2 := [16][189]GlyphFW{}
+ for dy := 0; dy < 16; dy++ {
+ for dx := 0; dx < 94; dx++ {
+ glyph := GlyphFW(0)
+ for i := 0; i < bitsFW; i++ {
+ x := dx*glyphWidthFW + i%glyphWidthFW
+ y := dy*glyphHeightFW + i/glyphWidthFW + yOffset
+ r, g, b, _ := img.At(x, y).RGBA()
+ if r == 0 && b == 0 && g == 0 {
+ glyph |= 1 << i
+ }
+ }
+ c1 := dy / 2
+ c2 := dx + (fwSecondByteEnd-fwSecondByteStart)*(dy%2)
+ gs2[c1][c2] = glyph
+ }
+ }
+
+ return &gs1, &gs2, nil
+}
+
+func prepareFont(fileHW, fileFW string) (*Font, error) {
+ glyphsHW, err := parseGlyphsHW(fileHW)
+ if err != nil {
+ return nil, err
+ }
+ glyphsFW1, glyphsFW2, err := parseGlyphsFW(fileFW)
+ if err != nil {
+ return nil, err
+ }
+ return &Font{glyphsHW, glyphsFW1, glyphsFW2}, nil
+}
+
+func main() {
+ if len(os.Args) <= 1 {
+ return
+ }
+
+ font, err := prepareFont(
+ "assets/misaki_gothic_2nd_4x8.png",
+ "assets/misaki_mincho.png",
+ )
+ if err != nil {
+ log.Fatalf("%+v", err)
+ }
+
+ r, err := NewRenderer(
+ tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset),
+ tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorOlive),
+ )
+ if err != nil {
+ log.Fatalf("%+v", err)
+ }
+ defer r.Fini()
+
+ banner, err := NewBanner(os.Args[1:])
+ if err != nil {
+ log.Fatalf("%+v", err)
+ }
+ drawBanner(r, banner, font)
+
+ for {
+ r.Show()
+
+ ev := r.PollEvent()
+ switch ev := ev.(type) {
+ case *tcell.EventResize:
+ drawBanner(r, banner, font)
+ r.Sync()
+ case *tcell.EventKey:
+ if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC || ev.Rune() == 'q' {
+ return
+ }
+ }
+ }
+}
diff --git a/screenshot.png b/screenshot.png
new file mode 100644
index 0000000..c527879
--- /dev/null
+++ b/screenshot.png
Binary files differ