diff --git a/.gitignore b/.gitignore index 862c206..1f9000f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,4 @@ !gh_rsa.enc !README.md !pictura-mediocritas.rc -!pictura-mediocritas.md +!pictura-mediocritas.1 diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index c8839ff..0000000 --- a/.gitmodules +++ /dev/null @@ -1,15 +0,0 @@ -[submodule "ext/TCLAP"] - path = ext/TCLAP - url = git://git.code.sf.net/p/tclap/code -[submodule "ext/Catch2"] - path = ext/Catch2 - url = https://github.com/catchorg/Catch2 - branch = v2.0.1 -[submodule "ext/pb-cpp"] - path = ext/pb-cpp - url = https://github.com/nabijaczleweli/pb-cpp - branch = v0.1.1 -[submodule "ext/ffmpeg"] - path = ext/ffmpeg - url = https://git.ffmpeg.org/ffmpeg.git - branch = n3.4 diff --git a/.travis.yml b/.travis.yml index ba2e5c3..533b5a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,6 +69,8 @@ matrix: - env: LANGUAGE=Ruby DEPLOY=true DEPLOY_FILE="$TRAVIS_BUILD_DIR/../pictura-mediocritas-man-$TRAVIS_TAG.tbz2" language: ruby rvm: "2.2" + allow_failures: + - env: LANGUAGE=C++ CXX="clang++-5.0 -stdlib=libc++" CC=clang-5.0 before_install: - if [ "$TRAVIS_SECURE_ENV_VARS" == "true" ]; then @@ -167,7 +169,7 @@ after_success: deploy: provider: releases api-key: - secure: stxnyBJJBRCIL5CdzwQeCGwrZ9Bh1Z8jZA4ojKZ/1RzXyAV/h1g2tQnbbW7cUA7QT4AvITwRDN5CW6KzZfUAqUr2Kz0XoMuE7+zuyFUihVt6Vmilr//ZUpkTbektfJDUvfYA4P4kna9S2zUfE1sW3wGheq5lbi70UVN39Zc1d8aE2dO25DP4Hcqf2HH/sHRA+U7uyMtD75qPlGgcl4YPQaPVyuVfIthMaXYYr2Pt21qIOoMib2oMIKJWLc9ftkBQaIvs9Zt0sECMTmQhaO0li8N4ZpaxiUJsSyGxt/ZkydRjDoReJVkQQO+/p4Ed7jQp8TcaBlSmFivNyd18/L06CX2h0BiawftbtEIDC9U1Xi+7VqVWfk2kIK6MqjnY0AtdsAJmQvTq60e7O1lnHkDZTu3tvXR8v+6HQdofeUM6oU75CRXgmO+MTtxQkyB1v1tDqh7REfYHCO4Q6HKaPVGpJP5XOuY0qN9vragTP5UlVgVDYi0+jzX8zrL+gE0SKm55lYkYrfNqheCvTref1No4K92Lt5fjQbT2LSjp3b/NESvcLHiL42S7KrkhI3qFqJwEYPVXl/3eGyRp/EpGUaNdFh9eZECTBOAaGLVtHQlVgfUpoST+9fBiFuRHmSexlV19Ke3NPa2h229kuIBshFWxq3Dlk9ZJMpWXKzJII36/TAA= + secure: jKKJHvnWlVzfHVn67OcSbtJTiDSHpAu7bMRs0Of64q2fqb6EAjL0l8+9ckgY1TnLU9AIXZrFJzkrt45t7qbWPb2ZsAUxd/O7n6cZuh+TDYkYj3SUsfNx9CAV8Gxb86GVogg1mXDuqYS+FKCK1lsfSV0y50bMkLz5oVtLP6Iq8/JkCugCO/XQluXCoE6tDH/lCVl3yLsgtG66Trv7bufLS05D9bnEAg/UvI4W4oxucnxRpwd0rSRCHi+rmaSjDVRFIvGTj8RzLtGm8SJTeedulT5CWXr7pvXxo19cnWSyXeRhjjrnV2aM3JEtDYerqeIW0e1TyRSs2q9oUmMFs1fQ2ZGwm5fLjeYSB2jVU637mdUnh/SmrvXQ5mv+IZTI/RejUm16qLqH4/IFlxkGmAwyhLtj0A+thula+PuZ2RMd2FfymVGvEklC7/hyWTjE3kJESc2uv0xph9iceJrLaQI4qXI++uJPVJntto7hoBEJWTLcc0nVvigjMLkCfmfifzb/KwItYyImHHjCs8dCYc771Oup3jEqHDprmiJtizp+mAi3cNIr3mCtspcll7cxELg9RBR9AUIhyql0FtfH1qatFQYB8KYyUMeLdmJRzkz023dCMznenkgIm/Pg7KzQjGxsRePJMA4qR4FxMOGnnvZMwziHh7DWQu6y7bpfMh8NVAw= file: "$DEPLOY_FILE" skip_cleanup: true on: diff --git a/Makefile b/Makefile index 549e3e7..f643bf1 100644 --- a/Makefile +++ b/Makefile @@ -23,18 +23,21 @@ include configMakefile -# If ffmpeg autodetects something for your system, you can put it here to link to -FFMPEG_PREREQUESITE_LD_LIBS := bz2 lzma z -LDAR := $(PIC) $(LNCXXAR) $(foreach l,ffmpeg/lib pb-cpp,-L$(BLDDIR)$(l)) $(foreach l,pb-cpp swscale avformat avcodec avutil freeimage $(FFMPEG_PREREQUESITE_LD_LIBS) $(OS_LD_LIBS),-l$(l)) -VERAR := $(foreach l,PICTURA_MEDIOCRITAS CATCH2 PB_CPP TCLAP,-D$(l)_VERSION='$($(l)_VERSION)') -INCAR := $(foreach l,$(foreach l,$(foreach l,pb-cpp pb-cpp/ext/optional-lite TCLAP,$(l)/include) Catch2/single_include,ext/$(l)) $(foreach l,ffmpeg,$(BLDDIR)$(l)/include),-isystem$(l)) +LDAR := $(PIC) $(LNCXXAR) $(foreach l,swscale avformat avcodec avutil freeimage $(OS_LD_LIBS),-l$(l)) +VERAR := $(foreach l,PICTURA_MEDIOCRITAS,-D$(l)_VERSION='$($(l)_VERSION)') TEST_SOURCES := $(sort $(wildcard tests/*.cpp tests/**/*.cpp tests/**/**/*.cpp tests/**/**/**/*.cpp)) BUILD_TEST_SOURCES := $(sort $(wildcard build-tests/*.cpp build-tests/**/*.cpp build-tests/**/**/*.cpp build-tests/**/**/**/*.cpp)) SOURCES := $(sort $(wildcard src/*.cpp src/**/*.cpp src/**/**/*.cpp src/**/**/**/*.cpp)) +PREFIX ?= /usr/local -.PHONY : all clean ffmpeg pb-cpp exe tests no-build-tests run-tests +.PHONY : all clean exe tests no-build-tests run-tests -all : ffmpeg pb-cpp exe tests no-build-tests run-tests +all : exe tests no-build-tests run-tests + +install : exe + mkdir -p $(DESTDIR)/$(PREFIX)/bin $(DESTDIR)/$(PREFIX)/share/man/man1 + cp $(OUTDIR)pictura-mediocritas$(EXE) $(DESTDIR)/$(PREFIX)/bin/ + gzip -9 < pictura-mediocritas.1 > $(DESTDIR)/$(PREFIX)/share/man/man1/pictura-mediocritas.1.gz clean : rm -rf $(OUTDIR) @@ -44,36 +47,29 @@ run-tests : $(OUTDIR)pictura-mediocritas-tests$(EXE) exe : $(OUTDIR)pictura-mediocritas$(EXE) tests : $(OUTDIR)pictura-mediocritas-tests$(EXE) -no-build-tests : $(subst build-tests/,$(BLDDIR)build_test_obj/,$(subst .cpp,$(OBJ),$(BUILD_TEST_SOURCES))) -ffmpeg : $(BLDDIR)ffmpeg/lib/libavcodec$(ARCH) -pb-cpp : $(BLDDIR)pb-cpp/libpb-cpp$(ARCH) +no-build-tests : $(subst build-tests/,$(BLDDIR)build_test_obj/,$(subst .cpp,.o,$(BUILD_TEST_SOURCES))) -$(OUTDIR)pictura-mediocritas$(EXE) : $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(SOURCES))) +$(OUTDIR)pictura-mediocritas$(EXE) : $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,.o,$(SOURCES))) $(CXX) $(CXXAR) -o$@ $^ $(PIC) $(LDAR) -$(OUTDIR)pictura-mediocritas-tests$(EXE) : $(subst tests/,$(BLDDIR)test_obj/,$(subst .cpp,$(OBJ),$(TEST_SOURCES))) $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(filter-out src/main.cpp,$(SOURCES)))) +$(OUTDIR)pictura-mediocritas-tests$(EXE) : $(subst tests/,$(BLDDIR)test_obj/,$(subst .cpp,.o,$(TEST_SOURCES))) $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,.o,$(filter-out src/main.cpp,$(SOURCES)))) $(CXX) $(CXXAR) -o$@ $^ $(PIC) $(LDAR) -$(BLDDIR)ffmpeg/lib/libavcodec$(ARCH) : ext/ffmpeg/configure - @mkdir -p $(abspath $(dir $@)..) - cd $(abspath $(dir $@)..) && $(abspath $^) --enable-static $(foreach l,shared programs doc avdevice swresample postproc avfilter iconv jack alsa appkit coreimage sndio schannel securetransport avfoundation encoders filters muxers libxcb libxcb-shm libxcb-xfixes libxcb-shape audiotoolbox encoders filters muxers,--disable-$(l)) --prefix="$(abspath $(dir $@)..)" && $(MAKE) install - -$(BLDDIR)pb-cpp/libpb-cpp$(ARCH) : ext/pb-cpp/Makefile - @mkdir -p $(abspath $(dir $@)..) - cd $(dir $^) && $(MAKE) OUTDIR="$(abspath $(dir $@))/" static - -$(OBJDIR)%$(OBJ) : $(SRCDIR)%.cpp +$(OBJDIR)%.o : $(SRCDIR)%.cpp @mkdir -p $(dir $@) - $(CXX) $(CXXAR) $(INCAR) $(VERAR) -c -o$@ $^ + $(CXX) $(CXXAR) $(VERAR) -c -o$@ $< -$(BLDDIR)test_obj/%$(OBJ) : tests/%.cpp +$(BLDDIR)test_obj/%.o : tests/%.cpp @mkdir -p $(dir $@) - $(CXX) $(CXXAR) $(INCAR) -Isrc -c -o$@ $^ + $(CXX) $(CXXAR) -Isrc -c -o$@ $< -$(BLDDIR)build_test_obj/%$(OBJ) : build-tests/%.cpp +$(BLDDIR)build_test_obj/%.o : build-tests/%.cpp @mkdir -p $(dir $@) - ! $(CXX) $(CXXAR) $(INCAR) -Isrc -c -o$@ $^ 2>$(subst $(OBJ),.err_out,$@) - grep -q "$(shell grep ERROR_MUST_CONTAIN $^ | sed -e 's/#define ERROR_MUST_CONTAIN "//' -e 's/"$$//')" $(subst $(OBJ),.err_out,$@) + ! $(CXX) $(CXXAR) -Isrc -c -o$@ $< 2>$(subst .o,.err_out,$@) + grep -q "$(shell grep ERROR_MUST_CONTAIN $^ | sed -e 's/#define ERROR_MUST_CONTAIN "//' -e 's/"$$//')" $(subst .o,.err_out,$@) touch $@ + + +include $(wildcard $(OBJDIR)*/*.d $(OBJDIR)*.d) diff --git a/PicturaMediocritas.sublime-project b/PicturaMediocritas.sublime-project index f2e45af..2858752 100644 --- a/PicturaMediocritas.sublime-project +++ b/PicturaMediocritas.sublime-project @@ -26,10 +26,6 @@ "name": "Build tests", "path": "build-tests" }, - { - "name": "External code", - "path": "ext" - }, { "file_include_patterns": [ diff --git a/README.md b/README.md index 9371218..af7a732 100644 --- a/README.md +++ b/README.md @@ -3,38 +3,26 @@ Like aurea mediocritas, but with frames in a video instead. Or, you know, get an average frame from a video. -## [Manpage](https://cdn.rawgit.com/LoungeCPP/PicturaMediocritas/man/pictura-mediocritas.1.html) +## Usage -## Examples +``` +usage: pictura-mediocritas in-video [out-image] +``` ```sh -# TODO: concretify $ pictura-mediocritas "video.mp4" -``` +# Averaged frame in "video.png" -For details, see the [manpage](https://cdn.rawgit.com/LoungeCPP/PicturaMediocritas/man/pictura-mediocritas.1.html). +$ pictura-mediocritas "video.mp4" "video-average.jpg" +``` ## Installation -Either acquire a binary release from [the releases page](https://github.com/LoungeCPP/PicturaMediocritas/releases), -or build it yourself: - ```sh -# You need FreeImage to be includable and linkable - -$ git clone --recursive https://github.com/LoungeCPP/PicturaMediocritas +# apt install libfreeimage-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev doctest-dev +# or pacman -S mingw-w64-x86_64-ffmpeg mingw-w64-x86_64-doctest mingw-w64-x86_64-freeimage +$ git clone https://github.com/LoungeCPP/PicturaMediocritas $ cd PicturaMediocritas -$ make -$ install out/pictura-mediocritas $(wherever) +$ make -j +$ make install ``` - -## Contributing - -Imperative commit messages, format your code with clang-format, you know the drill. - -Branch names *should* be in the format `{issue}-{name}-{desc}`, where -`issue` is the issue # this branch closes, -`name` is your name (or a shorthand, like "nab" for "nabijaczleweli"), and -`desc` is a few-word description of the changes in the branch (like "rework-dir-listing", "second-time"). - -Non-FF PRs (ergo, merge commits) are illegal. diff --git a/appveyor.yml b/appveyor.yml index 3de6239..1c2d842 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,6 +13,8 @@ install: - bash -lc "pacman --needed --noconfirm -Sy pacman-mirrors" - bash -lc "pacman --noconfirm -Syyu" - bash -lc "pacman --noconfirm -Su mingw-w64-x86_64-freeimage mingw-w64-x86_64-nasm" + - + - sed -i "s/FFMPEG_PREREQUESITE_LD_LIBS :=/\0 bcrypt/" Makefile build: off build_script: @@ -31,7 +33,7 @@ deploy: provider: GitHub artifact: pictura-mediocritas-v0.1.0.exe auth_token: - secure: /6TsIkMcI5dtUAvr8RJZxjUMR9uDgFLLZDyNbm5HqNCD0zbuj4TUY2JPIte4F+E7 + secure: ulV16u9GQNsy21XpNcP46K2SSbTHTv68LK0UGobzLbTEswEPEg0hso5XTpmuwa7Y on: appveyor_repo_tag: true diff --git a/configMakefile b/configMakefile index 0cbf657..a842fea 100644 --- a/configMakefile +++ b/configMakefile @@ -23,13 +23,11 @@ ifeq "$(OS)" "Windows_NT" EXE := .exe PIC := - ECHO := /usr/bin/echo - OS_LD_LIBS := ncurses ole32 ws2_32 + OS_LD_LIBS := else EXE := PIC := -fPIC - ECHO := echo - OS_LD_LIBS := ncurses dl pthread + OS_LD_LIBS := pthread endif ifneq "$(ADDITIONAL_INCLUDE_DIR)" "" @@ -44,18 +42,7 @@ else LNCXXAR := endif -# TODO: uncomment when there's a first tag -# PICTURA_MEDIOCRITAS_VERSION := "$(patsubst v%,%,$(shell git describe --tags --abbrev=0))" -PICTURA_MEDIOCRITAS_VERSION := "0.1.0" -CATCH2_VERSION := "$(patsubst v%,%,$(shell git -C ext/Catch2 describe --tags --abbrev=0))" -PB_CPP_VERSION := "$(patsubst v%,%,$(shell git -C ext/pb-cpp describe --tags --abbrev=0))" -TCLAP_VERSION := "$(shell cat ext/TCLAP/configure.in | grep AM_INIT_AUTOMAKE | sed "s/.*(tclap,\(.*\))/\1/")" - -OBJ := .o -ARCH := .a -AR := ar -CXXAR := -O3 -fomit-frame-pointer -std=c++14 -pedantic -Wall -Wextra -pipe $(INCCXXAR) $(PIC) -CCAR := -O3 -fomit-frame-pointer -std=c11 -pipe $(PIC) +CXXAR := -g -MD -O3 -fomit-frame-pointer -march=native -std=c++20 -pedantic -Wall -Wextra -Wno-missing-field-initializers -pipe $(INCCXXAR) $(PIC) OUTDIR := out/ BLDDIR := out/build/ diff --git a/ext/Catch2 b/ext/Catch2 deleted file mode 160000 index 19ab211..0000000 --- a/ext/Catch2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 19ab2117c5bac2f376f8da4a4b25e183137bcec0 diff --git a/ext/TCLAP b/ext/TCLAP deleted file mode 160000 index 3d02fed..0000000 --- a/ext/TCLAP +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3d02fed104fc9cfd1831195cb2ab82b4e08c02c8 diff --git a/ext/ffmpeg b/ext/ffmpeg deleted file mode 160000 index 01e291a..0000000 --- a/ext/ffmpeg +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 01e291a592452f27b3a4e811536aaaf94096e244 diff --git a/ext/pb-cpp b/ext/pb-cpp deleted file mode 160000 index 35c52af..0000000 --- a/ext/pb-cpp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 35c52af00b9f414fe9bd1ec38b9d88bfed80f5ff diff --git a/pictura-mediocritas.1 b/pictura-mediocritas.1 new file mode 100644 index 0000000..e92054d --- /dev/null +++ b/pictura-mediocritas.1 @@ -0,0 +1,34 @@ +.\" SPDX-License-Identifier: 0BSD +.\" +.Dd March 23, 2024 +.Dt PICTŪRA-MEDIOCRITAS 1 +.Os +. +.Sh NAME +.Nm pictura-mediocritas +.Nd average video frames into single image +.Sh SYNOPSIS +.Nm +.Ar video +.Op Ar image +. +.Sh DESCRIPTION +Like aurea mediocritas, but with frames in a video instead: +averages +.Ar video +.Pq which may be in GIF, or any Xr ffmpeg 1 Ns -understood, format +into +.Ar image +(which defaults to +.Ar video +but with the "extension" replaced with +.Pa .png ) . +. +.Sh AUTHORS +.An наб Aq Mt nabijaczleweli@nabijaczleweli.xyz +. +.Sh REPORTING BUGS +.Lk https://github.com/LoungeCPP/PicturaMediocritas/issues +. +.Sh SEE ALSO +.Lk https://github.com/LoungeCPP/PicturaMediocritas diff --git a/pictura-mediocritas.md b/pictura-mediocritas.md deleted file mode 100644 index 43d8b66..0000000 --- a/pictura-mediocritas.md +++ /dev/null @@ -1,38 +0,0 @@ -pictura-mediocritas(1) -- Like aurea mediocritas, but with frames in a video instead -==================================================================================== - -## SYNOPSIS - -`pictura-mediocritas` [OUT_IMAGE] - -## DESCRIPTION - -Like aurea mediocritas, but with frames in a video instead. - -Or, you know, get an average frame from a video. - -## OPTIONS - - - - Video to average, must exist. - - [OUT_IMAGE] - - Image to write the averaged frame to. - - The parent directory must exist. - - Default: IN_VIDEO, but the extension is "png" instead. - -## AUTHOR - -Written by nabijaczleweli <> - -## REPORTING BUGS - -<> - -## SEE ALSO - -<> diff --git a/src/average_frame.hpp b/src/average_frame.hpp index 1b5e6be..b96bfe6 100644 --- a/src/average_frame.hpp +++ b/src/average_frame.hpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -79,15 +80,18 @@ namespace pictura_mediocritas { /// Get a frame from the specified source. /// - /// The `frame` argument must be indexable by a `std::size_t` in [0; width * height * Channels) ∩ ℤ. - template - void process_frame(const FrameT & frame); + /// The `frame` argument must be indexable by a `{std::size_t, Additional...}` where the size_t is in [0; width * height * Channels) ∩ ℤ. + template + void process_frame(const FrameT & frame, Additional &&... frame_ctr); /// Get a frame from the specified source. /// - /// The `frame` argument must be indexable by a `std::size_t` in [0; width * height * Channels) ∩ ℤ. - template - void process_frame(FrameT & frame); + /// The `frame` argument must be indexable by a `{std::size_t, frame_ctr}` where the size_t is in [0; width * height * Channels) ∩ ℤ. + template + void process_frame(FrameT & frame, Additional &&... frame_ctr); + + /// Add the values from a partial frame to this one. + average_frame & operator+=(const average_frame & partial); /// Get the average channel at the given index. /// @@ -104,6 +108,9 @@ namespace pictura_mediocritas { /// /// The behaviour is undefined if `idx` ∉ [0; width * height) ∩ ℤ. std::array pixel(std::size_t idx) const; + + /// Swap pixels at the given coordinates, both within [0; width * height * Channels) ∩ ℤ. + void swap(std::size_t idx1, std::size_t idx2); }; diff --git a/src/average_frame.inc b/src/average_frame.inc index a1fb540..55f9c6b 100644 --- a/src/average_frame.inc +++ b/src/average_frame.inc @@ -48,28 +48,32 @@ AccT pictura_mediocritas::average_frame::processed_frames() cons return frames; } -#define PROCESS_FRAME_BODY() \ - do { \ - const auto pixels_len = pixels.size(); \ - for(std::size_t i = 0; i < pixels_len; ++i) \ - pixels[i] += frame[i]; \ - \ - ++frames; \ - } while(0) +#define PROCESS_FRAME_IMPL() \ + const auto pixels_len = pixels.size(); \ + for(std::size_t i = 0; i < pixels_len; ++i) \ + pixels[i] += frame[{i, frame_ctr...}]; \ + \ + ++frames; template -template -void pictura_mediocritas::average_frame::process_frame(const FrameT & frame) { - PROCESS_FRAME_BODY(); +template +void pictura_mediocritas::average_frame::process_frame(const FrameT & frame, Additional &&... frame_ctr) { + PROCESS_FRAME_IMPL(); } + template -template -void pictura_mediocritas::average_frame::process_frame(FrameT & frame) { - PROCESS_FRAME_BODY(); +template +void pictura_mediocritas::average_frame::process_frame(FrameT & frame, Additional &&... frame_ctr) { + PROCESS_FRAME_IMPL(); } -#undef PROCESS_FRAME_BODY +template +pictura_mediocritas::average_frame & pictura_mediocritas::average_frame::operator+=(const average_frame & partial) { + std::transform(std::begin(pixels), std::end(pixels), std::begin(partial.pixels), std::begin(pixels), std::plus()); + frames += partial.frames; + return *this; +} template AccT pictura_mediocritas::average_frame::operator[](std::size_t idx) const { @@ -87,6 +91,11 @@ std::array pictura_mediocritas::average_frame::p return res; } +template +void pictura_mediocritas::average_frame::swap(std::size_t idx1, std::size_t idx2) { + std::swap(pixels[idx1], pixels[idx2]); +} + template bool pictura_mediocritas::operator==(const average_frame & lhs, const average_frame & rhs) { diff --git a/src/main.cpp b/src/main.cpp index 7a4b220..1381eb4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,23 +20,54 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "progressbar/progressbar.hpp" - #include "average_frame.hpp" -#include "options/options.hpp" +#include "options.hpp" #include "output_image.hpp" #include "parser/ffmpeg.hpp" #include "parser/multi_image.hpp" #include "util.hpp" #include +#include #include #include +#include +#include +#include extern "C" { #include #include } +using namespace std::chrono_literals; + + +#define STATUSSY(donetest, curframe) \ + const auto start = std::chrono::high_resolution_clock::now(); \ + std::jthread statussy { \ + [&] { \ + auto write = [&](std::size_t done) { \ + const auto now = std::chrono::high_resolution_clock::now(); \ + const auto len = parser.length(); \ + std::fprintf(stderr, "\r%*zu/%zu\t%.4f/s", (int)std::log10(len | 1) + 1, done, len, \ + (done / static_cast(std::chrono::duration_cast(now - start).count()) * 1000)); \ + }; \ + \ + for(;;) { \ + for(int _ = 0; _ < 10; ++_) { \ + std::this_thread::sleep_for(100ms); \ + if(donetest) \ + goto done; \ + } \ + if(isatty(2)) \ + write(curframe); \ + } \ + done: \ + write(parser.length() ? parser.length() : curframe); \ + std::fputc('\n', stderr); \ + } \ + } + int main(int argc, const char ** argv) { const auto opts_r = pictura_mediocritas::options::parse(argc, argv); @@ -46,49 +77,79 @@ int main(int argc, const char ** argv) { } const auto opts = std::move(std::get<0>(opts_r)); -#ifdef _WIN32 - CoInitialize(nullptr); -#endif FreeImage_Initialise(); pictura_mediocritas::quickscope_wrapper freeimage_deinitialiser{FreeImage_DeInitialise}; pictura_mediocritas::average_frame_u64 avg_frame(0, 0); - - if(pictura_mediocritas::has_extension(opts.in_video.c_str(), "gif")) { - pictura_mediocritas::multi_image_parser parser(FreeImage_OpenMultiBitmap(FIF_GIF, opts.in_video.c_str(), false, true, true, GIF_LOAD256 | GIF_PLAYBACK), + if(pictura_mediocritas::has_extension(opts.in_video.data(), "gif")) { + pictura_mediocritas::multi_image_parser parser(FreeImage_OpenMultiBitmap(FIF_GIF, opts.in_video.data(), false, true, true, GIF_LOAD256 | GIF_PLAYBACK), decltype(avg_frame)::channels); - pictura_mediocritas::progressbar progress("Processing " + opts.in_video + ' ', parser.length()); - avg_frame = decltype(avg_frame)(parser.size()); - - for(auto i = 0u; i < parser.length(); ++i) { + avg_frame = decltype(avg_frame)(parser.size()); + std::size_t i = 0; + STATUSSY(i == parser.length(), i); + for(; i < parser.length(); ++i) { avg_frame.process_frame(parser); parser.next(); - progress.inc(); } } else { - av_register_all(); - avcodec_register_all(); - - - pictura_mediocritas::ffmpeg_parser parser(opts.in_video.c_str(), decltype(avg_frame)::channels); +#define MAXTHREADS 8u + auto thread_cnt = std::clamp(std::thread::hardware_concurrency(), 1u, MAXTHREADS); + pictura_mediocritas::ffmpeg_parser parser(opts.in_video.data(), decltype(avg_frame)::channels, thread_cnt); if(parser) { - std::unique_ptr progress; + struct thread { + pictura_mediocritas::average_frame_u64 avg_frame; + std::thread thread; + pthread_barrier_t barrier; + std::atomic cur_frame_num; + std::atomic_flag done; + }; + thread threads[MAXTHREADS] = {{{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}, {{0, 0}}}; + STATUSSY(threads[0].done.test(), threads[0].cur_frame_num.load(std::memory_order_relaxed)); if(!parser.process([&]() { - if(!progress) - progress = std::make_unique("Processing " + opts.in_video + ' ', parser.length()); - if(avg_frame.size().first == 0) + if(avg_frame.size().first == 0) { avg_frame = decltype(avg_frame)(parser.size()); - avg_frame.process_frame(parser); - progress->inc(); + for(auto i = 0u; i < thread_cnt; ++i) { + threads[i].avg_frame = decltype(avg_frame)(parser.size()); + while(pthread_barrier_init(&threads[i].barrier, nullptr, 2)) + ; + threads[i].thread = std::thread{[&, i = i] { + auto & self = threads[i]; + + pthread_barrier_wait(&self.barrier); + for(;;) { + pthread_barrier_wait(&self.barrier); + if(self.done.test()) + break; + auto frame = self.cur_frame_num.load(std::memory_order_relaxed); + self.avg_frame.process_frame(parser, frame); + pthread_barrier_wait(&self.barrier); + } + }}; + } + } + + auto & thread = threads[parser.frame_num % thread_cnt]; + pthread_barrier_wait(&thread.barrier); + thread.cur_frame_num.store(parser.frame_num, std::memory_order_relaxed); + pthread_barrier_wait(&thread.barrier); return true; })) { - std::cerr << "\nParsing " << opts.in_video << " failed: " << *parser.error() << '\n'; - return 1; - } else - progress->finish(); + std::cerr << "Parsing " << opts.in_video << " failed: " << *parser.error() << '\n'; + std::exit(1); + } else { + for(auto i = 0u; i < thread_cnt; ++i) { + threads[i].done.test_and_set(); + pthread_barrier_wait(&threads[i].barrier); + pthread_barrier_wait(&threads[i].barrier); + threads[i].thread.join(); + avg_frame += threads[i].avg_frame; + } + } + + parser.postprocess(avg_frame); } else if(parser.error() == "") { std::cerr << "Couldn't open " << opts.in_video << ".\n"; return 1; @@ -99,7 +160,7 @@ int main(int argc, const char ** argv) { } - std::cout << "\nWriting to " << opts.out_image << '\n'; + std::cout << "Writing to " << opts.out_image << '\n'; switch(pictura_mediocritas::output_image(avg_frame.size(), decltype(avg_frame)::channels, avg_frame, opts.out_image.c_str())) { case pictura_mediocritas::output_image_result_t::ok: break; diff --git a/tests/util/directory_exists.cpp b/src/options.cpp similarity index 59% rename from tests/util/directory_exists.cpp rename to src/options.cpp index bb02300..249f16e 100644 --- a/tests/util/directory_exists.cpp +++ b/src/options.cpp @@ -20,26 +20,37 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "../test_util.hpp" +#include "options.hpp" #include "util.hpp" -#include -#include -#include - using namespace std::literals; -TEST_CASE("util::directory_exists() -- nonexistant", "[util]") { - const auto temp = temp_dir() + "/PicturaMediocritas/util/directory_exists/"s; - make_directory_recursive(temp.c_str()); +std::tuple pictura_mediocritas::options::parse(int, const char * const * argv) { + auto self = *argv ? *argv : "pictura-mediocritas"; + if(*argv) + ++argv; + + options ret{}; + if(!*argv) + usage: + return std::make_tuple(ret, 1, ("usage: "s += self) += " in-video [out-image]"); + ret.in_video = *argv++; - REQUIRE_FALSE(pictura_mediocritas::directory_exists((temp + "nonexistant_dir").c_str())); + if(*argv) { + ret.out_image = *argv++; + if(*argv) + goto usage; + } else + ret.out_image = switch_extenstion(ret.in_video, "png"); + + return std::make_tuple(ret, 0, ""s); } -TEST_CASE("util::directory_exists() -- existant file", "[util]") { - const auto temp = temp_dir() + "/PicturaMediocritas/util/directory_exists/"s; - make_directory_recursive(temp.c_str()); +bool pictura_mediocritas::operator==(const options & lhs, const options & rhs) { + return lhs.in_video == rhs.in_video && lhs.out_image == rhs.out_image; +} - REQUIRE(pictura_mediocritas::directory_exists(temp.c_str())); +bool pictura_mediocritas::operator!=(const options & lhs, const options & rhs) { + return !(lhs == rhs); } diff --git a/src/options/options.hpp b/src/options.hpp similarity index 97% rename from src/options/options.hpp rename to src/options.hpp index ec1d9a7..d5bb4e1 100644 --- a/src/options/options.hpp +++ b/src/options.hpp @@ -24,6 +24,7 @@ #include +#include #include @@ -33,7 +34,7 @@ namespace pictura_mediocritas { /// Path to the video file to analyse. /// /// Must exist. - std::string in_video; + std::string_view in_video; /// Path to the image file to write the result to. /// /// Parent directory must exist. diff --git a/src/options/existing_file_constraint.cpp b/src/options/existing_file_constraint.cpp deleted file mode 100644 index abb7e65..0000000 --- a/src/options/existing_file_constraint.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#include "existing_file_constraint.hpp" -#include "../util.hpp" - - -std::string pictura_mediocritas::existing_file_constraint::description() const { - return "existing file"; -} - -std::string pictura_mediocritas::existing_file_constraint::shortID() const { - return arg_name; -} - -bool pictura_mediocritas::existing_file_constraint::check(const std::string & value) const { - return file_exists(value.c_str()); -} - -pictura_mediocritas::existing_file_constraint::existing_file_constraint(std::string argname) : arg_name(std::move(argname)) {} diff --git a/src/options/existing_file_constraint.hpp b/src/options/existing_file_constraint.hpp deleted file mode 100644 index 42046df..0000000 --- a/src/options/existing_file_constraint.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#pragma once - - -#include -#include - - -namespace pictura_mediocritas { - class existing_file_constraint : public TCLAP::Constraint { - private: - std::string arg_name; - - public: - virtual std::string description() const override; - virtual std::string shortID() const override; - virtual bool check(const std::string & value) const override; - - existing_file_constraint(std::string argname); - - virtual ~existing_file_constraint() = default; - }; -} diff --git a/src/options/existing_parent_dir_constraint.cpp b/src/options/existing_parent_dir_constraint.cpp deleted file mode 100644 index 97a6df1..0000000 --- a/src/options/existing_parent_dir_constraint.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#include "existing_parent_dir_constraint.hpp" -#include "../util.hpp" - - -std::string pictura_mediocritas::existing_parent_dir_constraint::description() const { - return "creatable file"; -} - -std::string pictura_mediocritas::existing_parent_dir_constraint::shortID() const { - return arg_name; -} - -bool pictura_mediocritas::existing_parent_dir_constraint::check(const std::string & value) const { - auto last_slash_idx = value.find_last_of("\\/"); - if(last_slash_idx == std::string::npos) - return directory_exists("."); - else { - return directory_exists(value.substr(0, last_slash_idx).c_str()) && last_slash_idx < value.size() - 1; - } -} - -pictura_mediocritas::existing_parent_dir_constraint::existing_parent_dir_constraint(std::string argname) : arg_name(std::move(argname)) {} - diff --git a/src/options/existing_parent_dir_constraint.hpp b/src/options/existing_parent_dir_constraint.hpp deleted file mode 100644 index 12006f7..0000000 --- a/src/options/existing_parent_dir_constraint.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#pragma once - - -#include -#include - - -namespace pictura_mediocritas { - class existing_parent_dir_constraint : public TCLAP::Constraint { - private: - std::string arg_name; - - public: - virtual std::string description() const override; - virtual std::string shortID() const override; - virtual bool check(const std::string & value) const override; - - existing_parent_dir_constraint(std::string argname); - - virtual ~existing_parent_dir_constraint() = default; - }; -} diff --git a/src/options/options.cpp b/src/options/options.cpp deleted file mode 100644 index 33ef06a..0000000 --- a/src/options/options.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include "options.hpp" -#include "../util.hpp" -#include "existing_file_constraint.hpp" -#include "existing_parent_dir_constraint.hpp" -#include -#include -#include - - -using namespace std::literals; - - -std::tuple pictura_mediocritas::options::parse(int argc, const char * const * argv) { - options ret; - - try { - pictura_mediocritas::existing_file_constraint input_video_constraint("input video"); - pictura_mediocritas::existing_parent_dir_constraint out_image_constraint("output image"); - - TCLAP::CmdLine command_line("Pictūra Mediocritas -- like aurea mediocritas, but with frames in a video instead", ' ', PICTURA_MEDIOCRITAS_VERSION); - TCLAP::UnlabeledValueArg in_video("in_video", "Video to average", true, "", &input_video_constraint, command_line); - TCLAP::UnlabeledValueArg out_image("out_image", "Image to write the average frame to. Default: in_video.png", false, "", &out_image_constraint, - command_line); - - command_line.setExceptionHandling(false); - command_line.parse(argc, argv); - - ret.in_video = in_video; - - if(out_image.getValue().empty()) - ret.out_image = switch_extenstion(ret.in_video, "png"); - else - ret.out_image = out_image; - } catch(const TCLAP::ArgException & e) { - auto arg_id = e.argId(); - if(arg_id == " ") - arg_id = "undefined argument"; - return std::make_tuple(ret, 1, std::string(argv[0]) + ": error: parsing arguments failed (" + e.error() + ") for " + arg_id); - } catch(const TCLAP::ExitException & e) { - return std::make_tuple(ret, e.getExitStatus() ? e.getExitStatus() : 1, ""); - } - - return std::make_tuple(ret, 0, ""s); -} - -bool pictura_mediocritas::operator==(const options & lhs, const options & rhs) { - return lhs.in_video == rhs.in_video && lhs.out_image == rhs.out_image; -} - -bool pictura_mediocritas::operator!=(const options & lhs, const options & rhs) { - return !(lhs == rhs); -} diff --git a/src/parser/ffmpeg.cpp b/src/parser/ffmpeg.cpp index 39342e3..8d9c0d1 100644 --- a/src/parser/ffmpeg.cpp +++ b/src/parser/ffmpeg.cpp @@ -24,6 +24,8 @@ #include "ffmpeg.hpp" #include +using namespace std::literals; + void pictura_mediocritas::av_format_context_deleter::operator()(AVFormatContext * ctx) const noexcept { avformat_close_input(&ctx); @@ -53,6 +55,10 @@ bool pictura_mediocritas::ffmpeg_parser::send_packet(AVPacket * pkt) noexcept { case AVERROR_EOF: return false; + case AVERROR_INVALIDDATA: + av_log_set_callback(nullptr); + return false; + default: error_value = err; error_class = error_class_t::send_packet; @@ -62,6 +68,8 @@ bool pictura_mediocritas::ffmpeg_parser::send_packet(AVPacket * pkt) noexcept { bool pictura_mediocritas::ffmpeg_parser::receive_frame(const std::function & callback) noexcept { while((error_value = avcodec_receive_frame(best_codec_ctx.get(), orig_frame.get())) >= 0) { + ++frame_num; + auto & out_frame = out_frames[frame_num % out_frames.size()]; if(out_frame->width == 0) { out_frame->width = orig_frame->width; out_frame->height = orig_frame->height; @@ -92,13 +100,6 @@ bool pictura_mediocritas::ffmpeg_parser::receive_frame(const std::functionheight / 2; ++y) - for(auto x = 0; x < out_frame->width; ++x) - for(auto c = 0u; c < channels; ++c) - std::swap(out_frame->data[0][(y * out_frame->width + x) * channels + c], - out_frame->data[0][((out_frame->height - 1 - y) * out_frame->width + x) * channels + c]); - if(!callback()) return true; } @@ -121,8 +122,8 @@ std::string pictura_mediocritas::ffmpeg_parser::error_str() const { return buf; } -pictura_mediocritas::ffmpeg_parser::ffmpeg_parser(const char * filename, std::size_t c) - : best_stream(-69), best_codec(nullptr), channels(c), error_class(error_class_t::none), error_value(0) { +pictura_mediocritas::ffmpeg_parser::ffmpeg_parser(const char * filename, std::size_t c, std::size_t runners) + : best_stream(-69), best_codec(nullptr), channels(c), error_class(error_class_t::none), error_value(0), frame_num(-1) { AVFormatContext * container_in = nullptr; if((error_value = avformat_open_input(&container_in, filename, nullptr, nullptr)) != 0) { error_class = error_class_t::open_input; @@ -144,6 +145,8 @@ pictura_mediocritas::ffmpeg_parser::ffmpeg_parser(const char * filename, std::si return; } + av_dump_format(container.get(), 0, filename, false); + orig_frame.reset(av_frame_alloc()); if(!orig_frame) return; @@ -162,21 +165,26 @@ pictura_mediocritas::ffmpeg_parser::ffmpeg_parser(const char * filename, std::si return; } - out_frame.reset(av_frame_alloc()); - if(!out_frame) - return; + for(std::size_t i = 0; i < runners; ++i) { + out_frames.emplace_back(av_frame_alloc()); + if(!out_frames.back()) + return; + } switch(channels) { case 1: - out_frame->format = AV_PIX_FMT_GRAY8; + for(auto && out_frame : out_frames) + out_frame->format = AV_PIX_FMT_GRAY8; break; case 3: - out_frame->format = AV_PIX_FMT_RGB24; + for(auto && out_frame : out_frames) + out_frame->format = AV_PIX_FMT_RGB24; break; case 4: - out_frame->format = AV_PIX_FMT_RGBA; + for(auto && out_frame : out_frames) + out_frame->format = AV_PIX_FMT_RGBA; break; default: @@ -187,24 +195,26 @@ pictura_mediocritas::ffmpeg_parser::ffmpeg_parser(const char * filename, std::si } pictura_mediocritas::ffmpeg_parser::operator bool() const noexcept { - return packet && orig_frame && best_codec_ctx && out_frame && (error_class == error_class_t::none && error_value >= 0); + return packet && orig_frame && best_codec_ctx && + std::all_of(std::begin(out_frames), std::end(out_frames), [&](auto && out_frame) { return bool(out_frame); }) && + (error_class == error_class_t::none && error_value >= 0); } -nonstd::optional pictura_mediocritas::ffmpeg_parser::error() const { +std::optional pictura_mediocritas::ffmpeg_parser::error() const { switch(error_class) { case error_class_t::none: break; case error_class_t::open_input: - return {""}; + return ""s; case error_class_t::find_best_stream: switch(error_value) { case AVERROR_STREAM_NOT_FOUND: - return {"No video stream in input."}; + return "No video stream in input."s; case AVERROR_DECODER_NOT_FOUND: - return {"No available decoder for input stream."}; + return "No available decoder for input stream."s; default: // Unreachable @@ -212,70 +222,70 @@ nonstd::optional pictura_mediocritas::ffmpeg_parser::error() const } case error_class_t::find_stream_info: - return {"Couldn't find best stream info: " + error_str() + '.'}; + return ("Couldn't find best stream info: "s += error_str()) += '.'; case error_class_t::set_codec_parameters: - return {"Couldn't set codec context parameters: " + error_str() + '.'}; + return ("Couldn't set codec context parameters: "s += error_str()) += '.'; case error_class_t::open_codec: - return {"Couldn't open codec: " + error_str() + '.'}; + return ("Couldn't open codec: "s += error_str()) += '.'; case error_class_t::send_packet: switch(error_value) { case AVERROR(EINVAL): - return {"Couldn't send packet – invalid video."}; + return "Couldn't send packet – invalid video."s; case AVERROR(ENOMEM): - return {"Couldn't send packet – out of memory."}; + return "Couldn't send packet – out of memory."s; case AVERROR_INVALIDDATA: - return {"Couldn't send packet – invalid data."}; + return "Couldn't send packet – invalid data."s; default: - return {"Couldn't send packet: " + error_str() + '.'}; + return ("Couldn't send packet: "s += error_str()) += '.'; } case error_class_t::receive_frame: switch(error_value) { case AVERROR(EINVAL): - return {"Couldn't receive frame – invalid video."}; + return "Couldn't receive frame – invalid video."s; default: - return {"Couldn't receive frame: " + error_str() + '.'}; + return ("Couldn't receive frame: "s += error_str()) += '.'; } case error_class_t::channel_count: switch(channels) { case 0: - return {"Zero-channel video makes no sense."}; + return "Zero-channel video makes no sense."s; default: - return {"Invalid channel count: " + error_str() + '.'}; + return ("Invalid channel count: "s += error_str()) += '.'; } case error_class_t::read_frame: - return {"Couldn't read frame: " + error_str() + '.'}; + return ("Couldn't read frame: "s += error_str()) += '.'; case error_class_t::get_frame_buffer: - return {"Couldn't get output frame buffer: " + error_str() + '.'}; + return ("Couldn't get output frame buffer: "s += error_str()) += '.'; case error_class_t::scale: - return {"Couldn't scale image: " + error_str() + '.'}; + return ("Couldn't scale image: "s += error_str()) += '.'; } if(!packet) - return {"Couldn't allocate packet."}; + return "Couldn't allocate packet."s; if(!orig_frame) - return {"Couldn't allocate frame."}; + return "Couldn't allocate frame."s; if(!best_codec_ctx) - return {"Couldn't allocate codec context."}; + return "Couldn't allocate codec context."s; - if(!out_frame) - return {"Couldn't allocate output frame."}; + if(!out_frames.back()) + return "Couldn't allocate output frame."s; - return nonstd::nullopt; + return std::nullopt; } std::pair pictura_mediocritas::ffmpeg_parser::size() const noexcept { @@ -323,10 +333,3 @@ bool pictura_mediocritas::ffmpeg_parser::process(const std::function & c return true; } - -std::uint8_t pictura_mediocritas::ffmpeg_parser::operator[](std::size_t idx) const noexcept { - if(out_frame && out_frame->data[0]) - return out_frame->data[0][idx]; - else - return -1; -} diff --git a/src/parser/ffmpeg.hpp b/src/parser/ffmpeg.hpp index 06bb2c2..5c716f7 100644 --- a/src/parser/ffmpeg.hpp +++ b/src/parser/ffmpeg.hpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include extern "C" { @@ -82,10 +82,10 @@ namespace pictura_mediocritas { std::unique_ptr orig_frame; std::unique_ptr best_codec_ctx; std::unique_ptr colour_conv_ctx; - std::unique_ptr out_frame; + std::vector> out_frames; int best_stream; - AVCodec * best_codec; + const AVCodec * best_codec; std::size_t channels; error_class_t error_class; @@ -97,16 +97,18 @@ namespace pictura_mediocritas { public: - ffmpeg_parser(const char * filename, std::size_t channels); + std::size_t frame_num; + + ffmpeg_parser(const char * filename, std::size_t channels, std::size_t runners); /// Check if this parser is in a valid state. explicit operator bool() const noexcept; /// Get the error string, or `nullopt` if conversion to bool is `true`. - nonstd::optional error() const; + std::optional error() const; - /// Get current frame's size as `{width, height}`, or `{0, 0}` if noty yet ready. + /// Get current frame's size as `{width, height}`, or `{0, 0}` if not yet ready. std::pair size() const noexcept; /// Get frame count, or `0` if not yet ready. @@ -115,12 +117,37 @@ namespace pictura_mediocritas { /// Run through every frame, calling `callback()` after initialising it. bool process(const std::function & callback); + /// Finalise the specified framebuffer, which must have a &-returning operator[] valid in [0; width * height * channels) ∩ ℤ + template + void postprocess(FB & fb); + /// Get the specified colour fragment. /// /// Valid inside [0; width * height * channels) ∩ ℤ, undefined behaviour thereoutside. /// /// `idx` is in the format `(y * width + x) * decltype(frame)::channels + channel` /// (i.e. the one required by `average_frame`). - std::uint8_t operator[](std::size_t idx) const noexcept; + struct deref { + std::size_t idx; + std::size_t frame_num; + }; + template + std::uint8_t operator[](const deref & idx) const noexcept; }; + + + template + void pictura_mediocritas::ffmpeg_parser::postprocess(FB & fb) { + for(auto y = 0; y < out_frames[0]->height / 2; ++y) + for(auto x = 0; x < out_frames[0]->width; ++x) + for(auto c = 0u; c < channels; ++c) + fb.swap((y * out_frames[0]->width + x) * channels + c, ((out_frames[0]->height - 1 - y) * out_frames[0]->width + x) * channels + c); + } + + + template + std::uint8_t pictura_mediocritas::ffmpeg_parser::operator[](const deref & idx) const noexcept { + auto & out_frame = out_frames[idx.frame_num % out_frames.size()]; + return out_frame->data[0][idx.idx]; + } } diff --git a/src/parser/multi_image.cpp b/src/parser/multi_image.cpp index caa8107..9fc6164 100644 --- a/src/parser/multi_image.cpp +++ b/src/parser/multi_image.cpp @@ -65,41 +65,3 @@ void pictura_mediocritas::multi_image_parser::next() { ++cur_page_idx; lock_page(); } - -std::uint8_t pictura_mediocritas::multi_image_parser::operator[](std::size_t idx) { - if(!channels) - return -1; - - const auto width = size().first; - if(!width) - return -1; - - const auto channel = idx % channels; // idx = (y * width + x) * decltype(frame)::channels + channel - idx -= channel; // idx = (y * width + x) * decltype(frame)::channels - idx /= channels; // idx = y * width + x - const auto x = idx % width; // - idx -= x; // idx = y * width - idx /= width; // idx = y - const auto y = idx; // - - if(!cached || (cache_x != x || cache_y != y)) { - cached = FreeImage_GetPixelColor(cur_page.get(), x, y, &cache); - cache_x = x; - cache_y = y; - } - - if(cached) - switch(channel) { - case 0: - return cache.rgbRed; - case 1: - return cache.rgbGreen; - case 2: - return cache.rgbBlue; - case 3: - return cache.rgbReserved; - } - - std::cerr << "Failed to get pixel " << x << 'x' << y << " frame #" << cur_page_idx << ".\n"; - return -1; -} diff --git a/src/parser/multi_image.hpp b/src/parser/multi_image.hpp index 7371add..fa8d823 100644 --- a/src/parser/multi_image.hpp +++ b/src/parser/multi_image.hpp @@ -80,6 +80,45 @@ namespace pictura_mediocritas { /// /// `idx` is in the format `(y * width + x) * decltype(frame)::channels + channel` /// (i.e. the one required by `average_frame`). + template std::uint8_t operator[](std::size_t idx); }; + + + template + std::uint8_t pictura_mediocritas::multi_image_parser::operator[](std::size_t idx) { + if(!channels) + return -1; + + const auto width = size().first; + if(!width) + return -1; + + const auto channel = idx % channels; // idx = (y * width + x) * decltype(frame)::channels + channel + idx -= channel; // idx = (y * width + x) * decltype(frame)::channels + idx /= channels; // idx = y * width + x + const auto x = idx % width; // + idx -= x; // idx = y * width + idx /= width; // idx = y + const auto y = idx; // + + if(!cached || (cache_x != x || cache_y != y)) { + cached = FreeImage_GetPixelColor(cur_page.get(), x, y, &cache); + cache_x = x; + cache_y = y; + } + + if(cached) [[likely]] + switch(channel) { + case 0: + return cache.rgbRed; + case 1: + return cache.rgbGreen; + case 2: + return cache.rgbBlue; + case 3: + return cache.rgbReserved; + } + std::terminate(); + } } diff --git a/src/progressbar/os_progressbar.hpp b/src/progressbar/os_progressbar.hpp deleted file mode 100644 index bcb3e49..0000000 --- a/src/progressbar/os_progressbar.hpp +++ /dev/null @@ -1,72 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#pragma once - - -#include - -#ifdef _WIN32 -#include - -#include - -#include -#else -#endif - - -namespace pictura_mediocritas { - /// An OS-dependent progress bar. - /// - /// Taskbar progress on Windows. - /// No-op on non-Windows. - class os_progressbar { - private: -#ifdef _WIN32 - struct ITaskbarList3_releaser { - void operator()(ITaskbarList3 * list); - }; - - std::unique_ptr taskbar_list; - HWND console_window; - std::size_t curr_progress; - std::size_t max_progress; -#else -#endif - - public: - /// Create a new progressbar with the specified number of steps. - /// - /// @param max The number of times the progressbar must be incremented before it is considered complete, - /// or, in other words, the number of tasks that this progressbar is tracking. - os_progressbar(std::size_t max); - ~os_progressbar(); - - /// Increment progressbar. Don't increment past the initialized # of steps, though. - void inc(); - - /// Set the current status on the progressbar. - void update(std::size_t value); - }; -} diff --git a/src/progressbar/os_progressbar_non_windows.cpp b/src/progressbar/os_progressbar_non_windows.cpp deleted file mode 100644 index 96265d6..0000000 --- a/src/progressbar/os_progressbar_non_windows.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#ifdef _WIN32 -#else - - -#include "os_progressbar.hpp" - - -pictura_mediocritas::os_progressbar::os_progressbar(std::size_t) {} - -pictura_mediocritas::os_progressbar::~os_progressbar() {} - -void pictura_mediocritas::os_progressbar::inc() {} - -void pictura_mediocritas::os_progressbar::update(std::size_t) {} - - -#endif diff --git a/src/progressbar/os_progressbar_windows.cpp b/src/progressbar/os_progressbar_windows.cpp deleted file mode 100644 index f52ce87..0000000 --- a/src/progressbar/os_progressbar_windows.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#ifdef _WIN32 - - -#define INITGUID -#include "os_progressbar.hpp" -#include - - -void pictura_mediocritas::os_progressbar::ITaskbarList3_releaser::operator()(ITaskbarList3 * list) { - list->Release(); -} - -pictura_mediocritas::os_progressbar::os_progressbar(std::size_t max) : console_window(GetConsoleWindow()), curr_progress(0), max_progress(max) { - void * tbar; - CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_ALL, IID_ITaskbarList3, &tbar); - taskbar_list.reset(static_cast(tbar)); - - taskbar_list->SetProgressState(console_window, TBPF_NORMAL); -} - -pictura_mediocritas::os_progressbar::~os_progressbar() { - taskbar_list->SetProgressState(console_window, TBPF_NOPROGRESS); -} - -void pictura_mediocritas::os_progressbar::inc() { - if(curr_progress != std::numeric_limits::max()) - update(curr_progress + 1); -} - -void pictura_mediocritas::os_progressbar::update(std::size_t value) { - if(value <= max_progress) { - curr_progress = value; - taskbar_list->SetProgressValue(console_window, curr_progress, max_progress); - } -} - - -#else -#endif diff --git a/src/progressbar/progressbar.cpp b/src/progressbar/progressbar.cpp deleted file mode 100644 index 8bb0a85..0000000 --- a/src/progressbar/progressbar.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#include "progressbar.hpp" - - -using namespace std::literals; - - -void pictura_mediocritas::progressbar::set_defaults(std::size_t max) { - text_bar.max_refresh_rate(100ms); - if(!max) { - text_bar.show_tick = true; - text_bar.show_bar = false; - } -} - -pictura_mediocritas::progressbar::progressbar(const char * label, std::size_t max) : text_bar(max), os_bar(max) { - text_bar.message(label); - set_defaults(max); -} - -pictura_mediocritas::progressbar::progressbar(const std::string & label, std::size_t max) : text_bar(max), os_bar(max) { - text_bar.message(label); - set_defaults(max); -} - -void pictura_mediocritas::progressbar::inc() { - ++text_bar; - os_bar.inc(); -} - -void pictura_mediocritas::progressbar::update(std::size_t value) { - text_bar = value; - os_bar.update(value); -} - -void pictura_mediocritas::progressbar::finish() { - text_bar.finish(); -} diff --git a/src/progressbar/progressbar.hpp b/src/progressbar/progressbar.hpp deleted file mode 100644 index b47391d..0000000 --- a/src/progressbar/progressbar.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - - -#pragma once - - -#include -#include "os_progressbar.hpp" -#include - - -namespace pictura_mediocritas { - /// A colluded text and taskbar (on Windows) progress bar. - class progressbar { - private: - pb::progressbar text_bar; - os_progressbar os_bar; - - void set_defaults(std::size_t max); - - public: - /// Create a new progressbar with the specified label and number of steps. - /// - /// @param label The label that will prefix the progressbar. - /// @param max The number of times the progressbar must be incremented before it is considered complete, - /// or, in other words, the number of tasks that this progressbar is tracking. - progressbar(const char * label, std::size_t max); - progressbar(const std::string & label, std::size_t max); - - /// Increment progressbar. Don't increment past the initialized # of steps, though. - void inc(); - - /// Set the current status on the progressbar. - void update(std::size_t value); - - /// Mark the status bar as "finished". - void finish(); - }; -} diff --git a/src/util.cpp b/src/util.cpp index 9b8c2f7..dd82be8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -25,27 +25,10 @@ #include #include #include -#include #include #include -pictura_mediocritas::quickscope_wrapper::~quickscope_wrapper() { - if(func) - func(); -} - - -bool pictura_mediocritas::file_exists(const char * path) { - struct stat info; - return stat(path, &info) == 0 && info.st_mode & S_IFREG; -} - -bool pictura_mediocritas::directory_exists(const char * path) { - struct stat info; - return stat(path, &info) == 0 && info.st_mode & S_IFDIR; -} - bool pictura_mediocritas::has_extension(const char * path, const char * ext) { const auto path_len = std::strlen(path); auto path_ext = path + path_len; @@ -65,12 +48,9 @@ bool pictura_mediocritas::has_extension(const char * path, const char * ext) { return false; } -std::string pictura_mediocritas::switch_extenstion(const std::string & path, const char * new_ext) { +std::string pictura_mediocritas::switch_extenstion(const std::string_view & path, const char * new_ext) { const auto dot = path.find_last_of('.'); - if(dot == std::string::npos) - return path + '.' + new_ext; - else - return path.substr(0, dot) + '.' + new_ext; + return (std::string{(dot == std::string::npos) ? path : path.substr(0, dot)} += '.') += new_ext; } FREE_IMAGE_FORMAT pictura_mediocritas::deduce_image_format(const char * path) { @@ -99,17 +79,3 @@ FREE_IMAGE_FORMAT pictura_mediocritas::deduce_image_format(const char * path) { else return FIF_UNKNOWN; } - -std::vector pictura_mediocritas::read_file(const char * path) { - std::vector ret; - - std::ifstream in_file(path, std::ios::in | std::ios::binary); - std::uint8_t buf[1024]; - in_file.read(static_cast(static_cast(buf)), sizeof buf / sizeof *buf); - while(in_file.gcount()) { - ret.insert(ret.end(), buf, buf + in_file.gcount()); - in_file.read(static_cast(static_cast(buf)), sizeof buf / sizeof *buf); - } - - return ret; -} diff --git a/src/util.hpp b/src/util.hpp index fec7dce..58f5c50 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -24,27 +24,22 @@ #pragma once +#include #include -#include #include -#include -#include namespace pictura_mediocritas { + template struct quickscope_wrapper { - std::function func; - - ~quickscope_wrapper(); + F func; + ~quickscope_wrapper() { func(); } }; + template + quickscope_wrapper(F) -> quickscope_wrapper; - bool file_exists(const char * path); - bool directory_exists(const char * path); - bool has_extension(const char * path, const char * ext); - std::string switch_extenstion(const std::string & path, const char * new_ext); + std::string switch_extenstion(const std::string_view & path, const char * new_ext); FREE_IMAGE_FORMAT deduce_image_format(const char * path); - - std::vector read_file(const char * path); } diff --git a/tests/average_frame.cpp b/tests/average_frame.cpp index 9677bde..c4226cd 100644 --- a/tests/average_frame.cpp +++ b/tests/average_frame.cpp @@ -20,8 +20,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER -#include +#include #include "average_frame.hpp" #include @@ -31,20 +30,18 @@ using namespace std::literals; -namespace Catch { - template <> +namespace doctest { template struct StringMaker> { - static std::string convert(const pictura_mediocritas::average_frame & value) { + static String convert(const pictura_mediocritas::average_frame & value) { return "{size=" + StringMaker::convert(value.size()) + ", frames=" + StringMaker::convert(value.processed_frames()) + "}"; } }; - template <> template struct StringMaker> { - static std::string convert(const std::array & value) { + static String convert(const std::array & value) { std::stringstream oss; oss << "["; bool first = true; @@ -55,13 +52,13 @@ namespace Catch { oss << v; } oss << "]"; - return oss.str(); + return oss.str().c_str(); } }; } -TEST_CASE("pictura_mediocritas::channels", "[average_frame]") { +TEST_CASE("pictura_mediocritas::channels") { #define CHANNELS(ch) \ REQUIRE((pictura_mediocritas::average_frame::channels) == ch); \ REQUIRE((pictura_mediocritas::average_frame::channels) == ch); \ @@ -84,8 +81,8 @@ TEST_CASE("pictura_mediocritas::channels", "[average_frame]") { #undef CHANNELS } -TEST_CASE("pictura_mediocritas::value_type", "[average_frame]") { -#define TYPES(t) \ +TEST_CASE("pictura_mediocritas::value_type") { +#define TYPES(t) \ REQUIRE((std::is_same::value_type, t>::value)); \ REQUIRE((std::is_same::value_type, t>::value)); \ REQUIRE((std::is_same::value_type, t>::value)); \ @@ -106,7 +103,7 @@ TEST_CASE("pictura_mediocritas::value_type", "[average_frame]") { #undef TYPES } -TEST_CASE("pictura_mediocritas::average_frame(width, height)", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame(width, height)") { std::array zero{}; pictura_mediocritas::average_frame_u64 hd(1280, 720); @@ -129,7 +126,7 @@ TEST_CASE("pictura_mediocritas::average_frame(width, height)", "[average_frame]" REQUIRE(hd.pixel(i) == zero); } -TEST_CASE("pictura_mediocritas::average_frame(size)", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame(size)") { std::array zero{}; pictura_mediocritas::average_frame_u64 hd(std::make_pair(1280, 720)); @@ -151,29 +148,52 @@ TEST_CASE("pictura_mediocritas::average_frame(size)", "[average_frame]") { REQUIRE(hd.pixel(i) == zero); } -TEST_CASE("pictura_mediocritas::average_frame::operator==()", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame::operator==()") { REQUIRE(pictura_mediocritas::average_frame_u64(1280, 720) == pictura_mediocritas::average_frame_u64(std::make_pair(1280, 720))); REQUIRE(pictura_mediocritas::average_frame_u64(1920, 1080) == pictura_mediocritas::average_frame_u64(std::make_pair(1920, 1080))); } -TEST_CASE("pictura_mediocritas::average_frame::operator!=()", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame::operator!=()") { REQUIRE(pictura_mediocritas::average_frame_u64(1280, 720) != pictura_mediocritas::average_frame_u64(1920, 1080)); REQUIRE_FALSE(pictura_mediocritas::average_frame_u64(1280, 720) != pictura_mediocritas::average_frame_u64(std::make_pair(1280, 720))); REQUIRE_FALSE(pictura_mediocritas::average_frame_u64(1920, 1080) != pictura_mediocritas::average_frame_u64(std::make_pair(1920, 1080))); } -TEST_CASE("pictura_mediocritas::average_frame::processed_frames()", "[average_frame]") { +namespace { + template + struct faux { + std::uint8_t data[N]; + + struct idx { + std::size_t i; + }; + const std::uint8_t & operator[](const struct idx & idx) const { return data[idx.i]; } + }; + + template + struct faux2 { + std::uint8_t data[N]; + + struct idx { + std::size_t i; + int _; + }; + const std::uint8_t & operator[](const struct idx & idx) const { return data[idx.i]; } + }; +} + +TEST_CASE("pictura_mediocritas::average_frame::processed_frames()") { pictura_mediocritas::average_frame frame(1, 1); for(int i = 0; i < 10; ++i) { REQUIRE(frame.processed_frames() == i); - const std::uint8_t f[] = {static_cast('0' + i)}; + faux<1> f{static_cast('0' + i)}; frame.process_frame(f); REQUIRE(frame.processed_frames() == i + 1); } } -TEST_CASE("pictura_mediocritas::average_frame::operator[]()", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame::operator[]()") { pictura_mediocritas::average_frame frame(1, 1); REQUIRE(frame[0] == 0); @@ -181,13 +201,13 @@ TEST_CASE("pictura_mediocritas::average_frame::operator[]()", "[average_frame]") for(auto i = 0u; i < 256; ++i) { acc += i; - const std::uint8_t f[] = {static_cast(i)}; - frame.process_frame(f); + faux2<1> f{static_cast(i)}; + frame.process_frame(f, 0); REQUIRE(frame[0] == acc / (i + 1)); } } -TEST_CASE("pictura_mediocritas::average_frame::pixel()", "[average_frame]") { +TEST_CASE("pictura_mediocritas::average_frame::pixel()") { pictura_mediocritas::average_frame frame(1, 1); REQUIRE(frame.pixel(0) == (std::array{0, 0})); @@ -197,7 +217,7 @@ TEST_CASE("pictura_mediocritas::average_frame::pixel()", "[average_frame]") { acc_inc += i; acc_dec += 255 - i; - const std::uint8_t f[] = {static_cast(i), static_cast(255 - i)}; + faux<2> f{static_cast(i), static_cast(255 - i)}; frame.process_frame(f); REQUIRE(frame.pixel(0) == (std::array{acc_inc / (i + 1), acc_dec / (i + 1)})); } diff --git a/tests/main.cpp b/tests/main.cpp index b5f405b..39716d8 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -20,14 +20,12 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#define CATCH_CONFIG_MAIN -#include +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include #include "test_util.hpp" #include #include -#include -#include const char * temp_dir() { for(auto e : {"TEMP", "TMP"}) @@ -72,8 +70,3 @@ void make_directory_recursive(const char * path) { } make_last_dir(tmp.c_str()); } - -// Will fail spuriously otherwise, becasuse statics :angery: -void reset_TCLAP() { - TCLAP::OptionalUnlabeledTracker::alreadyOptional() = false; -} diff --git a/tests/options.cpp b/tests/options.cpp index a9fe8f0..e13ddf8 100644 --- a/tests/options.cpp +++ b/tests/options.cpp @@ -20,83 +20,50 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER -#include +#include -#include "options/options.hpp" +#include "options.hpp" #include "test_util.hpp" #include +#include #include #include - using namespace std::literals; -namespace Catch { +namespace doctest { template <> struct StringMaker { - static std::string convert(const pictura_mediocritas::options & value) { - return "{in_video=\'" + value.in_video + "\', out_image=\'" + value.out_image + "\'}"; // + static String convert(const pictura_mediocritas::options & value) { + return (((("{in_video=\'"s += value.in_video) += "\', out_image=\'") += value.out_image) + "\'}").c_str(); } }; } +#define TUPLEQ(l_, r_) \ + { \ + auto l = l_; \ + auto r = r_; \ + REQUIRE(std::get<0>(l) == std::get<0>(r)); \ + REQUIRE(std::get<1>(l) == std::get<1>(r)); \ + REQUIRE(std::get<2>(l) == std::get<2>(r)); \ + } -TEST_CASE("pictura_mediocritas::options::parse() -- not enough args", "[options]") { - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{}, 1, - "pictura-mediocritas tests: error: parsing arguments failed (Required argument missing: in_video) for undefined argument")); -} - -TEST_CASE("pictura_mediocritas::options::parse() -- nonexistant input file", "[options][incorrect]") { - const auto temp = temp_dir() + "/PicturaMediocritas/options/incorrect/nonexistant-input/"s; - make_directory_recursive(temp.c_str()); - - const auto nonexistant_file = temp + "nonexistant_file"; - - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", nonexistant_file.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{}, 1, - "pictura-mediocritas tests: error: parsing arguments failed (Value '" + nonexistant_file + - "' does not meet constraint: existing file) for Argument: (--in_video)")); -} - -TEST_CASE("pictura_mediocritas::options::parse() -- nonexistant output file parent", "[options][incorrect]") { - const auto temp = temp_dir() + "/PicturaMediocritas/options/incorrect/nonexistant-input/"s; - make_directory_recursive(temp.c_str()); - - const auto in_video = temp + "in_video.avi"; - const auto unparented_file = temp + "nonexistant_dir/unparented_file"; - std::ofstream{in_video}; - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", in_video.c_str(), unparented_file.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{}, 1, - "pictura-mediocritas tests: error: parsing arguments failed (Value '" + unparented_file + - "' does not meet constraint: creatable file) for Argument: (--out_image)")); +TEST_CASE("pictura_mediocritas::options::parse() -- not enough args") { + const char * args[] = {"pictura-mediocritas-tests", nullptr}; + TUPLEQ(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args), + std::make_tuple(pictura_mediocritas::options{}, 1, "usage: pictura-mediocritas-tests in-video [out-image]")); } -TEST_CASE("pictura_mediocritas::options::parse() -- output file a dir", "[options][incorrect]") { - const auto temp = temp_dir() + "/PicturaMediocritas/options/incorrect/nonexistant-input/"s; - make_directory_recursive(temp.c_str()); - - const auto in_video = temp + "in_video.avi"; - std::ofstream{in_video}; - - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", in_video.c_str(), temp.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{}, 1, - "pictura-mediocritas tests: error: parsing arguments failed (Value '" + temp + - "' does not meet constraint: creatable file) for Argument: (--out_image)")); +TEST_CASE("pictura_mediocritas::options::parse() -- too many args") { + const char * args[] = {"pictura-mediocritas-tests", "in", "out", "extra", nullptr}; + TUPLEQ(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args), + std::make_tuple(pictura_mediocritas::options{"in", "out"}, 1, "usage: pictura-mediocritas-tests in-video [out-image]")); } -TEST_CASE("pictura_mediocritas::options::parse() -- correct", "[options]") { +TEST_CASE("pictura_mediocritas::options::parse() -- correct") { const auto temp = temp_dir() + "/PicturaMediocritas/options/correct/"s; make_directory_recursive(temp.c_str()); @@ -107,30 +74,26 @@ TEST_CASE("pictura_mediocritas::options::parse() -- correct", "[options]") { std::ofstream{in_video_ext}; { - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", in_video.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{in_video, in_video + ".png"}, 0, "")); + const char * args[] = {"pictura-mediocritas-tests", in_video.c_str(), nullptr}; + TUPLEQ(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args), + std::make_tuple(pictura_mediocritas::options{in_video, in_video + ".png"}, 0, "")); } { - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", in_video_ext.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{in_video_ext, in_video + ".png"}, 0, "")); + const char * args[] = {"pictura-mediocritas-tests", in_video_ext.c_str(), nullptr}; + TUPLEQ(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args), + std::make_tuple(pictura_mediocritas::options{in_video_ext, in_video + ".png"}, 0, "")); } { - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", in_video.c_str(), out_image.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{in_video, out_image}, 0, "")); + const char * args[] = {"pictura-mediocritas-tests", in_video.c_str(), out_image.c_str(), nullptr}; + TUPLEQ(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args), + std::make_tuple(pictura_mediocritas::options{in_video, out_image}, 0, "")); } { - reset_TCLAP(); - const char * args[] = {"pictura-mediocritas tests", in_video_ext.c_str(), out_image.c_str(), nullptr}; - REQUIRE(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args) == - std::make_tuple(pictura_mediocritas::options{in_video_ext, out_image}, 0, "")); + const char * args[] = {"pictura-mediocritas-tests", in_video_ext.c_str(), out_image.c_str(), nullptr}; + TUPLEQ(pictura_mediocritas::options::parse(sizeof args / sizeof *args - 1, args), + std::make_tuple(pictura_mediocritas::options{in_video_ext, out_image}, 0, "")); } } diff --git a/tests/test_util.hpp b/tests/test_util.hpp index c468a2b..2d51e94 100644 --- a/tests/test_util.hpp +++ b/tests/test_util.hpp @@ -22,4 +22,3 @@ void make_directory_recursive(const char * path); const char * temp_dir(); -void reset_TCLAP(); diff --git a/tests/util/deduce_image_format.cpp b/tests/util/deduce_image_format.cpp index 2f72eac..8e3c3ca 100644 --- a/tests/util/deduce_image_format.cpp +++ b/tests/util/deduce_image_format.cpp @@ -22,7 +22,7 @@ #include "../test_util.hpp" #include "util.hpp" -#include +#include #include #include @@ -30,7 +30,7 @@ using namespace std::literals; -TEST_CASE("util::deduce_image_format() -- nonexistant", "[util]") { +TEST_CASE("util::deduce_image_format() -- nonexistant") { REQUIRE(pictura_mediocritas::deduce_image_format("image.bMP") == FIF_BMP); REQUIRE(pictura_mediocritas::deduce_image_format("image.Ico") == FIF_ICO); @@ -56,7 +56,7 @@ TEST_CASE("util::deduce_image_format() -- nonexistant", "[util]") { REQUIRE(pictura_mediocritas::deduce_image_format("image.WeBp") == FIF_WEBP); } -TEST_CASE("util::deduce_image_format() -- unrecognised", "[util]") { +TEST_CASE("util::deduce_image_format() -- unrecognised") { REQUIRE(pictura_mediocritas::deduce_image_format("README.md") == FIF_UNKNOWN); REQUIRE(pictura_mediocritas::deduce_image_format("Makefile") == FIF_UNKNOWN); } diff --git a/tests/util/file_exists.cpp b/tests/util/file_exists.cpp deleted file mode 100644 index 41c9a7a..0000000 --- a/tests/util/file_exists.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include "../test_util.hpp" -#include "util.hpp" -#include -#include -#include - - -using namespace std::literals; - - -TEST_CASE("util::file_exists() -- nonexistant", "[util]") { - const auto temp = temp_dir() + "/PicturaMediocritas/util/file_exists/"s; - make_directory_recursive(temp.c_str()); - - REQUIRE_FALSE(pictura_mediocritas::file_exists((temp + "nonexistant_file").c_str())); -} - -TEST_CASE("util::file_exists() -- existant file", "[util]") { - const auto temp = temp_dir() + "/PicturaMediocritas/util/file_exists/"s; - make_directory_recursive(temp.c_str()); - - std::ofstream(temp + "file"); - REQUIRE(pictura_mediocritas::file_exists((temp + "file").c_str())); -} diff --git a/tests/util/has_extension.cpp b/tests/util/has_extension.cpp index 475cde1..8f2a621 100644 --- a/tests/util/has_extension.cpp +++ b/tests/util/has_extension.cpp @@ -21,20 +21,20 @@ #include "util.hpp" -#include +#include -TEST_CASE("util::has_extension() -- equadistant", "[util]") { - REQUIRE(pictura_mediocritas::has_extension("gif_load/test.GIf", "giF")); - REQUIRE(pictura_mediocritas::has_extension("test.pnG", "PnG")); - REQUIRE(pictura_mediocritas::has_extension("README.MD", "md")); +TEST_CASE("util::has_extension() -- equadistant") { + REQUIRE(pictura_mediocritas::has_extension("gif_load/test.GIf", "GIF")); + REQUIRE(pictura_mediocritas::has_extension("test.pnG", "PNG")); + REQUIRE(pictura_mediocritas::has_extension("README.MD", "MD")); - REQUIRE_FALSE(pictura_mediocritas::has_extension("gif_load/test_GIf", "giF")); - REQUIRE_FALSE(pictura_mediocritas::has_extension("test", "PnG")); + REQUIRE_FALSE(pictura_mediocritas::has_extension("gif_load/test_GIf", "gif")); + REQUIRE_FALSE(pictura_mediocritas::has_extension("test", "gif")); } -TEST_CASE("util::has_extension() -- interdistant", "[util]") { - REQUIRE_FALSE(pictura_mediocritas::has_extension("gif_load/test.Gf", "giF")); - REQUIRE_FALSE(pictura_mediocritas::has_extension("test.", "PnG")); - REQUIRE_FALSE(pictura_mediocritas::has_extension("README.MD", "m")); +TEST_CASE("util::has_extension() -- interdistant") { + REQUIRE_FALSE(pictura_mediocritas::has_extension("gif_load/test.Gf", "GIF")); + REQUIRE_FALSE(pictura_mediocritas::has_extension("test.", "PNG")); + REQUIRE_FALSE(pictura_mediocritas::has_extension("README.MDD", "MD")); } diff --git a/tests/util/quickscope_wrapper.cpp b/tests/util/quickscope_wrapper.cpp index ad562d4..591ac61 100644 --- a/tests/util/quickscope_wrapper.cpp +++ b/tests/util/quickscope_wrapper.cpp @@ -21,10 +21,10 @@ #include "util.hpp" -#include +#include -TEST_CASE("util::quickscope_wrapper()", "[util]") { +TEST_CASE("util::quickscope_wrapper()") { bool updated = false; { pictura_mediocritas::quickscope_wrapper updated_updater{[&]() { updated = true; }}; diff --git a/tests/util/read_file.cpp b/tests/util/read_file.cpp deleted file mode 100644 index 4977bde..0000000 --- a/tests/util/read_file.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include "../test_util.hpp" -#include "util.hpp" -#include -#include -#include - - -using namespace std::literals; - - -TEST_CASE("util::read_file() -- nonexistant", "[util]") { - const auto temp = temp_dir() + "/PicturaMediocritas/util/read_file/"s; - make_directory_recursive(temp.c_str()); - - REQUIRE(pictura_mediocritas::read_file((temp + "nonexistant_file").c_str()) == std::vector{}); -} - -TEST_CASE("util::read_file() -- existant file", "[util]") { - const auto temp = temp_dir() + "/PicturaMediocritas/util/read_file/"s; - make_directory_recursive(temp.c_str()); - - std::ofstream(temp + "file"); - REQUIRE(pictura_mediocritas::read_file((temp + "file").c_str()) == std::vector{}); - - const auto data = R"~~( -// The MIT License (MIT) - -// Copyright (c) 2017 Lounge - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -)~~"s; - std::ofstream(temp + "non_empty_file", std::ios::out | std::ios::binary) << data; - REQUIRE(pictura_mediocritas::read_file((temp + "non_empty_file").c_str()) == (std::vector{data.begin(), data.end()})); -} diff --git a/tests/util/switch_extenstion.cpp b/tests/util/switch_extenstion.cpp index e419a40..4cddfaa 100644 --- a/tests/util/switch_extenstion.cpp +++ b/tests/util/switch_extenstion.cpp @@ -21,16 +21,16 @@ #include "util.hpp" -#include +#include -TEST_CASE("util::switch_extenstion() -- with extension", "[util]") { +TEST_CASE("util::switch_extenstion() -- with extension") { REQUIRE(pictura_mediocritas::switch_extenstion("gif_load/test.GIf", "png") == "gif_load/test.png"); REQUIRE(pictura_mediocritas::switch_extenstion("test.png", "png") == "test.png"); REQUIRE(pictura_mediocritas::switch_extenstion("README.md", "png") == "README.png"); } -TEST_CASE("util::switch_extenstion() -- no extension", "[util]") { +TEST_CASE("util::switch_extenstion() -- no extension") { REQUIRE(pictura_mediocritas::switch_extenstion("gif_load/test", "png") == "gif_load/test.png"); REQUIRE(pictura_mediocritas::switch_extenstion("test", "png") == "test.png"); REQUIRE(pictura_mediocritas::switch_extenstion("README", "png") == "README.png");