#include #include #include /* Target output size in bytes, not counting the final newline. */ #define SIZE 9999 static char points[] = { 15,70, 11,72, 10,79, 16,84, 20,87, 30,85, 29,79, 28,74, 19,68, 15,70, 0, 59,65, 59,68, 60,74, 65,76, 70,78, 75,71, 71,65, 68,61, 59,61, 59,65, 0, 37,8, 46,8, 45,2, 58,4, 71,6, 74,16, 76,31, 77,38, 84,39, 77,47, 64,58, 20,38, 17,26, 14,14, 25,8, 37,8, 0, 29,38, 23,33, 19,26, 24,23, 29,20, 37,28, 43,35, 48,41, 48,46, 57,49, 57,50, 57,52, 56,54, 65,57, 59,67, 55,70, 44,78, 31,77, 23,67, 12,51, 29,41, 29,38, 0, 19,75, 20,76, 21,77, 23,77, 24,74, 25,72, 30,72, 31,71, 30,69, 29,68, 23,68, 21,71, 19,75, 0, 59,70, 61,70, 62,69, 63,68, 64,68, 66,70, 66,71, 65,73, 61,75, 59,75, 58,74, 58,71, 59,70, 0, 59,70, 60,71, 60,74, 59,75, 54,75, 52,72, 50,69, 51,67, 52,66, 54,66, 55,68, 56,70, 59,70, 0, 0 }, *p = points, *image, *w, *output; static int width, height = 6, *range, x, y, t; static double ft, scale; /* Interpolate between two points. */ static double Interpolate(double a, double b) { return a + (b - a) * ft; } /* Interpolate a single component https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm */ static int InterpolateComponent() { return (int)Interpolate( Interpolate( Interpolate(scale * *p, scale * p[2]), Interpolate(scale * p[2], scale * p[4])), Interpolate( Interpolate(scale * p[2], scale * p[4]), Interpolate(scale * p[4], scale * p[6]))); } int main(int argc, char **argv) { /* Set width so that (width*height/6 + height) is roughly equal to target size, but not exceeding target size. Also try to maintain square canvas. */ for(; (width = (SIZE - 33 - height) * 6 / height) > height; height += 6) {} /* Compute padding size. */ x = SIZE - 33 - (width + 1) * height / 6; /* Allocate and zero-initialize output buffer. We need enough bytes for header and padding, plus one byte per pixel. We don't need extra bytes for footer because the image will be converted from 1 byte per pixel to 1/6 byte per pixel before adding the footer. */ output = calloc(31 + x + width * height, 1); range = malloc(height * 2 * sizeof(int)); if( output == NULL || range == NULL ) return printf("Out of memory\n"); scale = width / 90.0; /* Initialize output header with padding. */ strcpy(output, "\x1bPq#0;2;0;0;0#1;2;100;100;100#1"); for(image = output + 31; image < output + 31 + x;) *image++ = '$'; w = image; while( *p != 0 ) { /* Collect horizontal ranges for each scanline. This allows us to draw filled shapes one scanline at a time, and the shapes can be both convex and concave as long as each scanline is continuous. We can enforce the continuous requirement by tweaking our shapes, saving us the complexity of having to write a more featureful rasterizer. */ memset(range, 0, height * 2 * sizeof(int)); for(; p[2] != 0; p += 6) { for(t = 0; t < height * 4; t++) { ft = (double)t / (height * 4.0); x = InterpolateComponent(); p++; y = InterpolateComponent(); p--; if( x > 0 && x < width && y >= 0 && y < height ) { if( range[y * 2] == 0 ) { range[y * 2] = range[y * 2 + 1] = x; } else { range[y * 2] = range[y * 2] < x ? range[y * 2] : x; range[y * 2 + 1] = range[y * 2 + 1] > x ? range[y * 2 + 1] : x; } } } } /* Draw scanlines. */ for(y = 0; y < height; y++) { if( range[y * 2] != 0 ) { for(x = range[y * 2]; x <= range[y * 2 + 1]; x++) image[y * width + x] = p < points + 73 ? (x + y) & 1 : 1; } } /* p[0],p[1] is the last pair of coordinates. p[2] is a trailing zero. Thus, to get to the start of the next shape, we need to skip 3. */ p += 3; } /* Convert to Sixel. Each set of 6 rows costs width+1 bytes. */ for(y = 0; y < height; y += 6) { for(x = 0; x < width; x++) { *w++ = 0x3f + image[y * width + x] + (image[(y + 1) * width + x] << 1) + (image[(y + 2) * width + x] << 2) + (image[(y + 3) * width + x] << 3) + (image[(y + 4) * width + x] << 4) + (image[(y + 5) * width + x] << 5); } *w++ = '-'; } /* Write footer. */ strcpy(w, "\x1b\\"); /* Write output, with trailing newline. */ puts(output); return 0; }