diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d9c4d92fd..281c7ba34 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -82,6 +82,7 @@ target_link_libraries(zm
libbcrypt::bcrypt
jwt-cpp::jwt-cpp
RtspServer::RtspServer
+ martinmoene::span-lite
PRIVATE
zm-core-interface)
diff --git a/src/zm_font.cpp b/src/zm_font.cpp
index 92a36501d..11ba405c8 100644
--- a/src/zm_font.cpp
+++ b/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 .
+ */
+
#include "zm_font.h"
#include
-#include
+#include
-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 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 FontVariant::GetCodepoint(uint8 idx) const {
+ static constexpr std::array 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(&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 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(bitmap.data()), static_cast(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);
}
diff --git a/src/zm_font.h b/src/zm_font.h
index 22ecb4612..8ba9f7642 100644
--- a/src/zm_font.h
+++ b/src/zm_font.h
@@ -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 .
+ */
+
#ifndef ZM_FONT_H
#define ZM_FONT_H
#include "zm_define.h"
+#include
+#include
+#include "span.hpp"
#include
+#include
-#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 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 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 GetCodepoint(uint8 idx) const;
+
+ private:
+ uint16 char_height_;
+ uint16 char_width_;
+ uint8 codepoint_count_;
+ std::vector 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 variants_;
};
#endif
diff --git a/src/zm_image.cpp b/src/zm_image.cpp
index 0ce62eb13..de5492fd4 100644
--- a/src/zm_image.cpp
+++ b/src/zm_image.cpp
@@ -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;