imcat icon indicating copy to clipboard operation
imcat copied to clipboard

Building on Windows

Open eabase opened this issue 8 months ago • 0 comments

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:

  1. Open a DEV powershell in Windows Terminal.
  2. 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)
  1. Add a script (not needed) to simply copy paste or run cmake all 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
  1. Add .clang-format and .editorconfig formatting 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

  1. 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

eabase avatar Aug 07 '25 07:08 eabase