libansilove

Library for converting ANSI, ASCII, and other formats to PNG
Log | Files | Refs | README | LICENSE

tundra.c (4954B)


      1 /*
      2  * tundra.c
      3  * libansilove 1.2.7
      4  * https://www.ansilove.org
      5  *
      6  * Copyright (c) 2011-2020 Stefan Vogt, Brian Cassidy, and Frederic Cambus
      7  * All rights reserved.
      8  *
      9  * libansilove is licensed under the BSD 2-Clause License.
     10  * See LICENSE file for details.
     11  */
     12 
     13 #include <gd.h>
     14 #include <stddef.h>
     15 #include <stdint.h>
     16 #include <string.h>
     17 #include "ansilove.h"
     18 #include "drawchar.h"
     19 #include "fonts.h"
     20 #include "output.h"
     21 
     22 #define TUNDRA_VERSION 24
     23 #define TUNDRA_STRING "TUNDRA24"
     24 
     25 #define TUNDRA_HEADER_LENGTH 9 /* 8 + 1 */
     26 
     27 #define TUNDRA_POSITION 1
     28 #define TUNDRA_COLOR_FOREGROUND 2
     29 #define TUNDRA_COLOR_BACKGROUND 4
     30 #define TUNDRA_COLOR_BOTH 6
     31 
     32 int
     33 ansilove_tundra(struct ansilove_ctx *ctx, struct ansilove_options *options)
     34 {
     35 	char tundra_version;
     36 	int32_t column = 0, row = 1;
     37 	uint32_t cursor, character, background = 0, foreground = 0;
     38 	uint32_t width, height;
     39 	size_t loop = TUNDRA_HEADER_LENGTH;
     40 	struct fontStruct fontData;
     41 
     42 	/* libgd image pointers */
     43 	gdImagePtr canvas;
     44 
     45 	if (ctx == NULL || options == NULL) {
     46 		if (ctx)
     47 			ctx->error = ANSILOVE_INVALID_PARAM;
     48 
     49 		return -1;
     50 	}
     51 
     52 	if (ctx->length < TUNDRA_HEADER_LENGTH)
     53 		goto error;
     54 
     55 	if (options->bits != 8 && options->bits !=9) {
     56 		ctx->error = ANSILOVE_RANGE_ERROR;
     57 		return -1;
     58 	}
     59 
     60 	options->columns = options->columns ? options->columns : 80;
     61 	int16_t columns = options->columns;
     62 
     63 	if (columns < 1 || columns > 4096) {
     64 		ctx->error = ANSILOVE_RANGE_ERROR;
     65 		return -1;
     66 	}
     67 
     68 	/* font selection */
     69 	memset(&fontData, 0, sizeof(struct fontStruct));
     70 	select_font(&fontData, options->font);
     71 
     72 	/* extract tundra header */
     73 	tundra_version = ctx->buffer[0];
     74 
     75 	if (tundra_version != TUNDRA_VERSION ||
     76 	    strncmp((const char *)ctx->buffer + 1, TUNDRA_STRING, 8))
     77 		goto error;
     78 
     79 	/* read tundra file a first time to find the image size */
     80 	while (loop < ctx->length) {
     81 		if (column == columns) {
     82 			column = 0;
     83 			row++;
     84 		}
     85 
     86 		cursor = ctx->buffer[loop];
     87 
     88 		switch (cursor) {
     89 		case TUNDRA_POSITION:
     90 			if (loop + 8 < ctx->length) {
     91 				row = (ctx->buffer[loop + 1] << 24) +
     92 				    (ctx->buffer[loop + 2] << 16) +
     93 				    (ctx->buffer[loop + 3] << 8) +
     94 				    ctx->buffer[loop + 4];
     95 
     96 				column = (ctx->buffer[loop + 5] << 24) +
     97 				    (ctx->buffer[loop + 6] << 16) +
     98 				    (ctx->buffer[loop + 7] << 8) +
     99 				    ctx->buffer[loop + 8];
    100 
    101 				loop += 8;
    102 			} else {
    103 				goto error;
    104 			}
    105 
    106 			break;
    107 
    108 		case TUNDRA_COLOR_FOREGROUND:
    109 		case TUNDRA_COLOR_BACKGROUND:
    110 			loop += 5;
    111 			column++;
    112 			break;
    113 
    114 		case TUNDRA_COLOR_BOTH:
    115 			loop += 9;
    116 			column++;
    117 			break;
    118 
    119 		default:
    120 			column++;
    121 		}
    122 
    123 		loop++;
    124 	}
    125 
    126 	width = columns * options->bits;
    127 	height = row * fontData.height;
    128 
    129 	if (!width || !height)
    130 		goto error;
    131 
    132 	/* allocate buffer image memory */
    133 	canvas = gdImageCreateTrueColor(width, height);
    134 
    135 	if (!canvas) {
    136 		ctx->error = ANSILOVE_GD_ERROR;
    137 		return -1;
    138 	}
    139 
    140 	/* process tundra */
    141 	column = 0;
    142 	row = 0;
    143 
    144 	loop = TUNDRA_HEADER_LENGTH;
    145 
    146 	while (loop < ctx->length) {
    147 		if (column == columns) {
    148 			column = 0;
    149 			row++;
    150 		}
    151 
    152 		cursor = character = ctx->buffer[loop];
    153 
    154 		switch (cursor) {
    155 		case TUNDRA_POSITION:
    156 			if (loop + 8 < ctx->length) {
    157 				row = (ctx->buffer[loop + 1] << 24) +
    158 				    (ctx->buffer[loop + 2] << 16) +
    159 				    (ctx->buffer[loop + 3] << 8) +
    160 				    ctx->buffer[loop + 4];
    161 
    162 				column =
    163 				    (ctx->buffer[loop + 5] << 24) +
    164 				    (ctx->buffer[loop + 6] << 16) +
    165 				    (ctx->buffer[loop + 7] << 8) +
    166 				    ctx->buffer[loop + 8];
    167 
    168 				loop += 8;
    169 			} else {
    170 				goto error;
    171 			}
    172 
    173 			break;
    174 
    175 		case TUNDRA_COLOR_FOREGROUND:
    176 			if (loop + 5 < ctx->length) {
    177 				foreground =
    178 				    (ctx->buffer[loop + 3] << 16) +
    179 				    (ctx->buffer[loop + 4] << 8) +
    180 				    ctx->buffer[loop + 5];
    181 
    182 				character = ctx->buffer[loop+1];
    183 
    184 				loop += 5;
    185 			} else {
    186 				goto error;
    187 			}
    188 
    189 			break;
    190 
    191 		case TUNDRA_COLOR_BACKGROUND:
    192 			if (loop + 5 < ctx->length) {
    193 				background = (ctx->buffer[loop + 3] << 16) +
    194 				    (ctx->buffer[loop + 4] << 8) +
    195 				    ctx->buffer[loop + 5];
    196 
    197 				character = ctx->buffer[loop + 1];
    198 
    199 				loop += 5;
    200 			} else {
    201 				goto error;
    202 			}
    203 
    204 			break;
    205 
    206 		case TUNDRA_COLOR_BOTH:
    207 			if (loop + 9 < ctx->length) {
    208 				foreground =
    209 				    (ctx->buffer[loop + 3] << 16) +
    210 				    (ctx->buffer[loop + 4] << 8) +
    211 				    ctx->buffer[loop + 5];
    212 
    213 				background =
    214 				    (ctx->buffer[loop + 7] << 16) +
    215 				    (ctx->buffer[loop + 8] << 8) +
    216 				    ctx->buffer[loop + 9];
    217 
    218 				character = ctx->buffer[loop + 1];
    219 
    220 				loop += 9;
    221 			} else {
    222 				goto error;
    223 			}
    224 
    225 			break;
    226 		}
    227 
    228 		if (character != TUNDRA_POSITION &&
    229 		    character != TUNDRA_COLOR_BACKGROUND &&
    230 		    character != TUNDRA_COLOR_FOREGROUND &&
    231 		    character != TUNDRA_COLOR_BOTH) {
    232 			drawchar(canvas, fontData.font_data, options->bits,
    233 			    fontData.height, column, row, background,
    234 			    foreground, character);
    235 
    236 			column++;
    237 		}
    238 
    239 		loop++;
    240 	}
    241 
    242 	/* create output image */
    243 	return output(ctx, options, canvas);
    244 
    245 error:
    246 	ctx->error = ANSILOVE_FORMAT_ERROR;
    247 	return -1;
    248 }