├── .gitmodules ├── LICENSE.md ├── Makefile ├── README.md ├── ice-player.lua ├── ice-tester.sh ├── ice-wrapper.sh └── ice.c /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "argparse"] 2 | path = argparse 3 | url = https://github.com/Cofyc/argparse 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2016 Adrian "asie" Siekierka, Ben "GreaseMonkey" Russell 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 5 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 6 | to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of 9 | the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 12 | THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 14 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 15 | IN THE SOFTWARE. 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBS = -lm 2 | INCLUDES = argparse/argparse.h 3 | OBJS = argparse/argparse.o ice.o 4 | 5 | all: ice 6 | 7 | build/%.o: %.c $(INCLUDES) 8 | $(CC) -c -o $@ $< $(CFLAGS) 9 | 10 | ice: $(OBJS) 11 | $(CC) -o $@ $^ $(CFLAGS) $(LIBS) 12 | 13 | .PHONY: clean 14 | 15 | clean: 16 | rm -f $(OBJS) ice 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intelligent Cirno Experience 2 | 3 | ICE is a video codec for OpenComputers created by ChenThread in a few days for BetterThanMinecon 2016. 4 | 5 | Yes, it is slow. I recommend you compile with at least -O2. 6 | 7 | * **ice.c** - The encoder. Use --help for help. It doesn't let you do much right now - just encode 160x50 RGB24 data. 8 | * **ice-player.lua** - The ICE-encoded video player for OpenComputers. Will play a tape if one is inserted. 9 | * **ice-wrapper.sh** - A friendly wrapper which takes in a video file and outputs an ICE-encoded video file. Does not keep aspect ratio. Requires ffmpeg. 10 | * **Makefile** - It holds the bits together. 11 | * **README.md** - It explains the bits which are being held together. 12 | 13 | ## Compiling 14 | 15 | $ git submodule init 16 | $ git submodule update 17 | $ make 18 | 19 | ## TODO 20 | 21 | * Add parameters for width, height and tier to encoder. 22 | -------------------------------------------------------------------------------- /ice-player.lua: -------------------------------------------------------------------------------- 1 | local component = require("component") 2 | local term = require("term") 3 | local gpu = component.gpu 4 | local computer = require("computer") 5 | local tape = component.tape_drive 6 | 7 | COLMAP = {} 8 | local i 9 | for i=0,15 do 10 | local w = i*255/15 11 | COLMAP[i] = (w<<16)|(w<<8)|w 12 | gpu.setPaletteColor(i, COLMAP[i]) 13 | end 14 | for i=0,240-1 do 15 | local r = i%6 16 | local g = (i//6)%8 17 | local b = (i//(6*8)) 18 | 19 | r = (r*255+2)//5 20 | g = (g*255+3)//7 21 | b = (b*255+2)//4 22 | 23 | COLMAP[i+16] = (r<<16)|(g<<8)|b 24 | end 25 | 26 | if tape and tape.isReady() then 27 | tape.stop() 28 | while tape.seek(-tape.getSize()) ~= 0 do 29 | os.sleep(0.05) 30 | tape.stop() 31 | end 32 | tape.stop() 33 | end 34 | 35 | local fname = ... 36 | local fp = io.open(fname, "rb") 37 | 38 | local W = fp:read(1):byte() 39 | local H = fp:read(1):byte() 40 | gpu.setResolution(W, H) 41 | gpu.setBackground(0x000000) 42 | gpu.setForeground(0xFFFFFF) 43 | term.clear() 44 | 45 | if sysnative then 46 | tlast = os.clock() 47 | else 48 | os.sleep(0.05) 49 | if tape and tape.isReady() then 50 | tape.play() 51 | os.sleep(1.0) -- deal to sound latency 52 | end 53 | tlast = computer.uptime() 54 | end 55 | 56 | local delay_acc = 0.0 57 | local function delay(d) 58 | assert(d >= 0.0) 59 | delay_acc = delay_acc + d 60 | local dquo = math.floor(delay_acc / 0.05) * 0.05 61 | delay_acc = delay_acc - dquo 62 | os.sleep(dquo) 63 | end 64 | 65 | while true do 66 | local s = fp:read(1) 67 | if s == "" or s == nil then 68 | break 69 | end 70 | local c = s:byte() 71 | 72 | if c == 0xFF then 73 | tnow = computer.uptime() 74 | tlast = tlast + 0.05 75 | while tnow < tlast do 76 | --delay(tlast-tnow) 77 | os.sleep(tlast-tnow) 78 | tnow = computer.uptime() 79 | end 80 | elseif c == 0x00 then 81 | local col = COLMAP[fp:read(1):byte()] 82 | gpu.setBackground(col) 83 | else 84 | local by = fp:read(1):byte() 85 | local bx = fp:read(1):byte() 86 | local bw = fp:read(1):byte() 87 | local bh = c & 0x3F 88 | local type = c & 0x40 89 | if type == 0x40 then 90 | gpu.fill(bx, by, bw, bh, " ") 91 | else 92 | if bh < bw then 93 | for i = 0, bh-1 do 94 | gpu.set(bx, by + i, string.rep(" ", bw), false) 95 | end 96 | else 97 | for i = 0, bw - 1 do 98 | gpu.set(bx + i, by, string.rep(" ", bh), true) 99 | end 100 | end 101 | end 102 | end 103 | end 104 | 105 | gpu.setBackground(0x000000) 106 | gpu.setForeground(0xFFFFFF) 107 | 108 | -------------------------------------------------------------------------------- /ice-tester.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Video tester - requires mpv (alternatively you can use ffplay) 3 | ffmpeg -v 0 -i "$1" -s 160x50 -r 20 -f rawvideo -pix_fmt rgb24 - 2>/dev/null | \ 4 | ./ice -i /dev/stdin -o /dev/null -d /dev/stdout | \ 5 | ffmpeg -v 0 -f rawvideo -pix_fmt rgb24 -s 160x50 -r 20 -i - -vn -i "$1" -map 0:0 -map "1$2" -f avi -acodec mp3 -vcodec rawvideo -r 20 -s 160x50 -pix_fmt bgr24 -vf setdar=1.78 - 2>/dev/null | \ 6 | mpv - 7 | 8 | #ffplay - 2>/dev/null 9 | 10 | -------------------------------------------------------------------------------- /ice-wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Friendly script to take video files and output an ICE file. 3 | ffmpeg -v 0 -i "$1" -s 160x50 -r 20 -f rawvideo -pix_fmt rgb24 - | ./ice -i - -o "$2" 4 | -------------------------------------------------------------------------------- /ice.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016 Adrian "asie" Siekierka, Ben "GreaseMonkey" Russell 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 5 | * documentation files (the "Software"), to deal in the Software without restriction, including without limitation 6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 7 | * to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of 10 | * the Software. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 13 | * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 15 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 16 | * IN THE SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "argparse/argparse.h" 29 | 30 | #define VW 160 31 | #define VH 50 32 | 33 | FILE *infp; // Fi Ne Si Te 34 | FILE *fp; 35 | FILE *dumpfp; 36 | 37 | int gpu_max_budget; 38 | int verbose = 0; 39 | int frameno = 0; 40 | 41 | int palr[256]; 42 | int palg[256]; 43 | int palb[256]; 44 | int palyuv_int[256][3]; 45 | float paly[256]; 46 | float palu[256]; 47 | float palv[256]; 48 | 49 | // crazy internal variables 50 | 51 | uint8_t rawinbuf[VH][VW][3]; 52 | uint8_t inbuf[VH][VW]; 53 | uint8_t lastbuf[VH][VW]; 54 | uint8_t agebuf[VH][VW]; 55 | 56 | uint8_t exactnow = 0; 57 | 58 | uint32_t ccount[256]; 59 | uint32_t corder[256]; 60 | 61 | uint8_t cmatch2res[256][256]; 62 | 63 | typedef struct fillop { 64 | int bx, by, bw, bh, col; 65 | int type; 66 | } fillop; 67 | 68 | struct oplist { 69 | fillop* fillop; 70 | uint8_t valid; 71 | struct oplist* next; 72 | }; 73 | 74 | typedef struct oplist oplist; 75 | 76 | fillop gpuoplist[256]; 77 | uint8_t gpu_cu[256]; 78 | int gpu_ops = 0; 79 | int gpu_budget = 0; 80 | int gpu_sets = 0; 81 | int gpu_fills = 0; 82 | int gpu_cchanges = 0; 83 | 84 | int rgb_to_oct(int v) 85 | { 86 | if(v < 16) return 0777; 87 | assert(v >= 16 && v <= 255); 88 | v -= 16; 89 | 90 | int r = v%6; 91 | int g = (v/6)%8; 92 | int b = (v/6)/8; 93 | 94 | assert(r >= 0 && r < 5); 95 | assert(g >= 0 && g < 8); 96 | assert(b >= 0 && b < 6); 97 | 98 | return (r<<6)|(g<<3)|b; 99 | } 100 | 101 | int f_sort_corder(const void *av, const void *bv) 102 | { 103 | int32_t ai = *(int32_t *)av; 104 | int32_t bi = *(int32_t *)bv; 105 | 106 | return -(ccount[ai] - ccount[bi]); 107 | } 108 | 109 | static void rgb_to_yuv(float r, float g, float b, float *__restrict y, float *__restrict u, float *__restrict v) 110 | { 111 | *y = ( 0.299f * r + 0.587f * g + 0.114f * b); 112 | *u = (-0.147f * r - 0.289f * g + 0.436f * b); 113 | *v = ( 0.615f * r - 0.515f * g - 0.100f * b); 114 | } 115 | 116 | static void rgb_to_yuv_int(int r, int g, int b, int *__restrict y, int *__restrict u, int *__restrict v) 117 | { 118 | *y = ( 299 * r + 587 * g + 114 * b) / 100; 119 | *u = (-147 * r - 289 * g + 436 * b) / 100; 120 | *v = ( 615 * r - 515 * g - 100 * b) / 100; 121 | } 122 | 123 | static int cmatch4_int(int y1, int u1, int v1, int y2, int u2, int v2) 124 | { 125 | //return sqrt((y2-y1) * (y2-y1) + (u2-u1) * (u2-u1) + (v2-v1) * (v2-v1)); 126 | 127 | // no complex numbers here, all happening on reals 128 | // sqrt(x) < sqrt(y) && x, y >= 0 implies x < y 129 | // x = z^2 implies x >= 0 130 | // x + y where x, y >= 0 implies x + y >= 0 131 | return ((y2-y1) * (y2-y1) + (u2-u1) * (u2-u1) + (v2-v1) * (v2-v1)); 132 | } 133 | 134 | static float cmatch4(float y1, float u1, float v1, float y2, float u2, float v2) 135 | { 136 | //return sqrt((y2-y1) * (y2-y1) + (u2-u1) * (u2-u1) + (v2-v1) * (v2-v1)); 137 | 138 | // no complex numbers here, all happening on reals 139 | // sqrt(x) < sqrt(y) && x, y >= 0 implies x < y 140 | // x = z^2 implies x >= 0 141 | // x + y where x, y >= 0 implies x + y >= 0 142 | return ((y2-y1) * (y2-y1) + (u2-u1) * (u2-u1) + (v2-v1) * (v2-v1)); 143 | } 144 | 145 | static float cmatch3(int r1i, int g1i, int b1i, int r2i, int g2i, int b2i) 146 | { 147 | float r1 = r1i / 255.0f; 148 | float r2 = r2i / 255.0f; 149 | float g1 = g1i / 255.0f; 150 | float g2 = g2i / 255.0f; 151 | float b1 = b1i / 255.0f; 152 | float b2 = b2i / 255.0f; 153 | 154 | float y1, u1, v1; 155 | float y2, u2, v2; 156 | rgb_to_yuv(r1, g1, b1, &y1, &u1, &v1); 157 | rgb_to_yuv(r2, g2, b2, &y2, &u2, &v2); 158 | 159 | /* 160 | float y1 = 0.299f * r1 + 0.587f * g1 + 0.114f * b1; 161 | float u1 = -0.147f * r1 - 0.289f * g1 + 0.436f * b1; 162 | float v1 = 0.615f * r1 - 0.515f * g1 - 0.100f * b1; 163 | float y2 = 0.299f * r2 + 0.587f * g2 + 0.114f * b2; 164 | float u2 = -0.147f * r2 - 0.289f * g2 + 0.436f * b2; 165 | float v2 = 0.615f * r2 - 0.515f * g2 - 0.100f * b2; 166 | */ 167 | 168 | return cmatch4(y1, u1, v1, y2, u2, v2); 169 | } 170 | 171 | static int cmatch2a(int x, int y, int c1, int c2) 172 | { 173 | //return c1 == c2; 174 | //if(agebuf[y][x] > 10) return c1 == c2; 175 | 176 | int r1 = c1%6; 177 | int g1 = (c1/6)%8; 178 | int b1 = (c1/6)/8; 179 | 180 | int r2 = c2%6; 181 | int g2 = (c2/6)%8; 182 | int b2 = (c2/6)/8; 183 | 184 | int d = 0 185 | +(r1 > r2 ? r1-r2 : r2-r1) 186 | +(g1 > g2 ? g1-g2 : g2-g1) 187 | +(b1 > b2 ? b1-b2 : b2-b1); 188 | 189 | return d < 2; 190 | } 191 | 192 | #if 0 193 | #define cmatch2(x, y, c1, c2) \ 194 | (exactnow > 0 ? ((c1) == (c2)) : cmatch2res[(c1)][(c2)]) 195 | #else 196 | static int cmatch2(int x, int y, int c1, int c2) 197 | { 198 | if (c1 == c2) return 1; 199 | //return c1 == c2; 200 | //if(agebuf[y][x] > 10) return c1 == c2; 201 | 202 | return exactnow > 0 ? (c1 == c2) : cmatch2res[(c1)][(c2)]; 203 | } 204 | #endif 205 | 206 | #if 1 207 | #define cmatch(c1, c2) ((c1) == (c2)) 208 | #else 209 | static int cmatch(int c1, int c2) 210 | { 211 | return c1 == c2; 212 | } 213 | #endif 214 | 215 | void gpu_start() 216 | { 217 | int i; 218 | 219 | for (i = 0; i < 256; i++) { 220 | gpu_cu[i] = 0; 221 | } 222 | gpu_budget = 0; 223 | gpu_cchanges = 0; 224 | gpu_sets = 0; 225 | gpu_fills = 0; 226 | gpu_ops = 0; 227 | } 228 | 229 | void gpu_emit() 230 | { 231 | int i, colors_changed; 232 | #define COLORLIST_MAX (256*4) 233 | static oplist colorlist[COLORLIST_MAX]; 234 | int colorlist_tail = 256; 235 | int len[256]; 236 | 237 | for (i = 0; i < 256; i++) 238 | { 239 | colorlist[i].valid = 0; 240 | len[i] = 0; 241 | } 242 | 243 | gpu_cchanges = 0; 244 | 245 | for (i = 0; i < gpu_ops; i++) 246 | { 247 | fillop* op = &gpuoplist[i]; 248 | 249 | if (colorlist[op->col].valid == 0) 250 | { 251 | gpu_cchanges++; 252 | } 253 | 254 | oplist* list = &colorlist[op->col]; 255 | while (list->valid) 256 | { 257 | list = list->next; 258 | } 259 | 260 | list->fillop = op; 261 | list->valid = 1; 262 | assert(colorlist_tail < COLORLIST_MAX); 263 | list->next = &colorlist[colorlist_tail++]; 264 | list->next->valid = 0; 265 | } 266 | 267 | for (i = 0; i < 256; i++) 268 | if (colorlist[i].valid != 0) 269 | { 270 | if (fp != NULL) 271 | { 272 | fputc(0, fp); 273 | fputc(i, fp); 274 | } 275 | colors_changed++; 276 | oplist* list = &colorlist[i]; 277 | oplist* prev = list; 278 | while (list->valid) 279 | { 280 | fillop* op = list->fillop; 281 | if(fp != NULL) 282 | { 283 | fputc(((op->type - 1) << 6) | op->bh, fp); 284 | fputc(op->by + 1, fp); 285 | fputc(op->bx + 1, fp); 286 | fputc(op->bw, fp); 287 | } 288 | list = list->next; 289 | prev->valid = 0; 290 | prev = list; 291 | } 292 | } 293 | 294 | if(fp != NULL) fputc(0xFF, fp); 295 | } 296 | 297 | void gpu_fill(int bx, int by, int bw, int bh, int col) 298 | { 299 | int x, y; 300 | int diff = 0; 301 | 302 | for(y = 0; y < bh; y++) 303 | for(x = 0; x < bw; x++) 304 | { 305 | assert(x+bx >= 0 && x+bx < VW); 306 | assert(y+by >= 0 && y+by < VH); 307 | 308 | if (lastbuf[y+by][x+bx] != col) 309 | { 310 | lastbuf[y+by][x+bx] = col; 311 | diff++; 312 | } 313 | } 314 | 315 | if (diff == 0) return; 316 | 317 | fillop* op = &gpuoplist[gpu_ops++]; 318 | 319 | op->bx = bx; 320 | op->by = by; 321 | op->bw = bw; 322 | op->bh = bh; 323 | op->col = col; 324 | 325 | if((bh == 1 || bw == 1)) 326 | op->type = 1; 327 | else 328 | op->type = 2; 329 | 330 | if (gpu_cu[op->col] == 0) { 331 | gpu_budget += 2; 332 | gpu_cu[op->col] = 1; 333 | } 334 | 335 | if (op->type == 1) { 336 | gpu_sets++; gpu_budget+=1; 337 | } 338 | else { 339 | gpu_fills++; gpu_budget+=2; 340 | } 341 | 342 | gpu_cchanges++; 343 | } 344 | 345 | #if 1 346 | #define convert_age(x, y) (agebuf[(y)][(x)]) 347 | #else 348 | static inline int convert_age(int x, int y) 349 | { 350 | int v = agebuf[y][x]; 351 | return v; 352 | } 353 | #endif 354 | 355 | #if 1 356 | #define convert_age_hits(x, y) 1 357 | #else 358 | static inline int convert_age_hits(int x, int y) 359 | { 360 | return 1; 361 | int v = agebuf[y][x]; 362 | 363 | v -= 8; 364 | if(v < 1) return 1; 365 | v += 1; 366 | return v; 367 | /* 368 | int c1 = (int)inbuf[y][x]; 369 | int c2 = (int)lastbuf[y][x]; 370 | int r1 = c1%6; 371 | int g1 = (c1/6)%8; 372 | int b1 = (c1/6)/8; 373 | 374 | int r2 = c2%6; 375 | int g2 = (c2/6)%8; 376 | int b2 = (c2/6)/8; 377 | 378 | int diff = 0 379 | +(r1 > r2 ? r1-r2 : r2-r1) 380 | +(g1 > g2 ? g1-g2 : g2-g1) 381 | +(b1 > b2 ? b1-b2 : b2-b1); 382 | 383 | diff -= 7; 384 | if(diff < 1) return 1; 385 | return diff*diff; 386 | */ 387 | } 388 | #endif 389 | 390 | void algo_1(int variant) 391 | { 392 | int x, y, i, j; 393 | 394 | gpu_start(); 395 | 396 | for(j = 0; j < 16; j++) 397 | { 398 | if(gpu_budget > gpu_max_budget) 399 | break; 400 | 401 | exactnow = (j >= 8) ? 1 : 0; 402 | 403 | // sort by colour count 404 | for(i = 0; i < 256; i++) 405 | { 406 | ccount[i] = 0; 407 | corder[i] = i; 408 | } 409 | 410 | for(y = 0; y < VH; y++) 411 | for(x = 0; x < VW; x++) 412 | if(!cmatch2(x, y, inbuf[y][x], lastbuf[y][x])) 413 | ccount[inbuf[y][x]] += convert_age(x, y); 414 | 415 | qsort(corder, 256, sizeof(uint32_t), f_sort_corder); 416 | 417 | // do base fills 418 | for(i = 0; i < 28; i++) 419 | { 420 | if(gpu_budget > gpu_max_budget) 421 | break; 422 | 423 | if(ccount[corder[i]] == 0) continue; 424 | if(corder[i] < 0) continue; 425 | 426 | int xn = VW; 427 | int yn = VH; 428 | int xp = 0; 429 | int yp = 0; 430 | int hits = 0; 431 | int unhits = 0; 432 | 433 | // build initial box 434 | for(y = 0; y < VH; y++) 435 | for(x = 0; x < VW; x++) 436 | { 437 | if(cmatch(inbuf[y][x], corder[i]) && !cmatch2(x, y, lastbuf[y][x], corder[i])) 438 | // hits++; 439 | //if(inbuf[y][x] == corder[i] && lastbuf[y][x] != corder[i]) 440 | { 441 | hits += convert_age_hits(x, y); 442 | if(xn > x) xn = x; 443 | if(yn > y) yn = y; 444 | if(xp < x) xp = x; 445 | if(yp < y) yp = y; 446 | } 447 | } 448 | 449 | // calc unhits 450 | unhits = 0; 451 | for(y = yn; y <= yp; y++) 452 | for(x = xn; x <= xp; x++) 453 | { 454 | //if(!cmatch(lastbuf[y][x], inbuf[y][x])) 455 | if(!cmatch2(x, y, lastbuf[y][x], corder[i])) 456 | unhits += convert_age_hits(x, y); 457 | } 458 | 459 | // if everything is fine, skip 460 | if(hits == 0) continue; 461 | if(unhits == 0) continue; 462 | //fprintf(stderr, "%i %i %03o\n", hits, unhits, rgb_to_oct(corder[i])); 463 | 464 | if(((variant>>0)&0xF) == 1) 465 | { 466 | int begx = -1; 467 | int begy = -1; 468 | 469 | // find a point for a cluster start 470 | for(x = xn; x <= xp; x++) 471 | for(y = yn; y <= yp; y++) 472 | if(cmatch(inbuf[yn][x], corder[i]) && !cmatch(lastbuf[yn][x], corder[i])) 473 | { 474 | begx = x; 475 | begy = y; 476 | goto a1v1_got_cluster_start; // far break 477 | } 478 | 479 | a1v1_got_cluster_start: 480 | if(begx == -1) 481 | continue; 482 | 483 | // now cluster 484 | xn = xp = begx; 485 | yn = yp = begy; 486 | //fprintf(stderr, "C %i %i\n", xn, yn); 487 | 488 | for(;;) 489 | { 490 | for(x = xn; x <= xp; x++) 491 | if(cmatch(inbuf[yn][x], corder[i]) && !cmatch(lastbuf[yn][x], corder[i])) 492 | ; 493 | 494 | break; 495 | } 496 | 497 | // TODO: cluster! 498 | 499 | } else { 500 | // reduce box 501 | //while((xp-xn+1) > 0 && (yp-yn+1) > 0 && (hits*100)/((xp-xn+1)*(yp-yn+1)) < 70) 502 | while((xp-xn+1) > 0 && (yp-yn+1) > 0 && (hits*100)/unhits < 60) 503 | { 504 | int remxn = 0; 505 | int remyn = 0; 506 | int remxp = 0; 507 | int remyp = 0; 508 | int spcxn = 0; 509 | int spcyn = 0; 510 | int spcxp = 0; 511 | int spcyp = 0; 512 | 513 | for(x = xn; x <= xp; x++) 514 | { 515 | if(cmatch(inbuf[yn][x], corder[i]) && !cmatch2(x, yn, lastbuf[yn][x], corder[i])) 516 | remyn += convert_age_hits(x, yn); 517 | if(cmatch(inbuf[yp][x], corder[i]) && !cmatch2(x, yp, lastbuf[yp][x], corder[i])) 518 | remyp += convert_age_hits(x, yp); 519 | if(!cmatch2(x, yn, lastbuf[yn][x], corder[i])) 520 | spcyn += convert_age_hits(x, yn); 521 | if(!cmatch2(x, yp, lastbuf[yp][x], corder[i])) 522 | spcyp += convert_age_hits(x, yp); 523 | } 524 | 525 | for(y = yn; y <= yp; y++) 526 | { 527 | if(cmatch(inbuf[y][xn], corder[i]) && !cmatch2(xn, y, lastbuf[y][xn], corder[i])) 528 | remxn += convert_age_hits(xn, y); 529 | if(cmatch(inbuf[y][xp], corder[i]) && !cmatch2(xp, y, lastbuf[y][xp], corder[i])) 530 | remxp += convert_age_hits(xp, y); 531 | if(!cmatch2(xn, y, lastbuf[y][xn], corder[i])) 532 | spcxn += convert_age_hits(xn, y); 533 | if(!cmatch2(xp, y, lastbuf[y][xp], corder[i])) 534 | spcxp += convert_age_hits(xp, y); 535 | } 536 | 537 | if(remxn < remxp && remxn < remyn && remxn < remyp) 538 | { xn++; hits -= remxn; unhits -= spcxn; } 539 | else if(remxp < remyn && remxp < remyp) 540 | { xp--; hits -= remxp; unhits -= spcxp; } 541 | else if(remyn < remyp) 542 | { yn++; hits -= remyn; unhits -= spcyn; } 543 | else 544 | { yp--; hits -= remyp; unhits -= spcyp; } 545 | } 546 | } 547 | 548 | if(xn <= xp && yn <= yp) 549 | gpu_fill(xn, yn, xp-xn+1, yp-yn+1, corder[i]); 550 | } 551 | } 552 | 553 | gpu_emit(); 554 | frameno++; 555 | 556 | fprintf(stderr, "[%d] Call budget: %d/%d (%d sets, %d fills)\n", frameno, gpu_budget, gpu_max_budget + 4, gpu_sets, gpu_fills); 557 | } 558 | 559 | static const char *const usage[] = { 560 | "ice [options] -i input -o output", 561 | NULL, 562 | }; 563 | 564 | int main(int argc, const char *argv[]) 565 | { 566 | int x, y, i, j; 567 | int gpu_tier; 568 | char *infn = NULL, *outfn = NULL, *dumpfn = NULL; 569 | 570 | struct argparse argparse; 571 | struct argparse_option options[] = { 572 | OPT_HELP(), 573 | OPT_STRING('i', "input", &infn, "input filename, in form of raw RGB24 frames"), 574 | OPT_STRING('o', "output", &outfn, "output filename, in form of ICE-encoded data"), 575 | OPT_STRING('d', "dump", &dumpfn, "debug data filename, in form of raw RGB24 frames"), 576 | OPT_END() 577 | }; 578 | 579 | argparse_init(&argparse, options, usage, 0); 580 | argparse_describe(&argparse, "\nICE OpenComputers codec encoder", ""); 581 | argc = argparse_parse(&argparse, argc, argv); 582 | if (infn == NULL) 583 | { 584 | fprintf(stderr, "Error: Input filename not specified!\n"); 585 | return 1; 586 | } 587 | if (outfn == NULL) 588 | { 589 | fprintf(stderr, "Error: Output filename not specified!\n"); 590 | return 1; 591 | } 592 | 593 | // Keep in mind that a Tier 3 GPU has 256 "budget points". 594 | // We only verify whether the operation passed the budget 595 | // after the fact. Our most expensive single operation costs 596 | // 2 points, so we can have a max of at most 254 - but we 597 | // subtract another 2 just to be safe. 598 | gpu_max_budget = 252; 599 | 600 | infp = strcmp(infn, "-") == 0 ? stdin : fopen(infn, "rb"); 601 | if(infp == NULL) 602 | { 603 | perror(infn); 604 | return 1; 605 | } 606 | 607 | fp = strcmp(outfn, "-") == 0 ? stdout : fopen(outfn, "wb"); 608 | if(fp == NULL) 609 | { 610 | perror(outfn); 611 | return 1; 612 | } 613 | 614 | if(dumpfn != NULL) 615 | { 616 | dumpfp = strcmp(dumpfn, "-") == 0 ? stdout : fopen(dumpfn, "wb"); 617 | if(dumpfp == NULL) 618 | { 619 | perror(dumpfn); 620 | return 1; 621 | } 622 | } 623 | 624 | fputc(VW, fp); 625 | fputc(VH, fp); 626 | 627 | memset(lastbuf, 16, VW*VH); 628 | memset(agebuf, 2, VW*VH); 629 | 630 | for(i = 0; i < 256; i++) 631 | { 632 | int v = i; 633 | int r, g, b; 634 | if (v >= 16) { 635 | v -= 16; 636 | r = v%6; 637 | g = (v/6)%8; 638 | b = (v/6)/8; 639 | r *= 255; g *= 255; b *= 255; 640 | r += 2; g += 3; b += 2; 641 | r /= 5; g /= 7; b /= 4; 642 | } else { 643 | r = v*255/15; 644 | g = v*255/15; 645 | b = v*255/15; 646 | } 647 | assert(r >= 0 && r <= 255); 648 | assert(g >= 0 && g <= 255); 649 | assert(b >= 0 && b <= 255); 650 | palr[i] = r; 651 | palg[i] = g; 652 | palb[i] = b; 653 | rgb_to_yuv(r, g, b, &paly[i], &palu[i], &palv[i]); 654 | rgb_to_yuv_int(r, g, b, &palyuv_int[i][0], &palyuv_int[i][1], &palyuv_int[i][2]); 655 | } 656 | for (i = 0; i < 256; i++) 657 | { 658 | for(j = 0; j < 256; j++) 659 | { 660 | if (j < i) { 661 | cmatch2res[j][i] = cmatch2res[i][j]; 662 | } else { 663 | cmatch2res[j][i] = cmatch4(paly[i], palu[i], palv[i], paly[j], palu[j], palv[j]) < 0.1f*0.1f; // 0.01 * 255 664 | //cmatch2res[j][i] = cmatch3(palr[i], palg[i], palb[i], palr[j], palg[j], palb[j]) < 0.1f*0.1f; // 0.01 * 255 665 | } 666 | /*if (cmatch2res[j][i] == 0) { 667 | printf("%d %d %d %f : %d\n", i, j, cmatch2res[j][i], 668 | cmatch3(palr[i], palg[i], palb[i], palr[j], palg[j], palb[j]), cmatch2a(0, 0, i, j) 669 | ); 670 | }*/ 671 | } 672 | } 673 | 674 | for(;;) 675 | { 676 | // fetch 677 | if(fread(rawinbuf, VW*VH*3, 1, infp) <= 0) 678 | { 679 | //for(;;) fputc(rand()>>16, stdout); 680 | break; 681 | } 682 | 683 | // convert to palette colors 684 | for(y = 0; y < VH; y++) 685 | for(x = 0; x < VW; x++) 686 | { 687 | int r = rawinbuf[y][x][0]; 688 | int g = rawinbuf[y][x][1]; 689 | int b = rawinbuf[y][x][2]; 690 | int cc, r2, g2, b2; 691 | 692 | int yi, ui, vi; 693 | rgb_to_yuv_int(r, g, b, &yi, &ui, &vi); 694 | int cmin = 0; 695 | int dmin = 0x7FFFFFFF; 696 | int d; 697 | for (cc = 0; cc < 256; cc++) 698 | { 699 | d = cmatch4_int(yi, ui, vi, palyuv_int[cc][0], palyuv_int[cc][1], palyuv_int[cc][2]); 700 | if (d < dmin) { 701 | cmin = cc; 702 | dmin = d; 703 | } 704 | } 705 | inbuf[y][x] = cmin; 706 | 707 | /* 708 | r *= 5; g *= 7; b *= 4; 709 | r += 128; g += 128; b += 128; 710 | r /= 256; g /= 256; b /= 256; 711 | 712 | assert(r >= 0 && r < 5); 713 | assert(g >= 0 && g < 8); 714 | assert(b >= 0 && b < 6); 715 | 716 | int v = r+6*(g+8*b)+16; 717 | assert(v >= 16 && v <= 255); 718 | 719 | inbuf[y][x] = v; 720 | */ 721 | } 722 | 723 | // run algorithm 724 | algo_1(0x00000000); 725 | 726 | // update ages 727 | for(y = 0; y < VH; y++) 728 | for(x = 0; x < VW; x++) 729 | { 730 | if(!cmatch2(x, y, lastbuf[y][x], inbuf[y][x])) 731 | //if(!cmatch(lastbuf[y][x], inbuf[y][x])) 732 | agebuf[y][x]++; 733 | else 734 | agebuf[y][x] = 2; 735 | } 736 | 737 | // TESTING: 738 | // convert to rgb24 + write to stdout 739 | if (dumpfn != NULL) 740 | { 741 | for(y = 0; y < VH; y++) 742 | for(x = 0; x < VW; x++) 743 | { 744 | int r, g, b; 745 | int v = lastbuf[y][x]; 746 | assert(v >= 0 && v <= 255); 747 | 748 | rawinbuf[y][x][0] = palr[v]; 749 | rawinbuf[y][x][1] = palg[v]; 750 | rawinbuf[y][x][2] = palb[v]; 751 | } 752 | 753 | fwrite(rawinbuf, VW*VH*3, 1, dumpfp); 754 | } 755 | } 756 | 757 | fprintf(stderr, "**** DONE (%d frames) ****\n", frameno); 758 | 759 | if(fp != NULL) 760 | fclose(fp); 761 | if(infp != NULL) 762 | fclose(infp); 763 | if(dumpfp != NULL) 764 | fclose(dumpfp); 765 | 766 | return 0; 767 | } 768 | 769 | --------------------------------------------------------------------------------