tuxpaint-pencil-sharpener/magic/src/ascii.c
2024-09-27 23:23:32 -07:00

750 lines
22 KiB
C

/*
ascii.c
Converts the image to ASCII art.
Tux Paint - A simple drawing program for children.
Copyright (c) 2024 by Bill Kendrick
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
(See COPYING.txt)
Last updated: September 27, 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"
#include "SDL_image.h"
#include "SDL_mixer.h"
enum {
TOOL_TYPEWRITER,
TOOL_COMPUTER,
NUM_TOOLS
};
#define TOOL_COMPUTER_COLOR NUM_TOOLS
char * ascii_tool_names[NUM_TOOLS + 1] = {
gettext_noop("Typewriter"),
gettext_noop("Computer"),
gettext_noop("Color Computer"), // special version of "computer"
};
char * ascii_tool_filenames[NUM_TOOLS + 1] = {
"typewriter",
"computer",
"color_computer",
};
static Mix_Chunk *ascii_snd[NUM_TOOLS];
/* For each variation, we'll have a bitmap with an arbitrary number
* of potentially-proportionally-spaced characters (which we'll treat
* as fixed-width), so we need to keep track of each character's X
* poistion, how many characters there are, and the maximum width.
*/
#define MAX_CHARS 256
SDL_Surface * ascii_bitmap[NUM_TOOLS];
int ascii_char_x[NUM_TOOLS][MAX_CHARS];
int ascii_num_chars[NUM_TOOLS];
int ascii_char_maxwidth[NUM_TOOLS];
int ascii_char_brightness[NUM_TOOLS][MAX_CHARS];
SDL_Surface * ascii_snapshot = NULL;
int ascii_size;
Uint8 ascii_r, ascii_g, ascii_b;
Uint8 ascii_clear_r[NUM_TOOLS], ascii_clear_g[NUM_TOOLS], ascii_clear_b[NUM_TOOLS];
/* Based on CGA color palette
<https://en.wikipedia.org/wiki/Color_Graphics_Adapter#Color_palette> */
const Uint8 ascii_computer_colors[16][3] = {
{ 0x00, 0x00, 0x00 }, // Black
{ 0x55, 0x55, 0x55 }, // Dark gray
{ 0xAA, 0xAA, 0xAA }, // Light gray
{ 0xFF, 0xFF, 0xFF }, // White
{ 0x00, 0x00, 0xAA }, // Blue
{ 0x55, 0x55, 0xFF }, // Light blue
{ 0x00, 0xAA, 0x00 }, // Green
{ 0x55, 0xFF, 0x55 }, // Light green
{ 0x00, 0xAA, 0xAA }, // Cyan
{ 0x55, 0xFF, 0xFF }, // Light cyan
{ 0xAA, 0x00, 0x00 }, // Red
{ 0xFF, 0x55, 0x55 }, // Light red
{ 0xAA, 0x00, 0xAA }, // Magenta
{ 0xFF, 0x55, 0xFF }, // Light magenta
{ 0xAA, 0x55, 0x00 }, // Brown
{ 0xFF, 0xFF, 0x55 }, // Yellow
};
Uint32 ascii_api_version(void);
int ascii_init(magic_api * api, Uint8 disabled_features, Uint8 complexity_level);
int ascii_get_tool_count(magic_api * api);
SDL_Surface *ascii_get_icon(magic_api * api, int which);
char *ascii_get_name(magic_api * api, int which);
int ascii_get_group(magic_api * api, int which);
int ascii_get_order(int which);
char *ascii_get_description(magic_api * api, int which, int mode);
void ascii_drag(magic_api * api, int which, SDL_Surface * canvas,
SDL_Surface * last, int ox, int oy, int x, int y, SDL_Rect * update_rect);
void ascii_click(magic_api * api, int which, int mode,
SDL_Surface * canvas, SDL_Surface * last, int x, int y, SDL_Rect * update_rect);
void ascii_release(magic_api * api, int which,
SDL_Surface * canvas, SDL_Surface * last, int x, int y, SDL_Rect * update_rect);
void ascii_shutdown(magic_api * api);
void ascii_set_color(magic_api * api, int which, SDL_Surface * canvas,
SDL_Surface * last, Uint8 r, Uint8 g, Uint8 b, SDL_Rect * update_rect);
int ascii_requires_colors(magic_api * api, int which);
void ascii_switchin(magic_api * api, int which, int mode, SDL_Surface * canvas);
void ascii_switchout(magic_api * api, int which, int mode, SDL_Surface * canvas);
int ascii_modes(magic_api * api, int which);
Uint8 ascii_accepted_sizes(magic_api * api, int which, int mode);
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)
{
return (TP_MAGIC_API_VERSION);
}
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, 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++)
{
ascii_snd[i] = NULL;
ascii_bitmap[i] = NULL;
}
/* (N.B. Computer & Color Computer share sound & bitmap) */
for (i = 0; i < NUM_TOOLS; i++)
{
/* Load our sound */
snprintf(fname, sizeof(fname), "%ssounds/magic/ascii-%s.ogg", api->data_directory, ascii_tool_filenames[i]);
ascii_snd[i] = Mix_LoadWAV(fname);
/* Load and process our bitmap "font" */
snprintf(fname, sizeof(fname), "%simages/magic/ascii-%s.png", api->data_directory, ascii_tool_filenames[i]);
ascii_bitmap[i] = IMG_Load(fname);
if (ascii_bitmap[i] == NULL)
{
fprintf(stderr, "Cannot load %s\n", fname);
return(0);
}
clear_pixel = api->getpixel(ascii_bitmap[i], 0, 0);
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;
ascii_clear_r[i] = clear_r;
ascii_clear_g[i] = clear_g;
ascii_clear_b[i] = clear_b;
num_chars = 0;
for (x = 0; x < ascii_bitmap[i]->w; x++)
{
/* Skip whitespace between characters */
do
{
all_clear = 1;
for (y = 0; y < ascii_bitmap[i]->h /* && all_clear */; y++) {
pixel = api->getpixel(ascii_bitmap[i], x, y);
if (pixel != clear_pixel)
all_clear = 0;
}
if (all_clear)
x++;
}
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++)
{
all_clear = 1;
for (y = 0; y < ascii_bitmap[i]->h /* && all_clear */; y++)
{
pixel = api->getpixel(ascii_bitmap[i], xx, y);
if (pixel != clear_pixel)
{
all_clear = 0;
SDL_GetRGB(pixel, ascii_bitmap[i]->format, &r, &g, &b);
if (r == 255 && g == 0 && b == 255)
{
/* 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);
DEBUG_PRINTF("x");
}
else
{
DEBUG_PRINTF("#");
}
}
else
{
DEBUG_PRINTF("-");
}
}
DEBUG_PRINTF("\n");
}
x = xx - 1;
num_chars++;
DEBUG_PRINTF(".......................................\n");
}
ascii_num_chars[i] = 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;
ascii_char_maxwidth[i] = 0;
for (j = 0; j < num_chars; j++)
{
w = ascii_char_x[i][j + 1] - ascii_char_x[i][j];
DEBUG_PRINTF("%d->%d = %d\n", j, j + 1, w);
if (w > ascii_char_maxwidth[i])
{
ascii_char_maxwidth[i] = w;
}
}
/* Calculate the intensity of each character */
area = ascii_char_maxwidth[i] * ascii_bitmap[i]->h;
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++)
{
bright = 0;
for (y = 0; y < ascii_bitmap[i]->h; y++)
{
for (x = ascii_char_x[i][j]; x < ascii_char_x[i][j + 1]; x++)
{
pixel = api->getpixel(ascii_bitmap[i], x, y);
SDL_GetRGB(pixel, ascii_bitmap[i]->format, &r, &g, &b);
DEBUG_PRINTF("%3d (%3d) ", (r + g + b) / 3, get_bright(api, r, g, b));
bright += get_bright(api, r, g, b);
}
DEBUG_PRINTF("\n");
}
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);
}
int ascii_get_tool_count(magic_api * api ATTRIBUTE_UNUSED)
{
return (NUM_TOOLS + 1);
}
SDL_Surface *ascii_get_icon(magic_api * api, int which)
{
char fname[1024];
snprintf(fname, sizeof(fname), "%simages/magic/ascii_%s.png", api->data_directory, ascii_tool_filenames[which]);
return (IMG_Load(fname));
}
char *ascii_get_name(magic_api * api ATTRIBUTE_UNUSED, int which)
{
char tmp[1024];
snprintf(tmp, sizeof(tmp), gettext("ASCII %s"), gettext(ascii_tool_names[which]));
return(strdup(tmp));
}
int ascii_get_group(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED)
{
return MAGIC_TYPE_DISTORTS;
}
int ascii_get_order(int which)
{
return 2050 + which;
}
char *ascii_get_description(magic_api * api ATTRIBUTE_UNUSED, int which, int mode)
{
char tmp[1024];
if (mode == MODE_PAINT)
snprintf(tmp, sizeof(tmp), gettext("Click and drag to transform parts of your drawing to ASCII art (%s style)."), ascii_tool_names[which]);
else
snprintf(tmp, sizeof(tmp), gettext("Click to transform your entire drawing to ASCII art (%s style)."), ascii_tool_names[which]);
return(strdup(tmp));
}
void ascii_drag(magic_api * api, int which, SDL_Surface * canvas,
SDL_Surface * last, int ox, int oy, int x, int y, SDL_Rect * update_rect)
{
api->line((void *)api, which, canvas, last, ox, oy, x, y, 1, do_ascii_effect);
/* FIXME */
update_rect->x = 0;
update_rect->y = 0;
update_rect->w = canvas->w;
update_rect->h = canvas->h;
if (which == TOOL_COMPUTER_COLOR)
which = TOOL_COMPUTER;
api->playsound(ascii_snd[which], (x * 255) / canvas->w, 255);
}
void ascii_click(magic_api * api, int which, int mode,
SDL_Surface * canvas, SDL_Surface * last, int x, int y, SDL_Rect * update_rect)
{
if (mode == MODE_PAINT)
ascii_drag(api, which, canvas, last, x, y, x, y, update_rect);
else
{
int xx, yy;
if (which == TOOL_COMPUTER_COLOR)
api->playsound(ascii_snd[TOOL_COMPUTER], (x * 255) / canvas->w, 255);
else
api->playsound(ascii_snd[which], (x * 255) / canvas->w, 255);
for (yy = 0; yy < canvas->h; yy++)
{
for (xx = 0; xx < canvas->w; xx++)
{
do_ascii_effect(api, which, canvas, last, xx, yy);
}
if (yy % 10 == 0)
api->update_progress_bar();
}
update_rect->x = 0;
update_rect->y = 0;
update_rect->w = canvas->w;
update_rect->h = canvas->h;
}
}
void ascii_release(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED,
SDL_Surface * canvas ATTRIBUTE_UNUSED,
SDL_Surface * last ATTRIBUTE_UNUSED, int x ATTRIBUTE_UNUSED,
int y ATTRIBUTE_UNUSED, SDL_Rect * update_rect ATTRIBUTE_UNUSED)
{
api->stopsound();
}
void ascii_shutdown(magic_api * api ATTRIBUTE_UNUSED)
{
int i;
for (i = 0; i < NUM_TOOLS; i++)
{
if (ascii_snd[i] != NULL)
Mix_FreeChunk(ascii_snd[i]);
if (ascii_bitmap[i] != NULL)
SDL_FreeSurface(ascii_bitmap[i]);
}
if (ascii_snapshot != NULL)
{
SDL_FreeSurface(ascii_snapshot);
ascii_snapshot = NULL;
}
}
void ascii_set_color(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED, SDL_Surface * canvas ATTRIBUTE_UNUSED,
SDL_Surface * last ATTRIBUTE_UNUSED, Uint8 r, Uint8 g, Uint8 b, SDL_Rect * update_rect ATTRIBUTE_UNUSED)
{
/* If the bitmap's "clear" color, choose the opposite! */
if (abs(r - ascii_clear_r[which]) < 8 &&
abs(g - ascii_clear_g[which]) < 8 &&
abs(b - ascii_clear_b[which]) < 8)
{
r = 255 - r;
g = 255 - g;
b = 255 - b;
}
ascii_r = r;
ascii_g = g;
ascii_b = b;
}
int ascii_requires_colors(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED)
{
if (which == TOOL_TYPEWRITER || which == TOOL_COMPUTER)
return 1;
return 0;
}
void ascii_switchin(magic_api * api ATTRIBUTE_UNUSED,
int which ATTRIBUTE_UNUSED, int mode ATTRIBUTE_UNUSED, SDL_Surface * canvas ATTRIBUTE_UNUSED)
{
if (ascii_snapshot == NULL)
ascii_snapshot = SDL_CreateRGBSurface(SDL_SWSURFACE, canvas->w, canvas->h,
canvas->format->BitsPerPixel, canvas->format->Rmask,
canvas->format->Gmask, canvas->format->Bmask, canvas->format->Amask);
if (ascii_snapshot != NULL)
{
/* FIXME: When switching from PAINT to FULLSCREEN mode,
* we switch out & back in, which means we take a fresh
* snapshot even though we didn't leave the overall tool,
* which is less than ideal. -bjk 2024.09.27 */
SDL_BlitSurface(canvas, NULL, ascii_snapshot, NULL);
}
}
void ascii_switchout(magic_api * api ATTRIBUTE_UNUSED,
int which ATTRIBUTE_UNUSED, int mode ATTRIBUTE_UNUSED, SDL_Surface * canvas ATTRIBUTE_UNUSED)
{
}
int ascii_modes(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED)
{
return (MODE_PAINT | MODE_FULLSCREEN);
}
Uint8 ascii_accepted_sizes(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED, int mode ATTRIBUTE_UNUSED)
{
if (mode == MODE_PAINT)
return 6;
else
return 0;
}
Uint8 ascii_default_size(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED, int mode)
{
if (mode == MODE_PAINT)
return 3;
else
return 0;
}
void ascii_set_size(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED, int mode ATTRIBUTE_UNUSED,
SDL_Surface * canvas ATTRIBUTE_UNUSED, SDL_Surface * last ATTRIBUTE_UNUSED,
Uint8 size, SDL_Rect * update_rect ATTRIBUTE_UNUSED)
{
ascii_size = size;
}
void do_ascii_effect(void *ptr, int which, SDL_Surface * canvas, SDL_Surface * last ATTRIBUTE_UNUSED, int x, int y)
{
magic_api *api = (magic_api *) ptr;
int w, h, n, sx, sy, xx, yy, ww, brightness, rr, gg, bb;
Uint8 r, g, b;
Uint32 clear_pixel;
Uint8 clear_brightness;
SDL_Rect src, dest;
int computer_color = 0;
if (which == TOOL_COMPUTER_COLOR)
{
which = TOOL_COMPUTER;
computer_color = 1;
}
w = ascii_char_maxwidth[which];
h = ascii_bitmap[which]->h;
x = (x / w) * w;
y = (y / h) * h;
for (sy = y - (h * (ascii_size - 1)); sy <= y + (h * (ascii_size - 1)); sy += h)
{
for (sx = x - (w * (ascii_size - 1)); sx <= x + (w * (ascii_size - 1)); sx += w)
{
if (!api->touched(sx, sy))
{
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);
brightness = 0;
rr = 0;
gg = 0;
bb = 0;
for (yy = sy; yy < sy + h; yy++)
{
for (xx = sx; xx < sx + w; xx++)
{
SDL_GetRGB(api->getpixel(ascii_snapshot, xx, yy), ascii_snapshot->format, &r, &g, &b);
brightness += get_bright(api, r, g, b);
if (computer_color)
{
rr += r;
gg += g;
bb += b;
}
}
}
brightness = brightness / (w * h);
/* Background (and also, all we draw, if "Space") */
dest.x = sx;
dest.y = sy;
dest.w = w;
dest.h = h;
SDL_FillRect(canvas, &dest, clear_pixel);
if (brightness != clear_brightness)
{
/* A visible character */
if (computer_color)
{
int i, best;
/* Find the best color, based on the avg. of the
pixels we're replacing */
rr /= (w * h);
gg /= (w * h);
bb /= (w * h);
DEBUG_PRINTF("avg is %02x%02x%02x; ", rr, gg, bb);
/* Map each RGB component to _plausible_ values (0x00, 0x55, 0xAA, 0xFF) */
if (rr < 0x40)
rr = 0x00;
else if (rr <= 0x80)
rr = 0x55;
else if (rr <= 0xC0)
rr = 0xAA;
else
rr = 0xFF;
if (gg < 0x40)
gg = 0x00;
else if (gg <= 0x80)
gg = 0x55;
else if (gg <= 0xC0)
gg = 0xAA;
else
gg = 0xFF;
if (bb < 0x40)
bb = 0x00;
else if (bb <= 0x80)
bb = 0x55;
else if (bb <= 0xC0)
bb = 0xAA;
else
bb = 0xFF;
best = -1;
for (i = 0; i < 16; i++)
{
if (rr == ascii_computer_colors[i][0] &&
gg == ascii_computer_colors[i][1] &&
bb == ascii_computer_colors[i][2])
{
/* Exact match */
best = i;
}
}
if (best == -1)
{
for (i = 0; i < 16; i++)
{
if ((rr == ascii_computer_colors[i][0] &&
gg == ascii_computer_colors[i][1] &&
abs(bb - ascii_computer_colors[i][2]) <= 0x55) ||
(gg == ascii_computer_colors[i][1] &&
bb == ascii_computer_colors[i][2] &&
abs(rr - ascii_computer_colors[i][0]) <= 0x55) ||
(bb == ascii_computer_colors[i][2] &&
rr == ascii_computer_colors[i][0] &&
abs(gg - ascii_computer_colors[i][1]) <= 0x55))
{
/* Very close match */
best = i;
}
}
}
if (best == -1)
{
for (i = 0; i < 16; i++)
{
if ((rr == ascii_computer_colors[i][0] &&
abs(gg - ascii_computer_colors[i][1]) <= 0x55 &&
abs(bb - ascii_computer_colors[i][2]) <= 0x55) ||
(gg == ascii_computer_colors[i][1] &&
abs(bb - ascii_computer_colors[i][2]) <= 0x55 &&
abs(rr - ascii_computer_colors[i][0]) <= 0x55) ||
(bb == ascii_computer_colors[i][2] &&
abs(rr - ascii_computer_colors[i][0]) <= 0x55 &&
abs(gg - ascii_computer_colors[i][1]) <= 0x55))
{
/* Pretty close match */
best = i;
}
}
}
DEBUG_PRINTF("best for %02x%02x%02x = %d: ", rr, gg, bb, best);
if (best == -1)
best = 0; // oops!
rr = ascii_computer_colors[best][0];
gg = ascii_computer_colors[best][1];
bb = ascii_computer_colors[best][2];
DEBUG_PRINTF("%02x%02x%02x\n", rr, gg, bb);
}
else
{
/* Use the user-chosen color */
rr = ascii_r;
gg = ascii_g;
bb = ascii_b;
}
/* Blit the glyph */
n = get_best_char(which, brightness);
ww = ascii_char_x[which][n + 1] - ascii_char_x[which][n];
dest.x = sx + (w - ww) / 2;
dest.y = sy;
dest.w = ww;
dest.h = h;
SDL_FillRect(canvas, &dest, SDL_MapRGB(canvas->format, rr, gg, bb));
src.x = ascii_char_x[which][n];
src.y = 0;
src.w = ww;
src.h = h;
dest.x = sx + (w - src.w) / 2;
dest.y = sy;
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);
}