LINUX.ORG.RU

Растровый 64-битный шрифт 7x9

 , ,


3

2

Делюсь реализацией одной идеи. В библиотеках типа SDL2 своих встроенных шрифтов обычно нет. Для этого предусмотрены дополнительные библиотеки, зависеть от которых не хочется, да и придётся цеплять файл со шрифтом. Поэтому в Allegro, например, предусмотрели альтернативу: простейший растровый шрифт 8x8. Но там нет кириллицы.

У шрифтов 8x8 есть ещё такие недостатки: буквы маленькие и широкие, что особенно заметно на строчных. Это усугубляется тем, что некоторые буквы выступают вниз (например, «Д», «Щ») или вверх (Ё), из-за чего приходится оставлять пустое место сверху и снизу для остальных букв.

Шрифт 7x9 по битам занимает почти столько же, но выше и уже, что выглядит приятнее. Кроме того, лишний 64 бит можно использовать для признака сдвига вниз. Тогда не надо будет оставлять пустое место снизу. То есть строчка будет высотой даже не 9, а 10.

Коды для символов можно посмотреть тут. В отличие от Allegro я использовал не 8 uint8_t на символ, а одну uint64_t.

Исходный файл для правки тут. В качестве исходника используется не растровое изображение, а текстовое (ASCII art), для удобства трансляции. Приведу пример таких исходных символов:

В
.
.......
.#####.
.#....#
.#....#
.#####.
.#....#
.#....#
.#....#
.#####.
Г
.
.......
.#####.
.#.....
.#.....
.#.....
.#.....
.#.....
.#.....
.#.....
Д
#
...###.
..#..#.
.#...#.
.#...#.
.#...#.
.#...#.
.#...#.
#######
#.....#

Перед каждым изображением буквы оставил признак сдвига: точка если сдвига нет, # - если сдвиг на 1 вниз.


Спасибо, пригодится, например для вывода сообщения на экран об ошибке загрузки шрифта :D

Хотя можно конечно просто fnt + bitmap использовать, хотя если прям экономить экономить то почему бы и не использовать такое.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от firkax

Вообще, бит сдвига искушает задействовать больше битов для признаков (почти везде первая пара бит пустая, креме редких символов типа №, но и их можно поджать). Например, есть символы с точками, типа «Ё». Для этих верхних точек можно было бы бит заложить. Иначе получается либо Ё меньше Е, либо точки в крышу упираются:

..........#..#....#..#.
.######..........######
.#.......######..#.....
.#.......#.......#.....
.#####...#.......#####.
.#.......#####...#.....
.#.......#.......#.....
.#.......#.......#.....
.######..######..######

Ещё есть буквы типа «q» или «p», которые лучше на 2 линии вниз опустить (если поставить буквы со смещениями рядом, то это хорошо видно: «Др»). И для них бит с признаком заложить.

В качестве альтернативы набору битов можно было использовать удлинение поля символа: либо 7x9, либо 5x12 со сдвигом вверх на пиксель. Но тогда некоторые заглавные буквы слишком узкими будут (та же Д).

Пока я решил, что «лучшее - враг хорошего» и остановился на одном сдвиге.

Kogrom
() автор топика
Ответ на: комментарий от bga_

Тему про полуглифы я читал. Да, получается очень прикольно и экономно. Однако при этом у полуглифного шрифта видны угловатости и артефакты, типа асимметричности скобок. Кириллицы тоже нет. Как там будет Ю или Щ? У меня менее экономно и изобретательно, но при этом шрифт выглядит почти естественно (близко к Arial или System). Будет время поразбираться как картинку в Github добавить - приведу пример.

Kogrom
() автор топика

зависеть от которых не хочется, да и придётся цеплять файл со шрифтом

ну незнаю незнаю… всё давно прицеплено, вы изобрели очередной psf)

// generated binary alt-8x16.psf
#ifndef _SYSTEM_FONT_
#define _SYSTEM_FONT_

/// @private
const struct
{
    int  width;
    int  height;
    unsigned char data[4096];
} _fontpsf_altc_8x16 = { 8, 16, { XXXX } };

#endif
anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 1)
Ответ на: комментарий от anonymous2

Фишка не в формате или самом шрифте. Тут задача была запихать каждый символ в uint64_t, чтобы получить словарь типа:

"A" 9586501043363125248ULL;
"B" 9010036165744606720ULL;
"C" 8721238409112271872ULL;
"D" 4362321352444681728ULL;
"E" 18160783260291005952ULL;
"F" 290504029841227264ULL;
"G" 13369523871093439488ULL;
"H" 9586496952407769600ULL;
"I" 4044303388207488000ULL;
"J" 4073789554271730688ULL;
...

Соответственно, не надо никаких структур и массивов. Только одна целочисленная переменная на символ. Берёшь её, шифтом битики достаёшь и ставишь соответствующие пиксели.

А в Вашем примере 4096 байт на символ, если не считать переменных для длины и ширины. Ну даже если бы взяли 64 байта, то это не то же самое, что 64 бита.

Прикольно было бы ещё эти 64 бита пожать до 32. Тогда для каждого символа нужно было бы памяти как для кодировки типа UTF-32 и можно было бы создать кодировку, содержащую изображения символов в самой себе.

Kogrom
() автор топика
Ответ на: комментарий от Kogrom

