WIP ASCII art Magic tools; code working!

This commit is contained in:
Bill Kendrick 2024-09-26 00:35:01 -07:00
parent 6a591a2ef1
commit c9f978b1ca
7 changed files with 181 additions and 35 deletions

View file

@ -6,7 +6,7 @@ Copyright (c) 2002-2024
Various contributors (see below, and CHANGES.txt)
https://tuxpaint.org/
June 17, 2002 - September 24, 2024
June 17, 2002 - September 26, 2024
* Design and Coding:
@ -197,6 +197,22 @@ June 17, 2002 - September 24, 2024
<https://freesound.org/people/lorefold/sounds/607310/>
Creative Commons 0 by lorefold <https://freesound.org/people/lorefold/>
"ASCII Typewriter" & "ASCII Computer" magic tools
by Bill Kendrick <bill@newbreedsoftware.com>
"ASCII Computer" font: IBM CGA Adapter
taken from "Typography in 16-bits: System fonts" by Damien Guard
<https://damieng.com/blog/2011/03/27/typography-in-16-bits-system-fonts/>
"ASCII Typewriter" font based on: "Patrician"
<https://site.xavier.edu/polt/typewriters/patrician.html>
Creative Commons CC0 1.0 Universal
by Richard Polt (based on a 1959 Royal FP typewriter)
And using a subset of characters taken from
"Character representation of grey scale images"
<https://paulbourke.net/dataformats/asciiart/>
by Paul Bourke
Bloom magic tool
by Bill Kendrick <bill@newbreedsoftware.com>

View file

@ -6,7 +6,7 @@ Copyright (c) 2002-2024
Various contributors (see below, and AUTHORS.txt)
https://tuxpaint.org/
2024.September.25 (0.9.34)
2024.September.26 (0.9.34)
* New Magic Tools:
----------------
* "Comic Dots", draws repeating dots (using a multiply blend)
@ -31,6 +31,25 @@ https://tuxpaint.org/
<https://freesound.org/people/DigitalUnderglow/>
+ Closes https://sourceforge.net/p/tuxpaint/feature-requests/260/
* WIP "ASCII Typewriter" & "ASCII Computer", turn your drawing into
ASCII art.
+ TODO Sound effects
+ TODO Icons
+ TODO Documentation
+ Code by Bill Kendrick <bill@newbreedsoftware.com>
+ Computer font: IBM CGA Adapter
taken from "Typography in 16-bits: System fonts"
<https://damieng.com/blog/2011/03/27/typography-in-16-bits-system-fonts/>
by Damien Guard
+ Typewriter font based on: "Patrician"
<https://site.xavier.edu/polt/typewriters/patrician.html>
Creative Commons CC0 1.0 Universal
by Richard Polt (based on a 1959 Royal FP typewriter)
And using a subset of characters taken from
"Character representation of grey scale images"
<https://paulbourke.net/dataformats/asciiart/>
by Paul Bourke
* Magic Tool Improvements:
------------------------
* Sound pause/resume functions added to API

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before After
Before After

View file

