icons.cmake 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. if(NOT build_icons)
  2. # This entire subdirectory does nothing on platforms where we can't
  3. # build the icons in any case.
  4. return()
  5. endif()
  6. include(FindPerl)
  7. if(NOT PERL_EXECUTABLE)
  8. message(WARNING "Puzzle icons cannot be rebuilt (did not find Perl)")
  9. set(build_icons FALSE)
  10. return()
  11. endif()
  12. find_program(CONVERT convert)
  13. find_program(IDENTIFY identify)
  14. if(NOT CONVERT OR NOT IDENTIFY)
  15. message(WARNING "Puzzle icons cannot be rebuilt (did not find ImageMagick)")
  16. set(build_icons FALSE)
  17. return()
  18. endif()
  19. # For puzzles which have animated moves, it's nice to show the sample
  20. # image part way through the animation of a move. This setting will
  21. # cause a 'redo' action immediately after loading the save file,
  22. # causing the first undone move in the undo chain to be redone, and
  23. # then it will stop this far through the move animation to take the
  24. # screenshot.
  25. set(cube_redo 0.15)
  26. set(fifteen_redo 0.3)
  27. set(flip_redo 0.3)
  28. set(netslide_redo 0.3)
  29. set(sixteen_redo 0.3)
  30. set(twiddle_redo 0.3)
  31. # For many puzzles, we'd prefer that the icon zooms in on a couple of
  32. # squares of the playing area rather than trying to show the whole of
  33. # a game. These settings configure that. Each one indicates the
  34. # expected full size of the screenshot image, followed by the area we
  35. # want to crop to.
  36. #
  37. # (The expected full size is a safety precaution: if a puzzle changes
  38. # its default display size, then that won't match, and we'll get a
  39. # build error here rather than silently continuing to take the wrong
  40. # subrectangle of the resized puzzle display.)
  41. set(blackbox_crop 352x352 144x144+0+208)
  42. set(bridges_crop 264x264 107x107+157+157)
  43. set(dominosa_crop 304x272 152x152+152+0)
  44. set(fifteen_crop 240x240 120x120+0+120)
  45. set(filling_crop 256x256 133x133+14+78)
  46. set(flip_crop 288x288 145x145+120+72)
  47. set(galaxies_crop 288x288 165x165+0+0)
  48. set(guess_crop 263x420 178x178+75+17)
  49. set(inertia_crop 321x321 128x128+193+0)
  50. set(keen_crop 288x288 96x96+24+120)
  51. set(lightup_crop 256x256 112x112+144+0)
  52. set(loopy_crop 257x257 113x113+0+0)
  53. set(magnets_crop 264x232 96x96+36+100)
  54. set(mines_crop 240x240 110x110+130+130)
  55. set(mosaic_crop 288x288 97x97+142+78)
  56. set(net_crop 193x193 113x113+0+80)
  57. set(netslide_crop 289x289 144x144+0+0)
  58. set(palisade_crop 288x288 192x192+0+0)
  59. set(pattern_crop 384x384 223x223+0+0)
  60. set(pearl_crop 216x216 94x94+108+15)
  61. set(pegs_crop 263x263 147x147+116+0)
  62. set(range_crop 256x256 98x98+111+15)
  63. set(rect_crop 205x205 115x115+90+0)
  64. set(signpost_crop 240x240 98x98+23+23)
  65. set(singles_crop 224x224 98x98+15+15)
  66. set(sixteen_crop 288x288 144x144+144+144)
  67. set(slant_crop 321x321 160x160+160+160)
  68. set(solo_crop 481x481 145x145+24+24)
  69. set(tents_crop 320x320 165x165+142+0)
  70. set(towers_crop 300x300 102x102+151+6)
  71. set(tracks_crop 246x246 118x118+6+6)
  72. set(twiddle_crop 192x192 102x102+69+21)
  73. set(undead_crop 416x480 192x192+16+80)
  74. set(unequal_crop 208x208 104x104+104+104)
  75. set(untangle_crop 320x320 164x164+3+116)
  76. add_custom_target(icons)
  77. # All sizes of icon we make for any purpose.
  78. set(all_icon_sizes 128 96 88 64 48 44 32 24 16)
  79. # Sizes of icon we put into the Windows .ico files.
  80. set(win_icon_sizes 48 32 16)
  81. # Border thickness for each icon size.
  82. set(border_128 8)
  83. set(border_96 4)
  84. set(border_88 4)
  85. set(border_64 4)
  86. set(border_48 4)
  87. set(border_44 4)
  88. set(border_32 2)
  89. set(border_24 1)
  90. set(border_16 1)
  91. set(icon_srcdir ${CMAKE_SOURCE_DIR}/icons)
  92. set(icon_bindir ${CMAKE_BINARY_DIR}/icons)
  93. # We'll need to point $SGT_PUZZLES_DIR at an empty directory, to avoid
  94. # the icons reflecting the building user's display preferences.
  95. set(empty_config_dir ${CMAKE_BINARY_DIR}/icons/config)
  96. function(build_icon name)
  97. set(output_icon_files)
  98. # Compile the GTK puzzle binary without an icon, so that we can run
  99. # it to generate a screenshot to make the icon out of.
  100. add_executable(${NAME}-icon-maker ${NAME}.c
  101. ${CMAKE_SOURCE_DIR}/no-icon.c)
  102. target_link_libraries(${NAME}-icon-maker
  103. common ${platform_gui_libs} ${platform_libs})
  104. set_target_properties(${NAME}-icon-maker PROPERTIES
  105. RUNTIME_OUTPUT_DIRECTORY ${icon_bindir})
  106. # Now run that binary to generate a screenshot of the puzzle in
  107. # play, which will be the base image we make everything else out
  108. # out.
  109. if(DEFINED ${name}_redo)
  110. set(redo_arg --redo ${${name}_redo})
  111. else()
  112. set(redo_arg)
  113. endif()
  114. add_custom_command(OUTPUT ${icon_bindir}/${name}-base.png
  115. COMMAND ${CMAKE_COMMAND} -E make_directory ${empty_config_dir}
  116. COMMAND ${CMAKE_COMMAND} -E env
  117. ASAN_OPTIONS=detect_leaks=0
  118. SGT_PUZZLES_DIR=${empty_config_dir}
  119. ${icon_bindir}/${name}-icon-maker
  120. ${redo_arg}
  121. --screenshot ${icon_bindir}/${name}-base.png
  122. --load ${icon_srcdir}/${name}.sav
  123. DEPENDS
  124. ${name}-icon-maker ${icon_srcdir}/${name}.sav)
  125. # Shrink it to a fixed-size square image for the web page,
  126. # trimming boring border parts of the original image in the
  127. # process. Done by square.pl.
  128. add_custom_command(OUTPUT ${icon_bindir}/${name}-web.png
  129. COMMAND ${PERL_EXECUTABLE} ${icon_srcdir}/square.pl
  130. ${CONVERT} 150 5
  131. ${icon_bindir}/${name}-base.png
  132. ${icon_bindir}/${name}-web.png
  133. DEPENDS
  134. ${icon_srcdir}/square.pl
  135. ${icon_bindir}/${name}-base.png)
  136. list(APPEND output_icon_files ${icon_bindir}/${name}-web.png)
  137. # Shrink differently to an oblong for the KaiStore marketing
  138. # banner. This is dimmed behind the name of the application, so put
  139. # it at a jaunty angle to avoid unfortunate interactions with the
  140. # text.
  141. add_custom_command(OUTPUT ${icon_bindir}/${name}-banner.jpg
  142. COMMAND ${CONVERT} ${icon_bindir}/${name}-base.png
  143. -crop 1:1+0+0 -rotate -10 +repage -shave 13% -resize 240 -crop x130+0+0
  144. ${icon_bindir}/${name}-banner.jpg
  145. DEPENDS ${icon_bindir}/${name}-base.png)
  146. list(APPEND output_icon_files ${icon_bindir}/${name}-banner.jpg)
  147. # Make the base image for all the icons, by cropping out the most
  148. # interesting part of the whole screenshot.
  149. add_custom_command(OUTPUT ${icon_bindir}/${name}-ibase.png
  150. COMMAND ${icon_srcdir}/crop.sh
  151. ${IDENTIFY} ${CONVERT}
  152. ${icon_bindir}/${name}-base.png
  153. ${icon_bindir}/${name}-ibase.png
  154. ${${name}_crop}
  155. DEPENDS
  156. ${icon_srcdir}/crop.sh
  157. ${icon_bindir}/${name}-base.png)
  158. # Coerce that base image down to colour depth of 4 bits, using the
  159. # fixed 16-colour Windows palette. We do this before shrinking the
  160. # image, because I've found that gives better results than just
  161. # doing it after.
  162. add_custom_command(OUTPUT ${icon_bindir}/${name}-ibase4.png
  163. COMMAND ${CONVERT}
  164. -colors 16
  165. +dither
  166. -set colorspace RGB
  167. -map ${icon_srcdir}/win16pal.xpm
  168. ${icon_bindir}/${name}-ibase.png
  169. ${icon_bindir}/${name}-ibase4.png
  170. DEPENDS
  171. ${icon_srcdir}/win16pal.xpm
  172. ${icon_bindir}/${name}-ibase.png)
  173. foreach(size ${all_icon_sizes})
  174. # Make a 24-bit icon image at each size, by shrinking the base
  175. # icon image.
  176. add_custom_command(OUTPUT ${icon_bindir}/${name}-${size}d24.png
  177. COMMAND ${PERL_EXECUTABLE} ${icon_srcdir}/square.pl
  178. ${CONVERT} ${size} ${border_${size}}
  179. ${icon_bindir}/${name}-ibase.png
  180. ${icon_bindir}/${name}-${size}d24.png
  181. DEPENDS
  182. ${icon_srcdir}/square.pl
  183. ${icon_bindir}/${name}-ibase.png)
  184. list(APPEND output_icon_files ${icon_bindir}/${name}-${size}d24.png)
  185. # And reduce the colour depth of that one to make an 8-bit
  186. # version.
  187. add_custom_command(OUTPUT ${icon_bindir}/${name}-${size}d8.png
  188. COMMAND ${CONVERT}
  189. -colors 256
  190. ${icon_bindir}/${name}-${size}d24.png
  191. ${icon_bindir}/${name}-${size}d8.png
  192. DEPENDS ${icon_bindir}/${name}-${size}d24.png)
  193. list(APPEND output_icon_files ${icon_bindir}/${name}-${size}d8.png)
  194. endforeach()
  195. foreach(size ${win_icon_sizes})
  196. # 4-bit icons are only needed for Windows. We make each one by
  197. # first shrinking the large 4-bit image we made above ...
  198. add_custom_command(OUTPUT ${icon_bindir}/${name}-${size}d4pre.png
  199. COMMAND ${PERL_EXECUTABLE} ${icon_srcdir}/square.pl
  200. ${CONVERT} ${size} ${border_${size}}
  201. ${icon_bindir}/${name}-ibase4.png
  202. ${icon_bindir}/${name}-${size}d4pre.png
  203. DEPENDS
  204. ${icon_srcdir}/square.pl
  205. ${icon_bindir}/${name}-ibase4.png)
  206. # ... and then re-coercing the output back to 16 colours, since
  207. # that shrink operation will have introduced intermediate colour
  208. # values again.
  209. add_custom_command(OUTPUT ${icon_bindir}/${name}-${size}d4.png
  210. COMMAND ${CONVERT}
  211. -colors 16
  212. +dither
  213. -set colorspace RGB
  214. -map ${icon_srcdir}/win16pal.xpm
  215. ${icon_bindir}/${name}-${size}d4pre.png
  216. ${icon_bindir}/${name}-${size}d4.png
  217. DEPENDS ${icon_bindir}/${name}-${size}d4pre.png)
  218. list(APPEND output_icon_files ${icon_bindir}/${name}-${size}d4.png)
  219. endforeach()
  220. # Make the Windows icon.
  221. set(icon_pl_args)
  222. set(icon_pl_deps)
  223. foreach(depth 24 8 4)
  224. list(APPEND icon_pl_args -${depth})
  225. foreach(size ${win_icon_sizes})
  226. list(APPEND icon_pl_args ${icon_bindir}/${name}-${size}d${depth}.png)
  227. list(APPEND icon_pl_deps ${icon_bindir}/${name}-${size}d${depth}.png)
  228. endforeach()
  229. endforeach()
  230. add_custom_command(OUTPUT ${icon_bindir}/${name}.ico
  231. COMMAND ${PERL_EXECUTABLE} ${icon_srcdir}/icon.pl
  232. --convert=${CONVERT}
  233. ${icon_pl_args} > ${icon_bindir}/${name}.ico
  234. DEPENDS
  235. ${icon_srcdir}/icon.pl
  236. ${icon_pl_deps})
  237. list(APPEND output_icon_files ${icon_bindir}/${name}.ico)
  238. # Make a C source file containing XPMs of all the 24-bit images.
  239. set(cicon_pl_infiles)
  240. foreach(size ${all_icon_sizes})
  241. list(APPEND cicon_pl_infiles ${icon_bindir}/${name}-${size}d24.png)
  242. endforeach()
  243. add_custom_command(OUTPUT ${icon_bindir}/${name}-icon.c
  244. COMMAND ${PERL_EXECUTABLE} ${icon_srcdir}/cicon.pl
  245. ${CONVERT} ${cicon_pl_infiles} > ${icon_bindir}/${name}-icon.c
  246. DEPENDS
  247. ${icon_srcdir}/cicon.pl
  248. ${cicon_pl_infiles})
  249. list(APPEND output_icon_files ${icon_bindir}/${name}-icon.c)
  250. # Make the KaiOS icons, which have rounded corners and shadows
  251. # https://developer.kaiostech.com/docs/design-guide/launcher-icon
  252. foreach(size 56 112)
  253. math(EXPR srciconsize "${size} * 44 / 56")
  254. math(EXPR borderwidth "(${size} - ${srciconsize}) / 2")
  255. math(EXPR cornerradius "${size} * 5 / 56")
  256. math(EXPR sizeminusone "${srciconsize} - 1")
  257. math(EXPR shadowspread "${size} * 4 / 56")
  258. math(EXPR shadowoffset "${size} * 2 / 56")
  259. add_custom_command(OUTPUT ${icon_bindir}/${name}-${size}kai.png
  260. COMMAND ${CONVERT}
  261. ${icon_bindir}/${name}-${srciconsize}d24.png
  262. -alpha Opaque
  263. "\\(" -size ${srciconsize}x${srciconsize} -depth 8 canvas:none
  264. -draw "roundRectangle 0,0,${sizeminusone},${sizeminusone},${cornerradius},${cornerradius}" "\\)"
  265. -compose dst-in -composite
  266. -compose over -bordercolor transparent -border ${borderwidth}
  267. "\\(" +clone -background black
  268. -shadow 30x${shadowspread}+0+${shadowoffset} "\\)"
  269. +swap -background none -flatten -crop '${size}x${size}+0+0!' -depth 8
  270. ${icon_bindir}/${name}-${size}kai.png
  271. DEPENDS ${icon_bindir}/${name}-${srciconsize}d24.png)
  272. list(APPEND output_icon_files ${icon_bindir}/${name}-${size}kai.png)
  273. endforeach()
  274. add_custom_target(${name}-icons DEPENDS ${output_icon_files})
  275. add_dependencies(icons ${name}-icons)
  276. endfunction()