Merge pull request #3223 from Carbenium/font

Modernize the ZmFont API and add tests
pull/3224/head
Isaac Connor 2021-04-25 19:57:32 -04:00 committed by GitHub
commit 7bbcb7e36a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 2885 additions and 203 deletions

View File

@ -1,3 +1,4 @@
add_subdirectory(jwt-cpp)
add_subdirectory(libbcrypt)
add_subdirectory(RtspServer)
add_subdirectory(span-lite)

View File

@ -0,0 +1,9 @@
add_library(span-lite INTERFACE)
add_library(martinmoene::span-lite ALIAS span-lite)
target_include_directories(span-lite INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(span-lite
INTERFACE
zm-dependency-interface)

23
dep/span-lite/LICENSE.txt Normal file
View File

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

514
dep/span-lite/README.md Normal file
View File

@ -0,0 +1,514 @@
<a id="top"></a>
# span lite: A single-file header-only version of a C++20-like span for C++98, C++11 and later
[![Language](https://img.shields.io/badge/C%2B%2B-98/11/14/17/20-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [![License](https://img.shields.io/badge/license-BSL-blue.svg)](https://opensource.org/licenses/BSL-1.0) [![Build Status](https://travis-ci.org/martinmoene/span-lite.svg?branch=master)](https://travis-ci.org/martinmoene/span-lite) [![Build status](https://ci.appveyor.com/api/projects/status/1ha3wnxtam547m8p?svg=true)](https://ci.appveyor.com/project/martinmoene/span-lite) [![Version](https://badge.fury.io/gh/martinmoene%2Fspan-lite.svg)](https://github.com/martinmoene/span-lite/releases) [![download](https://img.shields.io/badge/latest-download-blue.svg)](https://github.com/martinmoene/span-lite/blob/master/include/nonstd/span.hpp) [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://conan.io/center/span-lite) [![Try it on wandbox](https://img.shields.io/badge/on-wandbox-blue.svg)](https://wandbox.org/permlink/venR3Ko2Q4tlvcVk) [![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/z/htwpnb)
**Contents**
- [Example usage](#example-usage)
- [In a nutshell](#in-a-nutshell)
- [License](#license)
- [Dependencies](#dependencies)
- [Installation and use](#installation-and-use)
- [Synopsis](#synopsis)
- [Reported to work with](#reported-to-work-with)
- [Building the tests](#building-the-tests)
- [Other implementations of span](#other-implementations-of-span)
- [Notes and references](#notes-and-references)
- [Appendix](#appendix)
## Example usage
```cpp
#include "nonstd/span.hpp"
#include <array>
#include <vector>
#include <iostream>
std::ptrdiff_t size( nonstd::span<const int> spn )
{
return spn.size();
}
int main()
{
int arr[] = { 1, };
std::cout <<
"C-array:" << size( arr ) <<
" array:" << size( std::array <int, 2>{ 1, 2, } ) <<
" vector:" << size( std::vector<int >{ 1, 2, 3, } );
}
```
### Compile and run
```bash
prompt> g++ -std=c++11 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe
C-array:1 array:2 vector:3
```
## In a nutshell
**span lite** is a single-file header-only library to provide a bounds-safe view for sequences of objects. The library provides a [C++20-like span](http://en.cppreference.com/w/cpp/container/span) for use with C++98 and later. If available, `std::span` is used, unless [configured otherwise](#configuration). *span-lite* can detect the presence of [*byte-lite*](https://github.com/martinmoene/byte-lite) and if present, it provides `as_bytes()` and `as_writable_bytes()` also for C++14 and earlier.
**Features and properties of span lite** are ease of installation (single header), freedom of dependencies other than the standard library. To compensate for the class template argument deduction that is missing from pre-C++17 compilers, `nonstd::span` can provide `make_span` functions. See [configuration](#configuration).
## License
*span lite* is distributed under the [Boost Software License](https://github.com/martinmoene/span-lite/blob/master/LICENSE.txt).
## Dependencies
*span lite* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header).
## Installation and use
*span lite* is a single-file header-only library. Put `span.hpp` in the [include](include) folder directly into the project source tree or somewhere reachable from your project.
## Synopsis
**Contents**
[Documentation of `std::span`](#documentation-of-stdspan)
[Later additions](#later-additions)
[Non-standard extensions](#non-standard-extensions)
[Configuration](#configuration)
## Documentation of `std::span`
Depending on the compiler and C++-standard used, `nonstd::span` behaves less or more like `std::span`. To get an idea of the capabilities of `nonstd::span` with your configuration, look at the output of the [tests](test/span.t.cpp), issuing `span-main.t --pass @`. For `std::span`, see its [documentation at cppreference](http://en.cppreference.com/w/cpp/container/span).
## Later additions
### `back()` and `front()`
*span lite* can provide `back()` and `front()` member functions for element access. See the table below and section [configuration](#configuration).
## Non-standard extensions
### Construct from container
To construct a span from a container with compilers that cannot constrain such a single-parameter constructor to containers, *span lite* provides a constructor that takes an additional parameter of type `with_container_t`. Use `with_container` as value for this parameter. See the table below and section [configuration](#configuration).
### Construct from `std::array` with const data
*span lite* can provide construction of a span from a `std::array` with const data. See the table below and section [configuration](#configuration).
### `operator()`
*span lite* can provide member function call `operator()` for element access. It is equivalent to `operator[]` and has been marked `[[deprecated]]`. Its main purpose is to provide a migration path.
### `at()`
*span lite* can provide member function `at()` for element access. Unless exceptions have been disabled, `at()` throws std::out_of_range if the index falls outside the span. With exceptions disabled, `at(index_t)` delegates bounds checking to `operator[](index_t)`. See the table below and sections [configuration](#configuration) and [disable exceptions](#disable-exceptions).
### `swap()`
*span lite* can provide a `swap()`member function. See the table below and section [configuration](#configuration).
### `operator==()` and other comparison functions
*span lite* can provide functions to compare the content of two spans. However, C++20's span will not provide comparison and _span lite_ will omit comparison at default in the near future. See the table below and section [configuration](#configuration). See also [Revisiting Regular Types](#regtyp).
### `same()`
*span lite* can provide function `same()` to determine if two spans refer as identical spans to the same data via the same type. If `same()` is enabled, `operator==()` incorporates it in its comparison. See the table below and section [configuration](#configuration).
### `first()`, `last()` and `subspan()`
*span lite* can provide functions `first()`, `last()` and `subspan()` to avoid having to use the *dot template* syntax when the span is a dependent type. See the table below and section [configuration](#configuration).
### `make_span()`
*span lite* can provide `make_span()` creator functions to compensate for the class template argument deduction that is missing from pre-C++17 compilers. See the table below and section [configuration](#configuration).
### `byte_span()`
*span lite* can provide `byte_span()` creator functions to represent an object as a span of bytes. This requires the C++17 type `std::byte` to be available. See the table below and section [configuration](#configuration).
| Kind | std | Function or method |
|--------------------|------|--------------------|
| **Macro** |&nbsp;| macro **`span_FEATURE_WITH_CONTAINER`**<br>macro **`span_FEATURE_WITH_CONTAINER_TO_STD`** |
| **Types** |&nbsp;| **with_container_t** type to disambiguate below constructors |
| **Objects** |&nbsp;| **with_container** value to disambiguate below constructors |
| **Constructors** |&nbsp;| macro **`span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE`**|
| &nbsp; |&nbsp;| template&lt;class Container><br>constexpr **span**(with_container_t, Container & cont) |
| &nbsp; |&nbsp;| template&lt;class Container><br>constexpr **span**(with_container_t, Container const & cont) |
| &nbsp; |&nbsp;| &nbsp; |
| **Methods** |&nbsp;| macro **`span_FEATURE_MEMBER_CALL_OPERATOR`** |
| &nbsp; |&nbsp;| constexpr reference **operator()**(index_t idx) const<br>Equivalent to **operator[]**(), marked `[[deprecated]]` |
| &nbsp; |&nbsp;| &nbsp; |
| **Methods** |&nbsp;| macro **`span_FEATURE_MEMBER_AT`** |
| &nbsp; |&nbsp;| constexpr reference **at**(index_t idx) const<br>May throw std::out_of_range exception |
| &nbsp; |&nbsp;| &nbsp; |
| **Methods** |&nbsp;| macro **`span_FEATURE_MEMBER_BACK_FRONT`** (on since v0.5.0) |
| &nbsp; |&nbsp;| constexpr reference **back()** const noexcept |
| &nbsp; |&nbsp;| constexpr reference **front()** const noexcept |
| &nbsp; |&nbsp;| &nbsp; |
| **Method** |&nbsp;| macro **`span_FEATURE_MEMBER_SWAP`** |
| &nbsp; |&nbsp;| constexpr void **swap**(span & other) noexcept |
| &nbsp; |&nbsp;| &nbsp; |
| **Free functions** |&nbsp;| macro **`span_FEATURE_COMPARISON`** |
|<br><br>== != < > <= >= |&nbsp;| template&lt;class T1, index_t E1, class T2, index_t E2><br>constexpr bool<br>**operator==**( span<T1,E1> const & l, span<T2,E2> const & r) noexcept |
| &nbsp; |&nbsp;| &nbsp; |
| **Free function** |&nbsp;| macro **`span_FEATURE_SAME`** |
| &nbsp; |&nbsp;| template&lt;class T1, index_t E1, class T2, index_t E2><br>constexpr bool<br>**same**( span<T1,E1> const & l, span<T2,E2> const & r) noexcept |
| &nbsp; |&nbsp;| &nbsp; |
| **Free functions** |&nbsp;| macro **`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB`** |
| &nbsp; | >= C++11 | template&lt;extent_t Count, class T><br>constexpr auto<br>**first**(T & t) ->... |
| &nbsp; | >= C++11 | template&lt;class T><br>constexpr auto<br>**first**(T & t, index_t count) ->... |
| &nbsp; | >= C++11 | template&lt;extent_t Count, class T><br>constexpr auto<br>**last**(T & t) ->... |
| &nbsp; | >= C++11 | template&lt;class T><br>constexpr auto<br>**last**(T & t, extent_t count) ->... |
| &nbsp; | >= C++11 | template&lt;index_t Offset, extent_t Count = dynamic_extent, class T><br>constexpr auto<br>**subspan**(T & t) ->... |
| &nbsp; | >= C++11 | template&lt;class T><br>constexpr auto<br>**subspan**(T & t, index_t offset, extent_t count = dynamic_extent) ->... |
| &nbsp; | &nbsp; | &nbsp; |
| **Free functions** |&nbsp;| macro **`span_FEATURE_MAKE_SPAN`**<br>macro **`span_FEATURE_MAKE_SPAN_TO_STD`** |
| &nbsp; | &nbsp; | template&lt;class T><br>constexpr span&lt;T><br>**make_span**(T \* first, T \* last) noexcept |
| &nbsp; | &nbsp; | template&lt;class T><br>constexpr span&lt;T><br>**make_span**(T \* ptr, index_t count) noexcept |
| &nbsp; | &nbsp; | template&lt;class T, size_t N><br>constexpr span&lt;T,N><br>**make_span**(T (&arr)[N]) noexcept |
| &nbsp; | >= C++11 | template&lt;class T, size_t N><br>constexpr span&lt;T,N><br>**make_span**(std::array&lt;T,N> & arr) noexcept |
| &nbsp; | >= C++11 | template&lt;class T, size_t N><br>constexpr span&lt;const T,N><br>**make_span**(std::array&lt;T,N > const & arr) noexcept |
| &nbsp; | >= C++11 | template&lt;class Container><br>constexpr auto<br>**make_span**(Container & cont) -><br>&emsp;span&lt;typename Container::value_type> noexcept |
| &nbsp; | >= C++11 | template&lt;class Container><br>constexpr auto<br>**make_span**(Container const & cont) -><br>&emsp;span&lt;const typename Container::value_type> noexcept |
| &nbsp; | &nbsp; | template&lt;class Container><br>span&lt;typename Container::value_type><br>**make_span**( with_container_t, Container & cont ) |
| &nbsp; | &nbsp; | template&lt;class Container><br>span&lt;const typename Container::value_type><br>**make_span**( with_container_t, Container const & cont ) |
| &nbsp; | < C++11 | template&lt;class T, Allocator><br>span&lt;T><br>**make_span**(std::vector&lt;T, Allocator> & cont) |
| &nbsp; | < C++11 | template&lt;class T, Allocator><br>span&lt;const T><br>**make_span**(std::vector&lt;T, Allocator> const & cont) |
| &nbsp; | &nbsp; | &nbsp; |
| **Free functions** |&nbsp;| macro **`span_FEATURE_BYTE_SPAN`** |
| &nbsp; | >= C++11 | template&lt;class T><br>span&lt;T, sizeof(T)><br>**byte_span**(T & t) |
| &nbsp; | >= C++11 | template&lt;class T><br>span&lt;const T, sizeof(T)><br>**byte_span**(T const & t) |
## Configuration
### Tweak header
If the compiler supports [`__has_include()`](https://en.cppreference.com/w/cpp/preprocessor/include), *span lite* supports the [tweak header](https://vector-of-bool.github.io/2020/10/04/lib-configuration.html) mechanism. Provide your *tweak header* as `nonstd/span.tweak.hpp` in a folder in the include-search-path. In the tweak header, provide definitions as documented below, like `#define span_CONFIG_NO_EXCEPTIONS 1`.
### Standard selection macro
\-D<b>span\_CPLUSPLUS</b>=199711L
Define this macro to override the auto-detection of the supported C++ standard, if your compiler does not set the `__cplusplus` macro correctly.
### Select `std::span` or `nonstd::span`
At default, *span lite* uses `std::span` if it is available and lets you use it via namespace `nonstd`. You can however override this default and explicitly request to use `std::span` or span lite's `nonstd::span` as `nonstd::span` via the following macros.
-D<b>span\_CONFIG\_SELECT\_SPAN</b>=span_SPAN_DEFAULT
Define this to `span_SPAN_STD` to select `std::span` as `nonstd::span`. Define this to `span_SPAN_NONSTD` to select `nonstd::span` as `nonstd::span`. Default is undefined, which has the same effect as defining to `span_SPAN_DEFAULT`.
### Select extent type
-D<b>span_CONFIG_EXTENT_TYPE</b>=std::size_t
Define this to `std::ptrdiff_t` to use the signed type. The default is `std::size_t`, as in C++20 (since v0.7.0).
### Select size type
-D<b>span_CONFIG_SIZE_TYPE</b>=std::size_t
Define this to `std::ptrdiff_t` to use the signed type. The default is `std::size_t`, as in C++20 (since v0.7.0). Note `span_CONFIG_SIZE_TYPE` replaces `span_CONFIG_INDEX_TYPE` which is deprecated.
### Disable exceptions
-D<b>span_CONFIG_NO_EXCEPTIONS</b>=0
Define this to 1 if you want to compile without exceptions. If not defined, the header tries and detect if exceptions have been disabled (e.g. via `-fno-exceptions`). Disabling exceptions will force contract violation to use termination, see [contract violation macros](#contract-violation-response-macros). Default is undefined.
### Provide construction using `with_container_t`
-D<b>span_FEATURE_WITH_CONTAINER</b>=0
Define this to 1 to enable constructing a span using `with_container_t`. Note that `span_FEATURE_WITH_CONTAINER` takes precedence over `span_FEATURE_WITH_CONTAINER_TO_STD`. Default is undefined.
-D<b>span_FEATURE_WITH_CONTAINER_TO_STD</b>=*n*
Define this to the highest C++ language version for which to enable constructing a span using `with_container_t`, like 98, 03, 11, 14, 17, 20. You can use 99 for inclusion with any standard, but prefer to use `span_FEATURE_WITH_CONTAINER` for this. Note that `span_FEATURE_WITH_CONTAINER` takes precedence over `span_FEATURE_WITH_CONTAINER_TO_STD`. Default is undefined.
### Provide construction from `std::array` with const data
-D<b>span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE</b>=0
Define this to 1 to enable constructing a span from a std::array with const data. Default is undefined.
### Provide `operator()` member function
-D<b>span_FEATURE_MEMBER_CALL_OPERATOR</b>=0
Define this to 1 to provide member function `operator()`for element access. It is equivalent to `operator[]` and has been marked `[[deprecated]]`. Its main purpose is to provide a migration path. Default is undefined.
### Provide `at()` member function
-D<b>span_FEATURE_MEMBER_AT</b>=0
Define this to 1 to provide member function `at()`. Define this to 2 to include index and size in message of std::out_of_range exception. Default is undefined.
### Provide `back()` and `front()` member functions
-D<b>span_FEATURE_MEMBER_BACK_FRONT</b>=1 _(on since v0.5.0)_
Define this to 0 to omit member functions `back()` and `front()`. Default is undefined.
### Provide `swap()` member function
-D<b>span_FEATURE_MEMBER_SWAP</b>=0
Define this to 1 to provide member function `swap()`. Default is undefined.
### Provide `operator==()` and other comparison functions
-D<b>span_FEATURE_COMPARISON</b>=0
Define this to 1 to include the comparison functions to compare the content of two spans. C++20's span does not provide comparison and _span lite_ omits comparison from v0.7.0. Default is undefined.
### Provide `same()` function
-D<b>span_FEATURE_SAME</b>=0
Define this to 1 to provide function `same()` to test if two spans refer as identical spans to the same data via the same type. If `same()` is enabled, `operator==()` incorporates it in its comparison. Default is undefined.
### Provide `first()`, `last()` and `subspan()` functions
-D<b>span_FEATURE_NON_MEMBER_FIRST_LAST_SUB</b>=0
Define this to 1 to provide functions `first()`, `last()` and `subspan()`. This implies `span_FEATURE_MAKE_SPAN` to provide functions `make_span()` that are required for this feature. Default is undefined.
### Provide `make_span()` functions
-D<b>span_FEATURE_MAKE_SPAN</b>=0
Define this to 1 to provide creator functions `nonstd::make_span()`. This feature is implied by using `span_FEATURE_NON_MEMBER_FIRST_LAST_SUB=1`. Note that `span_FEATURE_MAKE_SPAN` takes precedence over `span_FEATURE_MAKE_SPAN_TO_STD`. Default is undefined.
-D<b>span_FEATURE_MAKE_SPAN_TO_STD</b>=*n*
Define this to the highest C++ language version for which to provide creator functions `nonstd::make_span()`, like 98, 03, 11, 14, 17, 20. You can use 99 for inclusion with any standard, but prefer to use `span_FEATURE_MAKE_SPAN` for this. Note that `span_FEATURE_MAKE_SPAN` takes precedence over `span_FEATURE_MAKE_SPAN_TO_STD`. Default is undefined.
### Provide `byte_span()` functions
-D<b>span_FEATURE_BYTE_SPAN</b>=0
Define this to 1 to provide creator functions `nonstd::byte_span()`. Default is undefined.
### Contract violation response macros
*span-lite* provides contract violation response control as suggested in proposal [N4415](http://wg21.link/n4415).
\-D<b>span\_CONFIG\_CONTRACT\_LEVEL\_ON</b> (*default*)
Define this macro to include both `span_EXPECTS` and `span_ENSURES` in the code. This is the default case.
\-D<b>span\_CONFIG\_CONTRACT\_LEVEL\_OFF</b>
Define this macro to exclude both `span_EXPECTS` and `span_ENSURES` from the code.
\-D<b>span\_CONFIG_CONTRACT\_LEVEL\_EXPECTS\_ONLY</b>
Define this macro to include `span_EXPECTS` in the code and exclude `span_ENSURES` from the code.
\-D<b>span\_CONFIG\_CONTRACT\_LEVEL\_ENSURES\_ONLY</b>
Define this macro to exclude `span_EXPECTS` from the code and include `span_ENSURES` in the code.
\-D<b>span\_CONFIG\_CONTRACT\_VIOLATION\_TERMINATES</b> (*default*)
Define this macro to call `std::terminate()` on a contract violation in `span_EXPECTS`, `span_ENSURES`. This is the default case.
\-D<b>span\_CONFIG\_CONTRACT\_VIOLATION\_THROWS</b>
Define this macro to throw an exception of implementation-defined type that is derived from `std::runtime_exception` instead of calling `std::terminate()` on a contract violation in `span_EXPECTS` and `span_ENSURES`. See also [disable exceptions](#disable-exceptions).
Reported to work with
--------------------
The table below mentions the compiler versions *span lite* is reported to work with.
OS | Compiler | Where | Versions |
------------:|:-----------|:--------|:---------|
**GNU/Linux**| Clang/LLVM | Travis | 3.5.0, 3.6.2, 3.7.1, 3.8.0, 3.9.1, 4.0.1 |
&nbsp; | GCC | Travis | 5.5.0, 6.4.0, 7.3.0 |
**OS X** | ? | Local | ? |
**Windows** | Clang/LLVM | Local | 6.0.0 |
&nbsp; | GCC | Local | 7.2.0 |
&nbsp; | Visual C++<br>(Visual Studio)| Local | 8 (2005), 10 (2010), 11 (2012),<br>12 (2013), 14 (2015), 15 (2017) |
&nbsp; | Visual C++<br>(Visual Studio)| AppVeyor | 10 (2010), 11 (2012),<br>12 (2013), 14 (2015), 15 (2017) |
## Building the tests
To build the tests you need:
- [CMake](http://cmake.org), version 3.0 or later to be installed and in your PATH.
- A [suitable compiler](#reported-to-work-with).
The [*lest* test framework](https://github.com/martinmoene/lest) is included in the [test folder](test).
The following steps assume that the [*span lite* source code](https://github.com/martinmoene/span-lite) has been cloned into a directory named `./span-lite`.
1. Create a directory for the build outputs.
cd ./span-lite
md build && cd build
2. Configure CMake to use the compiler of your choice (run `cmake --help` for a list).
cmake -G "Unix Makefiles" -DSPAN_LITE_OPT_BUILD_TESTS=ON ..
3. Optional. You can control above configuration through the following options:
`-DSPAN_LITE_OPT_BUILD_TESTS=ON`: build the tests for span, default off
`-DSPAN_LITE_OPT_BUILD_EXAMPLES=OFF`: build the examples, default off
4. Build the test suite.
cmake --build .
5. Run the test suite.
ctest -V
All tests should pass, indicating your platform is supported and you are ready to use *span lite*.
## Other implementations of span
- *gsl-lite* [span](https://github.com/martinmoene/gsl-lite/blob/73c4f16f2b35fc174fc2f09d44d5ab13e5c638c3/include/gsl/gsl-lite.hpp#L1221).
- Microsoft GSL [span](https://github.com/Microsoft/GSL/blob/master/include/gsl/span).
- Google Abseil [span](https://github.com/abseil/abseil-cpp/blob/master/absl/types/span.h).
- Marshall Clow's [libc++ span snippet](https://github.com/mclow/snippets/blob/master/span.cpp).
- Tristan Brindle's [Implementation of C++20's std::span for older compilers](https://github.com/tcbrindle/span).
- [Search _span c++_ on GitHub](https://github.com/search?l=C%2B%2B&q=span+c%2B%2B&type=Repositories&utf8=%E2%9C%93).
## Notes and references
*Interface and specification*
- [span on cppreference](https://en.cppreference.com/w/cpp/container/span).
- [p0122 - C++20 Proposal](http://wg21.link/p0122).
- [span in C++20 Working Draft](http://eel.is/c++draft/views).
*Presentations*
- TBD
*Proposals*
- [p0122 - span: bounds-safe views for sequences of objects](http://wg21.link/p0122).
- [p1024 - Usability Enhancements for std::span](http://wg21.link/p1024).
- [p1419 - A SFINAE-friendly trait to determine the extent of statically sized containers](http://wg21.link/p1419).
- [p0805 - Comparing Containers](http://wg21.link/p0805).
- [p1085 - Should Span be Regular?](http://wg21.link/p0805).
- [p0091 - Template argument deduction for class templates](http://wg21.link/p0091).
- [p0856 - Restrict Access Property for mdspan and span](http://wg21.link/p0856).
- [p1428 - Subscripts and sizes should be signed](http://wg21.link/p1428).
- [p1089 - Sizes Should Only span Unsigned](http://wg21.link/p1089).
- [p1227 - Signed size() functions](http://wg21.link/p1227).
- [p1872 - span should have size_type, not index_type](http://wg21.link/p1872).
- [lwg 3101 - span's Container constructors need another constraint](https://cplusplus.github.io/LWG/issue3101).
- [Reddit - 2018-06 Rapperswil ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/8prqzm/2018_rapperswil_iso_c_committee_trip_report/)
- [Reddit - 2018-11 San Diego ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/9vwvbz/2018_san_diego_iso_c_committee_trip_report_ranges/).
- [Reddit - 2019-02 Kona ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/au0c4x/201902_kona_iso_c_committee_trip_report_c20/).
- [Reddit - 2019-07 Cologne ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/cfk9de/201907_cologne_iso_c_committee_trip_report_the/)
- [Reddit - 2019-11 Belfast ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/dtuov8/201911_belfast_iso_c_committee_trip_report/)
- <a id="regtyp"></a>Titus Winters. [Revisiting Regular Types](https://abseil.io/blog/20180531-regular-types). Abseil Blog. 31 May 2018.
## Appendix
### A.1 Compile-time information
The version of *span lite* is available via tag `[.version]`. The following tags are available for information on the compiler and on the C++ standard library used: `[.compiler]`, `[.stdc++]`, `[.stdlanguage]` and `[.stdlibrary]`.
### A.2 Span lite test specification
<details>
<summary>click to expand</summary>
<p>
```Text
span<>: Terminates construction from a nullptr and a non-zero size (C++11)
span<>: Terminates construction from two pointers in the wrong order
span<>: Terminates construction from a null pointer and a non-zero size
span<>: Terminates creation of a sub span of the first n elements for n exceeding the span
span<>: Terminates creation of a sub span of the last n elements for n exceeding the span
span<>: Terminates creation of a sub span outside the span
span<>: Terminates access outside the span
span<>: Throws on access outside the span via at(): std::out_of_range [span_FEATURE_MEMBER_AT>0][span_CONFIG_NO_EXCEPTIONS=0]
span<>: Termination throws std::logic_error-derived exception [span_CONFIG_CONTRACT_VIOLATION_THROWS=1]
span<>: Allows to default-construct
span<>: Allows to construct from a nullptr and a zero size (C++11)
span<>: Allows to construct from two pointers
span<>: Allows to construct from two iterators
span<>: Allows to construct from two iterators - empty range
span<>: Allows to construct from an iterator and a size
span<>: Allows to construct from an iterator and a size - empty range
span<>: Allows to construct from two pointers to const
span<>: Allows to construct from a non-null pointer and a size
span<>: Allows to construct from a non-null pointer to const and a size
span<>: Allows to construct from a temporary pointer and a size
span<>: Allows to construct from a temporary pointer to const and a size
span<>: Allows to construct from any pointer and a zero size (C++98)
span<>: Allows to construct from a pointer and a size via a deduction guide (C++17)
span<>: Allows to construct from an iterator and a size via a deduction guide (C++17)
span<>: Allows to construct from two iterators via a deduction guide (C++17)
span<>: Allows to construct from a C-array
span<>: Allows to construct from a C-array via a deduction guide (C++17)
span<>: Allows to construct from a const C-array
span<>: Allows to construct from a C-array with size via decay to pointer (potentially dangerous)
span<>: Allows to construct from a const C-array with size via decay to pointer (potentially dangerous)
span<>: Allows to construct from a std::initializer_list<> (C++11)
span<>: Allows to construct from a std::array<> (C++11)
span<>: Allows to construct from a std::array via a deduction guide (C++17)
span<>: Allows to construct from a std::array<> with const data (C++11, span_FEATURE_CONSTR..._ELEMENT_TYPE=1)
span<>: Allows to construct from an empty std::array<> (C++11)
span<>: Allows to construct from a container (std::vector<>)
span<>: Allows to construct from a container via a deduction guide (std::vector<>, C++17)
span<>: Allows to tag-construct from a container (std::vector<>)
span<>: Allows to tag-construct from a const container (std::vector<>)
span<>: Allows to copy-construct from another span of the same type
span<>: Allows to copy-construct from another span of a compatible type
span<>: Allows to copy-construct from a temporary span of the same type (C++11)
span<>: Allows to copy-assign from another span of the same type
span<>: Allows to copy-assign from a temporary span of the same type (C++11)
span<>: Allows to create a sub span of the first n elements
span<>: Allows to create a sub span of the last n elements
span<>: Allows to create a sub span starting at a given offset
span<>: Allows to create a sub span starting at a given offset with a given length
span<>: Allows to observe an element via array indexing
span<>: Allows to observe an element via call indexing
span<>: Allows to observe an element via at() [span_FEATURE_MEMBER_AT>0]
span<>: Allows to observe an element via data()
span<>: Allows to observe the first element via front() [span_FEATURE_MEMBER_BACK_FRONT=1]
span<>: Allows to observe the last element via back() [span_FEATURE_MEMBER_BACK_FRONT=1]
span<>: Allows to change an element via array indexing
span<>: Allows to change an element via call indexing
span<>: Allows to change an element via at() [span_FEATURE_MEMBER_AT>0]
span<>: Allows to change an element via data()
span<>: Allows to change the first element via front() [span_FEATURE_MEMBER_BACK_FRONT=1]
span<>: Allows to change the last element via back() [span_FEATURE_MEMBER_BACK_FRONT=1]
span<>: Allows to swap with another span [span_FEATURE_MEMBER_SWAP=1]
span<>: Allows forward iteration
span<>: Allows const forward iteration
span<>: Allows reverse iteration
span<>: Allows const reverse iteration
span<>: Allows to identify if a span is the same as another span [span_FEATURE_SAME=1]
span<>: Allows to compare equal to another span of the same type [span_FEATURE_COMPARISON=1]
span<>: Allows to compare unequal to another span of the same type [span_FEATURE_COMPARISON=1]
span<>: Allows to compare less than another span of the same type [span_FEATURE_COMPARISON=1]
span<>: Allows to compare less than or equal to another span of the same type [span_FEATURE_COMPARISON=1]
span<>: Allows to compare greater than another span of the same type [span_FEATURE_COMPARISON=1]
span<>: Allows to compare greater than or equal to another span of the same type [span_FEATURE_COMPARISON=1]
span<>: Allows to compare to another span of the same type and different cv-ness [span_FEATURE_SAME=0]
span<>: Allows to compare empty spans as equal [span_FEATURE_COMPARISON=1]
span<>: Allows to test for empty span via empty(), empty case
span<>: Allows to test for empty span via empty(), non-empty case
span<>: Allows to obtain the number of elements via size()
span<>: Allows to obtain the number of elements via ssize()
span<>: Allows to obtain the number of bytes via size_bytes()
span<>: Allows to view the elements as read-only bytes
span<>: Allows to view and change the elements as writable bytes
make_span() [span_FEATURE_MAKE_SPAN_TO_STD=99]
make_span(): Allows building from two pointers
make_span(): Allows building from two const pointers
make_span(): Allows building from a non-null pointer and a size
make_span(): Allows building from a non-null const pointer and a size
make_span(): Allows building from a C-array
make_span(): Allows building from a const C-array
make_span(): Allows building from a std::initializer_list<> (C++11)
make_span(): Allows building from a std::array<> (C++11)
make_span(): Allows building from a const std::array<> (C++11)
make_span(): Allows building from a container (std::vector<>)
make_span(): Allows building from a const container (std::vector<>)
make_span(): Allows building from a container (with_container_t, std::vector<>)
make_span(): Allows building from a const container (with_container_t, std::vector<>)
byte_span() [span_FEATURE_BYTE_SPAN=1]
byte_span(): Allows building a span of std::byte from a single object (C++17, byte-lite)
byte_span(): Allows building a span of const std::byte from a single const object (C++17, byte-lite)
first(), last(), subspan() [span_FEATURE_NON_MEMBER_FIRST_LAST_SUB=1]
first(): Allows to create a sub span of the first n elements
last(): Allows to create a sub span of the last n elements
subspan(): Allows to create a sub span starting at a given offset
size(): Allows to obtain the number of elements via size()
ssize(): Allows to obtain the number of elements via ssize()
tuple_size<>: Allows to obtain the number of elements via std::tuple_size<> (C++11)
tuple_element<>: Allows to obtain an element via std::tuple_element<> (C++11)
tuple_element<>: Allows to obtain an element via std::tuple_element_t<> (C++11)
get<I>(spn): Allows to access an element via std::get<>()
tweak header: reads tweak header if supported [tweak]
```
</p>
</details>

File diff suppressed because it is too large Load Diff

Binary file not shown.

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,122 @@
/*
* 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 kRequiredZmFntVersion = 1;
font = new ZMFONT;
constexpr uint8 FontVariant::kMaxNumCodePoints;
constexpr uint8 FontVariant::kMaxCharHeight;
constexpr uint8 FontVariant::kMaxCharWidth;
size_t header_size = 8 + (sizeof(ZMFONT_BH) * NUM_FONT_SIZES);
FontVariant::FontVariant() : char_height_(0), char_width_(0), char_padding_(0), codepoint_count_(0) {}
// 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, uint8 char_padding, std::vector<uint64> bitmap)
: char_height_(char_height), char_width_(char_width), char_padding_(char_padding), 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.read(reinterpret_cast<char *>(&header.version), sizeof(header.version));
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 || file_header.version != kRequiredZmFntVersion) {
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, bitmap_header.char_padding, 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,93 @@
/*
* 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
uint8 char_padding; // padding around characters
uint8 pad[3]; // struct padding
};
#pragma pack(pop)
#pragma pack(push, 1)
struct FontFileHeader {
char magic[6]; // "ZMFNT\0"
uint8 version;
uint8 pad;
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, uint8 char_padding, std::vector<uint64> bitmap);
uint16 GetCharHeight() const { return char_height_; }
uint16 GetCharWidth() const { return char_width_; }
uint8 GetCharPadding() const { return char_padding_; }
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 char_padding_;
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

@ -130,7 +130,6 @@ Image::Image() :
buffer = 0;
buffertype = ZM_BUFTYPE_DONTFREE;
holdbuffer = 0;
text[0] = '\0';
blend = fptr_blend;
}
@ -150,7 +149,6 @@ Image::Image(const char *filename) {
buffertype = ZM_BUFTYPE_DONTFREE;
holdbuffer = 0;
ReadJpeg(filename, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
text[0] = '\0';
update_function_pointers();
}
@ -176,7 +174,6 @@ Image::Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint
} else {
AllocImgBuffer(size);
}
text[0] = '\0';
imagePixFormat = AVPixFormat();
update_function_pointers();
@ -204,14 +201,12 @@ Image::Image(int p_width, int p_linesize, int p_height, int p_colours, int p_sub
} else {
AllocImgBuffer(size);
}
text[0] = '\0';
imagePixFormat = AVPixFormat();
update_function_pointers();
}
Image::Image(const AVFrame *frame) {
text[0] = '\0';
width = frame->width;
height = frame->height;
pixels = width*height;
@ -336,7 +331,7 @@ Image::Image(const Image &p_image) {
holdbuffer = 0;
AllocImgBuffer(size);
(*fptr_imgbufcpy)(buffer, p_image.buffer, size);
strncpy(text, p_image.text, sizeof(text));
annotation_ = p_image.annotation_;
imagePixFormat = p_image.imagePixFormat;
update_function_pointers();
}
@ -564,10 +559,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;
@ -1191,8 +1186,8 @@ cinfo->out_color_space = JCS_RGB;
cinfo->dct_method = JDCT_FASTEST;
jpeg_start_compress(cinfo, TRUE);
if ( config.add_jpeg_comments && text[0] ) {
jpeg_write_marker(cinfo, JPEG_COM, (const JOCTET *)text, strlen(text));
if ( config.add_jpeg_comments && !annotation_.empty() ) {
jpeg_write_marker(cinfo, JPEG_COM, reinterpret_cast<const JOCTET *>(annotation_.c_str()), annotation_.size());
}
// If we have a non-zero time (meaning a parameter was passed in), then form a simple exif segment with that time as DateTimeOriginal and SubsecTimeOriginal
// No timestamp just leave off the exif section.
@ -1943,9 +1938,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);
@ -1997,144 +1992,122 @@ void Image::MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour
https://lemire.me/blog/2018/02/21/iterating-over-set-bits-quickly/
*/
void Image::Annotate(
const char *p_text,
const std::string &text,
const Coord &coord,
const unsigned int size,
const uint8 size,
const Rgb fg_colour,
const Rgb bg_colour) {
strncpy(text, p_text, sizeof(text)-1);
annotation_ = text;
unsigned int index = 0;
unsigned int line_no = 0;
unsigned int text_len = strlen(text);
unsigned int line_len = 0;
const char *line = text;
const uint8_t fg_r_col = RED_VAL_RGBA(fg_colour);
const uint8_t fg_g_col = GREEN_VAL_RGBA(fg_colour);
const uint8_t fg_b_col = BLUE_VAL_RGBA(fg_colour);
const uint8_t fg_bw_col = fg_colour & 0xff;
const Rgb fg_rgb_col = rgb_convert(fg_colour, subpixelorder);
const uint8_t bg_r_col = RED_VAL_RGBA(bg_colour);
const uint8_t bg_g_col = GREEN_VAL_RGBA(bg_colour);
const uint8_t bg_b_col = BLUE_VAL_RGBA(bg_colour);
const uint8_t bg_bw_col = bg_colour & 0xff;
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();
Debug(4, "Font size %d, char_width %d char_height %d", size, char_width, char_height);
FontVariant const &font_variant = font.GetFontVariant(size - 1);
const uint16 char_width = font_variant.GetCharWidth();
const uint16 char_height = font_variant.GetCharHeight();
while ( (index < text_len) && (line_len = strcspn(line, "\n")) ) {
unsigned int line_width = line_len * char_width;
std::vector<std::string> lines = Split(annotation_, '\n');
std::size_t max_line_length = 0;
for (const std::string &s : lines) {
max_line_length = std::max(max_line_length, s.size());
}
unsigned int lo_line_x = coord.X();
unsigned int lo_line_y = coord.Y() + (line_no * char_height);
uint32 x0_max = width - (max_line_length * char_width);
uint32 y0_max = height - (lines.size() * char_height);
unsigned int min_line_x = 0;
// FIXME What if line_width > width?
unsigned int max_line_x = width - line_width;
unsigned int min_line_y = 0;
unsigned int max_line_y = height - char_height;
// Calculate initial coordinates of annotation so that everything is displayed even if the
// user set coordinates would prevent that.
uint32 x0 = ZM::clamp(static_cast<uint32>(coord.X()), 0u, x0_max);
uint32 y0 = ZM::clamp(static_cast<uint32>(coord.Y()), 0u, y0_max);
if ( lo_line_x > max_line_x )
lo_line_x = max_line_x;
if ( lo_line_x < min_line_x )
lo_line_x = min_line_x;
if ( lo_line_y > max_line_y )
lo_line_y = max_line_y;
if ( lo_line_y < min_line_y )
lo_line_y = min_line_y;
uint32 y = y0;
for (const std::string &line : lines) {
uint32 x = x0;
unsigned int hi_line_x = lo_line_x + line_width;
unsigned int hi_line_y = lo_line_y + char_height;
// Clip anything that runs off the right of the screen
if ( hi_line_x > width )
hi_line_x = width;
if ( hi_line_y > height )
hi_line_y = height;
if ( colours == ZM_COLOUR_GRAY8 ) {
unsigned char *ptr = &buffer[(lo_line_y*width)+lo_line_x];
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];
if ( !bg_trans ) memset(temp_ptr, bg_bw_col, char_width);
while ( f != 0 ) {
uint64_t t = f & -f;
int idx = char_width - __builtin_ctzll(f) + size;
*(temp_ptr + idx) = fg_bw_col;
f ^= t;
if (colours == ZM_COLOUR_GRAY8) {
uint8 *ptr = &buffer[(y * width) + x0];
for (char c : line) {
for (uint64 cp_row : font_variant.GetCodepoint(c)) {
if (bg_colour != kRGBTransparent) {
std::fill(ptr, ptr + char_width, static_cast<uint8>(bg_colour & 0xff));
}
temp_ptr += char_width;
while (cp_row != 0) {
int column_idx = char_width - __builtin_ctzll(cp_row) + font_variant.GetCharPadding();
*(ptr + column_idx) = fg_colour & 0xff;
cp_row = cp_row & (cp_row - 1);
}
}
ptr -= (width * char_height);
ptr += char_width;
x += char_width;
if (x >= width) {
break;
}
}
} else if ( colours == ZM_COLOUR_RGB24 ) {
unsigned int wc = width * colours;
unsigned char *ptr = &buffer[((lo_line_y*width)+lo_line_x)*colours];
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];
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);
RED_PTR_RGBA(colour_ptr) = bg_r_col;
GREEN_PTR_RGBA(colour_ptr) = bg_g_col;
BLUE_PTR_RGBA(colour_ptr) = bg_b_col;
} else if (colours == ZM_COLOUR_RGB24) {
constexpr uint8 bytesPerPixel = 3;
uint8 *ptr = &buffer[((y * width) + x0) * bytesPerPixel];
for (char c : line) {
for (uint64 cp_row : font_variant.GetCodepoint(c)) {
if (bg_colour != kRGBTransparent) {
for (int i = 0; i < char_width; i++) { // We need to set individual r,g,b components
uint8 *colour_ptr = ptr + (i * bytesPerPixel);
RED_PTR_RGBA(colour_ptr) = RED_VAL_RGBA(bg_colour);
GREEN_PTR_RGBA(colour_ptr) = GREEN_VAL_RGBA(bg_colour);
BLUE_PTR_RGBA(colour_ptr) = BLUE_VAL_RGBA(bg_colour);
}
}
while ( f != 0 ) {
uint64_t t = f & -f;
int idx = char_width - __builtin_ctzll(f) + size;
unsigned char *colour_ptr = temp_ptr + (idx*3);
RED_PTR_RGBA(colour_ptr) = fg_r_col;
GREEN_PTR_RGBA(colour_ptr) = fg_g_col;
BLUE_PTR_RGBA(colour_ptr) = fg_b_col;
f ^= t;
while (cp_row != 0) {
int column_idx = char_width - __builtin_ctzll(cp_row) + font_variant.GetCharPadding();
uint8 *colour_ptr = ptr + (column_idx * bytesPerPixel);
RED_PTR_RGBA(colour_ptr) = RED_VAL_RGBA(fg_colour);
GREEN_PTR_RGBA(colour_ptr) = GREEN_VAL_RGBA(fg_colour);
BLUE_PTR_RGBA(colour_ptr) = BLUE_VAL_RGBA(fg_colour);
cp_row = cp_row & (cp_row - 1);
}
temp_ptr += char_width * colours;
}
ptr -= (width * char_height * bytesPerPixel);
ptr += char_width * bytesPerPixel;
x += char_width;
if (x >= width) {
break;
}
}
} else if ( colours == ZM_COLOUR_RGB32 ) {
unsigned int wc = width * colours;
} else if (colours == ZM_COLOUR_RGB32) {
constexpr uint8 bytesPerPixel = 4;
Rgb *ptr = reinterpret_cast<Rgb *>(&buffer[((y * width) + x0) * bytesPerPixel]);
uint8_t *ptr = &buffer[((lo_line_y*width)+lo_line_x) << 2];
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];
if ( !bg_trans ) {
for ( int i = 0; i < char_width; i++ )
*(temp_ptr + i) = bg_rgb_col;
for (char c : line) {
for (uint64 cp_row : font_variant.GetCodepoint(c)) {
if (bg_colour != kRGBTransparent) {
std::fill(ptr, ptr + char_width, bg_rgb_col);
}
while ( f != 0 ) {
uint64_t t = f & -f;
int idx = char_width - __builtin_ctzll(f) + size;
*(temp_ptr + idx) = fg_rgb_col;
f ^= t;
while (cp_row != 0) {
uint32 column_idx = char_width - __builtin_ctzll(cp_row) + font_variant.GetCharPadding();
*(ptr + column_idx) = fg_rgb_col;
cp_row = cp_row & (cp_row - 1);
}
temp_ptr += char_width;
ptr += width;
}
ptr -= (width * char_height);
ptr += char_width;
x += char_width;
if (x >= width) {
break;
}
}
} else {
Error("Annotate called with unexpected colours: %d", colours);
return;
}
index += line_len;
while ( text[index] == '\n' ) {
index++;
y += char_height;
if (y >= height) {
break;
}
line = text+index;
line_no++;
}
}

View File

@ -115,7 +115,7 @@ class Image {
inline void AllocImgBuffer(size_t p_bufsize) {
if ( buffer )
if ( buffer )
DumpImgBuffer();
buffer = AllocBuffer(p_bufsize);
@ -152,7 +152,7 @@ class Image {
uint8_t *buffer;
int buffertype; /* 0=not ours, no need to call free(), 1=malloc() buffer, 2=new buffer */
int holdbuffer; /* Hold the buffer instead of replacing it with new one */
char text[1024];
std::string annotation_;
public:
Image();
@ -254,7 +254,7 @@ class Image {
bool ReadJpeg(const char *filename, unsigned int p_colours, unsigned int p_subpixelorder);
bool WriteJpeg(const char *filename) const;
bool WriteJpeg(const char *filename, bool on_blocking_abort) const;
bool WriteJpeg(const char *filename, bool on_blocking_abort) const;
bool WriteJpeg(const char *filename, int quality_override) const;
bool WriteJpeg(const char *filename, struct timeval timestamp) const;
bool WriteJpeg(const char *filename, int quality_override, struct timeval timestamp) const;
@ -282,7 +282,11 @@ class Image {
const Coord centreCoord(const char *text, const int size) const;
void MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour=0x00222222 );
void Annotate(const char *p_text, const Coord &coord, unsigned int size = 1, Rgb fg_colour = kRGBWhite, Rgb bg_colour = kRGBBlack);
void Annotate(const std::string &text,
const Coord &coord,
uint8 size = 1,
Rgb fg_colour = kRGBWhite,
Rgb bg_colour = kRGBBlack);
Image *HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits=0 );
//Image *HighlightEdges( Rgb colour, const Polygon &polygon );
void Timestamp( const char *label, const time_t when, const Coord &coord, const int size );

View File

@ -23,6 +23,7 @@
#include <algorithm>
#include <chrono>
#include <ctime>
#include <functional>
#include <map>
#include <memory>
#include <stdexcept>
@ -87,6 +88,16 @@ typename std::enable_if<std::is_array<T>::value && std::extent<T>::value == 0, s
template<typename T, typename... Args>
inline auto make_unique(Args &&...) ->
typename std::enable_if<std::extent<T>::value != 0, void>::type = delete;
template<class T, class Compare>
constexpr const T &clamp(const T &v, const T &lo, const T &hi, Compare comp) {
return comp(v, lo) ? lo : comp(hi, v) ? hi : v;
}
template<class T>
constexpr const T &clamp(const T &v, const T &lo, const T &hi) {
return clamp(v, lo, hi, std::less<T>{});
}
}
typedef std::chrono::microseconds Microseconds;

View File

@ -14,6 +14,7 @@ include(Catch)
set(TEST_SOURCES
zm_comms.cpp
zm_crypt.cpp
zm_font.cpp
zm_utils.cpp)
add_executable(tests main.cpp ${TEST_SOURCES})
@ -31,3 +32,9 @@ target_include_directories(tests
${CMAKE_CURRENT_BINARY_DIR})
catch_discover_tests(tests)
add_custom_command(TARGET tests
PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/data/ ${CMAKE_CURRENT_BINARY_DIR}/data/
BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/data/)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,57 @@
#!/bin/python
import struct
GOOD_MAGIC = b"ZMFNT\0"
BAD_MAGIC = b"ABCDE\0"
NUM_FONT_SIZES = 4
ZMFNT_VERSION = 1
class FontFile:
def __init__(self, path):
self.path = path
def write_file_header(self, magic):
with open(self.path, "wb") as f:
f.write(magic)
f.write(struct.pack("B", ZMFNT_VERSION))
f.write(struct.pack("B", 0)) # pad
def write_bm_header(self, height, width, cp_count, idx, padding):
with open(self.path, "ab") as f:
f.write(struct.pack("HHIIBBBB", height, width, cp_count, idx, padding, 0, 0, 0))
def write_codepoints(self, value, height, count):
with open(self.path, "ab") as f:
for _ in range(height * count):
f.write(struct.pack("Q", value))
font = FontFile("01_bad_magic.zmfnt")
font.write_file_header(BAD_MAGIC)
# height, width and number of codepoints out of bounds
font = FontFile("02_variant_invalid.zmfnt")
font.write_file_header(GOOD_MAGIC)
font.write_bm_header(201, 65, 256, 0, 2)
# mismatch between number of codepoints specified in header and actually stored ones
font = FontFile("03_missing_cps.zmfnt")
font.write_file_header(GOOD_MAGIC)
offs = 0
for _ in range(NUM_FONT_SIZES):
font.write_bm_header(10, 10, 10, offs, 2)
offs += 10 * 10
for _ in range(NUM_FONT_SIZES):
font.write_codepoints(1, 10, 9)
font = FontFile("04_valid.zmfnt")
font.write_file_header(GOOD_MAGIC)
offs = 0
for i in range(NUM_FONT_SIZES):
font.write_bm_header(10 + i, 10 + i, 10, offs, 2)
offs += 10 * (10 + i)
for i in range(NUM_FONT_SIZES):
font.write_codepoints(i, 10 + i, 10)

161
tests/zm_font.cpp Normal file
View File

@ -0,0 +1,161 @@
/*
* 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 "catch2/catch.hpp"
#include "zm_font.h"
CATCH_REGISTER_ENUM(FontLoadError,
FontLoadError::kOk,
FontLoadError::kFileNotFound,
FontLoadError::kInvalidFile)
TEST_CASE("FontVariant: construction") {
FontVariant variant;
SECTION("default construction") {
REQUIRE(variant.GetCharHeight() == 0);
REQUIRE(variant.GetCharWidth() == 0);
}
SECTION("values in range") {
constexpr uint8 height = 10;
constexpr uint8 width = 10;
constexpr uint8 padding = 2;
std::vector<uint64> bitmap(FontVariant::kMaxNumCodePoints * height);
REQUIRE_NOTHROW(variant = FontVariant(height, width, padding, bitmap));
REQUIRE(variant.GetCharHeight() == height);
REQUIRE(variant.GetCharWidth() == width);
REQUIRE(variant.GetCodepointsCount() == FontVariant::kMaxNumCodePoints);
}
SECTION("height out of range") {
constexpr uint8 height = FontVariant::kMaxCharHeight + 1;
constexpr uint8 width = 10;
constexpr uint8 padding = 2;
std::vector<uint64> bitmap(FontVariant::kMaxNumCodePoints * height);
REQUIRE_THROWS(variant = FontVariant(height, width, padding, bitmap));
}
SECTION("width out of range") {
constexpr uint8 height = 10;
constexpr uint8 width = FontVariant::kMaxCharWidth + 1;
constexpr uint8 padding = 2;
std::vector<uint64> bitmap(FontVariant::kMaxNumCodePoints * height);
REQUIRE_THROWS(variant = FontVariant(height, width, padding, bitmap));
}
SECTION("bitmap of wrong size") {
constexpr uint8 height = 10;
constexpr uint8 width = 10;
constexpr uint8 padding = 2;
std::vector<uint64> bitmap(FontVariant::kMaxNumCodePoints * height + 1);
REQUIRE_THROWS(variant = FontVariant(height, width, padding, bitmap));
}
}
TEST_CASE("FontVariant: GetCodepoint") {
constexpr uint8 height = 10;
constexpr uint8 width = 10;
constexpr uint8 padding = 2;
std::vector<uint64> bitmap(FontVariant::kMaxNumCodePoints * height);
// fill bitmap for each codepoint alternating with 1 and std::numeric_limits<uint64>::max()
std::generate(bitmap.begin(), bitmap.end(),
[n = 0, zero = true]() mutable {
if (n == height) {
zero = !zero;
n = 0;
}
n++;
if (zero) {
return static_cast<uint64>(1);
} else {
return std::numeric_limits<uint64>::max();
}
});
FontVariant variant(height, width, padding, bitmap);
nonstd::span<const uint64> cp;
SECTION("in bounds") {
cp = variant.GetCodepoint(0);
REQUIRE(std::all_of(cp.begin(), cp.end(),
[](uint64 l) { return l == 1; }) == true);
cp = variant.GetCodepoint(1);
REQUIRE(std::all_of(cp.begin(), cp.end(),
[](uint64 l) { return l == std::numeric_limits<uint64>::max(); }) == true);
}
SECTION("out-of-bounds: all-zero bitmap") {
cp = variant.GetCodepoint(FontVariant::kMaxNumCodePoints);
REQUIRE(std::all_of(cp.begin(), cp.end(),
[](uint64 l) { return l == 0; }) == true);
}
}
TEST_CASE("ZmFont: variants not loaded") {
ZmFont font;
SECTION("returns empty variant") {
FontVariant variant;
REQUIRE_NOTHROW(variant = font.GetFontVariant(0));
REQUIRE(variant.GetCharHeight() == 0);
REQUIRE(variant.GetCharWidth() == 0);
REQUIRE(variant.GetCodepoint(0).empty() == true);
}
SECTION("variant idx out-of-bounds") {
REQUIRE_THROWS(font.GetFontVariant(kNumFontSizes));
}
}
TEST_CASE("ZmFont: load font file") {
ZmFont font;
SECTION("file not found") {
REQUIRE(font.LoadFontFile("does_not_exist.zmfnt") == FontLoadError::kFileNotFound);
}
SECTION("invalid files") {
REQUIRE(font.LoadFontFile("data/fonts/01_bad_magic.zmfnt") == FontLoadError::kInvalidFile);
REQUIRE(font.LoadFontFile("data/fonts/02_variant_invalid.zmfnt") == FontLoadError::kInvalidFile);
REQUIRE(font.LoadFontFile("data/fonts/03_missing_cps.zmfnt") == FontLoadError::kInvalidFile);
}
SECTION("valid file") {
REQUIRE(font.LoadFontFile("data/fonts/04_valid.zmfnt") == FontLoadError::kOk);
uint8 var_idx = GENERATE(range(static_cast<decltype(kNumFontSizes)>(0), kNumFontSizes));
FontVariant variant = font.GetFontVariant(var_idx);
REQUIRE(variant.GetCharHeight() == 10 + var_idx);
REQUIRE(variant.GetCharWidth() == 10 + var_idx);
uint8 cp_idx =
GENERATE_COPY(range(static_cast<decltype(variant.GetCodepointsCount())>(0), variant.GetCodepointsCount()));
nonstd::span<const uint64> cp = variant.GetCodepoint(cp_idx);
REQUIRE(std::all_of(cp.begin(), cp.end(),
[=](uint64 l) { return l == var_idx; }) == true);
}
}

View File

@ -150,6 +150,12 @@ TEST_CASE("Base64Encode") {
REQUIRE(Base64Encode("foobar") == "Zm9vYmFy");
}
TEST_CASE("ZM::clamp") {
REQUIRE(ZM::clamp(1, 0, 2) == 1);
REQUIRE(ZM::clamp(3, 0, 2) == 2);
REQUIRE(ZM::clamp(-1, 0, 2) == 0);
}
TEST_CASE("UriDecode") {
REQUIRE(UriDecode("abcABC123-_.~%21%28%29%26%3d%20") == "abcABC123-_.~!()&= ");
REQUIRE(UriDecode("abcABC123-_.~%21%28%29%26%3d+") == "abcABC123-_.~!()&= ");