prc_flag.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /*
  2. * prc_flag.c - draw the national flag of PRC
  3. *
  4. * $ cc prc_flag.c -I/usr/include/cairo -lcairo -lm -std=c99
  5. *
  6. * Copyright (C) 2014 Tom Li. All rights reserved.
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining
  9. * a copy of this software and associated documentation files (the
  10. * "Software"), to deal in the Software without restriction, including
  11. * without limitation the rights to use, copy, modify, merge, publish,
  12. * distribute, sublicense, and/or sell copies of the Software, and to
  13. * permit persons to whom the Software is furnished to do so, subject to
  14. * the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be
  17. * included in all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  21. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  22. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  23. * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  24. * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  25. * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  26. */
  27. #if __STDC_VERSION__ > 199900L
  28. #define __IS_C99_COMPILER
  29. #endif
  30. #ifndef __IS_C99_COMPILER
  31. #error "Please use a C99 compiler (or add '-std=c99') to compile"
  32. #endif
  33. #ifndef _GNU_SOURCE /* M_PI is a (GNU) extension */
  34. #define _GNU_SOURCE /* It was disabled by default in C99 */
  35. #endif
  36. #include <stdio.h>
  37. #include <math.h>
  38. #include <stdbool.h>
  39. #include <string.h>
  40. #include <stdlib.h>
  41. #include <getopt.h>
  42. #include <assert.h>
  43. #include <cairo.h>
  44. /* floating-point comparison */
  45. #ifndef NDEBUG
  46. #define EPSILON 0.0001
  47. #define fequal(a, b) ((fabs((a) - (b)) < EPSILON))
  48. #endif
  49. typedef struct Point {
  50. double x;
  51. double y;
  52. } Point;
  53. #define POINT(a, b) \
  54. (Point) \
  55. { \
  56. .x = (a), \
  57. .y = (b) \
  58. }
  59. #define WIDTH 960
  60. #define HEIGHT 640
  61. #define BIG_STAR POINT(5, 5)
  62. #define BIG_STAR_R 3
  63. #define SMALL_STAR_R 1
  64. #define SMALL_STAR_1 POINT(10, 2)
  65. #define SMALL_STAR_2 POINT(12, 4)
  66. #define SMALL_STAR_3 POINT(12, 7)
  67. #define SMALL_STAR_4 POINT(10, 9)
  68. #define BASE (HEIGHT / 2 / 10)
  69. static void star(cairo_t *cr, Point *center, double r, Point *circle);
  70. static void rotate(cairo_t *cr, Point *point, double radian);
  71. static Point circle_line_intersection(Point *circle, double r, Point *p1, Point *p2);
  72. static double radian_two_points(Point *p1, Point *p2, double r);
  73. static void grid(cairo_t *cr, Point *topleft, Point *botright, unsigned int row, unsigned int col);
  74. static void line(cairo_t *cr, Point *p1, Point *p2);
  75. static void flag_star(cairo_t *cr, Point *p, double r);
  76. static void flag_line(cairo_t *cr, Point *center);
  77. static void star(cairo_t *cr, Point *center, double r, Point *circle)
  78. {
  79. Point topleft = POINT(center->x - r * sin(2 * M_PI / 5),
  80. center->y - r * cos(2 * M_PI / 5));
  81. Point topright = POINT(center->x + r * sin(2 * M_PI / 5),
  82. center->y - r * cos(2 * M_PI / 5));
  83. Point botleft = POINT(center->x - r * sin(M_PI / 5),
  84. center->y + r * cos(M_PI / 5));
  85. Point topcenter = POINT(center->x,
  86. center->y - r);
  87. Point botright = POINT(center->x + r * sin(M_PI / 5),
  88. center->y + r * cos(M_PI / 5));
  89. cairo_save(cr);
  90. if (circle) {
  91. Point point = circle_line_intersection(center, r, circle, center);
  92. double rad = radian_two_points(&topleft, &point, r);
  93. rotate(cr, center, rad);
  94. }
  95. cairo_new_path(cr);
  96. cairo_move_to(cr, topleft.x, topleft.y);
  97. cairo_line_to(cr, topright.x, topright.y);
  98. cairo_line_to(cr, botleft.x, botleft.y);
  99. cairo_line_to(cr, topcenter.x, topcenter.y);
  100. cairo_line_to(cr, botright.x, botright.y);
  101. cairo_close_path(cr);
  102. cairo_set_source_rgb(cr, 255, 255, 0);
  103. cairo_fill(cr);
  104. cairo_restore(cr);
  105. }
  106. static void rotate(cairo_t *cr, Point *point, double radian)
  107. {
  108. cairo_translate(cr, point->x, point->y);
  109. cairo_rotate(cr, radian);
  110. cairo_translate(cr, -point->x, -point->y);
  111. }
  112. static Point circle_line_intersection(Point *circle, double r, Point *p1, Point *p2)
  113. {
  114. /* y = mx + c, solve m and c from p1 and p2 */
  115. double lm = (p2->y - p1->y) / (p2->x - p1->x);
  116. double lc = p1->y - p1->x * lm;
  117. assert(fequal(lc, p2->y - p2->x * lm));
  118. /* solve (x - p) ^ 2 + (y - q) ^ 2 = r ^ 2 and y = mx + c */
  119. double a = pow(lm, 2) + 1;
  120. double b = 2 * ((lm * lc) - (lm * circle->y) - circle->x);
  121. double c = pow(circle->y, 2) - pow(r, 2) + pow(circle->x, 2) - (2 * lc * circle->y) + pow(lc, 2);
  122. double delta = pow(b, 2) - 4 * a * c;
  123. assert(delta >= 0);
  124. delta = sqrt(delta);
  125. double x1 = (-b - delta) / (2 * a);
  126. double y1 = lm * x1 + lc;
  127. return POINT(x1, y1);
  128. }
  129. static double radian_two_points(Point *p1, Point *p2, double r)
  130. {
  131. double distance = sqrt(pow(p2->x - p1->x, 2) + pow(p2->y - p1->y, 2));
  132. double angle = 2 * asin(distance / 2 / r);
  133. if (angle > M_PI && p2->y < p1->y) {
  134. angle = -angle;
  135. }
  136. else if (angle < M_PI && p2->y > p1->y) {
  137. angle = -angle;
  138. }
  139. return angle;
  140. }
  141. static void grid(cairo_t *cr, Point *topleft, Point *botright, unsigned int row, unsigned int col)
  142. {
  143. cairo_save(cr);
  144. cairo_set_source_rgb(cr, 0, 0, 0); /* black */
  145. cairo_set_line_width(cr, 2);
  146. double max_y = botright->y - topleft->y;
  147. double max_x = botright->x - topleft->x;
  148. for (int i = 0; i < max_y; i += max_y / row) {
  149. cairo_move_to(cr, i, topleft->y);
  150. cairo_line_to(cr, i, botright->x);
  151. }
  152. for (int i = 0; i < max_x; i += max_x / col) {
  153. cairo_move_to(cr, topleft->x, i);
  154. cairo_line_to(cr, botright->y, i);
  155. }
  156. cairo_stroke(cr);
  157. cairo_restore(cr);
  158. }
  159. static void line(cairo_t *cr, Point *p1, Point *p2)
  160. {
  161. cairo_save(cr);
  162. cairo_set_source_rgb(cr, 0, 0, 0); /* black */
  163. cairo_move_to(cr, p1->x, p1->y);
  164. cairo_line_to(cr, p2->x, p2->y);
  165. cairo_stroke(cr);
  166. cairo_restore(cr);
  167. }
  168. static void flag_star(cairo_t *cr, Point *p, double r)
  169. {
  170. Point *circle = NULL;
  171. if (p->x != BIG_STAR.y && p->y != BIG_STAR.y) {
  172. circle = &POINT(BIG_STAR.x * BASE,
  173. BIG_STAR.y * BASE);
  174. circle->x = BIG_STAR.x * BASE;
  175. circle->y = BIG_STAR.y * BASE;
  176. }
  177. p->x *= BASE;
  178. p->y *= BASE;
  179. r *= BASE;
  180. star(cr, p, r, circle);
  181. }
  182. static void flag_line(cairo_t *cr, Point *center)
  183. {
  184. line(cr,
  185. &POINT(BIG_STAR.x * BASE,
  186. BIG_STAR.y * BASE),
  187. &POINT(center->x * BASE,
  188. center->y * BASE));
  189. }
  190. static void show_usage(char *name)
  191. {
  192. printf("Usage: %s [OPTION]\n", name);
  193. printf(" -a\tDraw all auxiliary lines\n");
  194. printf(" -o [FILE]\tOutput file (will auto-append \".png\" if not ends with it)\n");
  195. printf(" -h\tThis help message\n");
  196. }
  197. static int str_ends_with(const char *str, const char *suffix)
  198. {
  199. size_t lenstr = strlen(str);
  200. size_t lensuffix = strlen(suffix);
  201. if (lensuffix > lenstr) {
  202. return 0;
  203. }
  204. return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
  205. }
  206. static void *xmalloc(size_t size)
  207. {
  208. static char error_msg[] = "malloc() failed\n";
  209. void *ptr = malloc(size);
  210. if (!ptr) {
  211. fputs(error_msg, stderr);
  212. abort();
  213. }
  214. return ptr;
  215. }
  216. int main(int argc, char **argv)
  217. {
  218. static struct option longopts[] = {
  219. {"auxiliary-lines", no_argument, 0, 'a'},
  220. {"output", required_argument, 0, 'o'},
  221. {"help", no_argument, 0, 'h'},
  222. };
  223. bool auxiliary_line = false;
  224. char *output_arg = NULL;
  225. char *output_real = NULL;
  226. int opt;
  227. while ((opt = getopt_long(argc, argv, "hao:", longopts, NULL)) != -1) {
  228. switch (opt) {
  229. case 'a':
  230. auxiliary_line = true;
  231. break;
  232. case 'o':
  233. output_arg = optarg;
  234. break;
  235. case '?':
  236. case 'h':
  237. show_usage(argv[0]);
  238. exit(0);
  239. }
  240. }
  241. if (!output_arg) {
  242. show_usage(argv[0]);
  243. exit(0);
  244. }
  245. else {
  246. if (!str_ends_with(output_arg, ".png")) {
  247. output_real = xmalloc(strlen(output_arg) + strlen(".png"));
  248. sprintf(output_real, "%s%s", output_arg, ".png");
  249. printf("renamed '%s' to '%s'\n", output_arg, output_real);
  250. }
  251. else {
  252. output_real = xmalloc(strlen(output_arg));
  253. strcpy(output_real, output_arg);
  254. }
  255. }
  256. cairo_surface_t *surface;
  257. cairo_t *cr;
  258. surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT);
  259. cr = cairo_create(surface);
  260. /* fill background to red */
  261. cairo_set_source_rgb(cr, 255, 0, 0); /* red */
  262. cairo_rectangle(cr, 0, 0, WIDTH, HEIGHT);
  263. cairo_fill(cr);
  264. flag_star(cr, &BIG_STAR, BIG_STAR_R);
  265. flag_star(cr, &SMALL_STAR_1, SMALL_STAR_R);
  266. flag_star(cr, &SMALL_STAR_2, SMALL_STAR_R);
  267. flag_star(cr, &SMALL_STAR_3, SMALL_STAR_R);
  268. flag_star(cr, &SMALL_STAR_4, SMALL_STAR_R);
  269. if (auxiliary_line) {
  270. flag_line(cr, &SMALL_STAR_1);
  271. flag_line(cr, &SMALL_STAR_2);
  272. flag_line(cr, &SMALL_STAR_3);
  273. flag_line(cr, &SMALL_STAR_4);
  274. grid(cr, &POINT(0, 0), &POINT(HEIGHT, WIDTH),
  275. 2, 2);
  276. grid(cr, &POINT(0, 0), &POINT(HEIGHT / 2, WIDTH / 2),
  277. 15, 10);
  278. }
  279. assert(output_real != NULL);
  280. cairo_status_t status = cairo_surface_write_to_png(surface, output_real);
  281. if (status != CAIRO_STATUS_SUCCESS) {
  282. fprintf(stderr, "cairo_surface_write_to_png() failed: %s\n", cairo_status_to_string(status));
  283. exit(1);
  284. }
  285. free(output_real);
  286. cairo_destroy(cr);
  287. cairo_surface_destroy(surface);
  288. return 0;
  289. }