Начальная реализация поддержки шрифтов PSF

This commit is contained in:
Евгений Титаренко 2024-04-10 15:39:45 +03:00
parent 63b0d22340
commit 9af7778b77
5 changed files with 193 additions and 11 deletions

View file

@ -5,4 +5,6 @@ set(CMAKE_CXX_STANDARD 17)
add_executable(image_test_cpp main.cpp
bmpimage.h
bmpimage.cpp)
bmpimage.cpp
psf.cpp
psf.h)

View file

@ -40,13 +40,15 @@ struct Pixel {
};
#pragma pack(pop)
class PixelArray{
class PixelArray {
Pixel **array;
uint32_t _width;
uint32_t _height;
public:
PixelArray(uint32_t width, uint32_t height);
PixelArray(const PixelArray &);
~PixelArray();
[[nodiscard]] const uint32_t &width() const;
@ -56,7 +58,8 @@ public:
// Pixel operator()(const uint32_t &, const uint32_t &);
// Pixel* operator()(const uint32_t &);
Pixel &operator()(const uint32_t &, const uint32_t &);
Pixel* &operator()(const uint32_t &);
Pixel *&operator()(const uint32_t &);
};
class BMPImage {
@ -64,9 +67,9 @@ class BMPImage {
BITMAPINFOHEADER infoHeader;
PixelArray pixelArray;
public:
BMPImage(const BitmapFileHeader &fileHeader, const BITMAPINFOHEADER &infoHeader, const PixelArray& pixelArray);
BMPImage(const BitmapFileHeader &fileHeader, const BITMAPINFOHEADER &infoHeader, const PixelArray &pixelArray);
BMPImage(const PixelArray& pixelArray);
BMPImage(const PixelArray &pixelArray);
~BMPImage();

View file

@ -1,5 +1,6 @@
#include <iostream>
#include "bmpimage.h"
#include "psf.h"
const std::string FILENAME = "../elef.bmp";
const std::string FILENAME_OUT = "../elef_out.bmp";
@ -22,6 +23,7 @@ void lab01() {
og_image.appendRight(lighterImg).appendRight(darkenImg).appendRight(invert_img).appendRight(grayscale_img).save(
"../lab01/elef1.bmp");
}
//
void lab02_01() {
auto img = readBMPImage("../tea.bmp");
@ -47,6 +49,7 @@ void lab02_01() {
// img.appendRight(img_from_512)->appendRight(img_from_256)->appendRight(img_from_128)->save("../lab02/tea_upscale.bmp");
//// newImg2->save("../lab02/tea_upscale.bmp");
}
//
void lab02_02() {
auto img = readBMPImage("../tea.bmp");
@ -57,21 +60,21 @@ void lab02_02() {
auto height = gsImg.height();
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
gsPixels(i,j) = (gsPixels(i,j) / 2) * 2;
gsPixels(i, j) = (gsPixels(i, j) / 2) * 2;
}
}
auto gsImg128 = BMPImage(gsPixels);
gsPixels = gsImg.pixels_copy();
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
gsPixels(i,j) = (gsPixels(i,j) / 4) * 4;
gsPixels(i, j) = (gsPixels(i, j) / 4) * 4;
}
}
auto gsImg64 = BMPImage(gsPixels);
gsPixels = gsImg.pixels_copy();
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
gsPixels(i,j) = (gsPixels(i,j) / 8) * 8;
gsPixels(i, j) = (gsPixels(i, j) / 8) * 8;
}
}
auto gsImg32 = BMPImage(gsPixels);
@ -80,8 +83,9 @@ void lab02_02() {
gsImg32.save("../lab02/tea_grayscale_32.bmp");
gsImg.appendRight(gsImg128).appendRight(gsImg64).appendRight(gsImg32).save("../lab02/tea_grayscale.bmp");
}
//
void lab02_03(){
void lab02_03() {
auto img = readBMPImage("../tea.bmp");
auto img_1_5 = upscale1_5x(img);
auto img_2 = upscale2x(img);
@ -89,7 +93,8 @@ void lab02_03(){
img_1_5.save("../lab02/lab02_03/tea_x1_5.bmp");
img_2.save("../lab02/lab02_03/tea_x2.bmp");
img_3.save("../lab02/lab02_03/tea_x3.bmp");
img.appendRight(img_1_5).appendRight(img_2).appendRight(img_3).save("../lab02/lab02_03/tea_upscale.bmp"); //.appendRight(img_3)
img.appendRight(img_1_5).appendRight(img_2).appendRight(img_3).save(
"../lab02/lab02_03/tea_upscale.bmp"); //.appendRight(img_3)
// img.appendRight(img_1_5).save("../lab02/lab02_03/tea_upscale.bmp");
}
@ -115,11 +120,43 @@ void test() {
test_scale.save("../test_scale.bmp");
}
void test2() {
auto background_color = Pixel{0, 0, 0};
auto font_color = Pixel{255, 255, 255};
auto font = readPSF("../fonts/ruscii_8x16_2.psf");
auto glyph = font._glyphs[u'б'];
auto w = glyph.width;
auto h = glyph.height;
int imgWidth = 16 * glyph.width;
int imgHeight = 16 * glyph.height;
PixelArray test(imgWidth, imgHeight);
std::u16string str = u"Hello, World! Привет, Мир!";
uint32_t k = 0;
for (auto it: font._glyphs) {
if (k == 256) continue;
// glyph = font._glyphs[u'☺'];
// glyph = font._glyphs[str[k % str.size()]];
glyph = it.second;
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
if (glyph.glyph[i][j])
test((k / 16) * h + i, (k % 16) * w + j) = font_color;
else
test((k / 16) * h + i, (k % 16) * w + j) = background_color;
}
}
k++;
}
BMPImage(test).save("test_font.bmp");
}
int main() {
// lab01();
// lab02_01();
lab02_02();
// lab02_02();
// lab02_03();
// test();
test2();
return 0;
}

