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;