|
- /*
- * libcucul Unicode canvas library
- * Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
- * All Rights Reserved
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the Do What The Fuck You Want To
- * Public License, Version 2, as published by Sam Hocevar. See
- * http://sam.zoy.org/wtfpl/COPYING for more details.
- */
-
- /** \file cucul.c
- * \version \$Id$
- * \author Sam Hocevar <sam@zoy.org>
- * \brief Main \e libcucul functions
- *
- * This file contains the main functions used by \e libcucul applications
- * to initialise a drawing context.
- */
-
- #include "config.h"
-
- #if !defined(__KERNEL__)
- # include <stdlib.h>
- # include <string.h>
- #endif
-
- #include "cucul.h"
- #include "cucul_internals.h"
-
- static void cucul_read_environment(cucul_t *);
-
- /** \brief Initialise \e libcucul.
- *
- * This function initialises internal \e libcucul structures and the backend
- * that will be used for subsequent graphical operations. It must be the
- * first \e libcucul function to be called in a function. cucul_end() should
- * be called at the end of the program to free all allocated resources.
- *
- * \return 0 upon success, a non-zero value if an error occurs.
- */
- cucul_t * cucul_init(void)
- {
- cucul_t *qq = malloc(sizeof(cucul_t));
- int i;
-
- cucul_read_environment(qq);
-
- qq->fgcolor = CUCUL_COLOR_LIGHTGRAY;
- qq->bgcolor = CUCUL_COLOR_BLACK;
-
- /* Initialise to a default size. 80x32 is arbitrary but matches AAlib's
- * default X11 window. When a graphic driver attaches to us, it can set
- * a different size. */
- qq->width = 80;
- qq->height = 32;
-
- qq->chars = malloc(qq->width * qq->height * sizeof(uint32_t));
- qq->attr = malloc(qq->width * qq->height * sizeof(uint8_t));
-
- for(i = qq->width * qq->height; i--; )
- qq->chars[i] = (uint32_t)' ';
- memset(qq->attr, 0, qq->width * qq->height * sizeof(uint8_t));
-
- qq->empty_line = malloc(qq->width + 1);
- memset(qq->empty_line, ' ', qq->width);
- qq->empty_line[qq->width] = '\0';
-
- qq->scratch_line = malloc(qq->width + 1);
-
- if(_cucul_init_bitmap())
- {
- free(qq);
- return NULL;
- }
-
- return qq;
- }
-
- /** \brief Set the canvas size.
- *
- * This function sets the canvas width and height, in character cells.
- *
- * It is an error to try to resize the canvas if an output driver has
- * been attached to the canvas using caca_attach(). You need to remove
- * the output driver using caca_detach() before you can change the
- * canvas size again.
- *
- * However, the caca output driver can cause a canvas resize through
- * user interaction. See the caca_event() documentation for more about
- * this.
- *
- * \param width The desired canvas width
- * \param height The desired canvas height
- */
- void cucul_set_size(cucul_t *qq, unsigned int width, unsigned int height)
- {
- if(qq->refcount)
- return;
-
- _cucul_set_size(qq, width, height);
- }
-
- /** \brief Get the canvas width.
- *
- * This function returns the current canvas width, in character cells.
- *
- * \return The canvas width.
- */
- unsigned int cucul_get_width(cucul_t *qq)
- {
- return qq->width;
- }
-
- /** \brief Get the canvas height.
- *
- * This function returns the current canvas height, in character cells.
- *
- * \return The canvas height.
- */
- unsigned int cucul_get_height(cucul_t *qq)
- {
- return qq->height;
- }
-
- /** \brief Translate a colour index into the colour's name.
- *
- * This function translates a cucul_color enum into a human-readable
- * description string of the associated colour.
- *
- * \param color The colour value.
- * \return A static string containing the colour's name.
- */
- char const *cucul_get_color_name(enum cucul_color color)
- {
- static char const *color_names[] =
- {
- "black",
- "blue",
- "green",
- "cyan",
- "red",
- "magenta",
- "brown",
- "light gray",
- "dark gray",
- "light blue",
- "light green",
- "light cyan",
- "light red",
- "light magenta",
- "yellow",
- "white",
- };
-
- if(color < 0 || color > 15)
- return "unknown";
-
- return color_names[color];
- }
-
- /** \brief Get the current value of a feature.
- *
- * This function retrieves the value of an internal \e libcucul feature. A
- * generic feature value is expected, such as CUCUL_ANTIALIASING.
- *
- * \param feature The requested feature.
- * \return The current value of the feature or CUCUL_FEATURE_UNKNOWN if an
- * error occurred..
- */
- enum cucul_feature cucul_get_feature(cucul_t *qq, enum cucul_feature feature)
- {
- switch(feature)
- {
- case CUCUL_BACKGROUND:
- return qq->background;
- case CUCUL_ANTIALIASING:
- return qq->antialiasing;
- case CUCUL_DITHERING:
- return qq->dithering;
-
- default:
- return CUCUL_FEATURE_UNKNOWN;
- }
- }
-
- /** \brief Set a feature.
- *
- * This function sets an internal \e libcucul feature such as the antialiasing
- * or dithering modes. If a specific feature such as CUCUL_DITHERING_RANDOM,
- * cucul_set_feature() will set it immediately. If a generic feature is given
- * instead, such as CUCUL_DITHERING, the default value will be used instead.
- *
- * \param feature The requested feature.
- */
- void cucul_set_feature(cucul_t *qq, enum cucul_feature feature)
- {
- switch(feature)
- {
- case CUCUL_BACKGROUND:
- feature = CUCUL_BACKGROUND_SOLID;
- case CUCUL_BACKGROUND_BLACK:
- case CUCUL_BACKGROUND_SOLID:
- qq->background = feature;
- break;
-
- case CUCUL_ANTIALIASING:
- feature = CUCUL_ANTIALIASING_PREFILTER;
- case CUCUL_ANTIALIASING_NONE:
- case CUCUL_ANTIALIASING_PREFILTER:
- qq->antialiasing = feature;
- break;
-
- case CUCUL_DITHERING:
- feature = CUCUL_DITHERING_FSTEIN;
- case CUCUL_DITHERING_NONE:
- case CUCUL_DITHERING_ORDERED2:
- case CUCUL_DITHERING_ORDERED4:
- case CUCUL_DITHERING_ORDERED8:
- case CUCUL_DITHERING_RANDOM:
- case CUCUL_DITHERING_FSTEIN:
- qq->dithering = feature;
- break;
-
- case CUCUL_FEATURE_UNKNOWN:
- break;
- }
- }
-
- /** \brief Translate a feature value into the feature's name.
- *
- * This function translates a cucul_feature enum into a human-readable
- * description string of the associated feature.
- *
- * \param feature The feature value.
- * \return A static string containing the feature's name.
- */
- char const *cucul_get_feature_name(enum cucul_feature feature)
- {
- switch(feature)
- {
- case CUCUL_BACKGROUND_BLACK: return "black background";
- case CUCUL_BACKGROUND_SOLID: return "solid background";
-
- case CUCUL_ANTIALIASING_NONE: return "no antialiasing";
- case CUCUL_ANTIALIASING_PREFILTER: return "prefilter antialiasing";
-
- case CUCUL_DITHERING_NONE: return "no dithering";
- case CUCUL_DITHERING_ORDERED2: return "2x2 ordered dithering";
- case CUCUL_DITHERING_ORDERED4: return "4x4 ordered dithering";
- case CUCUL_DITHERING_ORDERED8: return "8x8 ordered dithering";
- case CUCUL_DITHERING_RANDOM: return "random dithering";
- case CUCUL_DITHERING_FSTEIN: return "Floyd-Steinberg dithering";
-
- default: return "unknown";
- }
- }
-
- /** \brief Uninitialise \e libcucul.
- *
- * This function frees all resources allocated by cucul_init(). After
- * cucul_end() has been called, no other \e libcucul functions may be used
- * unless a new call to cucul_init() is done.
- */
- void cucul_end(cucul_t *qq)
- {
- _cucul_end_bitmap();
-
- free(qq->empty_line);
- free(qq->scratch_line);
-
- free(qq->chars);
- free(qq->attr);
-
- free(qq);
- }
-
- struct cucul_export * cucul_get_export(cucul_t *qq, enum cucul_format format)
- {
- struct cucul_export *ex;
-
- ex = malloc(sizeof(struct cucul_export));
-
- switch(format)
- {
- case CUCUL_FORMAT_ANSI:
- _cucul_get_ansi(qq, ex);
- break;
- case CUCUL_FORMAT_HTML:
- _cucul_get_html(qq, ex);
- break;
- case CUCUL_FORMAT_HTML3:
- _cucul_get_html3(qq, ex);
- break;
- case CUCUL_FORMAT_IRC:
- _cucul_get_irc(qq, ex);
- break;
- case CUCUL_FORMAT_PS:
- _cucul_get_ps(qq, ex);
- break;
- case CUCUL_FORMAT_SVG:
- _cucul_get_svg(qq, ex);
- break;
- default:
- free(ex);
- return NULL;
- }
-
- return ex;
- }
-
- void cucul_free_export(struct cucul_export *ex)
- {
- free(ex->buffer);
- free(ex);
- }
-
- /*
- * XXX: The following functions are local.
- */
-
- static void cucul_read_environment(cucul_t * qq)
- {
- /* FIXME: if strcasecmp isn't available, use strcmp */
- #if defined(HAVE_GETENV) && defined(HAVE_STRCASECMP)
- char *var;
- #endif
-
- cucul_set_feature(qq, CUCUL_BACKGROUND);
- cucul_set_feature(qq, CUCUL_ANTIALIASING);
- cucul_set_feature(qq, CUCUL_DITHERING);
-
- #if defined(HAVE_GETENV) && defined(HAVE_STRCASECMP)
- if((var = getenv("CUCUL_BACKGROUND")) && *var)
- {
- if(!strcasecmp("black", var))
- cucul_set_feature(qq, CUCUL_BACKGROUND_BLACK);
- else if(!strcasecmp("solid", var))
- cucul_set_feature(qq, CUCUL_BACKGROUND_SOLID);
- }
-
- if((var = getenv("CUCUL_ANTIALIASING")) && *var)
- {
- if(!strcasecmp("none", var))
- cucul_set_feature(qq, CUCUL_ANTIALIASING_NONE);
- else if(!strcasecmp("prefilter", var))
- cucul_set_feature(qq, CUCUL_ANTIALIASING_PREFILTER);
- }
-
- if((var = getenv("CUCUL_DITHERING")) && *var)
- {
- if(!strcasecmp("none", var))
- cucul_set_feature(qq, CUCUL_DITHERING_NONE);
- else if(!strcasecmp("ordered2", var))
- cucul_set_feature(qq, CUCUL_DITHERING_ORDERED2);
- else if(!strcasecmp("ordered4", var))
- cucul_set_feature(qq, CUCUL_DITHERING_ORDERED4);
- else if(!strcasecmp("ordered8", var))
- cucul_set_feature(qq, CUCUL_DITHERING_ORDERED8);
- else if(!strcasecmp("random", var))
- cucul_set_feature(qq, CUCUL_DITHERING_RANDOM);
- else if(!strcasecmp("fstein", var))
- cucul_set_feature(qq, CUCUL_DITHERING_FSTEIN);
- }
- #endif
- }
-
- void _cucul_set_size(cucul_t *qq, unsigned int width, unsigned int height)
- {
- unsigned int x, y, old_width, old_height, new_size, old_size;
-
- old_width = qq->width;
- old_height = qq->height;
- old_size = old_width * old_height;
-
- qq->width = width;
- qq->height = height;
- new_size = width * height;
-
- /* Step 1: if new area is bigger, resize the memory area now. */
- if(new_size > old_size)
- {
- qq->chars = realloc(qq->chars, new_size * sizeof(uint32_t));
- qq->attr = realloc(qq->attr, new_size * sizeof(uint8_t));
- }
-
- /* Step 2: move line data if necessary. */
- if(width == old_width)
- {
- /* Width did not change, which means we do not need to move data. */
- ;
- }
- else if(width > old_width)
- {
- /* New width is bigger than old width, which means we need to
- * copy lines starting from the bottom of the screen otherwise
- * we will overwrite information. */
- for(y = height < old_height ? height : old_height; y--; )
- {
- for(x = old_width; x--; )
- {
- qq->chars[y * width + x] = qq->chars[y * old_width + x];
- qq->attr[y * width + x] = qq->attr[y * old_width + x];
- }
-
- /* Zero the end of the line */
- for(x = width - old_width; x--; )
- qq->chars[y * width + old_width + x] = (uint32_t)' ';
- memset(qq->attr + y * width + old_width, 0,
- width - old_width);
- }
- }
- else
- {
- /* New width is smaller. Copy as many lines as possible. Ignore
- * the first line, it is already in place. */
- unsigned int lines = height < old_height ? height : old_height;
-
- for(y = 1; y < lines; y++)
- {
- for(x = 0; x < width; x++)
- {
- qq->chars[y * width + x] = qq->chars[y * old_width + x];
- qq->attr[y * width + x] = qq->attr[y * old_width + x];
- }
- }
- }
-
- /* Step 3: fill the bottom of the new screen if necessary. */
- if(height > old_height)
- {
- /* Zero the bottom of the screen */
- for(x = (height - old_height) * width; x--; )
- qq->chars[old_height * width + x] = (uint32_t)' ';
- memset(qq->attr + old_height * width, 0,
- (height - old_height) * width);
- }
-
- /* Step 4: if new area is smaller, resize memory area now. */
- if(new_size <= old_size)
- {
- qq->chars = realloc(qq->chars, new_size * sizeof(uint32_t));
- qq->attr = realloc(qq->attr, new_size * sizeof(uint8_t));
- }
-
- /* Recompute the scratch line and the empty line */
- if(width != old_width)
- {
- qq->empty_line = realloc(qq->empty_line, width + 1);
- memset(qq->empty_line, ' ', width);
- qq->empty_line[width] = '\0';
-
- qq->scratch_line = realloc(qq->scratch_line, width + 1);
- }
- }
|