ZmFont: Rework/modernize API
This commit is contained in:
parent
9f56f633f1
commit
0c939f45dd
|
@ -82,6 +82,7 @@ target_link_libraries(zm
|
|||
libbcrypt::bcrypt
|
||||
jwt-cpp::jwt-cpp
|
||||
RtspServer::RtspServer
|
||||
martinmoene::span-lite
|
||||
PRIVATE
|
||||
zm-core-interface)
|
||||
|
||||
|
|
154
src/zm_font.cpp
154
src/zm_font.cpp
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue