libansilove

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

icedraw.c (3946B)


      1 /*
      2  * icedraw.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 <stdint.h>
     15 #include <stdlib.h>
     16 #include "ansilove.h"
     17 #include "drawchar.h"
     18 #include "output.h"
     19 
     20 #define IDF_HEADER_LENGTH 12
     21 #define IDF_FONT_LENGTH 4096
     22 #define IDF_PALETTE_LENGTH 48
     23 
     24 #define STATE_CHARACTER 0
     25 #define STATE_ATTRIBUTE 1
     26 #define STATE_RLE 2
     27 
     28 int
     29 ansilove_icedraw(struct ansilove_ctx *ctx, struct ansilove_options *options)
     30 {
     31 	size_t index, loop = IDF_HEADER_LENGTH;
     32 	uint8_t *ptr, *idf_buffer;
     33 	uint8_t *cursor, state = STATE_CHARACTER;
     34 	uint32_t width, height;
     35 	uint32_t column = 0, row = 0;
     36 	uint32_t character, attribute, foreground, background;
     37 	uint32_t colors[16];
     38 	uint32_t idf_sequence_length, i = 0;
     39 
     40 	/* libgd image pointers */
     41 	gdImagePtr canvas;
     42 
     43 	if (ctx == NULL || options == NULL) {
     44 		if (ctx)
     45 			ctx->error = ANSILOVE_INVALID_PARAM;
     46 
     47 		return -1;
     48 	}
     49 
     50 	if (ctx->length < IDF_HEADER_LENGTH + IDF_FONT_LENGTH + IDF_PALETTE_LENGTH) {
     51 		ctx->error = ANSILOVE_FORMAT_ERROR;
     52 		return -1;
     53 	}
     54 
     55 	/* Get number of columns, 16-bit endian unsigned short */
     56 	uint32_t x2 = (ctx->buffer[9] << 8) + ctx->buffer[8] + 1;
     57 
     58 	if (x2 < 1 || x2 > 4096) {
     59 		ctx->error = ANSILOVE_RANGE_ERROR;
     60 		return -1;
     61 	}
     62 
     63 	/* process IDF */
     64 	/* dynamically allocated memory buffer for IDF data */
     65 	idf_buffer = malloc(2);
     66 
     67 	if (idf_buffer == NULL) {
     68 		ctx->error = ANSILOVE_MEMORY_ERROR;
     69 		return -1;
     70 	}
     71 
     72 	while (loop < ctx->length - IDF_FONT_LENGTH - IDF_PALETTE_LENGTH) {
     73 		cursor = &ctx->buffer[loop];
     74 
     75 		switch (state) {
     76 		case STATE_CHARACTER:
     77 			if (*cursor == 1) {
     78 				state = STATE_RLE;
     79 				loop++;
     80 			} else {
     81 				ptr = realloc(idf_buffer, i + 2);
     82 				if (ptr == NULL) {
     83 					ctx->error = ANSILOVE_MEMORY_ERROR;
     84 					goto error;
     85 				}
     86 
     87 				idf_buffer = ptr;
     88 				idf_buffer[i] = *cursor;
     89 				i++;
     90 				state = STATE_ATTRIBUTE;
     91 			}
     92 
     93 			loop++;
     94 			break;
     95 		case STATE_ATTRIBUTE:
     96 			idf_buffer[i] = *cursor;
     97 			i++;
     98 
     99 			state = STATE_CHARACTER;
    100 
    101 			loop++;
    102 			break;
    103 		case STATE_RLE:
    104 			/* RLE compressed data */
    105 			idf_sequence_length = *cursor;
    106 
    107 			if (loop + 3 >= ctx->length) {
    108 				ctx->error = ANSILOVE_FORMAT_ERROR;
    109 				goto error;
    110 			}
    111 
    112 			while (idf_sequence_length--)
    113 			{
    114 				/* reallocate IDF buffer memory */
    115 				ptr = realloc(idf_buffer, i + 2);
    116 				if (ptr == NULL) {
    117 					ctx->error = ANSILOVE_MEMORY_ERROR;
    118 					goto error;
    119 				}
    120 
    121 				idf_buffer = ptr;
    122 
    123 				idf_buffer[i] = ctx->buffer[loop +2];
    124 				idf_buffer[i+1] = ctx->buffer[loop + 3];
    125 				i += 2;
    126 			}
    127 
    128 			loop += 4;
    129 			state = STATE_CHARACTER;
    130 		}
    131 	}
    132 
    133 	width = x2 * 8;
    134 	height = i / 2 / 80 * 16;
    135 
    136 	if (!width || !height) {
    137 		ctx->error = ANSILOVE_FORMAT_ERROR;
    138 		goto error;
    139 	}
    140 
    141 	/* create IDF instance */
    142 	canvas = gdImageCreate(width, height);
    143 
    144 	/* error output */
    145 	if (!canvas) {
    146 		ctx->error = ANSILOVE_GD_ERROR;
    147 		goto error;
    148 	}
    149 
    150 	/* process IDF palette */
    151 	for (loop = 0; loop < 16; loop++) {
    152 		index = (loop * 3) + ctx->length - IDF_PALETTE_LENGTH;
    153 		colors[loop] = gdImageColorAllocate(canvas,
    154 		    ctx->buffer[index] << 2 | ctx->buffer[index] >> 4,
    155 		    ctx->buffer[index + 1] << 2 | ctx->buffer[index + 1] >> 4,
    156 		    ctx->buffer[index + 2] << 2 | ctx->buffer[index + 2] >> 4);
    157 	}
    158 
    159 	/* render IDF */
    160 	for (loop = 0; loop < i; loop += 2) {
    161 		if (column == x2) {
    162 			column = 0;
    163 			row++;
    164 		}
    165 
    166 		character = idf_buffer[loop];
    167 		attribute = idf_buffer[loop+1];
    168 
    169 		background = (attribute & 240) >> 4;
    170 		foreground = attribute & 15;
    171 
    172 		drawchar(canvas, ctx->buffer+(ctx->length - IDF_FONT_LENGTH - IDF_PALETTE_LENGTH),
    173 		    8, 16, column, row,
    174 		    colors[background], colors[foreground], character);
    175 
    176 		column++;
    177 	}
    178 
    179 	/* create output file */
    180 	if (output(ctx, options, canvas) != 0)
    181 		goto error;
    182 
    183 	free(idf_buffer);
    184 	return 0;
    185 
    186 error:
    187 	free(idf_buffer);
    188 	return -1;
    189 }