@ -22,9 +22,17 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
(See COPYING.txt)
Last updated: September 25, 2024
Last updated: September 26, 2024
*/
//#define DEBUG
#if defined(DEBUG)
#define DEBUG_PRINTF(...) printf(__VA_ARGS__)
#else
#define DEBUG_PRINTF(...)
#endif
#include <stdio.h>
#include <string.h>
#include "tp_magic_api.h"
@ -38,8 +46,8 @@ enum {
};
char * ascii_tool_names[NUM_TOOLS] = {
"Typewriter",
"Computer",
gettext_noop("Typewriter"),
gettext_noop("Computer"),
};
char * ascii_tool_filenames[NUM_TOOLS] = {
@ -91,6 +99,8 @@ Uint8 ascii_default_size(magic_api * api, int which, int mode);
void ascii_set_size(magic_api * api, int which, int mode, SDL_Surface * canvas, SDL_Surface * last, Uint8 size,
SDL_Rect * update_rect);
void do_ascii_effect(void *ptr, int which, SDL_Surface * canvas, SDL_Surface * last, int x, int y);
int get_best_char(int which, int brightness);
int get_bright(magic_api * api, int r, int g, int b);
Uint32 ascii_api_version(void)
@ -101,9 +111,11 @@ Uint32 ascii_api_version(void)
int ascii_init(magic_api * api, Uint8 disabled_features ATTRIBUTE_UNUSED, Uint8 complexity_level ATTRIBUTE_UNUSED)
{
char fname[1024];
int i, j, x, y, xx, w, num_chars, all_clear, area, bright;
int i, j, x, y, xx, w, num_chars, all_clear, area, bright, clear_brightness;
int min_bright, max_bright;
Uint32 clear_pixel, pixel;
Uint8 r, g, b;
Uint8 clear_r, clear_g, clear_b;
for (i = 0; i < NUM_TOOLS; i++)
{
@ -129,12 +141,14 @@ int ascii_init(magic_api * api, Uint8 disabled_features ATTRIBUTE_UNUSED, Uint8
}
clear_pixel = api->getpixel(ascii_bitmap[i], 0, 0);
// printf("%s; clear pixel %d\n", fname, clear_pixel);
SDL_GetRGB(clear_pixel, ascii_bitmap[i]->format, &clear_r, &clear_g, &clear_b);
DEBUG_PRINTF("%s; clear pixel %d (%d,%d,%d)\n", fname, clear_pixel, clear_r, clear_g, clear_b);
clear_brightness = (clear_r + clear_g + clear_b) / 3;
num_chars = 0;
for (x = 0; x < ascii_bitmap[i]->w; x++)
{
ascii_char_x[i][num_chars] = x;
/* Skip whitespace between characters */
do
{
all_clear = 1;
@ -148,6 +162,9 @@ int ascii_init(magic_api * api, Uint8 disabled_features ATTRIBUTE_UNUSED, Uint8
}
while (all_clear && x < ascii_bitmap[i]->w);
ascii_char_x[i][num_chars] = x;
/* Capture the extent of the character */
all_clear = 0;
for (xx = x; xx < ascii_bitmap[i]->w && !all_clear; xx++)
{
@ -164,26 +181,26 @@ int ascii_init(magic_api * api, Uint8 disabled_features ATTRIBUTE_UNUSED, Uint8
/* Magenta counts as a connecting pixel, but we
* want it to appear as the clear color */
api->putpixel(ascii_bitmap[i], xx, y, clear_pixel);
// printf("x");
DEBUG_PRINTF("x");
}
else
{
// printf("#");
DEBUG_PRINTF("#");
}
}
else
{
// printf("-");
DEBUG_PRINTF("-");
}
}
// printf("\n");
DEBUG_PRINTF("\n");
}
x = xx;
x = xx - 1;
num_chars++;
// printf(".......................................\n");
DEBUG_PRINTF(".......................................\n");
}
ascii_num_chars[i] = num_chars;
printf("%s has %d characters\n", fname, num_chars);
DEBUG_PRINTF("%s has %d characters\n", fname, num_chars);
/* Determine the max. width of any character */
ascii_char_x[i][num_chars] = x;
@ -191,7 +208,7 @@ int ascii_init(magic_api * api, Uint8 disabled_features ATTRIBUTE_UNUSED, Uint8
for (j = 0; j < num_chars; j++)
{
w = ascii_char_x[i][j + 1] - ascii_char_x[i][j];
// printf("%d->%d = %d\n", j, j + 1, w);
DEBUG_PRINTF("%d->%d = %d\n", j, j + 1, w);
if (w > ascii_char_maxwidth[i])
{
ascii_char_maxwidth[i] = w;
@ -201,7 +218,7 @@ int ascii_init(magic_api * api, Uint8 disabled_features ATTRIBUTE_UNUSED, Uint8
/* Calculate the intensity of each character */
area = ascii_char_maxwidth[i] * ascii_bitmap[i]->h;
// printf("%s max char width is %d -- * %d = area %d\n", fname, ascii_char_maxwidth[i], ascii_bitmap[i]->h, area);
DEBUG_PRINTF("%s max char width is %d -- * %d = area %d\n", fname, ascii_char_maxwidth[i], ascii_bitmap[i]->h, area);
for (j = 0; j < num_chars; j++)
{
@ -213,14 +230,40 @@ int ascii_init(magic_api * api, Uint8 disabled_features ATTRIBUTE_UNUSED, Uint8
pixel = api->getpixel(ascii_bitmap[i], x, y);
SDL_GetRGB(pixel, ascii_bitmap[i]->format, &r, &g, &b);
// printf("%3d ", (r + g + b) / 3);
bright += ((r + g + b) / 3);
DEBUG_PRINTF("%3d (%3d) ", (r + g + b) / 3, get_bright(api, r, g, b));
bright += get_bright(api, r, g, b);
}
// printf("\n");
DEBUG_PRINTF("\n");
}
// printf("char %d brightness = %d\n", j, bright / area);
DEBUG_PRINTF("char %3d brightness = %3d before padding -- ", j, bright / area);
w = ascii_char_maxwidth[i] - (ascii_char_x[i][j + 1] - ascii_char_x[i][j]) - 2; /* don't let padding affect _too_ much */
if (w >= 1)
bright += (clear_brightness * ascii_bitmap[i]->h * w);
DEBUG_PRINTF("%3d after padding %d width\n", bright / area, w);
ascii_char_brightness[i][j] = bright / area;
}
/* Stretch the brightnesses, so we cover more of 0->255 */
min_bright = 255;
max_bright = 0;
for (j = 0; j < num_chars; j++)
{
if (ascii_char_brightness[i][j] > max_bright)
max_bright = ascii_char_brightness[i][j];
if (ascii_char_brightness[i][j] < max_bright)
min_bright = ascii_char_brightness[i][j];
}
DEBUG_PRINTF("brightnesses between %d and %d\n", min_bright, max_bright);
/* https://rosettacode.org/wiki/Map_range#C */
#define map_range(a1,a2,b1,b2,s) (b1 + (s-a1)*(b2-b1)/(a2-a1))
for (j = 0; j < num_chars; j++)
{
DEBUG_PRINTF("mapping %3d -> ", ascii_char_brightness[i][j]);
ascii_char_brightness[i][j] = map_range(min_bright, max_bright, 0, 255, ascii_char_brightness[i][j]);
DEBUG_PRINTF("%3d\n", ascii_char_brightness[i][j]);
}
}
return (1);
@ -246,7 +289,7 @@ char *ascii_get_name(magic_api * api ATTRIBUTE_UNUSED, int which)
{
char tmp[1024];
snprintf(tmp, sizeof(tmp), gettext("ASCII %s"), ascii_tool_names[which]);
snprintf(tmp, sizeof(tmp), gettext("ASCII %s"), gettext(ascii_tool_names[which]));
return(strdup(tmp));
}
@ -373,8 +416,10 @@ void ascii_set_size(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED
void do_ascii_effect(void *ptr, int which, SDL_Surface * canvas, SDL_Surface * last, int x, int y)
{
magic_api *api = (magic_api *) ptr;
int w, h, n;
int w, h, n, xx, yy, brightness;
Uint8 r, g, b;
Uint32 clear_pixel;
Uint8 clear_brightness;
SDL_Rect src, dest;
w = ascii_char_maxwidth[which];
@ -386,6 +431,8 @@ void do_ascii_effect(void *ptr, int which, SDL_Surface * canvas, SDL_Surface * l
if (!api->touched(x, y))
{
clear_pixel = api->getpixel(ascii_bitmap[which], 0, 0);
SDL_GetRGB(clear_pixel, ascii_bitmap[which]->format, &r, &g, &b);
clear_brightness = ((r + g + b) / 3.0);
dest.x = x;
dest.y = y;
@ -394,7 +441,22 @@ void do_ascii_effect(void *ptr, int which, SDL_Surface * canvas, SDL_Surface * l
SDL_FillRect(canvas, &dest, clear_pixel);
n = rand() % ascii_num_chars[which];
brightness = 0;
for (yy = y; yy < y + h; yy++)
{
for (xx = x; xx < x + w; xx++)
{
SDL_GetRGB(api->getpixel(last, xx, yy), last->format, &r, &g, &b);
brightness += get_bright(api, r, g, b);
}
}
brightness = brightness / (w * h);
/* FIXME: Increase contrast */
if (brightness != clear_brightness)
{
n = get_best_char(which, brightness);
src.x = ascii_char_x[which][n];
src.y = 0;
src.w = ascii_char_x[which][n + 1] - ascii_char_x[which][n];
@ -405,4 +467,53 @@ void do_ascii_effect(void *ptr, int which, SDL_Surface * canvas, SDL_Surface * l
SDL_BlitSurface(ascii_bitmap[which], &src, canvas, &dest);
}
}
}
int get_best_char(int which, int brightness)
{
int i, diff, best_idx, best_diff;
best_idx = -1;
best_diff = 255;
for (i = 0; i < ascii_num_chars[which]; i++)
{
diff = abs(ascii_char_brightness[which][i] - brightness);
if (diff == best_diff)
{
if (rand() % 10 <= 3)
best_idx = 1;
}
else if (diff < best_diff)
{
best_diff = diff;
best_idx = i;
}
}
if (best_idx == -1)
{
/* Shouldn't happen, but just in case */
best_idx = rand() % ascii_num_chars[which];
printf("!?\n");
}
DEBUG_PRINTF("best for brightness %d is %d (brightness %d)\n",
brightness, best_idx, ascii_char_brightness[which][best_idx]);
return best_idx;
}
int get_bright(magic_api * api, int r, int g, int b)
{
float fr, fg, fb, y;
fr = api->sRGB_to_linear(r);
fg = api->sRGB_to_linear(g);
fb = api->sRGB_to_linear(b);
y = (0.2126 * fr) + (0.7152 * fg) + (0.0722 * fb);
return (int) (y * 255);
}

View file

@ -47,10 +47,10 @@
</screenshot>
</screenshots>
<releases>
<release version="0.9.34" date="2024-09-24">
<release version="0.9.34" date="2024-09-26">
<description>
<p>New Fill mode: "Eraser" flood fill.</p>
<p>New Magic tools: "Comic dots", "Rotate", and various "Fractals".</p>
<p>New Magic tools: "Comic dots", "Rotate", "ASCII Computer", "ASCII Typewriter", and various "Fractals".</p>
<p>New brush: Fluff (gradient).</p>
</description>
</release>

View file

@ -11918,7 +11918,7 @@ static SDL_Surface *thumbnail2(SDL_Surface * src, int max_x, int max_y, int keep
SDL_GetRGBA(getpixel(src, src_x, src_y), src->format, &r, &g, &b, &a);
#ifdef GAMMA_CORRECTED_THUMBNAILS
/* per: http://www.4p8.com/eric.brasseur/gamma.html */
/* per: http://www.ericbrasseur.org/gamma.html?i=1 */
tr = tr + sRGB_to_linear_table[r];
tg = tg + sRGB_to_linear_table[g];