commit
7bbcb7e36a
|
@ -1,3 +1,4 @@
|
|||
add_subdirectory(jwt-cpp)
|
||||
add_subdirectory(libbcrypt)
|
||||
add_subdirectory(RtspServer)
|
||||
add_subdirectory(span-lite)
|
||||
|
|
|
@ -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)
|
|
@ -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.
|
|
@ -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
|
||||
|
||||
[](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [](https://opensource.org/licenses/BSL-1.0) [](https://travis-ci.org/martinmoene/span-lite) [](https://ci.appveyor.com/project/martinmoene/span-lite) [](https://github.com/martinmoene/span-lite/releases) [](https://github.com/martinmoene/span-lite/blob/master/include/nonstd/span.hpp) [](https://conan.io/center/span-lite) [](https://wandbox.org/permlink/venR3Ko2Q4tlvcVk) [](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** | | macro **`span_FEATURE_WITH_CONTAINER`**<br>macro **`span_FEATURE_WITH_CONTAINER_TO_STD`** |
|
||||
| **Types** | | **with_container_t** type to disambiguate below constructors |
|
||||
| **Objects** | | **with_container** value to disambiguate below constructors |
|
||||
| **Constructors** | | macro **`span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE`**|
|
||||
| | | template<class Container><br>constexpr **span**(with_container_t, Container & cont) |
|
||||
| | | template<class Container><br>constexpr **span**(with_container_t, Container const & cont) |
|
||||
| | | |
|
||||
| **Methods** | | macro **`span_FEATURE_MEMBER_CALL_OPERATOR`** |
|
||||
| | | constexpr reference **operator()**(index_t idx) const<br>Equivalent to **operator[]**(), marked `[[deprecated]]` |
|
||||
| | | |
|
||||
| **Methods** | | macro **`span_FEATURE_MEMBER_AT`** |
|
||||
| | | constexpr reference **at**(index_t idx) const<br>May throw std::out_of_range exception |
|
||||
| | | |
|
||||
| **Methods** | | macro **`span_FEATURE_MEMBER_BACK_FRONT`** (on since v0.5.0) |
|
||||
| | | constexpr reference **back()** const noexcept |
|
||||
| | | constexpr reference **front()** const noexcept |
|
||||
| | | |
|
||||
| **Method** | | macro **`span_FEATURE_MEMBER_SWAP`** |
|
||||
| | | constexpr void **swap**(span & other) noexcept |
|
||||
| | | |
|
||||
| **Free functions** | | macro **`span_FEATURE_COMPARISON`** |
|
||||
|<br><br>== != < > <= >= | | template<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 |
|
||||
| | | |
|
||||
| **Free function** | | macro **`span_FEATURE_SAME`** |
|
||||
| | | template<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 |
|
||||
| | | |
|
||||
| **Free functions** | | macro **`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB`** |
|
||||
| | >= C++11 | template<extent_t Count, class T><br>constexpr auto<br>**first**(T & t) ->... |
|
||||
| | >= C++11 | template<class T><br>constexpr auto<br>**first**(T & t, index_t count) ->... |
|
||||
| | >= C++11 | template<extent_t Count, class T><br>constexpr auto<br>**last**(T & t) ->... |
|
||||
| | >= C++11 | template<class T><br>constexpr auto<br>**last**(T & t, extent_t count) ->... |
|
||||
| | >= C++11 | template<index_t Offset, extent_t Count = dynamic_extent, class T><br>constexpr auto<br>**subspan**(T & t) ->... |
|
||||
| | >= C++11 | template<class T><br>constexpr auto<br>**subspan**(T & t, index_t offset, extent_t count = dynamic_extent) ->... |
|
||||
| | | |
|
||||
| **Free functions** | | macro **`span_FEATURE_MAKE_SPAN`**<br>macro **`span_FEATURE_MAKE_SPAN_TO_STD`** |
|
||||
| | | template<class T><br>constexpr span<T><br>**make_span**(T \* first, T \* last) noexcept |
|
||||
| | | template<class T><br>constexpr span<T><br>**make_span**(T \* ptr, index_t count) noexcept |
|
||||
| | | template<class T, size_t N><br>constexpr span<T,N><br>**make_span**(T (&arr)[N]) noexcept |
|
||||
| | >= C++11 | template<class T, size_t N><br>constexpr span<T,N><br>**make_span**(std::array<T,N> & arr) noexcept |
|
||||
| | >= C++11 | template<class T, size_t N><br>constexpr span<const T,N><br>**make_span**(std::array<T,N > const & arr) noexcept |
|
||||
| | >= C++11 | template<class Container><br>constexpr auto<br>**make_span**(Container & cont) -><br> span<typename Container::value_type> noexcept |
|
||||
| | >= C++11 | template<class Container><br>constexpr auto<br>**make_span**(Container const & cont) -><br> span<const typename Container::value_type> noexcept |
|
||||
| | | template<class Container><br>span<typename Container::value_type><br>**make_span**( with_container_t, Container & cont ) |
|
||||
| | | template<class Container><br>span<const typename Container::value_type><br>**make_span**( with_container_t, Container const & cont ) |
|
||||
| | < C++11 | template<class T, Allocator><br>span<T><br>**make_span**(std::vector<T, Allocator> & cont) |
|
||||
| | < C++11 | template<class T, Allocator><br>span<const T><br>**make_span**(std::vector<T, Allocator> const & cont) |
|
||||
| | | |
|
||||
| **Free functions** | | macro **`span_FEATURE_BYTE_SPAN`** |
|
||||
| | >= C++11 | template<class T><br>span<T, sizeof(T)><br>**byte_span**(T & t) |
|
||||
| | >= C++11 | template<class T><br>span<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 |
|
||||
| GCC | Travis | 5.5.0, 6.4.0, 7.3.0 |
|
||||
**OS X** | ? | Local | ? |
|
||||
**Windows** | Clang/LLVM | Local | 6.0.0 |
|
||||
| GCC | Local | 7.2.0 |
|
||||
| Visual C++<br>(Visual Studio)| Local | 8 (2005), 10 (2010), 11 (2012),<br>12 (2013), 14 (2015), 15 (2017) |
|
||||
| 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.
|
@ -82,6 +82,7 @@ target_link_libraries(zm
|
|||
libbcrypt::bcrypt
|
||||
jwt-cpp::jwt-cpp
|
||||
RtspServer::RtspServer
|
||||
martinmoene::span-lite
|
||||
PRIVATE
|
||||
zm-core-interface)
|
||||
|
||||
|
|
155
src/zm_font.cpp
155
src/zm_font.cpp
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
217
src/zm_image.cpp
217
src/zm_image.cpp
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
@ -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)
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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-_.~!()&= ");
|
||||
|
|
Loading…
Reference in New Issue