А в Вашем примере 4096 байт на символ

LOL, alt-8x16.psf это системный шрифт, пример структуры - как шрифт добавить в проект, 8x16 это символ - 128бит Карл!

Хочешь 64 бит? давно всё сделали за вас, думаешь что у тебя получится лучше, не верю)

ls /lib/kbd/consolefonts/*8x8*

alt-8x8.gz cp865-8x8.psfu.gz Cyr_a8x8.psfu.gz gr737a-8x8.psfu.gz koi8r-8x8.gz koi8u_8x8.psfu.gz UniCyr_8x8.psf.gz cp850-8x8.psfu.gz  cp866-8x8.psf.gz drdos8x8.psfu.gz gr737c-8x8.psfu.gz koi8r.8x8.psfu.gz ruscii_8x8.psfu.gz```
anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 3)
Ответ на: комментарий от anonymous2

8x8

Высота строчных букв у такого шрифта около 5 пикселей. Если мы добавим 1 пиксель, то прирост будет 20%, а если 2 - то 40%. Меньше надо будет напрягать глаза. Об этом тема и даже по заголовку можно догадаться.

Kogrom
() автор топика
Ответ на: комментарий от interrupted

А код где?

Люди, которым может потребоваться такой шрифт, напишут код не хуже меня на своём любимом языке. У меня код есть, но он мало чем поможет.

Вот код перевода из ASCII art в uint64_t, например:

def shift_symbol
    mref code:uint index:uint
    cref ch:string
    if ch "#"
        code | index
    if < index 0x8000000000000000ULL
        index << 1


def load_symbols
    *symbolsFile @file @set_mode "r" @open "symbols_64_vertical_7x9.txt"
    *outFile @file @set_mode "w" @open "font_dict_7x9.txt"
    *bom @string
    bom symbolsFile @getline
    for
        *name @string ""
        name symbolsFile @getline
        if name ""
            break
        *code @uint 0
        *index @uint 1
        *shiftSymbol @string
        shiftSymbol symbolsFile @getline
        code index @shift_symbol shiftSymbol
        for *i in 0;9;1
            *s @string
            s symbolsFile @getline
            for *j in 0;7;1
                *ch @string s|j
                code index @shift_symbol ch
        @print* name ": " code "\n"
        *sCode @string @to_string code
        if name in "\"" "\'" "\\"
            outFile @write* "\"\\" name "\" " sCode "ULL;\n"
        else
            outFile @write* "\"" name "\" " sCode "ULL;\n"
    symbolsFile @close        
    outFile @close        

def main
    @load_symbols

А вот я тестирую шрифт:

import ^ graph
import ^ font_dict_7x9

def draw_letter
    mref graph:Graph fontDict:dict|string,uint
    cref letter:string x:int y:int

    *code @uint fontDict|"?"
    if *b @bool in b @has_key fontDict letter 
        code = fontDict|letter
    *shiftBit @uint code & 1
    *shift @int 0
    if shiftBit
        shift = 1
    *index @uint 2    
    for *yy in 0;9;1
        for *xx in 0;7;1
            *codeMask @uint code & index
            if codeMask
                *xCoord @int xx + x
                *yCoord @int yy + y + shift
                graph @^graph:draw_point xCoord yCoord
            try
                index << 1
            catch domain_error e
                break

def draw_text    
    mref graph:Graph fontDict:dict|string,uint
    cref text:string x:int y:int

    *sz @int @size text
    *xCoord @int x
    *index @int 0
    for
        *letter @string ""
        *code @u8 text|index$u8 & 0xC0
        if code 0xC0
            for
                letter + text|index
                index + 1
                if >= index sz
                    break
                code = text|index$u8 & 0xC0
                if < code 0x80
                    break
                if code 0xC0
                    break
        else
            letter + text|index
            index + 1
        graph fontDict @draw_letter letter xCoord y
        xCoord + 8
        if >= index sz
            break

def main
    *graph @Graph @init
    *quitFlag @bool false
    *fontDict @dict|string,uint @^font_dict_7x9:init
    for
        *xx @int -1
        *yy @int -1
        xx yy quitFlag @get_state
        if quitFlag
            break
        graph @clear_background
        graph fontDict @draw_text "0123456789~`!@#$%^&*()+-=_|{}[]\"\'\\/?,.<>:;" 50 30;
                                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 50 50;
                                   "abcdefghijklmnopqrstuvwxyz" 50 70;
                                   "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ №" 50 90;
                                   "абвгдеёжзийклмнопрстуфхцчшщъыьэюя" 50 110;
                                   "Текстовый документ 15 КБ ©" 50 130
        graph @repaint
        @^graph:delay 50
    graph @close 
Kogrom
() автор топика
Ответ на: комментарий от interrupted

Это самодельный ЯП, который я написал во время локдауна (ссылка). Была идея написать максимально примитивный высокоуровневый язык. Другим его не рекомендую, ибо есть очевидные недостатки, но сам использую для своих некоммерческих проектов. Развлекаюсь так.

Kogrom
() автор топика
Ответ на: комментарий от Kogrom

давайте расчеты для вашей ширины 7 и вы наверно в курсе почему используют 8 или нет? а при использовании psf 8х14 вообще ничего напрягать не нужно, потому что все сделано до вас!

anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 1)