libansilove

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

icedraw.c (3990B)


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