pngenerator.c (5159B)
1 #include <math.h> 2 #include <png.h> 3 #include <stdint.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 8 #define WIDTH 256 9 #define HEIGHT 256 10 const uint16_t ITERATIONS = 201; 11 const uint16_t MAX_STEPS = 14; 12 const double MIN_DISTANCE = 0.008; 13 const size_t CAPACITY = 16384; 14 15 typedef struct vec3 { 16 double x, y, z; 17 } vec3; 18 19 static vec3 vec3_add(const vec3 a, const vec3 b) { 20 vec3 v = {a.x + b.x, a.y + b.y, a.z + b.z}; 21 return v; 22 } 23 24 static vec3 vec3_sub(const vec3 a, const vec3 b) { 25 vec3 v = {a.x - b.x, a.y - b.y, a.z - b.z}; 26 return v; 27 } 28 29 static vec3 vec3_mult(const vec3 a, const double c) { 30 vec3 v = {c*a.x, c*a.y, c*a.z}; 31 return v; 32 } 33 34 static double sqnorm(const vec3 v) { 35 return v.x*v.x + v.y*v.y + v.z*v.z; 36 } 37 38 static double norm(const vec3 v) { 39 return sqrt(sqnorm(v)); 40 } 41 42 static double vec3_sqdist(const vec3 v, const vec3 w) { 43 return sqnorm(vec3_sub(v, w)); 44 } 45 46 typedef struct slice { 47 char* buffer; 48 size_t len; 49 size_t cap; 50 } slice; 51 52 static void 53 png_write_slice(png_structp png_ptr, const png_bytep data, const png_size_t size) 54 { 55 slice* p = png_get_io_ptr(png_ptr); 56 if ((p->len + size) > p->cap) { 57 size_t newcap = 2*p->cap + size; 58 p->buffer = realloc(p->buffer, newcap); 59 p->cap = newcap; 60 } 61 memcpy(p->buffer + p->len, data, size); 62 p->len += size; 63 } 64 65 static png_color 66 num2col(const uint32_t num) 67 { 68 png_color col = {(num >> 16) & 0xff, (num >> 8) & 0xff, num & 0xff}; 69 return col; 70 } 71 72 extern slice 73 pngenerate(char * const text, const uint32_t background, const uint32_t foreground) 74 { 75 slice state = { 76 .buffer = malloc(CAPACITY), 77 .len = 0, 78 .cap = CAPACITY 79 }; 80 81 png_structp png_ptr = 82 png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 83 png_set_write_fn(png_ptr, &state, png_write_slice, NULL); 84 png_infop info_ptr = png_create_info_struct(png_ptr); 85 png_set_compression_level(png_ptr, 9); 86 87 png_color b = num2col(background), f = num2col(foreground); 88 png_color step = {(f.red - b.red) / 4, (f.green - b.green) / 4, (f.blue - b.blue) / 4}; 89 png_color palette[4]; 90 palette[0] = b; 91 for (size_t i = 0; i < 3; i++) { 92 png_color c = {f.red - i*step.red, f.green - i*step.green, f.blue - i*step.blue}; 93 palette[3 - i] = c; 94 } 95 png_set_IHDR(png_ptr, info_ptr, WIDTH, HEIGHT, 2, PNG_COLOR_TYPE_PALETTE, 96 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, 97 PNG_FILTER_TYPE_DEFAULT); 98 png_set_PLTE(png_ptr, info_ptr, palette, 4); 99 100 const png_text page = { 101 .compression = PNG_TEXT_COMPRESSION_NONE, 102 .key = "Page", 103 .text = text 104 }; 105 png_set_text(png_ptr, info_ptr, &page, 1); 106 107 png_bytep buffer = calloc(WIDTH * HEIGHT / 4, sizeof(png_byte)); 108 png_bytep rows[HEIGHT]; 109 for (size_t i = 0; i < HEIGHT; i++) 110 rows[i] = buffer + i * WIDTH / 4; 111 112 time_t t; 113 srand((unsigned) time(&t)); 114 115 vec3 v[4] = {{-sqrt(3), -sqrt(2), -1}, {sqrt(3), -sqrt(2), -1}, {0, -sqrt(2), 2}, {0, sqrt(2), 0}}; 116 double s = -sqrt(3) * ((double) rand() / RAND_MAX - 0.5); 117 double c = sqrt(1.0 - s*s); 118 for (size_t i = 0; i < 3; i++) { 119 double ox = v[i].x; 120 v[i].x = c*v[i].x - s*v[i].z; 121 v[i].z = s*ox + c*v[i].z; 122 } 123 for (size_t j = 0; j < HEIGHT; j++) { 124 for (size_t i = 0; i < WIDTH; i++) { 125 double x = (double) i * 2 / (double) WIDTH - 1.0; 126 double y = (double) j * -2 / (double) HEIGHT + 1.0; 127 vec3 position = {x, y, -1.1}; 128 vec3 dir = {x, y, 2.2}; 129 double distance = norm(dir); 130 const vec3 direction = vec3_mult(dir, 1.0/distance); 131 132 uint16_t steps; 133 for(steps = 0; steps < MAX_STEPS; steps++) { 134 double d; 135 vec3 p = position; 136 for (int i = 0; i < 9; i++) { 137 vec3 center = v[0]; 138 d = vec3_sqdist(p, v[0]); 139 for (int j = 1; j < 4; j++) { 140 double di = vec3_sqdist(p, v[j]); 141 if (di < d) { 142 d = di; 143 center = v[j]; 144 } 145 } 146 p = vec3_sub(vec3_mult(p, 2), center); 147 } 148 d = sqrt(d) / (double) (1 << 8); 149 150 if (d < MIN_DISTANCE) { 151 break; 152 } 153 distance += d; 154 if (distance >= 6.0) { 155 steps = MAX_STEPS; 156 break; 157 } 158 position = vec3_add(position, vec3_mult(direction, d)); 159 } 160 char val = (char) (3.6 - 3.0*steps/MAX_STEPS) & 3; 161 buffer[j*(WIDTH >> 2) + (i >> 2)] |= val << (((i & 3) ^ 3) << 1); 162 } 163 } 164 png_set_rows(png_ptr, info_ptr, rows); 165 166 png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); 167 png_destroy_write_struct(&png_ptr, &info_ptr); 168 169 return state; 170 } 171 172 int 173 main() 174 { 175 FILE* fp = fopen("test.png", "wb"); 176 const slice s = pngenerate("<h1>Title</h1>Hello!!<img src=\"#\">", 0x153e4f, 0x118282); 177 fwrite(s.buffer, 1, s.len, fp); 178 fclose(fp); 179 return 0; 180 }