From a8c9f29787372b15c1f4c0072393399b468bbce1 Mon Sep 17 00:00:00 2001 From: antirez Date: Mon, 7 Oct 2019 12:35:05 +0200 Subject: [PATCH] LOLWUT: version 6 initial concept. --- src/lolwut5.c | 4 +-- src/lolwut6.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/src/lolwut5.c b/src/lolwut5.c index 4e9828145..0200fece6 100644 --- a/src/lolwut5.c +++ b/src/lolwut5.c @@ -106,7 +106,7 @@ lwCanvas *lwDrawSchotter(int console_cols, int squares_per_row, int squares_per_ * logical canvas. The actual returned string will require a terminal that is * width/2 large and height/4 tall in order to hold the whole image without * overflowing or scrolling, since each Barille character is 2x4. */ -sds lwRenderCanvas(lwCanvas *canvas) { +static sds renderCanvas(lwCanvas *canvas) { sds text = sdsempty(); for (int y = 0; y < canvas->height; y += 4) { for (int x = 0; x < canvas->width; x += 2) { @@ -166,7 +166,7 @@ void lolwut5Command(client *c) { /* Generate some computer art and reply. */ lwCanvas *canvas = lwDrawSchotter(cols,squares_per_row,squares_per_col); - sds rendered = lwRenderCanvas(canvas); + sds rendered = renderCanvas(canvas); rendered = sdscat(rendered, "\nGeorg Nees - schotter, plotter on paper, 1968. Redis ver. "); rendered = sdscat(rendered,REDIS_VERSION); diff --git a/src/lolwut6.c b/src/lolwut6.c index 68f09036a..8cc8d6e04 100644 --- a/src/lolwut6.c +++ b/src/lolwut6.c @@ -36,6 +36,94 @@ #include "server.h" #include "lolwut.h" +/* Render the canvas using the four gray levels of the standard color + * terminal: they match very well to the grayscale display of the gameboy. */ +static sds renderCanvas(lwCanvas *canvas) { + sds text = sdsempty(); + for (int y = 0; y < canvas->height; y++) { + for (int x = 0; x < canvas->width; x++) { + int color = lwGetPixel(canvas,x,y); + char *ce; /* Color escape sequence. */ + + /* Note that we set both the foreground and background color. + * This way we are able to get a more consistent result among + * different terminals implementations. */ + switch(color) { + case 0: ce = "0;30;40m"; break; /* Black */ + case 1: ce = "0;90;100m"; break; /* Gray 1 */ + case 2: ce = "0;37;47m"; break; /* Gray 2 */ + case 3: ce = "0;97;107m"; break; /* White */ + } + text = sdscatprintf(text,"\033[%s \033[0m",ce); + } + if (y != canvas->height-1) text = sdscatlen(text,"\n",1); + } + return text; +} + +/* Draw a skyscraper on the canvas, according to the parameters in the + * 'skyscraper' structure. Window colors are random and are always selected + * to be different than the color of the skyscraper itsefl. */ +struct skyscraper { + int xoff; /* X offset. */ + int width; /* Pixels width. */ + int height; /* Pixels height. */ + int windows; /* Draw windows if true. */ + int color; /* Color of the skyscraper. */ +}; + +void generateSkyscraper(lwCanvas *canvas, struct skyscraper *si) { + int starty = canvas->height-1; + int endy = starty - si->height + 1; + for (int y = starty; y >= endy; y--) { + for (int x = si->xoff; x < si->xoff+si->width; x++) { + /* The roof is four pixels less wide. */ + if (y == endy && (x <= si->xoff+1 || x >= si->xoff+si->width-2)) + continue; + int color = si->color; + /* Alter the color if this is a place where we want to + * draw a window. We check that we are in the inner part of the + * skyscraper, so that windows are far from the borders. */ + if (si->windows && + x > si->xoff+1 && + x < si->xoff+si->width-2 && + y > endy+1 && + y < starty-1) + { + /* Calculate the x,y position relative to the start of + * the window area. */ + int relx = x - (si->xoff+1); + int rely = y - (endy+1); + + /* Note that we want the windows to be two pixels wide + * but just one pixel tall, because terminal "pixels" + * (characters) are not square. */ + if (relx/2 % 2 && rely % 2) { + do { + color = rand() % 4; + } while (color == si->color); + /* Except we want adjacent pixels creating the same + * window to be the same color. */ + if (relx % 2) color = lwGetPixel(canvas,x-1,y); + } + } + lwDrawPixel(canvas,x,y,color); + } + } +} + +/* Generate a skyline inspired by the parallax backgrounds of 8 bit games. */ +void generateSkyline(lwCanvas *canvas) { + struct skyscraper si = { + .xoff = 4, + .width = 13, + .height = 15, + .windows = 1, + .color = 1 + }; + generateSkyscraper(canvas, &si); +} + /* The LOLWUT 6 command: * * LOLWUT [columns] [rows] @@ -45,7 +133,7 @@ */ void lolwut6Command(client *c) { long cols = 80; - long rows = 40; + long rows = 20; /* Parse the optional arguments if any. */ if (c->argc > 1 && @@ -64,12 +152,14 @@ void lolwut6Command(client *c) { if (rows > 1000) rows = 1000; /* Generate the city skyline and reply. */ - sds rendered = sdsempty(); + lwCanvas *canvas = lwCreateCanvas(cols,rows); + generateSkyline(canvas); + sds rendered = renderCanvas(canvas); rendered = sdscat(rendered, "\nDedicated to the 8 bit game developers of the past. Redis ver. "); rendered = sdscat(rendered,REDIS_VERSION); rendered = sdscatlen(rendered,"\n",1); addReplyVerbatim(c,rendered,sdslen(rendered),"txt"); sdsfree(rendered); - // lwFreeCanvas(canvas); + lwFreeCanvas(canvas); }