imcat
imcat copied to clipboard
Building on Windows
I managed to get this built out of the box using VS 2022. With minor modification when using the code from here:
- https://github.com/belfner/imcat
Here are the instructions:
- Open a DEV powershell in
Windows Terminal. - Create the following files:
# cat .\CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(imcat C)
#--------------------------------------------------------------------
# Original Makefile is using:
# $(CC) -D_POSIX_C_SOURCE=2 -std=c99 -Wall -g -o imcat imcat.c -lm
#--------------------------------------------------------------------
# Set the source file
add_executable(imcat imcat.c )
# Set compiler flags based on compiler
if(MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded")
# MSVC-specific flags
# /Ox, full optimization
# /Os, favour small code
add_compile_options(/Ox /Os)
target_compile_options(imcat PRIVATE /W4 /Zi)
# Suppress WARNINGS
# C4996
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
else()
# GCC/Clang flags
target_compile_options(imcat PRIVATE -D_POSIX_C_SOURCE=2 -std=c99 -Wall -g)
target_link_libraries(imcat m)
endif()
# Set the C standard (applies to all compilers)
# C_STANDARD 99 : Tells CMake you want to use C99.
# C_STANDARD_REQUIRED YES : Ensures the compiler doesn’t silently fall back to an older standard.
set_target_properties(imcat PROPERTIES C_STANDARD 99 C_STANDARD_REQUIRED YES)
- Add a script (not needed) to simply copy paste or run
cmakeall cleanup and commands.
# cat .\build.ps1
#!/usr/bin/evn pwsh
# TODO:
# [ ] Check if in DEV environment
# [ ] Set (this) CWD to the cd path below
cd "C:\ADD-YOUR-PATH-TO\imcat" && rmdir .\build\ -Force
mkdir build && cd build
cmake -S .. -B . -Ax64 -Wno-dev -Wno-deprecated --fresh --install-prefix 'C:\YOU-PATH-TO\gitclones\imcat\build\bin' --loglevel=VERBOSE -G "Visual Studio 17 2022"
cmake --build . --config Release --parallel 8
- Add
.clang-formatand.editorconfigformatting files to remedy crappy C style formatting.
# cat .\.editorconfig
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
# cat .\.clang-format
#------------------------------------------------------------------------------
# Author : eabase
# Date : 2025-07-22
# Filename : .clang-format
# Used for : -
#------------------------------------------------------------------------------
# References:
# [1] https://clang.llvm.org/docs/ClangFormatStyleOptions.html
# [2] https://leimao.github.io/blog/Clang-Format-Quick-Tutorial/ # Example
# [3] https://www.kernel.org/doc/html/next/dev-tools/clang-format.html#reformatting-blocks-of-code
#
#------------------------------------------------------------------------------
BasedOnStyle: LLVM
ColumnLimit: 200
IndentWidth: 4
TabWidth: 4
#LineEndingStyle: LE_CRLF
#LineEndingStyle: LE_LF
#MaxEmptyLinesToKeep: 3
# alignment of: // comments
AlignTrailingComments: true
# alignment of: {} Braces
BreakBeforeBraces: Attach
# alignment of: ()
AllowAllParametersOfDeclarationOnNextLine: false
#AllowAllArgumentsOnNextLine: true
BinPackParameters: true
SpaceBeforeParens: true
# alignement of: #defines, #includes
AlignConsecutiveMacros: true
#AlignConsecutiveAssignments: true
#AlignConsecutiveDeclarations: true
SortIncludes: false
#SortUsingDeclarations: never
IncludeBlocks: Preserve
- Fix the @belfner code to play cool with MSVC.
Here I used git diff --ignore-all-space:
Click Here to see diff
diff --git a/imcat.c b/imcat.c
index afb5a4b..44eac22 100644
--- a/imcat.c
+++ b/imcat.c
@@ -9,6 +9,9 @@
#include <math.h>
#include <errno.h> // Needed for strtol error checking in get_int function
+#define LINESZ1 16384
+#define LINESZ2 32768
+
#if defined(_WIN64)
#define STBI_NO_SIMD
#endif
@@ -26,18 +29,17 @@ static int do_fit=-1;
#if defined(_WIN64)
#include <windows.h>
-static void get_terminal_size(void)
-{
+static void get_terminal_size(void) {
const HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(hStdout, &info);
termw = info.dwSize.X;
termh = info.dwSize.Y;
- if ( !termw ) termw = 80;
+ if (!termw)
+ termw = 80;
}
static int oldcodepage = 0;
-static void set_console_mode(void)
-{
+static void set_console_mode(void) {
DWORD mode = 0;
const HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleMode(hStdout, &mode);
@@ -48,11 +50,9 @@ static void set_console_mode(void)
doubleres = 1;
}
#else
-static void get_terminal_size(void)
-{
+static void get_terminal_size(void) {
FILE *f = popen("stty size", "r");
- if ( !f )
- {
+ if (!f) {
fprintf(stderr, "Failed to determine terminal size using stty.\n");
exit(1);
}
@@ -60,28 +60,19 @@ static void get_terminal_size(void)
assert(num == 2);
pclose(f);
}
-static void set_console_mode()
-{
- doubleres=1;
-}
+static void set_console_mode() { doubleres = 1; }
#endif
-
-
#define RESETALL "\x1b[0m"
-
-static void print_image_single_res( int w, int h, unsigned char* data )
-{
- const int linesz = 16384;
- char line[ linesz ];
+static void print_image_single_res(int w, int h, unsigned char *data) {
+ // const int linesz = 16384;
+ char line[LINESZ1];
unsigned char *reader = data;
- for ( int y=0; y<h; ++y )
- {
+ for (int y = 0; y < h; ++y) {
line[0] = 0;
- for ( int x=0; x<w; ++x )
- {
+ for (int x = 0; x < w; ++x) {
strncat(line, "\x1b[48;2;", sizeof(line) - strlen(line) - 1);
char tripl[80];
unsigned char r = *reader++;
@@ -114,21 +105,17 @@ static void print_image_single_res( int w, int h, unsigned char* data )
b = (b * t0 + termbg[2] * t1) / 255; \
}
-static void print_image_double_res( int w, int h, unsigned char* data )
-{
+static void print_image_double_res(int w, int h, unsigned char *data) {
if (h & 1)
h--;
- const int linesz = 32768;
- char line[ linesz ];
-
+ // const int linesz = 32768;
+ char line[LINESZ2];
- for ( int y=0; y<h; y+=2 )
- {
+ for (int y = 0; y < h; y += 2) {
const unsigned char *row0 = data + (y + 0) * w * 4;
const unsigned char *row1 = data + (y + 1) * w * 4;
line[0] = 0;
- for ( int x=0; x<w; ++x )
- {
+ for (int x = 0; x < w; ++x) {
// foreground colour.
strncat(line, "\x1b[38;2;", sizeof(line) - strlen(line) - 1);
char tripl[80];
@@ -156,9 +143,7 @@ static void print_image_double_res( int w, int h, unsigned char* data )
}
}
-
-static int process_image( const char* nm )
-{
+static int process_image(const char *nm) {
int imw = 0, imh = 0, n = 0;
int outw, outh;
@@ -169,49 +154,48 @@ static int process_image( const char* nm )
float aspectratio = imw / (float)imh;
- if ( do_fit != -1)
- {
+ if (do_fit != -1) {
int adj_h = doubleres ? termh * 2 : termh;
- if ( ( (float) termw / (float) adj_h ) < aspectratio )
- {
+ if (((float)termw / (float)adj_h) < aspectratio) {
outw = imw < termw ? imw : termw;
outh = (int)roundf(outw / aspectratio);
- }
- else
- {
+ } else {
outh = imh < adj_h ? imh : adj_h;
outw = (int)roundf(outh * aspectratio);
}
- }
- else if ( uw != -1 )
- {
+ } else if (uw != -1) {
outw = uw;
outh = (int)roundf(outw / aspectratio);
- }
- else if ( uh != -1 )
- {
+ } else if (uh != -1) {
outh = uh;
outw = (int)roundf(outh * aspectratio);
- }
- else
- {
+ } else {
outw = imw < termw ? imw : termw;
outh = (int)roundf(outw / aspectratio);
}
float pixels_per_char = imw / (float)outw;
- if ( pixels_per_char < 1 ) pixels_per_char = 1;
+ if (pixels_per_char < 1)
+ pixels_per_char = 1;
int kernelsize = (int)floorf(pixels_per_char);
- if ( (kernelsize&1) == 0 ) kernelsize--;
- if ( !kernelsize ) kernelsize=1;
+ if ((kernelsize & 1) == 0)
+ kernelsize--;
+ if (!kernelsize)
+ kernelsize = 1;
const int kernelradius = (kernelsize - 1) / 2;
// fprintf( stderr, "pixels per char: %f, kernelsize: %d, out: %dx%d\n", pixels_per_char, kernelsize, outw, outh );
- unsigned char out[ outh ][ outw ][ 4 ];
+ // MSVC apparently (??) doesn't like the 3D formalism, so we convert to a 1D buffer.
+ //unsigned char out[outh][outw][4];
+ unsigned char* out = malloc(outh * outw * 4);
+ if (!out) {
+ fprintf(stderr, "Memory allocation failed\n");
+ exit(1);
+ }
+
for (int y = 0; y < outh; ++y)
- for ( int x=0; x<outw; ++x )
- {
+ for (int x = 0; x < outw; ++x) {
const int cx = (int)roundf(pixels_per_char * x);
const int cy = (int)roundf(pixels_per_char * y);
int acc[4] = {0, 0, 0, 0};
@@ -224,9 +208,9 @@ static int process_image( const char* nm )
sx = sx < 0 ? 0 : sx;
int ex = cx + kernelradius;
ex = ex >= imw ? imw - 1 : ex;
+
for (int yy = sy; yy <= ey; ++yy)
- for ( int xx = sx; xx <= ex; ++xx )
- {
+ for (int xx = sx; xx <= ex; ++xx) {
unsigned char *reader = data + (yy * imw * 4) + xx * 4;
const int a = reader[3];
acc[0] += a * reader[0] / 255;
@@ -235,10 +219,20 @@ static int process_image( const char* nm )
acc[3] += reader[3];
numsamples++;
}
- out[ y ][ x ][ 0 ] = acc[ 0 ] / numsamples;
- out[ y ][ x ][ 1 ] = acc[ 1 ] / numsamples;
- out[ y ][ x ][ 2 ] = acc[ 2 ] / numsamples;
- out[ y ][ x ][ 3 ] = acc[ 3 ] / numsamples;
+
+ // OLD formalism:
+ //out[(y * outw + x) * 4 + channel]
+ //out[y][x][0] = (unsigned char) acc[0] / numsamples;
+ //out[y][x][1] = (unsigned char) acc[1] / numsamples;
+ //out[y][x][2] = (unsigned char) acc[2] / numsamples;
+ //out[y][x][3] = (unsigned char) acc[3] / numsamples;
+
+ // NEW
+ int index = (y * outw + x) * 4;
+ out[index + 0] = (unsigned char) (acc[0] / numsamples);
+ out[index + 1] = (unsigned char) (acc[1] / numsamples);
+ out[index + 2] = (unsigned char) (acc[2] / numsamples);
+ out[index + 3] = (unsigned char) (acc[3] / numsamples);
}
stbi_image_free(data);
@@ -246,14 +240,14 @@ static int process_image( const char* nm )
if (doubleres)
print_image_double_res(outw, outh, (unsigned char *)out);
- else
+ else {
print_image_single_res(outw, outh, (unsigned char *)out);
+ }
+ free(out);
return 0;
}
-
-int get_int( const char* num,const char* msg )
-{
+int get_int(const char *num, const char *msg) {
// Code loosely based on strtol's man page
char *endptr;
long val;
@@ -270,13 +264,10 @@ int get_int( const char* num,const char* msg )
return val;
}
-
-
-int main( int argc, char* argv[] )
-{
- if ( argc == 1 || !strcmp( argv[1], "--help" ) )
- {
- fprintf( stderr, "Usage:\n"
+int main(int argc, char *argv[]) {
+ if (argc == 1 || !strcmp(argv[1], "--help")) {
+ fprintf(stderr,
+ "Usage:\n"
" %s [options] image [image2 .. imageN]\n"
"Displays image in terminal\n\nOptions:\n"
" -f, --fit fit image to console size\n"
@@ -289,55 +280,40 @@ int main( int argc, char* argv[] )
// Get location and value of passed flags
int hloc = -1, wloc = -1, floc = -1;
- for ( int i=1; i<argc; ++i )
- {
- if ( !strcmp( argv[i], "-f" ) || !strcmp( argv[i], "--fit" ) )
- {
+ for (int i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--fit")) {
// Check to see if argument has already been passed
- if ( floc != -1 )
- {
+ if (floc != -1) {
fprintf(stderr, "Fit argument passed multiple times\n");
exit(1);
}
floc = i;
do_fit = 1;
- }
- else if ( !strcmp( argv[i], "-w" ) || !strcmp( argv[i], "--width" ) )
- {
+ } else if (!strcmp(argv[i], "-w") || !strcmp(argv[i], "--width")) {
// Check to see if argument has already been passed
- if ( wloc != -1 )
- {
+ if (wloc != -1) {
fprintf(stderr, "Width argument passed multiple times\n");
exit(1);
}
wloc = i;
- if ( i+1 < argc )
- {
+ if (i + 1 < argc) {
uw = get_int(argv[i + 1], "Invalid width value \"%s\"\n");
- }
- else
- {
+ } else {
fprintf(stderr, "Missing width value\n");
exit(-1);
}
- }
- else if ( !strcmp( argv[i], "-h" ) || !strcmp( argv[i], "--height" ) )
- {
+ } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--height")) {
// Check to see if argument has already been passed
- if ( hloc != -1 )
- {
+ if (hloc != -1) {
fprintf(stderr, "Height argument passed multiple times\n");
exit(1);
}
hloc = i;
- if ( i+1 < argc )
- {
+ if (i + 1 < argc) {
uh = get_int(argv[i + 1], "Invalid height value \"%s\"\n");
- }
- else
- {
+ } else {
fprintf(stderr, "Missing height value\n");
exit(-1);
}
@@ -346,8 +322,7 @@ int main( int argc, char* argv[] )
// Parse environment variable for terminal background colour.
const char *imcatbg = getenv("IMCATBG");
- if ( imcatbg )
- {
+ if (imcatbg) {
const int bg = strtol(imcatbg + 1, 0, 16);
termbg[2] = (bg >> 0) & 0xff;
termbg[1] = (bg >> 8) & 0xff;
@@ -363,11 +338,9 @@ int main( int argc, char* argv[] )
// fprintf( stderr, "Your terminal is size %dx%d\n", termw, termh );
// Step 2: Process all images on the command line.
- for ( int i=1; i<argc; ++i )
- {
+ for (int i = 1; i < argc; ++i) {
// Ignore arguments that are not image paths
- if ( i == hloc || i == hloc + 1 || i == wloc || i == wloc + 1 || i == floc )
- {
+ if (i == hloc || i == hloc + 1 || i == wloc || i == wloc + 1 || i == floc) {
continue;
}
const char *nm = argv[i];
Hope this helps.
Would be great to understand what is the issue with the font in #14