libansilove

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

tundra.c (4998B)


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