
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <png.h>
#define OUT_FILE "initials.png"
#define WIDTH 600
#define HEIGHT 600
#define COLOR_TYPE PNG_COLOR_TYPE_RGB
#define BIT_DEPTH 8
typedef struct {
   int x;
   int y;
} Point;
typedef struct {
   png_byte r;
   png_byte g;
   png_byte b;
} Color;
int x, y;
int width, height;
png_byte color_type;
png_byte bit_depth;
png_structp png_ptr;
png_infop info_ptr;
png_bytep *row_pointers;
Color CYAN = {0, 255, 255};
Color MAGENTA = {255, 0, 255};
Color GREEN = {0, 255, 0};
Color DARK = {0, 100, 100};
void abort_(const char *s, ...)
{
   va_list args;
   va_start(args, s);
   vfprintf(stderr, s, args);
   fprintf(stderr, "\n");
   va_end(args);
   abort();
}
void create_png_file()
{
   width = WIDTH;
   height = HEIGHT;
   bit_depth = BIT_DEPTH;
   color_type = COLOR_TYPE;
   row_pointers = malloc(sizeof(png_bytep) * height);
   if (!row_pointers) {
       abort_("Memory error");
   }
   for (y = 0; y < height; y++) {
       row_pointers[y] = malloc(width * 3);
       if (!row_pointers[y]) {
           abort_("Memory error");
       }
   }
}
void write_png_file(char *file_name)
{
   FILE *fp = fopen(file_name, "wb");
   if (!fp) {
       abort_("File could not be opened for writing");
   }
   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
   if (!png_ptr) {
       abort_("png_create_write_struct failed");
   }
   info_ptr = png_create_info_struct(png_ptr);
   if (!info_ptr) {
       abort_("png_create_info_struct failed");
   }
   if (setjmp(png_jmpbuf(png_ptr))) {
       abort_("Error during init_io");
   }
   png_init_io(png_ptr, fp);
   if (setjmp(png_jmpbuf(png_ptr))) {
       abort_("Error during writing header");
   }
   png_set_IHDR(
       png_ptr,
       info_ptr,
       width,
       height,
       bit_depth,
       color_type,
       PNG_INTERLACE_NONE,
       PNG_COMPRESSION_TYPE_BASE,
       PNG_FILTER_TYPE_BASE
   );
   png_write_info(png_ptr, info_ptr);
   if (setjmp(png_jmpbuf(png_ptr))) {
       abort_("Error during writing bytes");
   }
   png_write_image(png_ptr, row_pointers);
   if (setjmp(png_jmpbuf(png_ptr))) {
       abort_("Error during end of write");
   }
   png_write_end(png_ptr, NULL);
   fclose(fp);
}
void free_png_file()
{
   for (y = 0; y < height; y++) {
       free(row_pointers[y]);
   }
   free(row_pointers);
}
int same_color(Color a, Color b)
{
   return a.r == b.r && a.g == b.g && a.b == b.b;
}
void set_pixel(int px, int py, Color c)
{
   if (px < 0 || px >= width || py < 0 || py >= height) {
       return;
   }
   png_byte *ptr = &(row_pointers[py][px * 3]);
   ptr[0] = c.r;
   ptr[1] = c.g;
   ptr[2] = c.b;
}
Color get_pixel(int px, int py)
{
   Color c = {0, 0, 0};
   if (px < 0 || px >= width || py < 0 || py >= height) {
       return c;
   }
   png_byte *ptr = &(row_pointers[py][px * 3]);
   c.r = ptr[0];
   c.g = ptr[1];
   c.b = ptr[2];
   return c;
}
void clear_image(Color c)
{
   for (y = 0; y < height; y++) {
       for (x = 0; x < width; x++) {
           set_pixel(x, y, c);
       }
   }
}
/* Bresenham */
void draw_line(int x0, int y0, int x1, int y1, Color c)
{
   int dx = abs(x1 - x0);
   int sx = x0 < x1 ? 1 : -1;
   int dy = -abs(y1 - y0);
   int sy = y0 < y1 ? 1 : -1;
   int err = dx + dy;
   while (1) {
       set_pixel(x0, y0, c);
       if (x0 == x1 && y0 == y1) {
           break;
       }
       int e2 = 2 * err;
       if (e2 >= dy) {
           err += dy;
           x0 += sx;
       }
       if (e2 <= dx) {
           err += dx;
           y0 += sy;
       }
   }
}
void draw_thick_line(int x0, int y0, int x1, int y1, int t, Color c)
{
   int i;
   for (i = -t; i <= t; i++) {
       draw_line(x0 + i, y0, x1 + i, y1, c);
       draw_line(x0, y0 + i, x1, y1 + i, c);
   }
}
void draw_circle_points(int cx, int cy, int x, int y, Color c)
{
   set_pixel(cx + x, cy + y, c);
   set_pixel(cx - x, cy + y, c);
   set_pixel(cx + x, cy - y, c);
   set_pixel(cx - x, cy - y, c);
   set_pixel(cx + y, cy + x, c);
   set_pixel(cx - y, cy + x, c);
   set_pixel(cx + y, cy - x, c);
   set_pixel(cx - y, cy - x, c);
}
/* Circle rasterization */
void draw_circle(int cx, int cy, int r, Color c)
{
   int x = 0;
   int y = r;
   int d = 3 - 2 * r;
   while (y >= x) {
       draw_circle_points(cx, cy, x, y, c);
       x++;
       if (d > 0) {
           y--;
           d = d + 4 * (x - y) + 10;
       } else {
           d = d + 4 * x + 6;
       }
   }
}
void draw_thick_circle(int cx, int cy, int r, int t, Color c)
{
   int i;
   for (i = -t; i <= t; i++) {
       draw_circle(cx, cy, r + i, c);
   }
}
/* Bezpieczne flood fill 4-spojne, bez rekurencji */
void flood_fill(int sx, int sy, Color new_color)
{
   if (sx < 0 || sx >= width || sy < 0 || sy >= height) {
       return;
   }
   Color old_color = get_pixel(sx, sy);
   if (same_color(old_color, new_color)) {
       return;
   }
   int max = width * height;
   Point *queue = malloc(sizeof(Point) * max);
   if (!queue) {
       abort_("Flood fill memory error");
   }
   int head = 0;
   int tail = 0;
   queue[tail].x = sx;
   queue[tail].y = sy;
   tail++;
   set_pixel(sx, sy, new_color);
   while (head < tail) {
       Point p = queue[head];
       head++;
       int nx[4] = {p.x + 1, p.x - 1, p.x, p.x};
       int ny[4] = {p.y, p.y, p.y + 1, p.y - 1};
       int i;
       for (i = 0; i < 4; i++) {
           if (nx[i] < 0 || nx[i] >= width || ny[i] < 0 || ny[i] >= height) {
               continue;
           }
           if (same_color(get_pixel(nx[i], ny[i]), old_color)) {
               if (tail < max) {
                   queue[tail].x = nx[i];
                   queue[tail].y = ny[i];
                   tail++;
                   set_pixel(nx[i], ny[i], new_color);
               }
           }
       }
   }
   free(queue);
}
void draw_initials()
{
   /* Первая буква A */
   draw_thick_line(120, 400, 170, 180, 4, DARK);
   draw_thick_line(170, 180, 220, 400, 4, DARK);
   draw_thick_line(145, 310, 195, 310, 4, DARK);
   /* Вторая буква A (сдвинута вправо) */
   draw_thick_line(300, 400, 350, 180, 4, DARK);
   draw_thick_line(350, 180, 400, 400, 4, DARK);
   draw_thick_line(325, 310, 375, 310, 4, DARK);
}
void process_file(void)
{
   clear_image(CYAN);
   draw_initials();
   draw_thick_circle(300, 300, 240, 3, DARK);
   flood_fill(10, 10, MAGENTA);
   flood_fill(300, 300, GREEN);
   draw_initials();
   draw_thick_circle(300, 300, 240, 3, DARK);
}
int main(int argc, char **argv)
{
   create_png_file();
   printf("Start drawing...\n");
   process_file();
   printf("Writing PNG...\n");
   write_png_file(OUT_FILE);
   printf("PNG created: %s\n", OUT_FILE);
   free_png_file();
   return 0;
}
