Nikita Kalanakov 2 years ago
parent
commit
ad621465fd
2 changed files with 519 additions and 519 deletions
  1. 516 516
      makeshot/makeshot.c
  2. 3 3
      makeshot/mk.sh

+ 516 - 516
makeshot/makeshot.c

@@ -1,516 +1,516 @@
-#include <math.h>
-
-#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
-
-#define NICKNAME_TEXT_SIZE  23
-#define MESSAGE_TEXT_SIZE   24
-
-#define HPADDING            18
-#define VPADDING            8
-
-#define AVATAR_OFFSET       3
-#define AVATAR_PADDING      12
-#define AVATAR_DIM          75
-#define AVATAR_WIDTH        (AVATAR_DIM + AVATAR_PADDING)
-
-#define TAIL_WIDTH          18
-#define TAIL_HEIGHT         30
-
-#define MESSAGE_BOX_RADIUS  8
-#define MESSAGE_BOX_MIN     160
-
-#define WRAP_WIDTH          400
-
-#define TAIL_PATH           "./resources/tail.png"
-
-#define MAX(x, y) ((x) >= (y)? (x): (y))
-#define MIN(x, y) ((x) <= (y)? (x): (y))
-
-typedef unsigned int uint_t;
-
-typedef struct {
-  int r, g, b;
-} color_t;
-
-color_t COLORS_TABLE[] = {
-  /* Nickname colors */
-  {0xee, 0x49, 0x28}, /* 0 */
-  {0x41, 0xa9, 0x03}, /* 1 */
-  {0xe0, 0x96, 0x02}, /* 2 */
-  {0x0f, 0x94, 0xed}, /* 3 */
-  {0x8f, 0x3b, 0xf7}, /* 4 */
-  {0xfc, 0x43, 0x80}, /* 5 */
-  {0x00, 0xa1, 0xc4}, /* 6 */
-  {0xeb, 0x70, 0x02}, /* 7 */
-
-  /* Message text color */
-  {0xff, 0xff, 0xff}, /* 8 */
-
-  /* Message box color */
-  {0x2b, 0x2b, 0x2b}  /* 9 */
-};
-
-#define COLOR(i) (&COLORS_TABLE[i])
-
-#define C_MESSAGE_TEXT COLOR(8)
-#define C_MESSAGE_BOX  COLOR(9)
-
-typedef struct {
-  PangoLayout *layout;
-
-  uint_t w, h;
-} text_t;
-
-typedef struct {
-  unsigned char *data;
-  size_t size, allocated;
-} buffer_t;
-
-#define FLAG_BOLD    (1 << 0)
-#define FLAG_STRIKE  (1 << 1)
-#define FLAG_ITALIC  (1 << 2)
-#define FLAG_TT      (1 << 3)
-
-typedef struct _node_t {
-  uint_t flags;
-  buffer_t *buffer;
-
-  struct _node_t *next;
-} node_t;
-
-void *malloc_protected(size_t z) {
-  void *p;
-
-  if (!(p = malloc(z))) {
-    fputs("out of memory.\n", stderr);
-
-    exit(1);
-  }
-
-  return p;
-}
-
-void *realloc_protected(void *old_p, size_t z) {
-  void *p;
-
-  if (!(p = realloc(old_p, z))) {
-    fputs("out of memory.\n", stderr);
-
-    exit(1);
-  }
-
-  return p;
-}
-
-buffer_t *new_buffer() {
-  buffer_t *buffer;
-
-  buffer = malloc_protected(sizeof(buffer_t));
- 
-  buffer->data = NULL;
-  buffer->size = 0;
-  buffer->allocated = 0;
-
-  return buffer;
-}
-
-void free_buffer(buffer_t *buffer) {
-  free(buffer->data);
-  free(buffer);
-}
-
-void buffer_expand(buffer_t *buffer, size_t size) {
-  buffer->allocated += size;
-
-  buffer->data = realloc_protected(buffer->data, buffer->allocated);
-}
-
-void buffer_append_data(buffer_t *buffer, unsigned char *data, size_t data_length) {
-  size_t tail = buffer->size;
- 
-  buffer->size += data_length;
-
-  if (buffer->size > buffer->allocated)
-    buffer_expand(buffer, data_length);
-
-  for (size_t i = 0; i < data_length; i++)
-    buffer->data[tail + i] = data[i];
-}
-
-#define BUFFER_APPEND(b, s) buffer_append_data(b, s, strlen(s))
-#define BUFFER_CONCAT(b1, b2) buffer_append_data(b1, (b2)->data, (b2)->size);
-
-node_t *new_node() {
-  node_t *node;
-
-  node = malloc_protected(sizeof(node_t));
-
-  node->flags = 0;
-  node->buffer = new_buffer();
-  node->next = NULL;
-
-  return node;
-}
-
-void free_node(node_t *node) {
-  if (node->next)
-    free_node(node->next);
-
-  free_buffer(node->buffer);
-  free(node);
-}
-
-#define SET(flag) {\
-  if (tail->flags & FLAG_##flag) {\
-    tail = tail->next = new_node();\
-    continue;\
-  }\
-  uint_t flags = tail->flags;\
-  tail = tail->next = new_node();\
-  tail->flags = flags | FLAG_##flag;\
-}
-
-void preprocess_text(buffer_t *buffer, char *text) {
-  uint_t state;
-  node_t *head;
-  node_t *tail;
-
-  head = tail = new_node();
-
-  for (size_t i = 0; i < strlen(text); i++) {
-    unsigned char c, nc;
-
-    c = text[i];
-    nc = text[i+1];
-
-    if (c == '`') {
-      SET(TT);
-    } else if (c == '*' && nc == '*') {
-      i++;
-
-      SET(BOLD);
-    } else if (c == '~' && nc == '~') {
-      i++;
-
-      SET(STRIKE);
-    } else if (c == '_' && nc == '_') {
-      i++;
-
-      SET(ITALIC);
-    } else {
-      buffer_append_data(tail->buffer, &c, 1);
-    }
-  }
-
-  if (tail->flags) {
-    tail->flags = 0;
-  }
-
-  node_t *start = head;
-
-  while (head) {
-    if (head->flags & FLAG_BOLD)
-      BUFFER_APPEND(buffer, "<b>");
-    if (head->flags & FLAG_STRIKE)
-      BUFFER_APPEND(buffer, "<s>");
-    if (head->flags & FLAG_ITALIC)
-      BUFFER_APPEND(buffer, "<i>");
-    if (head->flags & FLAG_TT)
-      BUFFER_APPEND(buffer, "<tt>");
-
-    //printf("%d %d %d\n", head->flags & FLAG_BOLD, head->flags & FLAG_STRIKE, head->flags & FLAG_ITALIC);
-
-    BUFFER_CONCAT(buffer, head->buffer);
-
-    if (head->flags & FLAG_TT)
-      BUFFER_APPEND(buffer, "</tt>");
-    if (head->flags & FLAG_ITALIC)
-      BUFFER_APPEND(buffer, "</i>");
-    if (head->flags & FLAG_STRIKE)
-      BUFFER_APPEND(buffer, "</s>");
-    if (head->flags & FLAG_BOLD)
-      BUFFER_APPEND(buffer, "</b>");
-
-    head = head->next;
-  }
-
-  free_node(start);
-}
-
-text_t *new_text(cairo_t *cr, char *s, int size, int weight, int preprocess) {
-  text_t *text;
-  PangoFontDescription *font_description;
-  buffer_t *buffer;
-
-  text = malloc_protected(sizeof(text_t));
-
-  font_description = pango_font_description_from_string("Apple Color Emoji 16,Sans 16");
-  pango_font_description_set_absolute_size(font_description, size * PANGO_SCALE);
-  pango_font_description_set_weight(font_description, weight);
-
-  text->layout = pango_cairo_create_layout(cr);
-  pango_layout_set_font_description(text->layout, font_description);
-  pango_layout_set_wrap(text->layout, PANGO_WRAP_WORD);
-  pango_layout_set_width(text->layout, WRAP_WIDTH * PANGO_SCALE);
-
-  pango_font_description_free(font_description);
-
-  if (preprocess) {
-    buffer = new_buffer();
-    preprocess_text(buffer, s);
-
-    pango_layout_set_markup(text->layout, buffer->data, buffer->size);
-
-    free_buffer(buffer);
-  } else
-    pango_layout_set_text(text->layout, s, -1);
-
-  pango_layout_get_pixel_size(text->layout, &text->w, &text->h);
-
-  return text;
-}
-
-void free_text(text_t *text) {
-  g_object_unref(text->layout);
-
-  free(text);
-}
-
-/* TO-DO: do this better */
-void get_text_size(char *s, uint_t size, uint_t weight, uint_t *w, uint_t *h, int preprocess) {
-  cairo_surface_t *surface;
-  cairo_t *cr;
-
-  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
-  cr = cairo_create(surface);
-
-  text_t *text = new_text(cr, s, size, weight, preprocess);
-
-  *w = text->w;
-  *h = text->h;
-
-  free_text(text);
-  cairo_destroy(cr);
-  cairo_surface_destroy(surface);
-}
-
-void select_color(cairo_t *cr, color_t *color) {
-  cairo_set_source_rgb(cr, color->r / 255.0F, color->g / 255.0F, color->b / 255.0F);
-}
-
-void paste_clipped_image(cairo_t *cr, uint_t x, uint_t y, char *path) {
-  cairo_surface_t *surface, *surface2;
-  cairo_t *ic;
-
-  surface = cairo_image_surface_create_from_png(path);
-  ic = cairo_create(surface);
-
-  cairo_scale(ic, ((double)AVATAR_DIM) / cairo_image_surface_get_width(surface), ((double)AVATAR_DIM) / cairo_image_surface_get_height(surface));
-  cairo_set_source_surface(ic, surface, 0, 0);
-  cairo_paint(ic);
-
-  cairo_destroy(ic);
-
-  surface2 = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, AVATAR_DIM, AVATAR_DIM);
-  ic = cairo_create(surface2);
-
-  cairo_arc(ic, AVATAR_DIM/2, AVATAR_DIM/2, AVATAR_DIM/2, 0, 2*M_PI);
-  cairo_clip(ic);
-  cairo_new_path(ic); 
-
-  cairo_set_source_surface(ic, surface, 0, 0);
-  cairo_paint(ic);
-
-  cairo_set_source_surface(cr, surface2, x, y);
-  cairo_paint(cr);
-
-  cairo_destroy(ic);
-  cairo_surface_destroy(surface);
-  cairo_surface_destroy(surface2); 
-}
-
-void rounded_rectangle(cairo_t *cr, uint_t x, uint_t y, uint_t w, uint_t h, uint_t r) {
-  cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
-
-  cairo_new_sub_path(cr);
-  cairo_arc(cr, x + r, y + r, r, M_PI, 3 * M_PI / 2);
-  cairo_arc(cr, x + w - r, y + r, r, 3 *M_PI / 2, 2 * M_PI);
-  cairo_arc(cr, x + w - r, y + h - r, r, 0, M_PI / 2);
-  cairo_arc(cr, x + r, y + h - r, r, M_PI / 2, M_PI);
-  cairo_close_path(cr);
-
-  cairo_fill(cr);
-}
-
-void text(cairo_t *cr, uint_t x, uint_t y, text_t *text) {
-  cairo_move_to(cr, x, y);
-  pango_cairo_update_layout(cr, text->layout);
-  pango_cairo_show_layout(cr, text->layout);
-}
-
-void paste_image(cairo_t *cr, uint_t x, uint_t y, char *path) {
-  cairo_surface_t *surface;
-
-  surface = cairo_image_surface_create_from_png(path);
-  cairo_set_source_surface(cr, surface, x, y);
-  cairo_paint(cr);
-
-  cairo_surface_destroy(surface);
-}
-
-void fit(cairo_surface_t **surface, uint_t width, uint_t height) {
-  uint_t image_width, image_height;
-  uint_t new_width, new_height;
-  double image_ratio, new_ratio;
-
-  cairo_surface_t *surface2;
-  cairo_t *cr;
-
-  image_width = cairo_image_surface_get_width(*surface);
-  image_height = cairo_image_surface_get_height(*surface);
-
-  if (image_width <= width && image_height <= height)
-    return;
-
-  new_width = width;
-  new_height = height;
-
-  image_ratio = ((double)image_width) / ((double)image_height);
-  new_ratio = ((double)width) / ((double)height);
-
-  if (image_ratio > new_ratio)
-    new_height = ((double)image_height) / ((double)image_width) * ((double)width);
-  else
-    new_width = ((double)image_width) / ((double)image_height) * ((double)height);
-
-  surface2 = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, new_width, new_height);
-  cr = cairo_create(surface2);
-
-  cairo_scale(cr, ((double)new_width) / ((double)image_width), ((double)new_height) / ((double)image_height));
-  cairo_set_source_surface(cr, *surface, 0, 0);
-  cairo_paint(cr);
-
-  cairo_destroy(cr);
-  cairo_surface_destroy(*surface);
-
-  *surface = surface2;
-}
-
-void read_uint(uint_t *r) {
-  *r = 0;
-
-  for (uint_t i = 0; i < sizeof(uint_t); i++)
-    *r |= ((uint_t)fgetc(stdin)) << (i*8);
-}
-
-void read_string(buffer_t *buffer) {
-  uint_t length;
-
-  read_uint(&length);
-  buffer_expand(buffer, length);
-
-  buffer->size += fread(buffer->data + buffer->size, 1, length, stdin);
-
-  buffer_append_data(buffer, &"\0", 1);
-}
-
-main() {
-  uint_t avatar_width;
-  uint_t image_width;
-  uint_t image_height;
-
-  uint_t x, y;
-  uint_t w, h;
-
-  cairo_surface_t *surface;
-  cairo_t *cr;
-
-  text_t *nickname; 
-  text_t *message;
-  
-  buffer_t *output_path;
-  buffer_t *avatar_path;
-  buffer_t *username_text;
-  uint_t username_color;
-  buffer_t *message_text;
-  
-  uint_t nickname_w, nickname_h;
-  uint_t message_w, message_h;
-
-  output_path = new_buffer();
-  avatar_path = new_buffer();
-  username_text = new_buffer();
-  message_text = new_buffer();
-
-  read_string(output_path);
-  read_string(avatar_path);
-  read_string(username_text);
-  read_uint(&username_color);
-  read_string(message_text);
-
-  username_color = MIN(username_color, 6);
-
-  get_text_size(username_text->data, NICKNAME_TEXT_SIZE, PANGO_WEIGHT_BOLD, &nickname_w, &nickname_h, 0);
-  get_text_size(message_text->data, MESSAGE_TEXT_SIZE, PANGO_WEIGHT_NORMAL, &message_w, &message_h, 1);
-
-  image_width = AVATAR_WIDTH + MAX(message_w, nickname_w) + HPADDING*2;
-  image_height = message_h + nickname_h + VPADDING*2;
-
-  if (image_width - AVATAR_WIDTH < MESSAGE_BOX_MIN)
-    image_width = AVATAR_WIDTH + MESSAGE_BOX_MIN;
-
-  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, image_width, image_height+AVATAR_OFFSET);
-  cr = cairo_create(surface);
-
-  nickname = new_text(cr, username_text->data, NICKNAME_TEXT_SIZE, PANGO_WEIGHT_BOLD, 0);
-  message = new_text(cr, message_text->data, MESSAGE_TEXT_SIZE, PANGO_WEIGHT_NORMAL, 1); 
-
-  free_buffer(output_path);
-  free_buffer(avatar_path);
-  free_buffer(username_text);
-  free_buffer(message_text);
-
-  x = 0;
-  y = image_height - AVATAR_DIM + AVATAR_OFFSET;
-
-  paste_clipped_image(cr, x, y, avatar_path->data);
-
-  x = AVATAR_WIDTH;
-  y = 0;
-  w = MAX(message->w, nickname->w) + HPADDING*2;
-  h = message->h + nickname->h + VPADDING*2;
-
-  if (w < MESSAGE_BOX_MIN)
-    w = MESSAGE_BOX_MIN;
-
-  select_color(cr, C_MESSAGE_BOX);
-  rounded_rectangle(cr, x, y, w, h, MESSAGE_BOX_RADIUS);
-
-  x = AVATAR_WIDTH - TAIL_WIDTH + 9;
-  y = image_height - TAIL_HEIGHT;
-
-  paste_image(cr, x, y, TAIL_PATH);
-
-  x = AVATAR_WIDTH + HPADDING;
-  y = VPADDING / 2;
-
-  select_color(cr, COLOR(username_color));
-  text(cr, x, y, nickname);
-
-  x = AVATAR_WIDTH + HPADDING;
-  y = nickname->h + VPADDING - 3;
-
-  select_color(cr, C_MESSAGE_TEXT);
-  text(cr, x, y, message);
-
-  cairo_destroy(cr);
-
-  fit(&surface, 512, 512);
-  cairo_surface_write_to_png(surface, output_path->data);
-
-  cairo_surface_destroy(surface);
-
-  free_text(nickname);
-  free_text(message);
-}
+#include <math.h>
+
+#include <cairo/cairo.h>
+#include <pango/pangocairo.h>
+
+#define NICKNAME_TEXT_SIZE  23
+#define MESSAGE_TEXT_SIZE   24
+
+#define HPADDING            18
+#define VPADDING            8
+
+#define AVATAR_OFFSET       3
+#define AVATAR_PADDING      12
+#define AVATAR_DIM          75
+#define AVATAR_WIDTH        (AVATAR_DIM + AVATAR_PADDING)
+
+#define TAIL_WIDTH          18
+#define TAIL_HEIGHT         30
+
+#define MESSAGE_BOX_RADIUS  8
+#define MESSAGE_BOX_MIN     160
+
+#define WRAP_WIDTH          400
+
+#define TAIL_PATH           "./resources/tail.png"
+
+#define MAX(x, y) ((x) >= (y)? (x): (y))
+#define MIN(x, y) ((x) <= (y)? (x): (y))
+
+typedef unsigned int uint_t;
+
+typedef struct {
+  int r, g, b;
+} color_t;
+
+color_t COLORS_TABLE[] = {
+  /* Nickname colors */
+  {0xee, 0x49, 0x28}, /* 0 */
+  {0x41, 0xa9, 0x03}, /* 1 */
+  {0xe0, 0x96, 0x02}, /* 2 */
+  {0x0f, 0x94, 0xed}, /* 3 */
+  {0x8f, 0x3b, 0xf7}, /* 4 */
+  {0xfc, 0x43, 0x80}, /* 5 */
+  {0x00, 0xa1, 0xc4}, /* 6 */
+  {0xeb, 0x70, 0x02}, /* 7 */
+
+  /* Message text color */
+  {0xff, 0xff, 0xff}, /* 8 */
+
+  /* Message box color */
+  {0x2b, 0x2b, 0x2b}  /* 9 */
+};
+
+#define COLOR(i) (&COLORS_TABLE[i])
+
+#define C_MESSAGE_TEXT COLOR(8)
+#define C_MESSAGE_BOX  COLOR(9)
+
+typedef struct {
+  PangoLayout *layout;
+
+  uint_t w, h;
+} text_t;
+
+typedef struct {
+  unsigned char *data;
+  size_t size, allocated;
+} buffer_t;
+
+#define FLAG_BOLD    (1 << 0)
+#define FLAG_STRIKE  (1 << 1)
+#define FLAG_ITALIC  (1 << 2)
+#define FLAG_TT      (1 << 3)
+
+typedef struct _node_t {
+  uint_t flags;
+  buffer_t *buffer;
+
+  struct _node_t *next;
+} node_t;
+
+void *malloc_protected(size_t z) {
+  void *p;
+
+  if (!(p = malloc(z))) {
+    fputs("out of memory.\n", stderr);
+
+    exit(1);
+  }
+
+  return p;
+}
+
+void *realloc_protected(void *old_p, size_t z) {
+  void *p;
+
+  if (!(p = realloc(old_p, z))) {
+    fputs("out of memory.\n", stderr);
+
+    exit(1);
+  }
+
+  return p;
+}
+
+buffer_t *new_buffer() {
+  buffer_t *buffer;
+
+  buffer = malloc_protected(sizeof(buffer_t));
+ 
+  buffer->data = NULL;
+  buffer->size = 0;
+  buffer->allocated = 0;
+
+  return buffer;
+}
+
+void free_buffer(buffer_t *buffer) {
+  free(buffer->data);
+  free(buffer);
+}
+
+void buffer_expand(buffer_t *buffer, size_t size) {
+  buffer->allocated += size;
+
+  buffer->data = realloc_protected(buffer->data, buffer->allocated);
+}
+
+void buffer_append_data(buffer_t *buffer, unsigned char *data, size_t data_length) {
+  size_t tail = buffer->size;
+ 
+  buffer->size += data_length;
+
+  if (buffer->size > buffer->allocated)
+    buffer_expand(buffer, data_length);
+
+  for (size_t i = 0; i < data_length; i++)
+    buffer->data[tail + i] = data[i];
+}
+
+#define BUFFER_APPEND(b, s) buffer_append_data(b, s, strlen(s))
+#define BUFFER_CONCAT(b1, b2) buffer_append_data(b1, (b2)->data, (b2)->size);
+
+node_t *new_node() {
+  node_t *node;
+
+  node = malloc_protected(sizeof(node_t));
+
+  node->flags = 0;
+  node->buffer = new_buffer();
+  node->next = NULL;
+
+  return node;
+}
+
+void free_node(node_t *node) {
+  if (node->next)
+    free_node(node->next);
+
+  free_buffer(node->buffer);
+  free(node);
+}
+
+#define SET(flag) {\
+  if (tail->flags & FLAG_##flag) {\
+    tail = tail->next = new_node();\
+    continue;\
+  }\
+  uint_t flags = tail->flags;\
+  tail = tail->next = new_node();\
+  tail->flags = flags | FLAG_##flag;\
+}
+
+void preprocess_text(buffer_t *buffer, char *text) {
+  uint_t state;
+  node_t *head;
+  node_t *tail;
+
+  head = tail = new_node();
+
+  for (size_t i = 0; i < strlen(text); i++) {
+    unsigned char c, nc;
+
+    c = text[i];
+    nc = text[i+1];
+
+    if (c == '`') {
+      SET(TT);
+    } else if (c == '*' && nc == '*') {
+      i++;
+
+      SET(BOLD);
+    } else if (c == '~' && nc == '~') {
+      i++;
+
+      SET(STRIKE);
+    } else if (c == '_' && nc == '_') {
+      i++;
+
+      SET(ITALIC);
+    } else {
+      buffer_append_data(tail->buffer, &c, 1);
+    }
+  }
+
+  if (tail->flags) {
+    tail->flags = 0;
+  }
+
+  node_t *start = head;
+
+  while (head) {
+    if (head->flags & FLAG_BOLD)
+      BUFFER_APPEND(buffer, "<b>");
+    if (head->flags & FLAG_STRIKE)
+      BUFFER_APPEND(buffer, "<s>");
+    if (head->flags & FLAG_ITALIC)
+      BUFFER_APPEND(buffer, "<i>");
+    if (head->flags & FLAG_TT)
+      BUFFER_APPEND(buffer, "<tt>");
+
+    //printf("%d %d %d\n", head->flags & FLAG_BOLD, head->flags & FLAG_STRIKE, head->flags & FLAG_ITALIC);
+
+    BUFFER_CONCAT(buffer, head->buffer);
+
+    if (head->flags & FLAG_TT)
+      BUFFER_APPEND(buffer, "</tt>");
+    if (head->flags & FLAG_ITALIC)
+      BUFFER_APPEND(buffer, "</i>");
+    if (head->flags & FLAG_STRIKE)
+      BUFFER_APPEND(buffer, "</s>");
+    if (head->flags & FLAG_BOLD)
+      BUFFER_APPEND(buffer, "</b>");
+
+    head = head->next;
+  }
+
+  free_node(start);
+}
+
+text_t *new_text(cairo_t *cr, char *s, int size, int weight, int preprocess) {
+  text_t *text;
+  PangoFontDescription *font_description;
+  buffer_t *buffer;
+
+  text = malloc_protected(sizeof(text_t));
+
+  font_description = pango_font_description_from_string("Apple Color Emoji 16,Sans 16");
+  pango_font_description_set_absolute_size(font_description, size * PANGO_SCALE);
+  pango_font_description_set_weight(font_description, weight);
+
+  text->layout = pango_cairo_create_layout(cr);
+  pango_layout_set_font_description(text->layout, font_description);
+  pango_layout_set_wrap(text->layout, PANGO_WRAP_WORD);
+  pango_layout_set_width(text->layout, WRAP_WIDTH * PANGO_SCALE);
+
+  pango_font_description_free(font_description);
+
+  if (preprocess) {
+    buffer = new_buffer();
+    preprocess_text(buffer, s);
+
+    pango_layout_set_markup(text->layout, buffer->data, buffer->size);
+
+    free_buffer(buffer);
+  } else
+    pango_layout_set_text(text->layout, s, -1);
+
+  pango_layout_get_pixel_size(text->layout, &text->w, &text->h);
+
+  return text;
+}
+
+void free_text(text_t *text) {
+  g_object_unref(text->layout);
+
+  free(text);
+}
+
+/* TO-DO: do this better */
+void get_text_size(char *s, uint_t size, uint_t weight, uint_t *w, uint_t *h, int preprocess) {
+  cairo_surface_t *surface;
+  cairo_t *cr;
+
+  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
+  cr = cairo_create(surface);
+
+  text_t *text = new_text(cr, s, size, weight, preprocess);
+
+  *w = text->w;
+  *h = text->h;
+
+  free_text(text);
+  cairo_destroy(cr);
+  cairo_surface_destroy(surface);
+}
+
+void select_color(cairo_t *cr, color_t *color) {
+  cairo_set_source_rgb(cr, color->r / 255.0F, color->g / 255.0F, color->b / 255.0F);
+}
+
+void paste_clipped_image(cairo_t *cr, uint_t x, uint_t y, char *path) {
+  cairo_surface_t *surface, *surface2;
+  cairo_t *ic;
+
+  surface = cairo_image_surface_create_from_png(path);
+  ic = cairo_create(surface);
+
+  cairo_scale(ic, ((double)AVATAR_DIM) / cairo_image_surface_get_width(surface), ((double)AVATAR_DIM) / cairo_image_surface_get_height(surface));
+  cairo_set_source_surface(ic, surface, 0, 0);
+  cairo_paint(ic);
+
+  cairo_destroy(ic);
+
+  surface2 = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, AVATAR_DIM, AVATAR_DIM);
+  ic = cairo_create(surface2);
+
+  cairo_arc(ic, AVATAR_DIM/2, AVATAR_DIM/2, AVATAR_DIM/2, 0, 2*M_PI);
+  cairo_clip(ic);
+  cairo_new_path(ic); 
+
+  cairo_set_source_surface(ic, surface, 0, 0);
+  cairo_paint(ic);
+
+  cairo_set_source_surface(cr, surface2, x, y);
+  cairo_paint(cr);
+
+  cairo_destroy(ic);
+  cairo_surface_destroy(surface);
+  cairo_surface_destroy(surface2); 
+}
+
+void rounded_rectangle(cairo_t *cr, uint_t x, uint_t y, uint_t w, uint_t h, uint_t r) {
+  cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+  cairo_new_sub_path(cr);
+  cairo_arc(cr, x + r, y + r, r, M_PI, 3 * M_PI / 2);
+  cairo_arc(cr, x + w - r, y + r, r, 3 *M_PI / 2, 2 * M_PI);
+  cairo_arc(cr, x + w - r, y + h - r, r, 0, M_PI / 2);
+  cairo_arc(cr, x + r, y + h - r, r, M_PI / 2, M_PI);
+  cairo_close_path(cr);
+
+  cairo_fill(cr);
+}
+
+void text(cairo_t *cr, uint_t x, uint_t y, text_t *text) {
+  cairo_move_to(cr, x, y);
+  pango_cairo_update_layout(cr, text->layout);
+  pango_cairo_show_layout(cr, text->layout);
+}
+
+void paste_image(cairo_t *cr, uint_t x, uint_t y, char *path) {
+  cairo_surface_t *surface;
+
+  surface = cairo_image_surface_create_from_png(path);
+  cairo_set_source_surface(cr, surface, x, y);
+  cairo_paint(cr);
+
+  cairo_surface_destroy(surface);
+}
+
+void fit(cairo_surface_t **surface, uint_t width, uint_t height) {
+  uint_t image_width, image_height;
+  uint_t new_width, new_height;
+  double image_ratio, new_ratio;
+
+  cairo_surface_t *surface2;
+  cairo_t *cr;
+
+  image_width = cairo_image_surface_get_width(*surface);
+  image_height = cairo_image_surface_get_height(*surface);
+
+  if (image_width <= width && image_height <= height)
+    return;
+
+  new_width = width;
+  new_height = height;
+
+  image_ratio = ((double)image_width) / ((double)image_height);
+  new_ratio = ((double)width) / ((double)height);
+
+  if (image_ratio > new_ratio)
+    new_height = ((double)image_height) / ((double)image_width) * ((double)width);
+  else
+    new_width = ((double)image_width) / ((double)image_height) * ((double)height);
+
+  surface2 = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, new_width, new_height);
+  cr = cairo_create(surface2);
+
+  cairo_scale(cr, ((double)new_width) / ((double)image_width), ((double)new_height) / ((double)image_height));
+  cairo_set_source_surface(cr, *surface, 0, 0);
+  cairo_paint(cr);
+
+  cairo_destroy(cr);
+  cairo_surface_destroy(*surface);
+
+  *surface = surface2;
+}
+
+void read_uint(uint_t *r) {
+  *r = 0;
+
+  for (uint_t i = 0; i < sizeof(uint_t); i++)
+    *r |= ((uint_t)fgetc(stdin)) << (i*8);
+}
+
+void read_string(buffer_t *buffer) {
+  uint_t length;
+
+  read_uint(&length);
+  buffer_expand(buffer, length);
+
+  buffer->size += fread(buffer->data + buffer->size, 1, length, stdin);
+
+  buffer_append_data(buffer, &"\0", 1);
+}
+
+main() {
+  uint_t avatar_width;
+  uint_t image_width;
+  uint_t image_height;
+
+  uint_t x, y;
+  uint_t w, h;
+
+  cairo_surface_t *surface;
+  cairo_t *cr;
+
+  text_t *nickname; 
+  text_t *message;
+  
+  buffer_t *output_path;
+  buffer_t *avatar_path;
+  buffer_t *username_text;
+  uint_t username_color;
+  buffer_t *message_text;
+  
+  uint_t nickname_w, nickname_h;
+  uint_t message_w, message_h;
+
+  output_path = new_buffer();
+  avatar_path = new_buffer();
+  username_text = new_buffer();
+  message_text = new_buffer();
+
+  read_string(output_path);
+  read_string(avatar_path);
+  read_string(username_text);
+  read_uint(&username_color);
+  read_string(message_text);
+
+  username_color = MIN(username_color, 6);
+
+  get_text_size(username_text->data, NICKNAME_TEXT_SIZE, PANGO_WEIGHT_BOLD, &nickname_w, &nickname_h, 0);
+  get_text_size(message_text->data, MESSAGE_TEXT_SIZE, PANGO_WEIGHT_NORMAL, &message_w, &message_h, 1);
+
+  image_width = AVATAR_WIDTH + MAX(message_w, nickname_w) + HPADDING*2;
+  image_height = message_h + nickname_h + VPADDING*2;
+
+  if (image_width - AVATAR_WIDTH < MESSAGE_BOX_MIN)
+    image_width = AVATAR_WIDTH + MESSAGE_BOX_MIN;
+
+  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, image_width, image_height+AVATAR_OFFSET);
+  cr = cairo_create(surface);
+
+  nickname = new_text(cr, username_text->data, NICKNAME_TEXT_SIZE, PANGO_WEIGHT_BOLD, 0);
+  message = new_text(cr, message_text->data, MESSAGE_TEXT_SIZE, PANGO_WEIGHT_NORMAL, 1); 
+
+  free_buffer(output_path);
+  free_buffer(avatar_path);
+  free_buffer(username_text);
+  free_buffer(message_text);
+
+  x = 0;
+  y = image_height - AVATAR_DIM + AVATAR_OFFSET;
+
+  paste_clipped_image(cr, x, y, avatar_path->data);
+
+  x = AVATAR_WIDTH;
+  y = 0;
+  w = MAX(message->w, nickname->w) + HPADDING*2;
+  h = message->h + nickname->h + VPADDING*2;
+
+  if (w < MESSAGE_BOX_MIN)
+    w = MESSAGE_BOX_MIN;
+
+  select_color(cr, C_MESSAGE_BOX);
+  rounded_rectangle(cr, x, y, w, h, MESSAGE_BOX_RADIUS);
+
+  x = AVATAR_WIDTH - TAIL_WIDTH + 9;
+  y = image_height - TAIL_HEIGHT;
+
+  paste_image(cr, x, y, TAIL_PATH);
+
+  x = AVATAR_WIDTH + HPADDING;
+  y = VPADDING / 2;
+
+  select_color(cr, COLOR(username_color));
+  text(cr, x, y, nickname);
+
+  x = AVATAR_WIDTH + HPADDING;
+  y = nickname->h + VPADDING - 3;
+
+  select_color(cr, C_MESSAGE_TEXT);
+  text(cr, x, y, message);
+
+  cairo_destroy(cr);
+
+  fit(&surface, 512, 512);
+  cairo_surface_write_to_png(surface, output_path->data);
+
+  cairo_surface_destroy(surface);
+
+  free_text(nickname);
+  free_text(message);
+}

+ 3 - 3
makeshot/mk.sh

@@ -1,3 +1,3 @@
-#!/bin/bash
-
-gcc -w -Os `pkg-config --cflags pango` makeshot.c -o makeshot -lcairo -lpangocairo-1.0 -lpango-1.0 -lgobject-2.0
+#!/bin/bash
+
+gcc -w -Os `pkg-config --cflags pango` makeshot.c -o makeshot -lcairo -lpangocairo-1.0 -lpango-1.0 -lgobject-2.0