ZmFont: Rework/modernize API

This commit is contained in:
Peter Keresztes Schmidt 2021-04-12 09:26:48 +02:00
parent 9f56f633f1
commit 0c939f45dd
4 changed files with 182 additions and 91 deletions

View File

@ -82,6 +82,7 @@ target_link_libraries(zm
libbcrypt::bcrypt
jwt-cpp::jwt-cpp
RtspServer::RtspServer
martinmoene::span-lite
PRIVATE
zm-core-interface)

View File

@ -1,77 +1,119 @@
/*
* This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "zm_font.h"
#include <cstring>
#include <sys/stat.h>
#include <fstream>
int ZmFont::ReadFontFile(const std::string &loc) {
FILE *f = fopen(loc.c_str(), "rb");
if ( !f ) return -1; // FILE NOT FOUND
constexpr uint8 FontVariant::kMaxNumCodePoints;
constexpr uint8 FontVariant::kMaxCharHeight;
constexpr uint8 FontVariant::kMaxCharWidth;
font = new ZMFONT;
FontVariant::FontVariant() : char_height_(0), char_width_(0), codepoint_count_(0) {}
size_t header_size = 8 + (sizeof(ZMFONT_BH) * NUM_FONT_SIZES);
// MAGIC + pad + BitmapHeaders
size_t readsize = fread(&font[0], 1, header_size, f);
if ( readsize < header_size ) {
delete font;
font = nullptr;
fclose(f);
return -2; // EOF reached, invalid file
FontVariant::FontVariant(uint16 char_height, uint16 char_width, std::vector<uint64> bitmap)
: char_height_(char_height), char_width_(char_width), bitmap_(std::move(bitmap)) {
if (char_height_ > kMaxCharHeight) {
throw std::invalid_argument("char_height > kMaxCharHeight");
}
if ( memcmp(font->MAGIC, "ZMFNT", 5) != 0 ) { // Check whether magic is correct
delete font;
font = nullptr;
fclose(f);
return -3;
if (char_width_ > kMaxCharWidth) {
throw std::invalid_argument("char_width > kMaxCharWidth");
}
struct stat st;
stat(loc.c_str(), &st);
if (bitmap_.size() % char_height_ != 0) {
throw std::invalid_argument("bitmap has wrong length");
}
for ( int i = 0; i < NUM_FONT_SIZES; i++ ) {
/* Character Width cannot be greater than 64 as a row is represented as a uint64_t,
height cannot be greater than 200(arbitary number which i have chosen, shouldn't need more than this) and
idx should not be more than filesize
*/
if ( font->header[i].charWidth > 64 || font->header[i].charWidth == 0 ||
font->header[i].charHeight > 200 || font->header[i].charHeight == 0 ||
(font->header[i].idx > st.st_size) ) {
delete font;
font = nullptr;
fclose(f);
return -4;
codepoint_count_ = bitmap_.size() / char_height;
}
nonstd::span<const uint64> FontVariant::GetCodepoint(uint8 idx) const {
static constexpr std::array<uint64, kMaxCharHeight> empty_bitmap = {};
if (idx >= GetCodepointsCount()) {
return {empty_bitmap.begin(), GetCharHeight()};
}
return {bitmap_.begin() + (idx * GetCharHeight()), GetCharHeight()};
}
std::ifstream &operator>>(std::ifstream &stream, FontBitmapHeader &bm_header) {
stream.read(reinterpret_cast<char *>(&bm_header), sizeof(bm_header));
return stream;
}
std::ifstream &operator>>(std::ifstream &stream, FontFileHeader &header) {
stream.read(header.magic, sizeof(header.magic));
stream.seekg(sizeof(header.pad), std::ifstream::cur);
for (FontBitmapHeader &bm_header : header.bitmap_header)
stream >> bm_header;
return stream;
}
FontLoadError ZmFont::LoadFontFile(const std::string &loc) {
std::ifstream font_file(loc, std::ifstream::binary);
font_file.exceptions(std::ifstream::badbit);
if (!font_file.is_open()) {
return FontLoadError::kFileNotFound;
}
FontFileHeader file_header = {};
font_file >> file_header;
if (font_file.fail()) {
return FontLoadError::kInvalidFile;
}
if (memcmp(file_header.magic, "ZMFNT", 5) != 0) {
return FontLoadError::kInvalidFile;
}
for (int i = 0; i < kNumFontSizes; i++) {
FontBitmapHeader bitmap_header = file_header.bitmap_header[i];
if (bitmap_header.char_width > FontVariant::kMaxCharWidth
|| bitmap_header.char_height > FontVariant::kMaxCharHeight
|| bitmap_header.number_of_code_points > FontVariant::kMaxNumCodePoints) {
return FontLoadError::kInvalidFile;
}
} // end foreach font size
datasize = st.st_size - header_size;
std::vector<uint64> bitmap;
bitmap.resize(bitmap_header.number_of_code_points * bitmap_header.char_height);
font->data = new uint64_t[datasize/sizeof(uint64_t)];
readsize = fread(&font->data[0], 1, datasize, f);
if ( readsize < datasize ) { // Shouldn't happen
delete[] font->data;
font->data = nullptr;
delete font;
font = nullptr;
return -2;
}
fclose(f);
return 0;
}
std::size_t bitmap_bytes = bitmap.size() * sizeof(uint64);
font_file.read(reinterpret_cast<char *>(bitmap.data()), static_cast<std::streamsize>(bitmap_bytes));
ZmFont::~ZmFont() {
if ( font && font->data ) {
delete[] font->data;
font->data = nullptr;
variants_[i] =
{bitmap_header.char_height, bitmap_header.char_width, std::move(bitmap)};
}
if ( font ) {
delete font;
font = nullptr;
if (font_file.fail()) {
return FontLoadError::kInvalidFile;
}
return FontLoadError::kOk;
}
uint64_t *ZmFont::GetBitmapData() {
return &font->data[font->header[size].idx];
const FontVariant &ZmFont::GetFontVariant(uint8 idx) const {
return variants_.at(idx);
}

View File

@ -1,40 +1,89 @@
/*
* This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ZM_FONT_H
#define ZM_FONT_H
#include "zm_define.h"
#include <array>
#include <cassert>
#include "span.hpp"
#include <string>
#include <vector>
#define NUM_FONT_SIZES 4
constexpr uint8 kNumFontSizes = 4;
struct ZMFONT_BH{
uint16_t charHeight; // Height of every character
uint16_t charWidth; // Width of every character
uint32_t numberofCodePoints; // number of codepoints max 255 for now
uint32_t idx; // idx in data where data for the bitmap starts
uint32_t pad; // padding to round of the size
enum class FontLoadError {
kOk,
kFileNotFound,
kInvalidFile
};
struct ZMFONT {
char MAGIC[6]; // ZMFNT\0
char pad[2];
ZMFONT_BH header[NUM_FONT_SIZES];
uint64_t *data;
#pragma pack(push, 1)
struct FontBitmapHeader {
uint16 char_height; // height of every character
uint16 char_width; // width of every character
uint32 number_of_code_points; // number of codepoints; max. 255 for now
uint32 idx; // offset in data where data for the bitmap starts; not used
uint32 pad; // padding
};
#pragma pack(pop)
#pragma pack(push, 1)
struct FontFileHeader {
char magic[6]; // "ZMFNT\0"
uint8 pad[2];
std::array<FontBitmapHeader, kNumFontSizes> bitmap_header;
};
#pragma pack(pop)
class FontVariant {
public:
static constexpr uint8 kMaxNumCodePoints = 255;
// height cannot be greater than 200 (arbitrary number; shouldn't need more than this)
static constexpr uint8 kMaxCharHeight = 200;
// character width can't be greater than 64 as a row is represented as an uint64
static constexpr uint8 kMaxCharWidth = 64;
FontVariant();
FontVariant(uint16 char_height, uint16 char_width, std::vector<uint64> bitmap);
uint16 GetCharHeight() const { return char_height_; }
uint16 GetCharWidth() const { return char_width_; }
uint8 GetCodepointsCount() const { return codepoint_count_; }
// Returns the bitmap of the codepoint `idx`. If `idx` is greater than `GetCodepointsCount`
// a all-zero bitmap with `GetCharHeight` elements is returned.
nonstd::span<const uint64> GetCodepoint(uint8 idx) const;
private:
uint16 char_height_;
uint16 char_width_;
uint8 codepoint_count_;
std::vector<uint64> bitmap_;
};
class ZmFont {
public:
~ZmFont();
int ReadFontFile(const std::string &loc);
ZMFONT *GetFont() { return font; }
void SetFontSize(int _size) { size = _size; }
uint64_t *GetBitmapData();
uint16_t GetCharWidth() { return font->header[size].charWidth; }
uint16_t GetCharHeight() { return font->header[size].charHeight; }
FontLoadError LoadFontFile(const std::string &loc);
const FontVariant &GetFontVariant(uint8 idx) const;
private:
int size = 0;
size_t datasize = 0;
ZMFONT *font = nullptr;
std::array<FontVariant, kNumFontSizes> variants_;
};
#endif

View File

@ -564,10 +564,10 @@ void Image::Initialise() {
g_u_table = g_u_table_global;
b_u_table = b_u_table_global;
int res = font.ReadFontFile(config.font_file_location);
if ( res == -1 ) {
FontLoadError res = font.LoadFontFile(config.font_file_location);
if ( res == FontLoadError::kFileNotFound ) {
Panic("Invalid font location: %s", config.font_file_location);
} else if ( res == -2 || res == -3 || res == -4 ) {
} else if ( res == FontLoadError::kInvalidFile ) {
Panic("Invalid font file.");
}
initialised = true;
@ -1943,9 +1943,9 @@ const Coord Image::centreCoord( const char *text, int size=1 ) const {
line_no++;
}
font.SetFontSize(size-1);
uint16_t char_width = font.GetCharWidth();
uint16_t char_height = font.GetCharHeight();
FontVariant const &font_variant = font.GetFontVariant(size - 1);
uint16_t char_width = font_variant.GetCharWidth();
uint16_t char_height = font_variant.GetCharHeight();
int x = (width - (max_line_len * char_width )) / 2;
int y = (height - (line_no * char_height) ) / 2;
return Coord(x, y);
@ -2023,10 +2023,9 @@ void Image::Annotate(
const Rgb bg_rgb_col = rgb_convert(bg_colour, subpixelorder);
const bool bg_trans = (bg_colour == kRGBTransparent);
font.SetFontSize(size-1);
const uint16_t char_width = font.GetCharWidth();
const uint16_t char_height = font.GetCharHeight();
const uint64_t *font_bitmap = font.GetBitmapData();
FontVariant const &font_variant = font.GetFontVariant(size - 1);
const uint16_t char_width = font_variant.GetCharWidth();
const uint16_t char_height = font_variant.GetCharHeight();
Debug(4, "Font size %d, char_width %d char_height %d", size, char_width, char_height);
while ( (index < text_len) && (line_len = strcspn(line, "\n")) ) {
@ -2064,7 +2063,7 @@ void Image::Annotate(
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += width ) {
unsigned char *temp_ptr = ptr;
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
uint64_t f = font_bitmap[(line[c] * char_height) + r];
uint64_t f = font_variant.GetCodepoint(line[c])[r];
if ( !bg_trans ) memset(temp_ptr, bg_bw_col, char_width);
while ( f != 0 ) {
uint64_t t = f & -f;
@ -2081,7 +2080,7 @@ void Image::Annotate(
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += wc ) {
unsigned char *temp_ptr = ptr;
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
uint64_t f = font_bitmap[(line[c] * char_height) + r];
uint64_t f = font_variant.GetCodepoint(line[c])[r];
if ( !bg_trans ) {
for ( int i = 0; i < char_width; i++ ) { // We need to set individual r,g,b components
unsigned char *colour_ptr = temp_ptr + (i*3);
@ -2109,7 +2108,7 @@ void Image::Annotate(
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += wc ) {
Rgb* temp_ptr = (Rgb*)ptr;
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
uint64_t f = font_bitmap[(line[c] * char_height) + r];
uint64_t f = font_variant.GetCodepoint(line[c])[r];
if ( !bg_trans ) {
for ( int i = 0; i < char_width; i++ )
*(temp_ptr + i) = bg_rgb_col;