├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── README.txt ├── bmp.c ├── bmp.h ├── bmpto16.c ├── defplt.c ├── display16.c ├── dumpc.bat ├── image16c.c ├── image16c.h ├── play16.c └── txtdump.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.map 3 | *.obj 4 | *.bin 5 | *.bak 6 | *.bmp 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project("display16") 4 | 5 | #set c++ Standard to use 6 | set(CMAKE_CXX_STANDARD 14) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | 9 | 10 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 11 | message(FATAL_ERROR "x64 is not supported yet!") 12 | endif() 13 | 14 | if(NOT WIN32) 15 | message(FATAL_ERROR "This Platform is not supported yet!" ) 16 | endif() 17 | 18 | 19 | set(bmpto16_SRC 20 | "image16c.h" 21 | "image16c.c" 22 | "bmpto16.c" 23 | "bmp.h" 24 | "bmp.c" 25 | ) 26 | 27 | set(defplt_SRC 28 | "defplt.c" 29 | "image16c.h" 30 | "image16c.c" 31 | ) 32 | 33 | set(display16_SRC 34 | "display16.c" 35 | "image16c.h" 36 | "image16c.c" 37 | ) 38 | 39 | set(play16_SRC 40 | "play16.c" 41 | "image16c.h" 42 | "image16c.c" 43 | ) 44 | 45 | 46 | add_executable(bmpto16 ${bmpto16_SRC}) 47 | add_executable(defplt ${defplt_SRC}) 48 | add_executable(display16 ${display16_SRC}) 49 | 50 | link_libraries(winmm.lib) 51 | add_executable(play16 ${play16_SRC}) 52 | 53 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018 Electroduck 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | DISPLAY16: Display images and videos in the Command Prompt. 2 | 3 | Purpose of each program: 4 | bmpto16 - Convert 24bpp bitmaps to the display16 image format 5 | defplt - Restore the Command Prompt to the default palette 6 | display16 - Display an image in the display16 image format 7 | play16 - Play a video in the display16 video format 8 | txtdump - Dump a text file with a delay between printing each line 9 | The programs will give basic help when run without parameters. 10 | 11 | Building: 12 | Install Open Watcom and run build.bat. 13 | Use build_debug.bat to build with debugging symbols. 14 | 15 | To do list, for contributors, listed in order of importance: 16 | - Automatically restore the palette on exit (should work, doesn't now) 17 | - Add a program to generate videos, ideally from AVI/MP4 input files 18 | - Improve palette auto-generation 19 | -------------------------------------------------------------------------------- /bmp.c: -------------------------------------------------------------------------------- 1 | /* Create a bitmap from raw RGB24 data or vice versa. */ 2 | 3 | #include "bmp.h" 4 | 5 | #include 6 | #include 7 | 8 | #pragma pack(1) 9 | 10 | typedef struct { 11 | uint16_t type; 12 | uint32_t filesize; 13 | uint16_t r1, r2; 14 | uint32_t offset; 15 | } BMPHeader; 16 | 17 | typedef struct { 18 | uint32_t ihsize; 19 | int32_t width, height; 20 | uint16_t planes; 21 | uint16_t bpp; 22 | uint32_t comprtype; 23 | uint32_t imgsize; 24 | int32_t xres, yres; 25 | uint32_t colorct; 26 | uint32_t colorctimp; 27 | } BMPInfoHeader; 28 | 29 | #pragma pack() 30 | 31 | #ifdef EXE 32 | 33 | int main(int argc, char** argv) { 34 | int32_t w, h; 35 | FILE* in; 36 | FILE* out; 37 | size_t n_iod, n_pixels, n_bytes; 38 | uint8_t* databuf; 39 | char op; 40 | 41 | if(argc < 4) { 42 | puts("Usage: bmp c "); 43 | puts(" bmp e "); 44 | return 0; 45 | } 46 | 47 | op = *argv[1]; 48 | if((op == 'c') || (op == 'C')) { 49 | w = (int32_t)strtol(argv[2], 0, 0); 50 | h = (int32_t)strtol(argv[3], 0, 0); 51 | 52 | in = fopen(argv[4], "rb"); 53 | if(!in || ferror(in)) { 54 | printf("Error opening %s for read\n", argv[3]); 55 | return 1; 56 | } 57 | 58 | out = fopen(argv[5], "wb"); 59 | if(!out || ferror(out)) { 60 | printf("Error opening %s for write\n", argv[4]); 61 | return 2; 62 | } 63 | 64 | n_pixels = w * h; 65 | n_bytes = n_pixels * BMP_BYTESPERPIXEL; 66 | databuf = malloc(n_bytes); 67 | if(!databuf) { 68 | printf("Error allocating %u (0x%08X) bytes for data buffer\n", 69 | n_bytes, n_bytes); 70 | return 3; 71 | } 72 | 73 | printf("Reading file... "); 74 | n_iod = fread(databuf, BMP_BYTESPERPIXEL, n_pixels, in); 75 | printf("%u pixels read - ", n_iod); 76 | if(n_iod == n_pixels) 77 | puts("OK"); 78 | else { 79 | puts("Failed"); 80 | free(databuf); 81 | return 4; 82 | } 83 | 84 | printf("Writing file... "); 85 | n_iod = BMPWrite(out, w, h, databuf); 86 | if(n_iod) 87 | printf("%d pixels written - OK\n", n_iod); 88 | else { 89 | puts("Failed"); 90 | free(databuf); 91 | return 5; 92 | } 93 | 94 | free(databuf); 95 | } else if((op == 'e' || op == 'E')) { 96 | in = fopen(argv[2], "rb"); 97 | if(!in || ferror(in)) { 98 | printf("Error opening %s for read\n", argv[3]); 99 | return 21; 100 | } 101 | 102 | out = fopen(argv[3], "wb"); 103 | if(!out || ferror(out)) { 104 | printf("Error opening %s for write\n", argv[3]); 105 | return 22; 106 | } 107 | 108 | printf("Reading file... "); 109 | databuf = BMPRead(in, &w, &h, 0, 0); 110 | if(!databuf) { 111 | puts("Failed"); 112 | return 23; 113 | } 114 | puts("Done - OK"); 115 | 116 | n_pixels = w * h; 117 | printf("Writing file... "); 118 | n_iod = fwrite((void*)databuf, BMP_BYTESPERPIXEL, n_pixels, out); 119 | printf("%u pixels written - ", n_iod); 120 | if(n_iod == n_pixels) 121 | puts("OK"); 122 | else { 123 | puts("Failed"); 124 | free(databuf); 125 | return 24; 126 | } 127 | 128 | free(databuf); 129 | } 130 | 131 | return 0; 132 | } 133 | 134 | #endif 135 | 136 | size_t __stdcall BMPWrite(FILE* f, int32_t w, int32_t h, uint8_t* data_orig) { 137 | BMPHeader hdr; 138 | BMPInfoHeader ihdr; 139 | size_t pixelct, i, nbytes; 140 | uint8_t buf[BMP_BYTESPERPIXEL]; 141 | uint8_t* data; 142 | uint8_t* data_baseloc; 143 | 144 | if((w <= 0) || (h <= 0)) return 0; 145 | if(!f || !data_orig) return 0; 146 | if(ferror(f)) return 0; 147 | 148 | pixelct = w * h; 149 | nbytes = pixelct * BMP_BYTESPERPIXEL; 150 | 151 | data = malloc(nbytes); 152 | if(!data) return 0; 153 | data_baseloc = data; 154 | 155 | hdr.type = 0x4D42; // ASCII 'BM' 156 | hdr.filesize = nbytes + sizeof(BMPHeader) + sizeof(BMPInfoHeader); 157 | hdr.r1 = hdr.r2 = 0; 158 | hdr.offset = sizeof(BMPHeader) + sizeof(BMPInfoHeader); 159 | fwrite(&hdr, sizeof(BMPHeader), 1, f); 160 | if(ferror(f)) { free(data_baseloc); return 0; } 161 | 162 | ihdr.ihsize = sizeof(BMPInfoHeader); 163 | ihdr.width = w; 164 | ihdr.height = h; 165 | ihdr.planes = 1; 166 | ihdr.bpp = BMP_BITSPERPIXEL; 167 | ihdr.comprtype = 0; // no compression 168 | ihdr.imgsize = nbytes; 169 | ihdr.xres = BMP_DPM; 170 | ihdr.yres = BMP_DPM; 171 | ihdr.colorct = ihdr.colorctimp = 0; // no palette 172 | fwrite(&ihdr, sizeof(BMPInfoHeader), 1, f); 173 | if(ferror(f)) { free(data_baseloc); return 0; } 174 | 175 | // Must be flipped 176 | memcpy(data, data_orig, nbytes); 177 | BMPFlipVert(w, h, data); 178 | 179 | // Must be reordered from RGB to BGR 180 | for(i = 0; i < pixelct; i++) { 181 | buf[0] = data[2]; 182 | buf[1] = data[1]; 183 | buf[2] = data[0]; 184 | fwrite(buf, BMP_BYTESPERPIXEL, 1, f); 185 | if(ferror(f)) { free(data_baseloc); return 0; } 186 | data += BMP_BYTESPERPIXEL; 187 | } 188 | 189 | free(data_baseloc); 190 | return i; 191 | } 192 | 193 | void __stdcall BMPFlipVert(int32_t w, int32_t h, uint8_t* data) { 194 | int32_t line_a, line_b, x; 195 | uint8_t buf[BMP_BYTESPERPIXEL]; 196 | uint8_t* pos_a; 197 | uint8_t* pos_b; 198 | 199 | line_a = 0; 200 | line_b = h - 1; 201 | 202 | while(line_a < line_b) { 203 | for(x = 0; x < w; x++) { 204 | // Swap pixels 205 | pos_a = data + ((line_a * w + x) * BMP_BYTESPERPIXEL); 206 | pos_b = data + ((line_b * w + x) * BMP_BYTESPERPIXEL); 207 | memcpy(buf, pos_a, BMP_BYTESPERPIXEL); 208 | memcpy(pos_a, pos_b, BMP_BYTESPERPIXEL); 209 | memcpy(pos_b, buf, BMP_BYTESPERPIXEL); 210 | } 211 | 212 | line_a++; 213 | line_b--; 214 | } 215 | } 216 | 217 | uint8_t* __stdcall BMPRead(FILE* f, int32_t* w, int32_t* h, uint8_t* databuf, 218 | size_t databuf_len) { 219 | BMPHeader hdr; 220 | BMPInfoHeader ihdr; 221 | size_t pixelct, i, nbytes, databuf_len_px; 222 | uint8_t bgrbuf[BMP_BYTESPERPIXEL]; 223 | uint8_t* databuf_curpos; 224 | int databuf_allocd = 0; 225 | 226 | if(!(f && w && h)) return 0; 227 | if(ferror(f)) return 0; 228 | if(databuf_len != 0 && (databuf_len % BMP_BYTESPERPIXEL)) return 0; 229 | 230 | fread(&hdr, sizeof(BMPHeader), 1, f); 231 | if(feof(f) || ferror(f)) return 0; 232 | 233 | fread(&ihdr, sizeof(BMPInfoHeader), 1, f); 234 | if(feof(f) || ferror(f)) return 0; 235 | 236 | if(ihdr.bpp != BMP_BITSPERPIXEL) return 0; 237 | *w = ihdr.width; 238 | *h = ihdr.height; 239 | pixelct = *w * *h; 240 | nbytes = pixelct * BMP_BYTESPERPIXEL; 241 | 242 | if(!(databuf && databuf_len)) { 243 | // Databuf not already allocated 244 | databuf_len = nbytes; 245 | databuf = malloc(nbytes); 246 | if(!databuf) return 0; 247 | databuf_allocd = 1; 248 | } 249 | 250 | databuf_len_px = databuf_len / BMP_BYTESPERPIXEL; 251 | if(nbytes > databuf_len) { 252 | if(databuf_allocd) free(databuf); 253 | return 0; 254 | } 255 | 256 | fseek(f, hdr.offset, SEEK_SET); 257 | if(ferror(f) || feof(f)) { 258 | if(databuf_allocd) free(databuf); 259 | return 0; 260 | } 261 | 262 | databuf_curpos = databuf; 263 | for(i = 0; i < pixelct; i++) { 264 | fread(bgrbuf, BMP_BYTESPERPIXEL, 1, f); 265 | if(ferror(f) || feof(f)) { 266 | if(databuf_allocd) free(databuf); 267 | return 0; 268 | } 269 | databuf_curpos[0] = bgrbuf[2]; 270 | databuf_curpos[1] = bgrbuf[1]; 271 | databuf_curpos[2] = bgrbuf[0]; 272 | databuf_curpos += BMP_BYTESPERPIXEL; 273 | } 274 | 275 | BMPFlipVert(*w, *h, databuf); 276 | 277 | return databuf; 278 | } 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | -------------------------------------------------------------------------------- /bmp.h: -------------------------------------------------------------------------------- 1 | #ifndef BMP_H 2 | #define BMP_H 3 | 4 | #include 5 | #include 6 | 7 | #define BMP_BYTESPERPIXEL 3 8 | #define BMP_DPI 96 9 | 10 | #define BMP_BITSPERPIXEL (BMP_BYTESPERPIXEL * 8) 11 | #define BMP_DPM (BMP_DPI * 39) 12 | 13 | /** 14 | * BMPWrite: Write a bitmap file. 15 | * f File to write to (opened, writable) 16 | * w The width of the bitmap 17 | * h The height of the bitmap 18 | * data Raw image data of the bitmap 19 | * Returns: Number of pixels written, or 0 on error. 20 | */ 21 | size_t __stdcall BMPWrite(FILE* f, int32_t w, int32_t h, uint8_t* data); 22 | 23 | /** 24 | * BMPRead: Read a bitmap file. 25 | * f File to read from (opened, readable) 26 | * w Location to place the bitmap's width in 27 | * h Location to place the bitmap's height in 28 | * databuf Location to place the image data in, or 0 to allocate 29 | * dbuf_len Size of databuf, or 0 if databuf is null 30 | * Returns: Location of image data, or 0 on error. 31 | */ 32 | uint8_t* __stdcall BMPRead(FILE* f, int32_t* w, int32_t* h, uint8_t* databuf, 33 | size_t dbuf_len); 34 | 35 | /** 36 | * BMPFlipVert: Flip image vertically. 37 | * w Image width 38 | * h Image height 39 | * data Location of the image data 40 | */ 41 | void __stdcall BMPFlipVert(int32_t w, int32_t h, uint8_t* data); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /bmpto16.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "image16c.h" 6 | #include "bmp.h" 7 | 8 | int main(int argc, char** argv) { 9 | FILE* bmpFile; 10 | FILE* pltFile; 11 | FILE* outFile; 12 | uint32_t pltData[IMAGE16C_COLORCT]; 13 | uint32_t w, h; 14 | uint8_t* data24bpp; 15 | uint8_t* data16c; 16 | size_t nBytes; 17 | 18 | if(argc < 4) { 19 | puts("Usage: bmpto16 "); 20 | puts("\t = ##GEN to generate automatically."); 21 | return 1; 22 | } 23 | 24 | bmpFile = fopen(argv[1], "rb"); 25 | if(!bmpFile || ferror(bmpFile)) { 26 | puts("Failed to open bitmap file."); 27 | return 1; 28 | } 29 | 30 | if(!strcmp(argv[2], "##GEN")) 31 | pltFile = NULL; 32 | else { 33 | pltFile = fopen(argv[2], "rb"); 34 | if(!pltFile || ferror(pltFile)) { 35 | puts("Failed to open palette file."); 36 | return 1; 37 | } 38 | } 39 | 40 | outFile = fopen(argv[3], "wb"); 41 | if(!outFile || ferror(outFile)) { 42 | puts("Failed to open output file."); 43 | return 1; 44 | } 45 | 46 | data24bpp = BMPRead(bmpFile, (int32_t*)&w, (int32_t*)&h, NULL, 0); 47 | if(!data24bpp) { 48 | puts("Failed to read bitmap."); 49 | return 1; 50 | } 51 | 52 | if(pltFile) { 53 | if(fread(pltData, sizeof(uint32_t), IMAGE16C_COLORCT, pltFile) 54 | != IMAGE16C_COLORCT) { 55 | puts("Failed to read palette."); 56 | return 1; 57 | } 58 | fclose(pltFile); 59 | } else { 60 | GeneratePaletteFrom24bpp(pltData, w, h, data24bpp, 61 | IMAGE16C_PLTTHRESH_DEFAULT); 62 | } 63 | 64 | #ifdef IMAGE16_PACKED 65 | nBytes = (w/2) * h; 66 | #else 67 | nBytes = w * h; 68 | #endif 69 | 70 | data16c = malloc(nBytes); 71 | if(!data16c) { 72 | puts("Failed to allocate space for converted image."); 73 | return 1; 74 | } 75 | 76 | Convert24bppTo16c(w, h, data24bpp, data16c, pltData); 77 | 78 | if(!WriteImage16c(outFile, w, h, pltData, data16c)) { 79 | puts("Failed to write output image."); 80 | return 1; 81 | } 82 | 83 | fclose(outFile); 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /defplt.c: -------------------------------------------------------------------------------- 1 | #include "image16c.h" 2 | 3 | int main(int argc, char** argv) { 4 | return !RestoreConsoleToDefault(); 5 | } 6 | -------------------------------------------------------------------------------- /display16.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "image16c.h" 4 | 5 | int main(int argc, char** argv) { 6 | FILE* inFile; 7 | size_t imgW, imgH; 8 | uint32_t imgPlt[IMAGE16C_COLORCT]; 9 | uint8_t* imgData = NULL; 10 | 11 | if(argc < 2) { 12 | puts("Usage: display16 "); 13 | return 1; 14 | } 15 | 16 | inFile = fopen(argv[1], "rb"); 17 | if(!inFile || ferror(inFile)) { 18 | puts("Failed to open file."); 19 | return 1; 20 | } 21 | 22 | if(!ReadImage16c(inFile, &imgW, &imgH, imgPlt, &imgData)) { 23 | puts("Failed to read image."); 24 | return 1; 25 | } 26 | 27 | fclose(inFile); 28 | 29 | if(!DisplayImage16c(imgW, imgH, imgPlt, imgData)) { 30 | puts("Failed to display image."); 31 | return 1; 32 | } 33 | 34 | getchar(); 35 | 36 | free(imgData); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /dumpc.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | for %%f in (*.c;*.h) do ( 4 | echo. 5 | echo ****** FILE: %%f 6 | txtdump %%f 20 7 | ) -------------------------------------------------------------------------------- /image16c.c: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "image16c.h" 9 | 10 | static const uint32_t g_invalidPalette[IMAGE16C_COLORCT] = { 11 | 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 12 | 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 13 | 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 14 | 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000 }; 15 | 16 | static CONSOLE_SCREEN_BUFFER_INFOEX g_consoleDefault = { 17 | .cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX), 18 | .dwSize = { .X = 80, .Y = 300 }, 19 | .dwCursorPosition = { .X = 0, .Y = 0 }, 20 | .wAttributes = 0x07, 21 | .srWindow = { .Top = 0, .Left = 0, .Right = 80, .Bottom = 24 }, 22 | .dwMaximumWindowSize = { .X = 80, .Y = 300 }, 23 | .wPopupAttributes = 0x1F, 24 | .bFullscreenSupported = FALSE, 25 | .ColorTable = { 0x00000000, 0x00800000, 0x00008000, 0x00808000, 26 | 0x00000080, 0x00800080, 0x00008080, 0x00c0c0c0, 27 | 0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00, 28 | 0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff } 29 | }; 30 | 31 | static size_t g_zero = 0; 32 | 33 | int ReadImage16c(FILE* f, uint32_t* w, uint32_t* h, uint32_t* palette, 34 | uint8_t** data) { 35 | uint32_t nBytes; 36 | 37 | fread(w, sizeof(uint32_t), 1, f); 38 | if(ferror(f) || feof(f)) return 0; 39 | fread(h, sizeof(uint32_t), 1, f); 40 | if(ferror(f) || feof(f)) return 0; 41 | 42 | fread(palette, sizeof(uint32_t), IMAGE16C_COLORCT, f); 43 | if(ferror(f) || feof(f)) return 0; 44 | 45 | nBytes = *w * *h; 46 | 47 | #ifdef IMAGE16C_PACKED 48 | nBytes /= 2; 49 | #endif 50 | 51 | if(!*data) { 52 | *data = malloc(nBytes); 53 | if(!*data) return 0; 54 | } 55 | 56 | fread(*data, 1, nBytes, f); 57 | 58 | return 1; 59 | } 60 | 61 | int DisplayImage16c(uint32_t w, uint32_t h, uint32_t* palette, uint8_t* data) { 62 | CONSOLE_SCREEN_BUFFER_INFOEX csbi/*, csbiOld*/; 63 | HANDLE hOut; 64 | uint32_t x, y; 65 | uint8_t curColor; 66 | CHAR_INFO* cbuf; 67 | 68 | cbuf = alloca(w * h * sizeof(CHAR_INFO)); 69 | if(!cbuf) return 0; 70 | 71 | hOut = GetStdHandle(STD_OUTPUT_HANDLE); 72 | if(hOut == INVALID_HANDLE_VALUE) return 0; 73 | 74 | ZeroMemory(&csbi, sizeof(CONSOLE_SCREEN_BUFFER_INFOEX)); 75 | csbi.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); 76 | 77 | GetConsoleScreenBufferInfoEx(hOut, &csbi); 78 | //memcpy(&csbiOld, &csbi, sizeof(CONSOLE_SCREEN_BUFFER_INFOEX)); 79 | 80 | csbi.dwCursorPosition.X = 0; 81 | csbi.dwCursorPosition.Y = 0; 82 | csbi.dwSize.X = w; 83 | csbi.dwSize.Y = h; 84 | csbi.srWindow.Left = 0; 85 | csbi.srWindow.Top = 0; 86 | csbi.srWindow.Right = (SHORT)w; 87 | csbi.srWindow.Bottom = (SHORT)h; 88 | memcpy(csbi.ColorTable, palette, sizeof(uint32_t) * IMAGE16C_COLORCT); 89 | SetConsoleScreenBufferInfoEx(hOut, &csbi); 90 | 91 | for(y = 0; y < h; y++) { 92 | for(x = 0; x < w; x++) { 93 | #ifdef IMAGE16C_PACKED 94 | curColor = data[y * (w/2) + (x/2)] 95 | #else 96 | curColor = data[y * w + x]; 97 | #endif 98 | 99 | #ifdef IMAGE16C_PACKED 100 | if(x % 2) 101 | curColor = (curColor & 0x0F) << 4; 102 | else 103 | curColor = (curColor & 0xF0); 104 | #else 105 | curColor = curColor << 4; 106 | #endif 107 | 108 | //SetConsoleTextAttribute(hOut, curColor); 109 | //putchar(' '); 110 | //fflush(stdout); 111 | 112 | cbuf[y * w + x].Char.AsciiChar = ' '; 113 | cbuf[y * w + x].Attributes = curColor; 114 | } 115 | //putchar('\n'); 116 | //fflush(stdout); 117 | } 118 | 119 | WriteConsoleOutput(hOut, (CHAR_INFO*)cbuf, csbi.dwSize, 120 | csbi.dwCursorPosition, &csbi.srWindow); 121 | 122 | //putchar('\n'); 123 | 124 | //SetConsoleScreenBufferInfoEx(hOut, &csbiOld); 125 | 126 | return 1; 127 | } 128 | 129 | int WriteImage16c(FILE* f, uint32_t w, uint32_t h, uint32_t* palette, 130 | uint8_t* data) { 131 | uint32_t nBytes; 132 | 133 | fwrite(&w, sizeof(uint32_t), 1, f); 134 | if(ferror(f) || feof(f)) return 0; 135 | fwrite(&h, sizeof(uint32_t), 1, f); 136 | if(ferror(f) || feof(f)) return 0; 137 | 138 | fwrite(palette, sizeof(uint32_t), IMAGE16C_COLORCT, f); 139 | if(ferror(f) || feof(f)) return 0; 140 | 141 | nBytes = w * h; 142 | 143 | #ifdef IMAGE16C_PACKED 144 | nBytes /= 2; 145 | #endif 146 | 147 | fwrite(data, 1, nBytes, f); 148 | 149 | return 1; 150 | } 151 | 152 | void Convert24bppTo16c(uint32_t w, uint32_t h, uint8_t* data24bpp, 153 | uint8_t* data16c, uint32_t* palette) { 154 | uint32_t x, y; 155 | uint8_t curColor16c; 156 | 157 | #ifdef IMAGE16C_PACKED 158 | memset(data16c, 0, (w/2) * h); 159 | #endif 160 | 161 | for(y = 0; y < h; y++) { 162 | for(x = 0; x < w; x++) { 163 | curColor16c = (uint8_t)GetBestColorMatch(palette, 164 | IMAGE16C_24BPP_TO_PLTCOLOR(data24bpp, x, y, w), 165 | IMAGE16C_COLORCT); 166 | 167 | #ifdef IMAGE16C_PACKED 168 | if(x % 2) 169 | data16c[y * (w/2) + (x/2)] |= curColor16c; 170 | else 171 | data16c[y * (w/2) + (x/2)] |= curColor16c << 4; 172 | #else 173 | data16c[y * w + x] = curColor16c; 174 | #endif 175 | } 176 | } 177 | } 178 | 179 | size_t GetBestColorMatch(uint32_t* palette, uint32_t color, size_t pltSize) { 180 | int lowestDiff = 0x7FFFFFFF; 181 | int curDiff; 182 | size_t lowestDiffIndex = 0; 183 | size_t i; 184 | 185 | for(i = 0; i < pltSize; i++, palette++) { 186 | if(!(*palette & IMAGE16C_PLTINVALID)) { 187 | /*curDiff = abs((int)IMAGE16C_PALETTE_R(*palette) 188 | - (int)IMAGE16C_PALETTE_R(color)); 189 | curDiff += abs((int)IMAGE16C_PALETTE_G(*palette) 190 | - (int)IMAGE16C_PALETTE_G(color)); 191 | curDiff += abs((int)IMAGE16C_PALETTE_B(*palette) 192 | - (int)IMAGE16C_PALETTE_B(color)); 193 | curDiff /= 3;*/ 194 | curDiff = IMAGE16C_PLTCOLOR_DIFF(*palette, color); 195 | if(curDiff < lowestDiff) { 196 | lowestDiffIndex = i; 197 | lowestDiff = curDiff; 198 | } 199 | } 200 | } 201 | 202 | return lowestDiffIndex; 203 | } 204 | 205 | int RestoreConsoleToDefault(void) { 206 | HANDLE hOut; 207 | 208 | hOut = GetStdHandle(STD_OUTPUT_HANDLE); 209 | if(hOut == INVALID_HANDLE_VALUE) return 0; 210 | 211 | if(!SetConsoleScreenBufferInfoEx(hOut, &g_consoleDefault)) 212 | return 0; 213 | 214 | system("color 07"); 215 | system("cls"); 216 | 217 | return 1; 218 | } 219 | 220 | #define IMAGE16C_PLTGEN_CANCT 1024 221 | 222 | /* 223 | void GeneratePaletteFrom24bpp(uint32_t* palette, uint32_t w, uint32_t h, 224 | uint8_t* data24bpp, int threshold) { 225 | uint32_t x, y, colorAsPltColor; 226 | size_t bestMatch; 227 | size_t i, j, candidateCt; 228 | uint32_t candidates[IMAGE16C_PLTGEN_CANCT]; 229 | size_t candidateVotes[IMAGE16C_PLTGEN_CANCT]; 230 | uint32_t* bestCandidate; 231 | size_t* bestCandidateVotes; 232 | uint8_t diff; 233 | uint64_t totalR, totalG, totalB; 234 | uint32_t avgColor; 235 | 236 | memset(candidates, 0xFF, sizeof(uint32_t) * IMAGE16C_PLTGEN_CANCT); 237 | memset(candidateVotes, 0, sizeof(size_t) * IMAGE16C_PLTGEN_CANCT); 238 | 239 | srand(time(0)); 240 | 241 | // Build candidates list 242 | candidateCt = 0; 243 | for(y = 0; y < h; y++) { 244 | for(x = 0; x < w; x++) { 245 | colorAsPltColor = IMAGE16C_24BPP_TO_PLTCOLOR(data24bpp, x, y, w); 246 | bestMatch = GetBestColorMatch(candidates, colorAsPltColor, 247 | candidateCt); 248 | 249 | diff = IMAGE16C_PLTCOLOR_DIFF(candidates[bestMatch], colorAsPltColor); 250 | if(diff < threshold) 251 | candidateVotes[bestMatch]++; 252 | else { 253 | candidates[candidateCt] = colorAsPltColor; 254 | candidateVotes[candidateCt] = 1; 255 | candidateCt++; 256 | if(candidateCt == IMAGE16C_PLTGEN_CANCT) 257 | goto tooManyCandidates; // double break 258 | } 259 | } 260 | } 261 | tooManyCandidates: 262 | 263 | 264 | // Find average 265 | for(i = 0; i < candidateCt; i++) { 266 | totalR += IMAGE16C_PALETTE_R(candidates[i]); 267 | totalG += IMAGE16C_PALETTE_G(candidates[i]); 268 | totalB += IMAGE16C_PALETTE_B(candidates[i]); 269 | } 270 | avgColor = IMAGE16C_RGB_TO_PLTCOLOR(totalR / candidateCt, 271 | totalG / candidateCt, totalB / candidateCt); 272 | 273 | // Prioritize candidates away from average 274 | for(i = 0; i < candidateCt; i++) { 275 | diff = IMAGE16C_PLTCOLOR_DIFF(candidates[i], avgColor); 276 | candidateVotes[i] *= diff / 4; 277 | } 278 | 279 | // Get best candidates 280 | 281 | for(i = 0; i < IMAGE16C_COLORCT; i++) { 282 | bestCandidate = NULL; 283 | bestCandidateVotes = &g_zero; 284 | for(j = 0; j < candidateCt; j++) { 285 | if(candidateVotes[j] > *bestCandidateVotes) { 286 | bestCandidateVotes = &candidateVotes[j]; 287 | bestCandidate = &candidates[j]; 288 | } 289 | } 290 | 291 | if(!(bestCandidate && *bestCandidateVotes)) 292 | break; 293 | 294 | palette[i] = *bestCandidate; 295 | *bestCandidate = IMAGE16C_PLTINVALID; 296 | *bestCandidateVotes = 0; 297 | } 298 | 299 | // Get random candidates 300 | for(i = 0; i < IMAGE16C_COLORCT; i++) { 301 | palette[i] = candidates[rand() % candidateCt]; 302 | } 303 | } 304 | */ 305 | 306 | 307 | // This is a travesty. 308 | void GeneratePaletteFrom24bpp(uint32_t* palette, uint32_t w, uint32_t h, 309 | uint8_t* data24bpp, int threshold) { 310 | uint32_t x, y; 311 | uint8_t i; 312 | 313 | for(i = 0; i < IMAGE16C_COLORCT; i++) { 314 | x = rand() % w; 315 | y = rand() % h; 316 | 317 | palette[i] = IMAGE16C_24BPP_TO_PLTCOLOR(data24bpp, x, y, w); 318 | } 319 | } 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | -------------------------------------------------------------------------------- /image16c.h: -------------------------------------------------------------------------------- 1 | #ifndef IMAGE16_H 2 | #define IMAGE16_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define IMAGE16C_COLORCT 16 9 | 10 | //#define IMAGE16C_PACKED 11 | 12 | #define IMAGE16C_PALETTE_R(c) (uint8_t) ((c) & 0x000000FF) 13 | #define IMAGE16C_PALETTE_G(c) (uint8_t)(((c) & 0x0000FF00) >> 8) 14 | #define IMAGE16C_PALETTE_B(c) (uint8_t)(((c) & 0x00FF0000) >> 16) 15 | 16 | #define IMAGE16C_PLTINVALID 0xFF000000 17 | 18 | #define IMAGE16C_24BPP_TO_PLTCOLOR(i, x, y, w) \ 19 | ((uint32_t)(i)[((y) * (w) + (x)) * 3] \ 20 | | (uint32_t)(i)[((y) * (w) + (x)) * 3 + 1] << 8 \ 21 | | (uint32_t)(i)[((y) * (w) + (x)) * 3 + 2] << 16) 22 | 23 | #define IMAGE16C_RGB_TO_PLTCOLOR(r, g, b) \ 24 | ((uint32_t)(r) | (uint32_t)(g) << 8 | (uint32_t)(b) << 16) 25 | 26 | #define IMAGE16C_PLTCOLOR_DIFF(a, b) \ 27 | ((((a) & 0xFF000000) || ((b) & 0xFF000000)) ? 0xFF : \ 28 | ((abs((int)(a & 0x000000FF) - (int)(b & 0x000000FF)) \ 29 | + abs((int)(a & 0x0000FF00) - (int)(b & 0x0000FF00)) \ 30 | + abs((int)(a & 0x00FF0000) - (int)(b & 0x00FF0000))) \ 31 | / 3)) 32 | 33 | #define IMAGE16C_PLTTHRESH_DEFAULT 32 34 | #define IMAGE16C_PLTMAXITER_DEFAULT 100000 35 | 36 | int ReadImage16c(FILE* f, uint32_t* w, uint32_t* h, uint32_t* palette, 37 | uint8_t** data); 38 | 39 | int DisplayImage16c(uint32_t w, uint32_t h, uint32_t* palette, uint8_t* data); 40 | 41 | int WriteImage16c(FILE* f, uint32_t w, uint32_t h, uint32_t* palette, 42 | uint8_t* data); 43 | 44 | void Convert24bppTo16c(uint32_t w, uint32_t h, uint8_t* data24bpp, 45 | uint8_t* data16c, uint32_t* palette); 46 | 47 | size_t GetBestColorMatch(uint32_t* palette, uint32_t color, size_t pltSize); 48 | 49 | int RestoreConsoleToDefault(void); 50 | 51 | void GeneratePaletteFrom24bpp(uint32_t* palette, uint32_t w, uint32_t h, 52 | uint8_t* data24bpp, int threshold); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /play16.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "image16c.h" 5 | 6 | //#define WIN32_LEAN_AND_MEAN 7 | #include 8 | 9 | /* Format: 10 | uint16_t Milliseconds per frame 11 | uint32_t Location of WAV audio within file (zero: no audio) 12 | uint32_t Audio length in bytes 13 | uint32_t Number of frames 14 | Nx uint8_t Images 15 | Nx uint8_t WAV audio data 16 | */ 17 | 18 | void CtrlCHandler(int n); 19 | uint32_t millis(void); 20 | 21 | int main(int argc, char** argv) { 22 | FILE* inFile; 23 | size_t imgW, imgH, j; 24 | uint32_t imgPlt[IMAGE16C_COLORCT]; 25 | uint8_t* imgData = NULL; 26 | uint16_t msPerFrame; 27 | uint32_t msLast, msCur, audioLoc, audioLen, i, frameCt; 28 | COORD zeroPos = { .X=0, .Y=0 }; 29 | HANDLE hOut; 30 | fpos_t lastPos; 31 | uint8_t* audioData = NULL; 32 | 33 | if(argc < 2) { 34 | puts("Usage: play16 "); 35 | return 1; 36 | } 37 | 38 | hOut = GetStdHandle(STD_OUTPUT_HANDLE); 39 | if(!hOut) { 40 | puts("Failed to get handle to console output."); 41 | return 1; 42 | } 43 | 44 | inFile = fopen(argv[1], "rb"); 45 | if(!inFile || ferror(inFile)) { 46 | printf("Failed to open `%s'.\n", argv[1]); 47 | return 1; 48 | } 49 | 50 | if(!fread(&msPerFrame, sizeof(uint16_t), 1, inFile)) { 51 | puts("Error reading video FPS."); 52 | return 1; 53 | } 54 | 55 | if(!fread(&audioLoc, sizeof(uint32_t), 1, inFile)) { 56 | puts("Error reading audio position."); 57 | return 1; 58 | } 59 | 60 | if(!fread(&audioLen, sizeof(uint32_t), 1, inFile)) { 61 | puts("Error reading audio length."); 62 | return 1; 63 | } 64 | 65 | if(!fread(&frameCt, sizeof(uint32_t), 1, inFile)) { 66 | puts("Error reading frame count."); 67 | return 1; 68 | } 69 | 70 | system("cls"); 71 | 72 | signal(SIGINT, CtrlCHandler); 73 | 74 | if(audioLoc && audioLen) { 75 | fgetpos(inFile, &lastPos); 76 | 77 | if(fseek(inFile, (long int)audioLoc, SEEK_SET)) { 78 | if(MessageBoxA(NULL, 79 | "Unable to seek to audio location.\nContinue in silence?", 80 | NULL, MB_OKCANCEL | MB_ICONWARNING) == IDOK) 81 | goto noaudio; 82 | else 83 | return 1; 84 | } 85 | 86 | if(!(audioData = malloc(audioLen))) { 87 | if(MessageBoxA(NULL, 88 | "Unable to allocate space for audio.\nContinue in silence?", 89 | NULL, MB_OKCANCEL | MB_ICONWARNING) == IDOK) 90 | goto noaudio; 91 | else 92 | return 1; 93 | } 94 | 95 | if(fread(audioData, 1, audioLen, inFile) != audioLen) { 96 | if(MessageBoxA(NULL, 97 | "Failed to read audio entirely.\nContinue in silence?", 98 | NULL, MB_OKCANCEL | MB_ICONWARNING) == IDOK) 99 | goto noaudio; 100 | else 101 | return 1; 102 | } 103 | 104 | if(!PlaySoundA((LPCSTR)audioData, NULL, SND_ASYNC | SND_MEMORY)) { 105 | if(MessageBoxA(NULL, 106 | "Failed to read audio entirely.\nContinue in silence?", 107 | NULL, MB_OKCANCEL | MB_ICONWARNING) == IDOK) 108 | goto noaudio; 109 | else 110 | return 1; 111 | } 112 | 113 | noaudio: 114 | fsetpos(inFile, &lastPos); 115 | } 116 | 117 | msLast = millis(); 118 | for(i = 0; i < frameCt; i++) { 119 | if(!SetConsoleCursorPosition(hOut, zeroPos)) { 120 | printf("Failed to set console cursor pos on frame %u.\n", i); 121 | return 1; 122 | } 123 | 124 | if(!ReadImage16c(inFile, &imgW, &imgH, imgPlt, &imgData)) { 125 | printf("Error reading frame %u.\n", i); 126 | return 1; 127 | } 128 | 129 | if(!DisplayImage16c(imgW, imgH, imgPlt, imgData)) { 130 | printf("Error displaying frame %u.\n", i); 131 | return 1; 132 | } 133 | 134 | while((millis() - msLast) < msPerFrame) { 135 | } 136 | msLast = millis(); 137 | } 138 | 139 | return 0; 140 | } 141 | 142 | void CtrlCHandler(int n) { 143 | RestoreConsoleToDefault(); 144 | exit(0); 145 | } 146 | 147 | static LARGE_INTEGER g_perfFreq = { .QuadPart = 0 }; 148 | uint32_t millis(void) { 149 | LARGE_INTEGER curPerfCtr; 150 | 151 | if(!g_perfFreq.QuadPart) { 152 | if(!QueryPerformanceFrequency(&g_perfFreq)) { 153 | MessageBoxA(NULL, "No time-interval counter installed.", NULL, 154 | MB_ICONERROR); 155 | exit(1); 156 | } 157 | } 158 | 159 | QueryPerformanceCounter(&curPerfCtr); 160 | 161 | return (uint32_t)((curPerfCtr.QuadPart * 1000) / g_perfFreq.QuadPart); 162 | } 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /txtdump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | 6 | int main(int argc, char** argv) { 7 | FILE* in; 8 | int c; 9 | DWORD ms; 10 | 11 | if(argc < 3) { 12 | puts("Usage: txtdump "); 13 | return 1; 14 | } 15 | 16 | in = fopen(argv[1], "r"); 17 | if(!in || ferror(in)) { 18 | puts("Unable to open file."); 19 | return 1; 20 | } 21 | 22 | ms = (DWORD)strtol(argv[2], 0, 0); 23 | 24 | while((c = fgetc(in)) != EOF) { 25 | putchar(c); 26 | if(c == '\n') 27 | Sleep(ms); 28 | } 29 | 30 | return 0; 31 | } 32 | --------------------------------------------------------------------------------