91
psf.cpp Normal file
View file

@ -0,0 +1,91 @@
//
// Created by Evgenij on 09.04.2024.
//
#include <locale>
#include <codecvt>
#include <vector>
#include "psf.h"
std::wstring_convert<std::codecvt_utf8<char16_t>, char16_t> conv_utf8_utf16;
Font readPSF(const std::string &filename) {
{
PSF2Header psf2Header;
std::ifstream ifs(filename, std::ios_base::binary);
if (!ifs.good()) {
throw std::runtime_error("File read error");
}
uint8_t magicNumber[4];
ifs.read((char *) magicNumber, 4);
ifs.seekg(0, std::ios_base::beg);
if (magicNumber[0] == 0x36 && magicNumber[1] == 0x04)
throw std::runtime_error("PSF1 not supported");
if (magicNumber[0] != 0x72 || magicNumber[1] != 0xb5 || magicNumber[2] != 0x4a || magicNumber[3] != 0x86)
throw std::runtime_error("Invalid font file");
ifs.read((char *) &psf2Header, sizeof(psf2Header));
auto font = Font(psf2Header.numberOfGlyphs);
Glyph glyphs[psf2Header.numberOfGlyphs];
for (int i = 0; i < psf2Header.numberOfGlyphs; ++i) {
auto glyph = Glyph(psf2Header.glyphWidth, psf2Header.glyphHeight);
char glyphBytes[psf2Header.bytesPerGlyph];
ifs.read((char *) &glyphBytes, psf2Header.bytesPerGlyph);
for (int j = 0; j < psf2Header.bytesPerGlyph; ++j) {
for (int k = 0; k < 8; ++k) {
uint8_t bit = (glyphBytes[j] & (1 << k)) != 0; // Получить к-ый бит
glyph.glyph[(j * 8 + k) / psf2Header.glyphWidth][psf2Header.glyphWidth - 1 -
(j * 8 + k) % psf2Header.glyphWidth] = bit;
}
}
glyphs[i] = glyph;
}
// Чтение таблицы unicode
uint32_t curGlyph = 0;
std::vector<unsigned char> vectorOfBytes;
while (not ifs.eof() && curGlyph < psf2Header.numberOfGlyphs) {
unsigned char curByte;
ifs.read((char *) &curByte, 1);
if (curByte != 0xFF) {
vectorOfBytes.push_back(curByte);
} else {
unsigned long long int bytesCount = vectorOfBytes.size();
unsigned char bytes[bytesCount];
for (int i = 0; i < bytesCount; ++i) {
bytes[i] = vectorOfBytes[i];
}
char chars8[bytesCount + 1];
memcpy(chars8, bytes, bytesCount);
chars8[bytesCount] = 0;
std::string str8(chars8);
std::u16string str16 = conv_utf8_utf16.from_bytes(str8);
for (auto chr: str16) {
font._glyphs[chr] = glyphs[curGlyph];
}
curGlyph++;
vectorOfBytes.clear();
}
}
return font;
}
}
Glyph::Glyph(uint32_t width, uint32_t height) {
this->glyph = new uint8_t *[height];
for (int i = 0; i < height; ++i) {
this->glyph[i] = new uint8_t[width];
}
this->width = width;
this->height = height;
}
Glyph::Glyph() {
}
Font::Font(uint32_t glyphs) {
}
Font::Font() {
}

49
psf.h Normal file
View file

@ -0,0 +1,49 @@
#pragma once
#include <string>
#include <fstream>
#include <cstdint>
#include <map>
#pragma pack(push, 1)
struct PSF1Header {
char magicNumber[2];
uint8_t mode;
uint8_t glyphSize;
};
#pragma pack(pop)
#pragma pack(push, 1)
struct PSF2Header {
uint8_t magicNumber[4];
uint32_t version;
uint32_t headerSize;
uint32_t flags;
uint32_t numberOfGlyphs;
uint32_t bytesPerGlyph;
uint32_t glyphHeight;
uint32_t glyphWidth;
};
#pragma pack(pop)
class Glyph {
public:
uint8_t **glyph;
uint32_t width;
uint32_t height;
Glyph(uint32_t width, uint32_t height);
Glyph();
};
class Font {
public:
std::map<char16_t, Glyph> _glyphs;
Font();
Font(uint32_t glyphs);
};
Font readPSF(const std::string &filename);