Compare commits
49 commits
Author | SHA1 | Date | |
---|---|---|---|
|
4d21a05d80 | ||
|
a5b9c64c7a | ||
|
56bac07109 | ||
|
92cf12d2ec | ||
|
84a3616796 | ||
|
38ec4249b8 | ||
|
df32d7e1c1 | ||
|
8aca9afaf7 | ||
|
917b381bc3 | ||
|
a18c4e62c6 | ||
|
e5787c0288 | ||
|
2fa6ece556 | ||
|
4cb242b7f5 | ||
|
b783c67f66 | ||
|
8c9ca72902 | ||
|
a901624905 | ||
|
1c2c1c27e8 | ||
|
190689961e | ||
|
a216fc64eb | ||
|
ca97659f83 | ||
|
dedaa1b7c2 | ||
|
3373e02e4c | ||
|
4e1709a7c5 | ||
|
cc4b4b30bd | ||
|
516107ca4f | ||
|
8d785902e6 | ||
|
bc2d21db09 | ||
|
a64e5527a9 | ||
|
e70f05fdf6 | ||
|
7b5d7907d1 | ||
|
497b533010 | ||
|
c6b8855930 | ||
|
ac334fdfaa | ||
|
d9c1b47816 | ||
|
7b3c8286fe | ||
|
f51dc08b0a | ||
|
e636a03c13 | ||
|
9969dd1cff | ||
|
c001eca18d | ||
|
e72b11d359 | ||
|
e358e372ec | ||
|
0bfc892f4f | ||
|
bba4a692db | ||
|
512b3dd91c | ||
|
1154264c54 | ||
|
71d1a80d14 | ||
|
ba7bafe092 | ||
|
2b46a7a227 | ||
|
8f91b297f1 |
10 changed files with 2934 additions and 1642 deletions
35
.github/workflows/ci.yml
vendored
Normal file
35
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
name: Build and run
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
paths: ['src/**']
|
||||
pull_request:
|
||||
paths: ['src/**']
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get install -qy imagemagick
|
||||
- name: Validate gcc version
|
||||
run: |
|
||||
if [[ $(gcc --version | awk '/gcc/ && ($3+0)>13{print "gcc-13+"}') != "gcc-13+" ]]; then
|
||||
# Script courtesy of https://stackoverflow.com/a/67791068/16134571
|
||||
sudo apt install gcc-13 g++-13
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 100 --slave /usr/bin/g++ g++ /usr/bin/g++-13 --slave /usr/bin/gcov gcov /usr/bin/gcov-13
|
||||
sudo update-alternatives --set gcc /usr/bin/gcc-13
|
||||
fi
|
||||
- name: Build
|
||||
run: make -C src
|
||||
- name: Test
|
||||
run: |
|
||||
images=('/usr/local/share/icons/hicolor/128x128/apps/microsoft-edge.png' '/usr/local/share/icons/hicolor/128x128/apps/CMakeSetup.png' '/usr/local/doc/cmake/html/_static/file.png' '/usr/local/lib/android/sdk/extras/google/google_play_services/samples/tagmanager/cuteanimals/res/drawable/cat_1.jpg' '/usr/local/lib/android/sdk/extras/google/google_play_services/samples/wallet/res/drawable-ldpi/icon.png' '/usr/local/lib/android/sdk/extras/google/google_play_services/samples/wallet/res/drawable-hdpi/icon.png' '/usr/share/plymouth/themes/spinner/watermark.png' '/usr/share/apache2/icons/apache_pb.png' '/usr/share/doc/libpng-dev/examples/pngtest.png')
|
||||
image=${images[ $RANDOM % ${#images[@]} ]} # Get random image
|
||||
./src/tiv -w 160 -h 48 $image # Get random image
|
||||
echo $image
|
||||
./src/tiv -w 160 -h 48 /usr/share/pixmaps # Dir mode
|
43
.github/workflows/cimg.yml
vendored
Normal file
43
.github/workflows/cimg.yml
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
name: Update CImg
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0 0 3 * * # The first day after this workflow was merged
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
check-version:
|
||||
name: Check for updates
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
latest-tag: ${{ steps.latest-tag.outputs.result }}
|
||||
steps:
|
||||
- name: Fetch the latest tag (could be buggy)
|
||||
uses: actions/github-script@v7
|
||||
id: latest-tag
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
return (await github.rest.repos.listTags({ owner: 'GreycLab', repo: 'CImg', per_page: 28 })).data[27].name
|
||||
pull-file:
|
||||
name: Update CImg.h
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-version
|
||||
if: ${{ needs.check-version.outputs.latest-tag != 'v.3.3.6' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
name: Checkout this repository
|
||||
with:
|
||||
token: ${{ secrets.CIMG_UPDATE_TOKEN }}
|
||||
- name: Download new CImg version
|
||||
uses: carlosperate/download-file-action@v2
|
||||
with:
|
||||
file-url: 'https://github.com/GreycLab/CImg/raw/${{ needs.check-version.outputs.latest-tag }}/CImg.h'
|
||||
location: 'src'
|
||||
- name: Commit new CImg version (and update script)
|
||||
run: |
|
||||
sed -i 's/v.3.3.6/${{ needs.check-version.outputs.latest-tag }}/' .github/workflows/cimg.yml
|
||||
git config user.name 'GitHub Actions'
|
||||
git config user.email 'actions@github.com'
|
||||
git commit -am "Update CImg to ${{ needs.check-version.outputs.latest-tag }}"
|
||||
git push
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -47,6 +47,10 @@ x64/
|
|||
*.vcxproj.*
|
||||
|
||||
|
||||
###Visual Studio Code###
|
||||
.vscode/
|
||||
|
||||
|
||||
###Java###
|
||||
|
||||
# Compiled class file
|
||||
|
|
11
LICENSE
11
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2023 Stefan Haustein and Aaron Liu
|
||||
Copyright (c) 2024 Stefan Haustein and Aaron Liu
|
||||
|
||||
This program is free software: you may copy, redistribute and/or modify it
|
||||
under the terms of (at your option) either the Apache License, Version 2.0,
|
||||
|
@ -10,5 +10,10 @@ WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
For details, see either
|
||||
<https://www.apache.org/licenses/LICENSE-20> for the Apache License, Version 2.0
|
||||
, or <https://www.gnu.org/licenses/gpl-3.0.en.html> for the GNU GPL, version 3.
|
||||
<https://www.apache.org/licenses/LICENSE-20> for the Apache License, Version 2.0,
|
||||
or <https://www.gnu.org/licenses/gpl-3.0.en.html> for the GNU GPL, version 3.
|
||||
|
||||
Note that the code contained in this package contains the CImg library,
|
||||
which is licensed under either [CeCILL 2.0](https://spdx.org/licenses/CECILL-2.0.html)
|
||||
(close to GPL and compatible with it) or [CeCILL-C](https://spdx.org/licenses/CECILL-C)
|
||||
(close to LGPL and compatible with Apache).
|
||||
|
|
77
README.md
77
README.md
|
@ -21,19 +21,22 @@ The shell will expand wildcards. By default, thumbnails and file names will be d
|
|||
|
||||
## News
|
||||
|
||||
- 2020-10-22: The Java version is now **deprecated**. Development has long shifted to the C++ version since that was created, and the last meaningful update to it was in 2016.
|
||||
- 2021-05-22: We now support Apple Clang, thanks to the C++ filesystem library being no longer experimental. Issue forms have also been added to the GitHub repository.
|
||||
- 2023-09-29: Today marks the 40th anniversary of the GNU project. If you haven't learned the news concerning it and Stallman, please do. In project news, @aaronliu0130 will probably be developing this project from now on as the original author has moved on to better things to do. Support for MSVC has been added and the repository is now under an Apache 2.0 or GPL3 dual license. CI building for each release will hopefully be setup soon. The main program has also adopted a mostly Google code-style because I (aaron) think it simply makes sense.
|
||||
- 2024-03-20: Added a section on how to use the API.
|
||||
- 2024-02-01: We are currently working on splitting the source code into dependency-free library files and a client that uses CImg.
|
||||
- 2023-09-29: Today marks the 40th anniversary of the GNU project. If you haven't learned the news concerning it and Stallman, please do.
|
||||
Support for MSVC has been added and the repository is now under an Apache 2.0 or GPL3 dual license. CI building for each release will hopefully be setup soon. The main program has also adopted a mostly Google code-style because I (aaron) think it simply makes sense.
|
||||
`SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later`
|
||||
- 2021-05-22: We now support Apple Clang, thanks to the C++ filesystem library being no longer experimental. Issue forms have also been added to the GitHub repository.
|
||||
- 2020-10-22: The Java version is now **deprecated**. Development has long shifted to the C++ version since that was created, and the last meaningful update to it was in 2016.
|
||||
|
||||
## Installation
|
||||
|
||||
> [!IMPORTANT]
|
||||
> All installation methods require installing ImageMagick, a required dependency. Most package managers should install it automatically.
|
||||
|
||||
### Build from source
|
||||
### All platforms: Build from source
|
||||
|
||||
Our makefile currently only supports `g++`. It should be possible to compile `tiv` manually using any of your favorite compilers that support C++20 and Unix headers (`ioctl.h`, specifically) or `windows.h`. PRs are welcome.
|
||||
Our makefile currently only supports `g++`. It should be possible to compile `tiv` manually using any of your favorite compilers that support C++17 and Unix headers (`ioctl.h` and `sysexits.h`, specifically) or `windows.h`. PRs are welcome.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/stefanhaustein/TerminalImageViewer.git
|
||||
|
@ -44,36 +47,58 @@ make
|
|||
sudo make install
|
||||
```
|
||||
|
||||
### Homebrew
|
||||
Please don't forget to install ImageMagick... On Debian based Linux via `sudo apt install imagemagick` and
|
||||
on MacOS via `brew install imagemagick`.
|
||||
|
||||
Option 1:
|
||||
### Mac: Homebrew
|
||||
|
||||
```sh
|
||||
brew install tiv
|
||||
```
|
||||
|
||||
Option 2 (deprecated, for macOS Mojave and below):
|
||||
As the original Apple Shell only supports 256 color mode (-256) and there seems to be some extra
|
||||
line spacing, distorting the image, we also recommend installing iTerm2:
|
||||
|
||||
```sh
|
||||
brew tap stefanhaustein/TerminalImageViewer https://github.com/stefanhaustein/TerminalImageViewer
|
||||
brew install terminalimageviewer
|
||||
```
|
||||
brew install --cask iterm2
|
||||
```
|
||||
|
||||
### Snap (outdated)
|
||||
### Third-Party Packages
|
||||
|
||||
sudo snap install --edge tiv
|
||||
|
||||
## Packages
|
||||
|
||||
- @aaronliu0130 has added homebrew support.
|
||||
- @megamaced has created [an RPM for SUSE](https://build.opensuse.org/package/show/home:megamaced/terminalimageviewer)
|
||||
- @bperel has created [a Docker image](https://hub.docker.com/r/bperel/terminalimageviewer)
|
||||
- @teresaejunior has created a snapcraft.yaml file, which can build a Snap package with `sudo docker run -it --rm -v "$PWD:$PWD" -w "$PWD" snapcore/snapcraft sh -c 'apt-get update && snapcraft'`, and then installed with `sudo snap install --dangerous ./*.snap`. This is no longer supported.
|
||||
- An AUR package is also available, though the maintainer appears to be inactive. Replacements coming soon.
|
||||
|
||||
|
||||
## Common problems / Troubleshooting
|
||||
|
||||
- Errors such as "unrecognized file format"? Make sure ImageMagic is installed.
|
||||
- On some linux platforms, an extra flag seems to be required: `make LDLIBS=-lstdc++fs` (but it also breaks MacOs), see <https://github.com/stefanhaustein/TerminalImageViewer/issues/103>
|
||||
- If you see strange horizontal lines, the characters don't fully fill the character cell. Remove additional line spacing in your terminal app
|
||||
- Wrong colors? Try -256 to use a 256 color palette instead of 24 bit colors
|
||||
- Strange characters? Try -0 or install an use full unicode font (e.g. inconsolata or firacode)
|
||||
|
||||
## Using the TIV API
|
||||
|
||||
Tiv can be used as an API. So if you always wanted to run your favorite FPS in a shell, this is the opportunity.
|
||||
|
||||
All the code useful as a library is isolated in [tiv_lib.h](https://github.com/stefanhaustein/TerminalImageViewer/blob/master/src/tiv_lib.h)
|
||||
and [tiv_lib.cc](https://github.com/stefanhaustein/TerminalImageViewer/blob/master/src/tiv_lib.cc).
|
||||
|
||||
The main entry point is
|
||||
|
||||
```cpp
|
||||
CharData findCharData(GetPixelFunction get_pixel, int x0, int y0, const int &flags)
|
||||
```
|
||||
|
||||
The call takes a std::Function that allows the TIV code to request pixels from your framebuffer.
|
||||
|
||||
From this framebuffer, the call will query pixels for a 4x8 pixel rectangle, where x0 and y0
|
||||
define the top left corner. The call searches the best unicode graphics character and colors to approximate this
|
||||
cell of the image, and returns these in a CharData struct.
|
||||
|
||||
## Contributions
|
||||
|
||||
- 2019-03-26: Exciting week: @Cableo has fixed output redirection, @boretom has added cross-compilation support to the build file and @AlanDeSmet has fixed tall thumbnails and greyscale images.
|
||||
- 2019-03-26: Exciting week: @cabelo has fixed output redirection, @boretom has added cross-compilation support to the build file and @AlanDeSmet has fixed tall thumbnails and greyscale images.
|
||||
- 2020-07-05: @cxwx has fixed homebrew support.
|
||||
|
||||
I am happy to accept useful contributions under the Apache 2.0 license, but...
|
||||
|
@ -83,13 +108,6 @@ I am happy to accept useful contributions under the Apache 2.0 license, but...
|
|||
- This program currently only depends on CImg and ImageMagick as image processing libraries and I'd prefer to keep it that way.
|
||||
- Support for additional platforms, CPUs or similar will require somebody who is happy to help with maintenance, in particular if I don't have access to it.
|
||||
|
||||
## Common problems
|
||||
|
||||
- On some linux platforms, an extra flag seems to be required: `make LDLIBS=-lstdc++fs` (but it also breaks MacOs), see <https://github.com/stefanhaustein/TerminalImageViewer/issues/103>
|
||||
- If you see strange horizontal lines, the characters don't fully fill the character cell. Remove additional line spacing in your terminal app
|
||||
- Wrong colors? Try -256 to use a 256 color palette instead of 24 bit colors
|
||||
- Strange characters? Try -0 or install an use full unicode font (e.g. inconsolata or firacode)
|
||||
|
||||
## Examples
|
||||
|
||||
Most examples were shot with the Java version of this program, which should have equivalent output but slower by millenia in CPU years.
|
||||
|
@ -103,3 +121,8 @@ If multiple images match the filename spec, thumbnails are shown.
|
|||
For the example below, the top image was generated with the character optimization disabled via the `-0` option.
|
||||
|
||||

|
||||
|
||||
## Licensing
|
||||
|
||||
You are free to use this code under either the GPL (3 or later) or version 2.0 of the Apache license. We include the CImg library, which
|
||||
is licensed under either [CeCILL 2.0](https://spdx.org/licenses/CECILL-2.0.html) (close to GPL and compatible with it) or [CeCILL-C] (https://spdx.org/licenses/CECILL-C) (close to LGPL and compatible with Apache).
|
||||
|
|
3366
src/CImg.h
3366
src/CImg.h
File diff suppressed because it is too large
Load diff
10
src/Makefile
10
src/Makefile
|
@ -1,7 +1,7 @@
|
|||
PROGNAME = tiv
|
||||
|
||||
OBJECTS = tiv.o
|
||||
|
||||
OBJECTS = tiv.o tiv_lib.o
|
||||
|
||||
CXX ?= g++
|
||||
CXXFLAGS ?= -O2 -fpermissive
|
||||
INSTALL ?= install
|
||||
|
@ -12,12 +12,14 @@ prefix ?= /usr/local
|
|||
exec_prefix ?= $(prefix)
|
||||
bindir ?= $(exec_prefix)/bin
|
||||
|
||||
override CXXFLAGS += -std=c++2a -Wall -fexceptions
|
||||
override CXXFLAGS += -std=c++17 -Wall -fexceptions
|
||||
override LDFLAGS += -pthread
|
||||
|
||||
all: $(PROGNAME)
|
||||
|
||||
tiv.o: CImg.h
|
||||
tiv_lib.o: tiv_lib.h
|
||||
|
||||
tiv.o: CImg.h tiv_lib.h
|
||||
|
||||
$(PROGNAME): $(OBJECTS)
|
||||
$(CXX) $(LDFLAGS) $^ -o $@ $(LOADLIBES) $(LDLIBS)
|
||||
|
|
534
src/tiv.cpp
534
src/tiv.cpp
|
@ -1,23 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Aaron Liu
|
||||
* Copyright (c) 2017-2023, Stefan Haustein, Aaron Liu
|
||||
*
|
||||
* This file is free software: you may copy, redistribute and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
* This file is free software: you may copy, redistribute and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This file 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.
|
||||
* This file 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 <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright (c) 2017—2021, Stefan Haustein
|
||||
* Alternatively, you may copy, redistribute and/or modify this file under
|
||||
* the terms of the Apache License, version 2.0:
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -37,388 +35,39 @@
|
|||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// CImg, the superior grafiks library
|
||||
#include "tiv_lib.h"
|
||||
|
||||
// This #define tells CImg that we use the library without any display options
|
||||
// -- just for loading images.
|
||||
#define cimg_display 0
|
||||
#include "CImg.h"
|
||||
|
||||
// First include for detecting console output size,
|
||||
// everything else for exit codes
|
||||
#ifdef _POSIX_VERSION
|
||||
// Console output size detection
|
||||
#include <sys/ioctl.h>
|
||||
#include <sysexits.h>
|
||||
// Error explanation, for some reason
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
// Following codes copied from /usr/include/sysexits.h,
|
||||
// license: https://opensource.org/license/BSD-3-clause/
|
||||
#define EX_OK 0 /* successful termination */
|
||||
#define EX__BASE 64 /* base value for error messages */
|
||||
#define EX_USAGE 64 /* command line usage error */
|
||||
#define EX_DATAERR 65 /* data format error */
|
||||
#define EX_NOINPUT 66 /* cannot open input */
|
||||
#define EX_SOFTWARE 70 /* internal software error */
|
||||
#define EX_CANTCREAT 73 /* can't create (user) output file */
|
||||
#define EX_IOERR 74 /* input/output error */
|
||||
#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
|
||||
#define EX_NOPERM 77 /* permission denied */
|
||||
#define EX_CONFIG 78 /* configuration error */
|
||||
// Error explanation
|
||||
#include <system_error>
|
||||
#endif
|
||||
|
||||
// using namespace std; // haha nope, bad style
|
||||
// especially when we're also using the CImg namespace
|
||||
// Program exit code constants compatible with sysexits.h.
|
||||
#define EXITCODE_OK 0
|
||||
#define EXITCODE_COMMAND_LINE_USAGE_ERROR 64
|
||||
#define EXITCODE_DATA_FORMAT_ERROR 65
|
||||
#define EXITCODE_NO_INPUT_ERROR 66
|
||||
|
||||
// Implementation of flag representation for flags in the main() method
|
||||
constexpr int FLAG_FG = 1;
|
||||
constexpr int FLAG_BG = 2;
|
||||
constexpr int FLAG_MODE_256 = 4; // Limit colors to 256-color mode
|
||||
constexpr int FLAG_24BIT = 8; // 24-bit color mode
|
||||
constexpr int FLAG_NOOPT = 16; // Only use the same half-block character
|
||||
constexpr int FLAG_TELETEXT = 32; // Use teletext characters
|
||||
|
||||
// Steps (@TODO: Figure out what exactly they represent)
|
||||
constexpr int COLOR_STEP_COUNT = 6;
|
||||
constexpr int COLOR_STEPS[COLOR_STEP_COUNT] = {0, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
|
||||
|
||||
constexpr int GRAYSCALE_STEP_COUNT = 24;
|
||||
constexpr int GRAYSCALE_STEPS[GRAYSCALE_STEP_COUNT] = {
|
||||
0x08, 0x12, 0x1c, 0x26, 0x30, 0x3a, 0x44, 0x4e, 0x58, 0x62, 0x6c, 0x76,
|
||||
0x80, 0x8a, 0x94, 0x9e, 0xa8, 0xb2, 0xbc, 0xc6, 0xd0, 0xda, 0xe4, 0xee};
|
||||
|
||||
constexpr unsigned int BITMAPS[] = {
|
||||
0x00000000, 0x00a0,
|
||||
|
||||
// Block graphics
|
||||
// 0xffff0000, 0x2580, // upper 1/2; redundant with inverse lower 1/2
|
||||
|
||||
0x0000000f, 0x2581, // lower 1/8
|
||||
0x000000ff, 0x2582, // lower 1/4
|
||||
0x00000fff, 0x2583, 0x0000ffff, 0x2584, // lower 1/2
|
||||
0x000fffff, 0x2585, 0x00ffffff, 0x2586, // lower 3/4
|
||||
0x0fffffff, 0x2587,
|
||||
// 0xffffffff, 0x2588, // full; redundant with inverse space
|
||||
|
||||
0xeeeeeeee, 0x258a, // left 3/4
|
||||
0xcccccccc, 0x258c, // left 1/2
|
||||
0x88888888, 0x258e, // left 1/4
|
||||
|
||||
0x0000cccc, 0x2596, // quadrant lower left
|
||||
0x00003333, 0x2597, // quadrant lower right
|
||||
0xcccc0000,
|
||||
0x2598, // quadrant upper left
|
||||
// 0xccccffff, 0x2599, // 3/4 redundant with inverse 1/4
|
||||
0xcccc3333, 0x259a, // diagonal 1/2
|
||||
// 0xffffcccc, 0x259b, // 3/4 redundant
|
||||
// 0xffff3333, 0x259c, // 3/4 redundant
|
||||
0x33330000, 0x259d, // quadrant upper right
|
||||
// 0x3333cccc, 0x259e, // 3/4 redundant
|
||||
// 0x3333ffff, 0x259f, // 3/4 redundant
|
||||
|
||||
// Line drawing subset: no double lines, no complex light lines
|
||||
|
||||
0x000ff000, 0x2501, // Heavy horizontal
|
||||
0x66666666, 0x2503, // Heavy vertical
|
||||
|
||||
0x00077666, 0x250f, // Heavy down and right
|
||||
0x000ee666, 0x2513, // Heavy down and left
|
||||
0x66677000, 0x2517, // Heavy up and right
|
||||
0x666ee000, 0x251b, // Heavy up and left
|
||||
|
||||
0x66677666, 0x2523, // Heavy vertical and right
|
||||
0x666ee666, 0x252b, // Heavy vertical and left
|
||||
0x000ff666, 0x2533, // Heavy down and horizontal
|
||||
0x666ff000, 0x253b, // Heavy up and horizontal
|
||||
0x666ff666, 0x254b, // Heavy cross
|
||||
|
||||
0x000cc000, 0x2578, // Bold horizontal left
|
||||
0x00066000, 0x2579, // Bold horizontal up
|
||||
0x00033000, 0x257a, // Bold horizontal right
|
||||
0x00066000, 0x257b, // Bold horizontal down
|
||||
|
||||
0x06600660, 0x254f, // Heavy double dash vertical
|
||||
|
||||
0x000f0000, 0x2500, // Light horizontal
|
||||
0x0000f000, 0x2500, //
|
||||
0x44444444, 0x2502, // Light vertical
|
||||
0x22222222, 0x2502,
|
||||
|
||||
0x000e0000, 0x2574, // light left
|
||||
0x0000e000, 0x2574, // light left
|
||||
0x44440000, 0x2575, // light up
|
||||
0x22220000, 0x2575, // light up
|
||||
0x00030000, 0x2576, // light right
|
||||
0x00003000, 0x2576, // light right
|
||||
0x00004444, 0x2577, // light down
|
||||
0x00002222, 0x2577, // light down
|
||||
|
||||
// Misc technical
|
||||
|
||||
0x44444444, 0x23a2, // [ extension
|
||||
0x22222222, 0x23a5, // ] extension
|
||||
|
||||
0x0f000000, 0x23ba, // Horizontal scanline 1
|
||||
0x00f00000, 0x23bb, // Horizontal scanline 3
|
||||
0x00000f00, 0x23bc, // Horizontal scanline 7
|
||||
0x000000f0, 0x23bd, // Horizontal scanline 9
|
||||
|
||||
// Geometrical shapes. Tricky because some of them are too wide.
|
||||
|
||||
// 0x00ffff00, 0x25fe, // Black medium small square
|
||||
0x00066000, 0x25aa, // Black small square
|
||||
|
||||
// 0x11224488, 0x2571, // diagonals
|
||||
// 0x88442211, 0x2572,
|
||||
// 0x99666699, 0x2573,
|
||||
// 0x000137f0, 0x25e2, // Triangles
|
||||
// 0x0008cef0, 0x25e3,
|
||||
// 0x000fec80, 0x25e4,
|
||||
// 0x000f7310, 0x25e5,
|
||||
|
||||
0, 0, // End marker for "regular" characters
|
||||
|
||||
// Teletext / legacy graphics 3x2 block character codes.
|
||||
// Using a 3-2-3 pattern consistently, perhaps we should create automatic
|
||||
// variations....
|
||||
|
||||
0xccc00000, 0xfb00, 0x33300000, 0xfb01, 0xfff00000, 0xfb02, 0x000cc000,
|
||||
0xfb03, 0xccccc000, 0xfb04, 0x333cc000, 0xfb05, 0xfffcc000, 0xfb06,
|
||||
0x00033000, 0xfb07, 0xccc33000, 0xfb08, 0x33333000, 0xfb09, 0xfff33000,
|
||||
0xfb0a, 0x000ff000, 0xfb0b, 0xcccff000, 0xfb0c, 0x333ff000, 0xfb0d,
|
||||
0xfffff000, 0xfb0e, 0x00000ccc, 0xfb0f,
|
||||
|
||||
0xccc00ccc, 0xfb10, 0x33300ccc, 0xfb11, 0xfff00ccc, 0xfb12, 0x000ccccc,
|
||||
0xfb13, 0x333ccccc, 0xfb14, 0xfffccccc, 0xfb15, 0x00033ccc, 0xfb16,
|
||||
0xccc33ccc, 0xfb17, 0x33333ccc, 0xfb18, 0xfff33ccc, 0xfb19, 0x000ffccc,
|
||||
0xfb1a, 0xcccffccc, 0xfb1b, 0x333ffccc, 0xfb1c, 0xfffffccc, 0xfb1d,
|
||||
0x00000333, 0xfb1e, 0xccc00333, 0xfb1f,
|
||||
|
||||
0x33300333, 0x1b20, 0xfff00333, 0x1b21, 0x000cc333, 0x1b22, 0xccccc333,
|
||||
0x1b23, 0x333cc333, 0x1b24, 0xfffcc333, 0x1b25, 0x00033333, 0x1b26,
|
||||
0xccc33333, 0x1b27, 0xfff33333, 0x1b28, 0x000ff333, 0x1b29, 0xcccff333,
|
||||
0x1b2a, 0x333ff333, 0x1b2b, 0xfffff333, 0x1b2c, 0x00000fff, 0x1b2d,
|
||||
0xccc00fff, 0x1b2e, 0x33300fff, 0x1b2f,
|
||||
|
||||
0xfff00fff, 0x1b30, 0x000ccfff, 0x1b31, 0xcccccfff, 0x1b32, 0x333ccfff,
|
||||
0x1b33, 0xfffccfff, 0x1b34, 0x00033fff, 0x1b35, 0xccc33fff, 0x1b36,
|
||||
0x33333fff, 0x1b37, 0xfff33fff, 0x1b38, 0x000fffff, 0x1b39, 0xcccfffff,
|
||||
0x1b3a, 0x333fffff, 0x1b3b,
|
||||
|
||||
0, 1 // End marker for extended TELETEXT mode.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Struct to represent a character to be drawn.
|
||||
* @param fgColor RGB
|
||||
* @param bgColor RGB
|
||||
* @param codePoint The code point of the character to be drawn.
|
||||
*/
|
||||
struct CharData {
|
||||
std::array<int, 3> fgColor = std::array<int, 3>{0, 0, 0};
|
||||
std::array<int, 3> bgColor = std::array<int, 3>{0, 0, 0};
|
||||
int codePoint;
|
||||
};
|
||||
|
||||
// Return a CharData struct with the given code point and corresponding averag
|
||||
// fg and bg colors.
|
||||
CharData createCharData(const cimg_library::CImg<unsigned char> &image, int x0,
|
||||
int y0, int codepoint, int pattern) {
|
||||
CharData result;
|
||||
result.codePoint = codepoint;
|
||||
int fg_count = 0;
|
||||
int bg_count = 0;
|
||||
unsigned int mask = 0x80000000;
|
||||
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
int *avg;
|
||||
if (pattern & mask) {
|
||||
avg = result.fgColor.data();
|
||||
fg_count++;
|
||||
} else {
|
||||
avg = result.bgColor.data();
|
||||
bg_count++;
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
avg[i] += image(x0 + x, y0 + y, 0, i);
|
||||
}
|
||||
mask = mask >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the average color value for each bucket
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (bg_count != 0) {
|
||||
result.bgColor[i] /= bg_count;
|
||||
}
|
||||
if (fg_count != 0) {
|
||||
result.fgColor[i] /= fg_count;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find the best character and colors
|
||||
* for a 4x8 part of the image at the given position
|
||||
*
|
||||
* @param image
|
||||
* @param x0
|
||||
* @param y0
|
||||
* @param flags
|
||||
* @return CharData
|
||||
*/
|
||||
CharData findCharData(const cimg_library::CImg<unsigned char> &image, int x0,
|
||||
int y0, const int &flags) {
|
||||
int min[3] = {255, 255, 255};
|
||||
int max[3] = {0};
|
||||
std::map<long, int> count_per_color;
|
||||
|
||||
// Determine the minimum and maximum value for each color channel
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
long color = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int d = image(x0 + x, y0 + y, 0, i);
|
||||
min[i] = std::min(min[i], d);
|
||||
max[i] = std::max(max[i], d);
|
||||
color = (color << 8) | d;
|
||||
}
|
||||
count_per_color[color]++;
|
||||
}
|
||||
}
|
||||
|
||||
std::multimap<int, long> color_per_count;
|
||||
for (auto i = count_per_color.begin(); i != count_per_color.end(); ++i) {
|
||||
color_per_count.insert(std::pair<int, long>(i->second, i->first));
|
||||
}
|
||||
|
||||
auto iter = color_per_count.rbegin();
|
||||
int count2 = iter->first;
|
||||
long max_count_color_1 = iter->second;
|
||||
long max_count_color_2 = max_count_color_1;
|
||||
if ((++iter) != color_per_count.rend()) {
|
||||
count2 += iter->first;
|
||||
max_count_color_2 = iter->second;
|
||||
}
|
||||
|
||||
unsigned int bits = 0;
|
||||
bool direct = count2 > (8 * 4) / 2;
|
||||
|
||||
if (direct) {
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
bits = bits << 1;
|
||||
int d1 = 0;
|
||||
int d2 = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int shift = 16 - 8 * i;
|
||||
int c1 = (max_count_color_1 >> shift) & 255;
|
||||
int c2 = (max_count_color_2 >> shift) & 255;
|
||||
int c = image(x0 + x, y0 + y, 0, i);
|
||||
d1 += (c1 - c) * (c1 - c);
|
||||
d2 += (c2 - c) * (c2 - c);
|
||||
}
|
||||
if (d1 > d2) {
|
||||
bits |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Determine the color channel with the greatest range.
|
||||
int splitIndex = 0;
|
||||
int bestSplit = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (max[i] - min[i] > bestSplit) {
|
||||
bestSplit = max[i] - min[i];
|
||||
splitIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// We just split at the middle of the interval instead of computing the
|
||||
// median.
|
||||
int splitValue = min[splitIndex] + bestSplit / 2;
|
||||
|
||||
// Compute a bitmap using the given split and sum the color values for
|
||||
// both buckets.
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
bits = bits << 1;
|
||||
if (image(x0 + x, y0 + y, 0, splitIndex) > splitValue) {
|
||||
bits |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the best bitmap match by counting the bits that don't match,
|
||||
// including the inverted bitmaps.
|
||||
int best_diff = 8;
|
||||
unsigned int best_pattern = 0x0000ffff;
|
||||
int codepoint = 0x2584;
|
||||
bool inverted = false;
|
||||
unsigned int end_marker = flags & FLAG_TELETEXT ? 1 : 0;
|
||||
for (int i = 0; BITMAPS[i + 1] != end_marker; i += 2) {
|
||||
// Skip all end markers
|
||||
if (BITMAPS[i + 1] < 32) {
|
||||
continue;
|
||||
}
|
||||
unsigned int pattern = BITMAPS[i];
|
||||
for (int j = 0; j < 2; j++) {
|
||||
int diff = (std::bitset<32>(pattern ^ bits)).count();
|
||||
if (diff < best_diff) {
|
||||
best_pattern = BITMAPS[i]; // pattern might be inverted.
|
||||
codepoint = BITMAPS[i + 1];
|
||||
best_diff = diff;
|
||||
inverted = best_pattern != pattern;
|
||||
}
|
||||
pattern = ~pattern;
|
||||
}
|
||||
}
|
||||
|
||||
if (direct) {
|
||||
CharData result;
|
||||
if (inverted) {
|
||||
long tmp = max_count_color_1;
|
||||
max_count_color_1 = max_count_color_2;
|
||||
max_count_color_2 = tmp;
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int shift = 16 - 8 * i;
|
||||
result.fgColor[i] = (max_count_color_2 >> shift) & 255;
|
||||
result.bgColor[i] = (max_count_color_1 >> shift) & 255;
|
||||
result.codePoint = codepoint;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return createCharData(image, x0, y0, codepoint, best_pattern);
|
||||
}
|
||||
|
||||
int clamp_byte(int value) {
|
||||
return value < 0 ? 0 : (value > 255 ? 255 : value);
|
||||
}
|
||||
|
||||
double sqr(double n) { return n * n; }
|
||||
|
||||
int best_index(int value, const int STEPS[], int count) {
|
||||
int best_diff = std::abs(STEPS[0] - value);
|
||||
int result = 0;
|
||||
for (int i = 1; i < count; i++) {
|
||||
int diff = std::abs(STEPS[i] - value);
|
||||
if (diff < best_diff) {
|
||||
result = i;
|
||||
best_diff = diff;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void emit_color(const int &flags, int r, int g, int b) {
|
||||
void printTermColor(const int &flags, int r, int g, int b) {
|
||||
r = clamp_byte(r);
|
||||
g = clamp_byte(g);
|
||||
b = clamp_byte(b);
|
||||
|
@ -455,7 +104,7 @@ void emit_color(const int &flags, int r, int g, int b) {
|
|||
std::cout << (bg ? "\x1B[48;5;" : "\u001B[38;5;") << color_index << "m";
|
||||
}
|
||||
|
||||
void emitCodepoint(int codepoint) {
|
||||
void printCodepoint(int codepoint) {
|
||||
if (codepoint < 128) {
|
||||
std::cout << static_cast<char>(codepoint);
|
||||
} else if (codepoint < 0x7ff) {
|
||||
|
@ -475,22 +124,28 @@ void emitCodepoint(int codepoint) {
|
|||
}
|
||||
}
|
||||
|
||||
void emit_image(const cimg_library::CImg<unsigned char> &image,
|
||||
void printImage(const cimg_library::CImg<unsigned char> &image,
|
||||
const int &flags) {
|
||||
GetPixelFunction get_pixel = [&](int x, int y) -> unsigned long {
|
||||
return (((unsigned long) image(x, y, 0, 0)) << 16)
|
||||
| (((unsigned long) image(x, y, 0, 1)) << 8)
|
||||
| (((unsigned long) image(x, y, 0, 2)));
|
||||
};
|
||||
|
||||
CharData lastCharData;
|
||||
for (int y = 0; y <= image.height() - 8; y += 8) {
|
||||
for (int x = 0; x <= image.width() - 4; x += 4) {
|
||||
CharData charData =
|
||||
flags & FLAG_NOOPT
|
||||
? createCharData(image, x, y, 0x2584, 0x0000ffff)
|
||||
: findCharData(image, x, y, flags);
|
||||
? createCharData(get_pixel, x, y, 0x2584, 0x0000ffff)
|
||||
: findCharData(get_pixel, x, y, flags);
|
||||
if (x == 0 || charData.bgColor != lastCharData.bgColor)
|
||||
emit_color(flags | FLAG_BG, charData.bgColor[0],
|
||||
charData.bgColor[1], charData.bgColor[2]);
|
||||
printTermColor(flags | FLAG_BG, charData.bgColor[0],
|
||||
charData.bgColor[1], charData.bgColor[2]);
|
||||
if (x == 0 || charData.fgColor != lastCharData.fgColor)
|
||||
emit_color(flags | FLAG_FG, charData.fgColor[0],
|
||||
charData.fgColor[1], charData.fgColor[2]);
|
||||
emitCodepoint(charData.codePoint);
|
||||
printTermColor(flags | FLAG_FG, charData.fgColor[0],
|
||||
charData.fgColor[1], charData.fgColor[2]);
|
||||
printCodepoint(charData.codePoint);
|
||||
lastCharData = charData;
|
||||
}
|
||||
std::cout << "\x1b[0m" << std::endl;
|
||||
|
@ -538,14 +193,14 @@ cimg_library::CImg<unsigned char> load_rgb_CImg(const char *const &filename) {
|
|||
}
|
||||
|
||||
// Implements --help
|
||||
void emit_usage() {
|
||||
void printUsage() {
|
||||
std::cerr << R"(
|
||||
Terminal Image Viewer v1.2.1
|
||||
usage: tiv [options] <image> [<image>...]
|
||||
-0 : No block character adjustment, always use top half block char.
|
||||
-2, --256 : Use 256-bit colors. Needed to display properly on macOS Terminal.
|
||||
-c <num> : Number of thumbnail columns in 'dir' mode (3 by default).
|
||||
-d, --dir : Force 'dir' mode. Automatially selected for more than one input.
|
||||
-d, --dir : Force 'dir' mode. Automatically selected for more than one input.
|
||||
-f, --full: Force 'full' mode. Automatically selected for one input.
|
||||
--help : Display this help text.
|
||||
-h <num> : Set the maximum output height to <num> lines.
|
||||
|
@ -558,41 +213,12 @@ enum Mode { AUTO, THUMBNAILS, FULL_SIZE };
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
std::ios::sync_with_stdio(false); // apparently makes printing faster
|
||||
bool detectSize = true;
|
||||
|
||||
// Platform-specific implementations for determining console size, better
|
||||
// implementations are welcome Fallback sizes when unsuccesful
|
||||
// implementations are welcome Fallback sizes when unsuccessful
|
||||
int maxWidth = 80;
|
||||
int maxHeight = 24;
|
||||
#ifdef _POSIX_VERSION
|
||||
struct winsize w;
|
||||
// If redirecting STDOUT to one file ( col or row == 0, or the previous
|
||||
// ioctl call's failed )
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != 0 ||
|
||||
(w.ws_col | w.ws_row) == 0) {
|
||||
std::cerr << "Warning: failed to determine most reasonable size, "
|
||||
"defaulting to 80x24"
|
||||
<< std::endl;
|
||||
} else {
|
||||
maxWidth = w.ws_col * 4;
|
||||
maxHeight = w.ws_row * 8;
|
||||
}
|
||||
#elif defined _WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO w;
|
||||
if (GetConsoleScreenBufferInfo(
|
||||
GetStdHandle(STD_OUTPUT_HANDLE),
|
||||
&w)) { // just like powershell, but without the hyphens, hooray
|
||||
maxWidth = w.dwSize.X * 4;
|
||||
maxHeight = w.dwSize.Y * 8;
|
||||
} else {
|
||||
std::cerr
|
||||
<< "Warning: failed to determine most reasonable size: Error code"
|
||||
<< GetLastError() << ", defaulting to 80x24" << std::endl;
|
||||
}
|
||||
#else
|
||||
std::cerr << "Warning: failed to determine most reasonable size: "
|
||||
"unrecognized system, defaulting to 80x24"
|
||||
<< std::endl;
|
||||
#endif
|
||||
|
||||
// Reading input
|
||||
char flags = 0; // bitwise representation of flags,
|
||||
|
@ -601,11 +227,11 @@ int main(int argc, char *argv[]) {
|
|||
int columns = 3;
|
||||
|
||||
std::vector<std::string> file_names;
|
||||
int ret = EX_OK; // The return code for the program
|
||||
int ret = EXITCODE_OK; // The return code for the program
|
||||
|
||||
if (argc <= 1) {
|
||||
emit_usage();
|
||||
return 0;
|
||||
printUsage();
|
||||
return EXITCODE_COMMAND_LINE_USAGE_ERROR;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
|
@ -617,7 +243,7 @@ int main(int argc, char *argv[]) {
|
|||
columns = std::stoi(argv[++i]);
|
||||
} else {
|
||||
std::cerr << "Error: -c requires a number" << std::endl;
|
||||
ret = EX_USAGE;
|
||||
ret = EXITCODE_COMMAND_LINE_USAGE_ERROR;
|
||||
}
|
||||
} else if (arg == "-d" || arg == "--dir") {
|
||||
mode = THUMBNAILS;
|
||||
|
@ -625,25 +251,25 @@ int main(int argc, char *argv[]) {
|
|||
mode = FULL_SIZE;
|
||||
} else if (arg == "-w") {
|
||||
if (i < argc - 1) {
|
||||
maxWidth = 4 * std::stoi(argv[++i]);
|
||||
maxWidth = 4 * std::stoi(argv[++i]), detectSize = false;
|
||||
} else {
|
||||
std::cerr << "Error: -w requires a number" << std::endl;
|
||||
ret = EX_USAGE;
|
||||
ret = EXITCODE_COMMAND_LINE_USAGE_ERROR;
|
||||
}
|
||||
} else if (arg == "-h") {
|
||||
if (i < argc - 1)
|
||||
maxHeight = 8 * std::stoi(argv[++i]);
|
||||
maxHeight = 8 * std::stoi(argv[++i]), detectSize = false;
|
||||
else
|
||||
emit_usage();
|
||||
printUsage(); // people might confuse this with help
|
||||
} else if (arg == "--256" || arg == "-2" || arg == "-256") {
|
||||
flags |= FLAG_MODE_256;
|
||||
} else if (arg == "--help" || arg == "-help") {
|
||||
emit_usage();
|
||||
printUsage();
|
||||
} else if (arg == "-x") {
|
||||
flags |= FLAG_TELETEXT;
|
||||
} else if (arg[0] == '-') {
|
||||
std::cerr << "Error: Unrecognized argument: " << arg << std::endl;
|
||||
ret = EX_USAGE;
|
||||
ret = EXITCODE_COMMAND_LINE_USAGE_ERROR;
|
||||
} else {
|
||||
// Arguments that will be displayed
|
||||
if (std::filesystem::is_directory(arg)) {
|
||||
|
@ -651,19 +277,51 @@ int main(int argc, char *argv[]) {
|
|||
if (std::filesystem::is_regular_file(p.path()))
|
||||
file_names.push_back(p.path().string());
|
||||
} else {
|
||||
// Check if file can be opened
|
||||
// Check if file can be opened, @TODO find better way
|
||||
std::ifstream fin(arg.c_str());
|
||||
if (fin) {
|
||||
file_names.push_back(arg);
|
||||
} else {
|
||||
std::cerr << "Error: Cannot open '" << arg
|
||||
<< "', permission issue?" << std::endl;
|
||||
ret = EX_NOINPUT;
|
||||
ret = EXITCODE_NO_INPUT_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (detectSize) {
|
||||
#ifdef _POSIX_VERSION
|
||||
struct winsize w;
|
||||
// If redirecting STDOUT to one file ( col or row == 0, or the previous
|
||||
// ioctl call's failed )
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != 0 ||
|
||||
(w.ws_col | w.ws_row) == 0) {
|
||||
std::cerr << "Warning: failed to determine most reasonable size: "
|
||||
<< strerror(errno) << ", defaulting to 20x6" << std::endl;
|
||||
} else {
|
||||
maxWidth = w.ws_col * 4;
|
||||
maxHeight = w.ws_row * 8;
|
||||
}
|
||||
#elif defined _WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO w;
|
||||
if (GetConsoleScreenBufferInfo(
|
||||
GetStdHandle(STD_OUTPUT_HANDLE),
|
||||
&w)) { // just like PowerShell, but without the hyphens, hooray
|
||||
maxWidth = w.dwSize.X * 4;
|
||||
maxHeight = w.dwSize.Y * 8;
|
||||
} else {
|
||||
std::cerr << "Warning: failed to determine most reasonable size: "
|
||||
<< std::system_category().message(GetLastError())
|
||||
<< ", defaulting to 80x24" << std::endl;
|
||||
}
|
||||
#else
|
||||
std::cerr << "Warning: failed to determine most reasonable size: "
|
||||
"unrecognized system, defaulting to 80x24"
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (mode == FULL_SIZE || (mode == AUTO && file_names.size() == 1)) {
|
||||
for (const auto &filename : file_names) {
|
||||
try {
|
||||
|
@ -676,12 +334,12 @@ int main(int argc, char *argv[]) {
|
|||
image.resize(new_size.width, new_size.height, -100, -100,
|
||||
5);
|
||||
}
|
||||
// the acutal magic which generates the output
|
||||
emit_image(image, flags);
|
||||
// the actual magic which generates the output
|
||||
printImage(image, flags);
|
||||
} catch (cimg_library::CImgIOException &e) {
|
||||
std::cerr << "Error: '" << filename
|
||||
<< "' has an unrecognized file format" << std::endl;
|
||||
ret = EX_DATAERR;
|
||||
ret = EXITCODE_DATA_FORMAT_ERROR;
|
||||
}
|
||||
}
|
||||
} else { // Thumbnail mode
|
||||
|
@ -717,7 +375,7 @@ int main(int argc, char *argv[]) {
|
|||
// Probably no image; ignore.
|
||||
}
|
||||
}
|
||||
if (count) emit_image(image, flags);
|
||||
if (count) printImage(image, flags);
|
||||
std::cout << sb << std::endl << std::endl;
|
||||
}
|
||||
}
|
||||
|
|
397
src/tiv_lib.cpp
Normal file
397
src/tiv_lib.cpp
Normal file
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2023, Stefan Haustein, Aaron Liu
|
||||
*
|
||||
* This file is free software: you may copy, redistribute and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This file 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Alternatively, you may copy, redistribute and/or modify this file under
|
||||
* the terms of the Apache License, version 2.0:
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "tiv_lib.h"
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
const int END_MARKER = 0;
|
||||
|
||||
// An interleaved map of 4x8 bit character bitmaps (each hex digit represents a
|
||||
// row) to the corresponding Unicode character code point.
|
||||
constexpr unsigned int BITMAPS[] = {
|
||||
0x00000000, 0x00a0, 0,
|
||||
|
||||
// Block graphics
|
||||
// 0xffff0000, 0x2580, 0, // upper 1/2; redundant with inverse lower 1/2
|
||||
|
||||
0x0000000f, 0x2581, 0, // lower 1/8
|
||||
0x000000ff, 0x2582, 0, // lower 1/4
|
||||
0x00000fff, 0x2583, 0,
|
||||
0x0000ffff, 0x2584, 0, // lower 1/2
|
||||
0x000fffff, 0x2585, 0,
|
||||
0x00ffffff, 0x2586, 0, // lower 3/4
|
||||
0x0fffffff, 0x2587, 0,
|
||||
// 0xffffffff, 0x2588, // full; redundant with inverse space
|
||||
|
||||
0xeeeeeeee, 0x258a, 0, // left 3/4
|
||||
0xcccccccc, 0x258c, 0, // left 1/2
|
||||
0x88888888, 0x258e, 0, // left 1/4
|
||||
|
||||
0x0000cccc, 0x2596, 0, // quadrant lower left
|
||||
0x00003333, 0x2597, 0, // quadrant lower right
|
||||
0xcccc0000, 0x2598, 0, // quadrant upper left
|
||||
// 0xccccffff, 0x2599, // 3/4 redundant with inverse 1/4
|
||||
0xcccc3333, 0x259a, 0, // diagonal 1/2
|
||||
// 0xffffcccc, 0x259b, // 3/4 redundant
|
||||
// 0xffff3333, 0x259c, // 3/4 redundant
|
||||
0x33330000, 0x259d, 0, // quadrant upper right
|
||||
// 0x3333cccc, 0x259e, // 3/4 redundant
|
||||
// 0x3333ffff, 0x259f, // 3/4 redundant
|
||||
|
||||
// Line drawing subset: no double lines, no complex light lines
|
||||
|
||||
0x000ff000, 0x2501, 0, // Heavy horizontal
|
||||
0x66666666, 0x2503, 0, // Heavy vertical
|
||||
|
||||
0x00077666, 0x250f, 0, // Heavy down and right
|
||||
0x000ee666, 0x2513, 0, // Heavy down and left
|
||||
0x66677000, 0x2517, 0, // Heavy up and right
|
||||
0x666ee000, 0x251b, 0, // Heavy up and left
|
||||
|
||||
0x66677666, 0x2523, 0, // Heavy vertical and right
|
||||
0x666ee666, 0x252b, 0, // Heavy vertical and left
|
||||
0x000ff666, 0x2533, 0, // Heavy down and horizontal
|
||||
0x666ff000, 0x253b, 0, // Heavy up and horizontal
|
||||
0x666ff666, 0x254b, 0, // Heavy cross
|
||||
|
||||
0x000cc000, 0x2578, 0, // Bold horizontal left
|
||||
0x00066000, 0x2579, 0, // Bold horizontal up
|
||||
0x00033000, 0x257a, 0, // Bold horizontal right
|
||||
0x00066000, 0x257b, 0, // Bold horizontal down
|
||||
|
||||
0x06600660, 0x254f, 0, // Heavy double dash vertical
|
||||
|
||||
0x000f0000, 0x2500, 0, // Light horizontal
|
||||
0x0000f000, 0x2500, 0, //
|
||||
0x44444444, 0x2502, 0, // Light vertical
|
||||
0x22222222, 0x2502, 0,
|
||||
|
||||
0x000e0000, 0x2574, 0, // light left
|
||||
0x0000e000, 0x2574, 0, // light left
|
||||
0x44440000, 0x2575, 0, // light up
|
||||
0x22220000, 0x2575, 0, // light up
|
||||
0x00030000, 0x2576, 0, // light right
|
||||
0x00003000, 0x2576, 0, // light right
|
||||
0x00004444, 0x2577, 0, // light down
|
||||
0x00002222, 0x2577, 0, // light down
|
||||
|
||||
// Misc technical
|
||||
|
||||
0x44444444, 0x23a2, 0, // [ extension
|
||||
0x22222222, 0x23a5, 0, // ] extension
|
||||
|
||||
0x0f000000, 0x23ba, 0, // Horizontal scanline 1
|
||||
0x00f00000, 0x23bb, 0, // Horizontal scanline 3
|
||||
0x00000f00, 0x23bc, 0, // Horizontal scanline 7
|
||||
0x000000f0, 0x23bd, 0, // Horizontal scanline 9
|
||||
|
||||
// Geometrical shapes. Tricky because some of them are too wide.
|
||||
|
||||
// 0x00ffff00, 0x25fe, 0, // Black medium small square
|
||||
0x00066000, 0x25aa, 0, // Black small square
|
||||
|
||||
// 0x11224488, 0x2571, 0, // diagonals
|
||||
// 0x88442211, 0x2572, 0,
|
||||
// 0x99666699, 0x2573, 0,
|
||||
// 0x000137f0, 0x25e2, 0, // Triangles
|
||||
// 0x0008cef0, 0x25e3, 0,
|
||||
// 0x000fec80, 0x25e4, 0,
|
||||
// 0x000f7310, 0x25e5, 0,
|
||||
|
||||
// Teletext / legacy graphics 3x2 block character codes.
|
||||
// Using a 3-2-3 pattern consistently, perhaps we should create automatic
|
||||
// variations....
|
||||
|
||||
0xccc00000, 0xfb00, FLAG_TELETEXT,
|
||||
0x33300000, 0xfb01, FLAG_TELETEXT,
|
||||
0xfff00000, 0xfb02, FLAG_TELETEXT,
|
||||
0x000cc000, 0xfb03, FLAG_TELETEXT,
|
||||
0xccccc000, 0xfb04, FLAG_TELETEXT,
|
||||
0x333cc000, 0xfb05, FLAG_TELETEXT,
|
||||
0xfffcc000, 0xfb06, FLAG_TELETEXT,
|
||||
0x00033000, 0xfb07, FLAG_TELETEXT,
|
||||
0xccc33000, 0xfb08, FLAG_TELETEXT,
|
||||
0x33333000, 0xfb09, FLAG_TELETEXT,
|
||||
0xfff33000, 0xfb0a, FLAG_TELETEXT,
|
||||
0x000ff000, 0xfb0b, FLAG_TELETEXT,
|
||||
0xcccff000, 0xfb0c, FLAG_TELETEXT,
|
||||
0x333ff000, 0xfb0d, FLAG_TELETEXT,
|
||||
0xfffff000, 0xfb0e, FLAG_TELETEXT,
|
||||
0x00000ccc, 0xfb0f, FLAG_TELETEXT,
|
||||
|
||||
0xccc00ccc, 0xfb10, FLAG_TELETEXT,
|
||||
0x33300ccc, 0xfb11, FLAG_TELETEXT,
|
||||
0xfff00ccc, 0xfb12, FLAG_TELETEXT,
|
||||
0x000ccccc, 0xfb13, FLAG_TELETEXT,
|
||||
0x333ccccc, 0xfb14, FLAG_TELETEXT,
|
||||
0xfffccccc, 0xfb15, FLAG_TELETEXT,
|
||||
0x00033ccc, 0xfb16, FLAG_TELETEXT,
|
||||
0xccc33ccc, 0xfb17, FLAG_TELETEXT,
|
||||
0x33333ccc, 0xfb18, FLAG_TELETEXT,
|
||||
0xfff33ccc, 0xfb19, FLAG_TELETEXT,
|
||||
0x000ffccc, 0xfb1a, FLAG_TELETEXT,
|
||||
0xcccffccc, 0xfb1b, FLAG_TELETEXT,
|
||||
0x333ffccc, 0xfb1c, FLAG_TELETEXT,
|
||||
0xfffffccc, 0xfb1d, FLAG_TELETEXT,
|
||||
0x00000333, 0xfb1e, FLAG_TELETEXT,
|
||||
0xccc00333, 0xfb1f, FLAG_TELETEXT,
|
||||
|
||||
0x33300333, 0x1b20, FLAG_TELETEXT,
|
||||
0xfff00333, 0x1b21, FLAG_TELETEXT,
|
||||
0x000cc333, 0x1b22, FLAG_TELETEXT,
|
||||
0xccccc333, 0x1b23, FLAG_TELETEXT,
|
||||
0x333cc333, 0x1b24, FLAG_TELETEXT,
|
||||
0xfffcc333, 0x1b25, FLAG_TELETEXT,
|
||||
0x00033333, 0x1b26, FLAG_TELETEXT,
|
||||
0xccc33333, 0x1b27, FLAG_TELETEXT,
|
||||
0xfff33333, 0x1b28, FLAG_TELETEXT,
|
||||
0x000ff333, 0x1b29, FLAG_TELETEXT,
|
||||
0xcccff333, 0x1b2a, FLAG_TELETEXT,
|
||||
0x333ff333, 0x1b2b, FLAG_TELETEXT,
|
||||
0xfffff333, 0x1b2c, FLAG_TELETEXT,
|
||||
0x00000fff, 0x1b2d, FLAG_TELETEXT,
|
||||
0xccc00fff, 0x1b2e, FLAG_TELETEXT,
|
||||
0x33300fff, 0x1b2f, FLAG_TELETEXT,
|
||||
|
||||
0xfff00fff, 0x1b30, FLAG_TELETEXT,
|
||||
0x000ccfff, 0x1b31, FLAG_TELETEXT,
|
||||
0xcccccfff, 0x1b32, FLAG_TELETEXT,
|
||||
0x333ccfff, 0x1b33, FLAG_TELETEXT,
|
||||
0xfffccfff, 0x1b34, FLAG_TELETEXT,
|
||||
0x00033fff, 0x1b35, FLAG_TELETEXT,
|
||||
0xccc33fff, 0x1b36, FLAG_TELETEXT,
|
||||
0x33333fff, 0x1b37, FLAG_TELETEXT,
|
||||
0xfff33fff, 0x1b38, FLAG_TELETEXT,
|
||||
0x000fffff, 0x1b39, FLAG_TELETEXT,
|
||||
0xcccfffff, 0x1b3a, FLAG_TELETEXT,
|
||||
0x333fffff, 0x1b3b, FLAG_TELETEXT,
|
||||
|
||||
0, END_MARKER, 0 // End marker
|
||||
};
|
||||
|
||||
// The channel indices are 0, 1, 2 for R, G, B
|
||||
unsigned char get_channel(unsigned long rgb, int index) {
|
||||
return (unsigned char) ((rgb >> ((2 - index) * 8)) & 255);
|
||||
}
|
||||
|
||||
CharData createCharData(GetPixelFunction get_pixel, int x0, int y0,
|
||||
int codepoint, int pattern) {
|
||||
CharData result;
|
||||
result.codePoint = codepoint;
|
||||
int fg_count = 0;
|
||||
int bg_count = 0;
|
||||
unsigned int mask = 0x80000000;
|
||||
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
int *avg;
|
||||
if (pattern & mask) {
|
||||
avg = result.fgColor.data();
|
||||
fg_count++;
|
||||
} else {
|
||||
avg = result.bgColor.data();
|
||||
bg_count++;
|
||||
}
|
||||
long rgb = get_pixel(x0 + x, y0 + y);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
avg[i] += get_channel(rgb, i);
|
||||
}
|
||||
mask = mask >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the average color value for each bucket
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (bg_count != 0) {
|
||||
result.bgColor[i] /= bg_count;
|
||||
}
|
||||
if (fg_count != 0) {
|
||||
result.fgColor[i] /= fg_count;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CharData findCharData(GetPixelFunction get_pixel, int x0, int y0,
|
||||
const int &flags) {
|
||||
int min[3] = {255, 255, 255};
|
||||
int max[3] = {0};
|
||||
std::map<long, int> count_per_color;
|
||||
|
||||
// Determine the minimum and maximum value for each color channel
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
long color = 0;
|
||||
long rgb = get_pixel(x0 + x, y0 + y);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int d = get_channel(rgb, i);
|
||||
min[i] = std::min(min[i], d);
|
||||
max[i] = std::max(max[i], d);
|
||||
color = (color << 8) | d;
|
||||
}
|
||||
count_per_color[color]++;
|
||||
}
|
||||
}
|
||||
|
||||
std::multimap<int, long> color_per_count;
|
||||
for (auto i = count_per_color.begin(); i != count_per_color.end(); ++i) {
|
||||
color_per_count.insert(std::pair<int, long>(i->second, i->first));
|
||||
}
|
||||
|
||||
auto iter = color_per_count.rbegin();
|
||||
int count2 = iter->first;
|
||||
long max_count_color_1 = iter->second;
|
||||
long max_count_color_2 = max_count_color_1;
|
||||
if ((++iter) != color_per_count.rend()) {
|
||||
count2 += iter->first;
|
||||
max_count_color_2 = iter->second;
|
||||
}
|
||||
|
||||
unsigned int bits = 0;
|
||||
bool direct = count2 > (8 * 4) / 2;
|
||||
|
||||
if (direct) {
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
bits = bits << 1;
|
||||
int d1 = 0;
|
||||
int d2 = 0;
|
||||
unsigned long rgb = get_pixel(x0 + x, y0 + y);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int shift = 16 - 8 * i;
|
||||
int c1 = (max_count_color_1 >> shift) & 255;
|
||||
int c2 = (max_count_color_2 >> shift) & 255;
|
||||
int c = get_channel(rgb, i);
|
||||
d1 += (c1 - c) * (c1 - c);
|
||||
d2 += (c2 - c) * (c2 - c);
|
||||
}
|
||||
if (d1 > d2) {
|
||||
bits |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Determine the color channel with the greatest range.
|
||||
int splitIndex = 0;
|
||||
int bestSplit = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (max[i] - min[i] > bestSplit) {
|
||||
bestSplit = max[i] - min[i];
|
||||
splitIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// We just split at the middle of the interval instead of computing the
|
||||
// median.
|
||||
int splitValue = min[splitIndex] + bestSplit / 2;
|
||||
|
||||
// Compute a bitmap using the given split and sum the color values for
|
||||
// both buckets.
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
bits = bits << 1;
|
||||
if (get_channel(get_pixel(x0 + x, y0 + y), splitIndex) > splitValue) {
|
||||
bits |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the best bitmap match by counting the bits that don't match,
|
||||
// including the inverted bitmaps.
|
||||
int best_diff = 8;
|
||||
unsigned int best_pattern = 0x0000ffff;
|
||||
int codepoint = 0x2584;
|
||||
bool inverted = false;
|
||||
for (int i = 0; BITMAPS[i + 1] != END_MARKER; i += 3) {
|
||||
if ((BITMAPS[i + 2] & flags) != BITMAPS[i + 2]) {
|
||||
continue;
|
||||
}
|
||||
unsigned int pattern = BITMAPS[i];
|
||||
for (int j = 0; j < 2; j++) {
|
||||
int diff = (std::bitset<32>(pattern ^ bits)).count();
|
||||
if (diff < best_diff) {
|
||||
best_pattern = BITMAPS[i]; // pattern might be inverted.
|
||||
codepoint = BITMAPS[i + 1];
|
||||
best_diff = diff;
|
||||
inverted = best_pattern != pattern;
|
||||
}
|
||||
pattern = ~pattern;
|
||||
}
|
||||
}
|
||||
|
||||
if (direct) {
|
||||
CharData result;
|
||||
if (inverted) {
|
||||
long tmp = max_count_color_1;
|
||||
max_count_color_1 = max_count_color_2;
|
||||
max_count_color_2 = tmp;
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int shift = 16 - 8 * i;
|
||||
result.fgColor[i] = (max_count_color_2 >> shift) & 255;
|
||||
result.bgColor[i] = (max_count_color_1 >> shift) & 255;
|
||||
result.codePoint = codepoint;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return createCharData(get_pixel, x0, y0, codepoint, best_pattern);
|
||||
}
|
||||
|
||||
int clamp_byte(int value) {
|
||||
return value < 0 ? 0 : (value > 255 ? 255 : value);
|
||||
}
|
||||
|
||||
double sqr(double n) { return n * n; }
|
||||
|
||||
int best_index(int value, const int STEPS[], int count) {
|
||||
int best_diff = std::abs(STEPS[0] - value);
|
||||
int result = 0;
|
||||
for (int i = 1; i < count; i++) {
|
||||
int diff = std::abs(STEPS[i] - value);
|
||||
if (diff < best_diff) {
|
||||
result = i;
|
||||
best_diff = diff;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
99
src/tiv_lib.h
Normal file
99
src/tiv_lib.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2017-2023, Stefan Haustein, Aaron Liu
|
||||
*
|
||||
* This file is free software: you may copy, redistribute and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This file 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Alternatively, you may copy, redistribute and/or modify this file under
|
||||
* the terms of the Apache License, version 2.0:
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TIV_LIB_H
|
||||
#define TIV_LIB_H
|
||||
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
// Implementation of flag representation for flags in the main() method
|
||||
constexpr int FLAG_FG = 1;
|
||||
constexpr int FLAG_BG = 2;
|
||||
constexpr int FLAG_MODE_256 = 4; // Limit colors to 256-color mode
|
||||
constexpr int FLAG_24BIT = 8; // 24-bit color mode
|
||||
constexpr int FLAG_NOOPT = 16; // Only use the same half-block character
|
||||
constexpr int FLAG_TELETEXT = 32; // Use teletext characters
|
||||
|
||||
|
||||
// Color saturation value steps from 0 to 255
|
||||
constexpr int COLOR_STEP_COUNT = 6;
|
||||
constexpr int COLOR_STEPS[COLOR_STEP_COUNT] = {0, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
|
||||
|
||||
// Grayscale saturation value steps from 0 to 255
|
||||
constexpr int GRAYSCALE_STEP_COUNT = 24;
|
||||
constexpr int GRAYSCALE_STEPS[GRAYSCALE_STEP_COUNT] = {
|
||||
0x08, 0x12, 0x1c, 0x26, 0x30, 0x3a, 0x44, 0x4e, 0x58, 0x62, 0x6c, 0x76,
|
||||
0x80, 0x8a, 0x94, 0x9e, 0xa8, 0xb2, 0xbc, 0xc6, 0xd0, 0xda, 0xe4, 0xee};
|
||||
|
||||
|
||||
typedef std::function<unsigned long(int, int)> GetPixelFunction;
|
||||
|
||||
int clamp_byte(int value);
|
||||
|
||||
int best_index(int value, const int STEPS[], int count);
|
||||
|
||||
double sqr(double n);
|
||||
|
||||
/**
|
||||
* @brief Struct to represent a character to be drawn.
|
||||
* @param fgColor RGB
|
||||
* @param bgColor RGB
|
||||
* @param codePoint The code point of the character to be drawn.
|
||||
*/
|
||||
struct CharData {
|
||||
std::array<int, 3> fgColor = std::array<int, 3>{0, 0, 0};
|
||||
std::array<int, 3> bgColor = std::array<int, 3>{0, 0, 0};
|
||||
int codePoint;
|
||||
};
|
||||
|
||||
// Return a CharData struct with the given code point and corresponding averag
|
||||
// fg and bg colors.
|
||||
CharData createCharData(GetPixelFunction get_pixel, int x0, int y0,
|
||||
int codepoint, int pattern);
|
||||
|
||||
/**
|
||||
* @brief Find the best character and colors
|
||||
* for a 4x8 part of the image at the given position
|
||||
*
|
||||
* @param image
|
||||
* @param x0
|
||||
* @param y0
|
||||
* @param flags
|
||||
* @return CharData
|
||||
*/
|
||||
CharData findCharData(GetPixelFunction get_pixel, int x0, int y0,
|
||||
const int &flags);
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue