1 /* halftone.c
2 
3    Last modified: 2021.02.20
4 */
5 
6 
7 /* Inclusion of header files: */
8 /* -------------------------- */
9 
10 #include <stdio.h>
11 #include <string.h>
12 #include <libintl.h>
13 #include <math.h>
14 
15 #include "tp_magic_api.h"
16 #include "SDL_image.h"
17 #include "SDL_mixer.h"
18 
19 enum
20 {
21   TOOL_HALFTONE,
22   NUM_TOOLS
23 };
24 
25 
26 const char *snd_filenames[NUM_TOOLS] = {
27   "halftone.ogg",
28 };
29 
30 const char *icon_filenames[NUM_TOOLS] = {
31   "halftone.png",
32 };
33 
34 const char *names[NUM_TOOLS] = {
35   gettext_noop("Halftone"),
36 };
37 
38 const char *descs[NUM_TOOLS] = {
39   gettext_noop("Click and drag to turn your drawing into a newspaper."),
40 };
41 
42 Mix_Chunk *snd_effect[NUM_TOOLS];
43 
44 static SDL_Surface *canvas_backup, *square;
45 
46 /* Function Prototypes: */
47 
48 void halftone_drag(magic_api * api, int which, SDL_Surface * canvas,
49                    SDL_Surface * snapshot, int ox, int oy, int x, int y, SDL_Rect * update_rect);
50 void halftone_line_callback(void *ptr, int which, SDL_Surface * canvas, SDL_Surface * snapshot, int x, int y);
51 Uint32 halftone_api_version(void);
52 int halftone_init(magic_api * api);
53 int halftone_get_tool_count(magic_api * api);
54 SDL_Surface *halftone_get_icon(magic_api * api, int which);
55 char *halftone_get_name(magic_api * api, int which);
56 char *halftone_get_description(magic_api * api, int which, int mode);
57 int halftone_requires_colors(magic_api * api, int which);
58 int halftone_modes(magic_api * api, int which);
59 void halftone_shutdown(magic_api * api);
60 void halftone_click(magic_api * api, int which, int mode,
61                     SDL_Surface * canvas, SDL_Surface * snapshot, int x, int y, SDL_Rect * update_rect);
62 void halftone_release(magic_api * api, int which,
63                       SDL_Surface * canvas, SDL_Surface * snapshot, int x, int y, SDL_Rect * update_rect);
64 void halftone_set_color(magic_api * api, Uint8 r, Uint8 g, Uint8 b);
65 void halftone_switchin(magic_api * api, int which, int mode, SDL_Surface * canvas);
66 void halftone_switchout(magic_api * api, int which, int mode, SDL_Surface * canvas);
67 void halftone_rgb2cmyk(Uint8 r, Uint8 g, Uint8 b, float cmyk[]);
68 
halftone_api_version(void)69 Uint32 halftone_api_version(void)
70 {
71   return (TP_MAGIC_API_VERSION);
72 }
73 
halftone_init(magic_api * api)74 int halftone_init(magic_api * api)
75 {
76   int i;
77   char fname[1024];
78 
79   canvas_backup = NULL;
80   square = NULL;
81 
82   for (i = 0; i < NUM_TOOLS; i++)
83     {
84       snprintf(fname, sizeof(fname), "%s/sounds/magic/%s", api->data_directory, snd_filenames[i]);
85 
86       snd_effect[i] = Mix_LoadWAV(fname);
87 /*
88       if (snd_effect[i] == NULL)
89         {
90           SDL_FreeSurface(canvas_backup);
91           SDL_FreeSurface(square);
92           return (0);
93         }
94 */
95     }
96 
97 
98   return (1);
99 }
100 
halftone_get_tool_count(magic_api * api ATTRIBUTE_UNUSED)101 int halftone_get_tool_count(magic_api * api ATTRIBUTE_UNUSED)
102 {
103   return (NUM_TOOLS);
104 }
105 
halftone_get_icon(magic_api * api,int which)106 SDL_Surface *halftone_get_icon(magic_api * api, int which)
107 {
108   char fname[1024];
109 
110   snprintf(fname, sizeof(fname), "%s/images/magic/%s", api->data_directory, icon_filenames[which]);
111 
112   return (IMG_Load(fname));
113 }
114 
halftone_get_name(magic_api * api ATTRIBUTE_UNUSED,int which)115 char *halftone_get_name(magic_api * api ATTRIBUTE_UNUSED, int which)
116 {
117   const char *our_name_english;
118   const char *our_name_localized;
119 
120   our_name_english = names[which];
121   our_name_localized = gettext(our_name_english);
122 
123   return (strdup(our_name_localized));
124 }
125 
halftone_get_description(magic_api * api ATTRIBUTE_UNUSED,int which,int mode ATTRIBUTE_UNUSED)126 char *halftone_get_description(magic_api * api ATTRIBUTE_UNUSED, int which, int mode ATTRIBUTE_UNUSED)
127 {
128   const char *our_desc_english;
129   const char *our_desc_localized;
130 
131   our_desc_english = descs[which];
132   our_desc_localized = gettext(our_desc_english);
133 
134   return (strdup(our_desc_localized));
135 }
136 
halftone_requires_colors(magic_api * api ATTRIBUTE_UNUSED,int which ATTRIBUTE_UNUSED)137 int halftone_requires_colors(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED)
138 {
139   return 0;
140 }
141 
halftone_modes(magic_api * api ATTRIBUTE_UNUSED,int which ATTRIBUTE_UNUSED)142 int halftone_modes(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED)
143 {
144   return MODE_PAINT;
145 }
146 
halftone_shutdown(magic_api * api ATTRIBUTE_UNUSED)147 void halftone_shutdown(magic_api * api ATTRIBUTE_UNUSED)
148 {
149   int i;
150 
151   for (i = 0; i < NUM_TOOLS; i++) {
152     if (snd_effect[i] != NULL) {
153       Mix_FreeChunk(snd_effect[i]);
154     }
155   }
156 
157   SDL_FreeSurface(canvas_backup);
158   SDL_FreeSurface(square);
159 }
160 
halftone_click(magic_api * api,int which,int mode ATTRIBUTE_UNUSED,SDL_Surface * canvas,SDL_Surface * snapshot,int x,int y,SDL_Rect * update_rect)161 void halftone_click(magic_api * api, int which, int mode ATTRIBUTE_UNUSED,
162                     SDL_Surface * canvas, SDL_Surface * snapshot, int x, int y, SDL_Rect * update_rect)
163 {
164   halftone_drag(api, which, canvas, snapshot, x, y, x, y, update_rect);
165 }
166 
halftone_drag(magic_api * api,int which,SDL_Surface * canvas,SDL_Surface * snapshot,int ox,int oy,int x,int y,SDL_Rect * update_rect)167 void halftone_drag(magic_api * api, int which, SDL_Surface * canvas,
168                    SDL_Surface * snapshot, int ox, int oy, int x, int y, SDL_Rect * update_rect)
169 {
170   api->line((void *)api, which, canvas, snapshot, ox, oy, x, y, 4, halftone_line_callback);
171 
172   if (ox > x)
173     {
174       int tmp = ox;
175 
176       ox = x;
177       x = tmp;
178     }
179   if (oy > y)
180     {
181       int tmp = oy;
182 
183       oy = y;
184       y = tmp;
185     }
186 
187   update_rect->x = ox - 16;
188   update_rect->y = oy - 16;
189   update_rect->w = (x + 16) - update_rect->x;
190   update_rect->h = (y + 16) - update_rect->h;
191 
192   api->playsound(snd_effect[which], (x * 255) / canvas->w,      // pan
193                  255);          // distance
194 }
195 
196 enum
197 {
198   CHAN_CYAN,
199   CHAN_MAGENTA,
200   CHAN_YELLOW,
201   CHAN_BLACK,
202   NUM_CHANS
203 };
204 
205 Uint8 chan_colors[NUM_CHANS][3] = {
206   {0, 255, 255},
207   {255, 0, 255},
208   {255, 255, 0},
209   {0, 0, 0}
210 };
211 
212 int chan_angles[NUM_CHANS] = {
213   100,
214   15,
215   0,
216   45
217 };
218 
halftone_release(magic_api * api ATTRIBUTE_UNUSED,int which ATTRIBUTE_UNUSED,SDL_Surface * canvas ATTRIBUTE_UNUSED,SDL_Surface * snapshot ATTRIBUTE_UNUSED,int x ATTRIBUTE_UNUSED,int y ATTRIBUTE_UNUSED,SDL_Rect * update_rect ATTRIBUTE_UNUSED)219 void halftone_release(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED,
220                       SDL_Surface * canvas ATTRIBUTE_UNUSED, SDL_Surface * snapshot ATTRIBUTE_UNUSED,
221                       int x ATTRIBUTE_UNUSED, int y ATTRIBUTE_UNUSED, SDL_Rect * update_rect ATTRIBUTE_UNUSED)
222 {
223 }
224 
halftone_set_color(magic_api * api ATTRIBUTE_UNUSED,Uint8 r ATTRIBUTE_UNUSED,Uint8 g ATTRIBUTE_UNUSED,Uint8 b ATTRIBUTE_UNUSED)225 void halftone_set_color(magic_api * api ATTRIBUTE_UNUSED, Uint8 r ATTRIBUTE_UNUSED,
226                         Uint8 g ATTRIBUTE_UNUSED, Uint8 b ATTRIBUTE_UNUSED)
227 {
228 }
229 
halftone_line_callback(void * ptr,int which ATTRIBUTE_UNUSED,SDL_Surface * canvas,SDL_Surface * snapshot ATTRIBUTE_UNUSED,int x,int y)230 void halftone_line_callback(void *ptr, int which ATTRIBUTE_UNUSED,
231                             SDL_Surface * canvas, SDL_Surface * snapshot ATTRIBUTE_UNUSED, int x, int y)
232 {
233   Uint8 r, g, b, or, og, ob;
234   Uint32 total_r, total_g, total_b;
235   Uint32 pixel;
236   int xx, yy, xxx, yyy, channel, ox, oy, sqx, sqy;
237   SDL_Rect dest;
238   magic_api *api = (magic_api *) ptr;
239   float cmyk[4];
240 
241   pixel = SDL_MapRGB(square->format, 255, 255, 255);
242   SDL_FillRect(square, NULL, pixel);
243 
244   /* Lock to grid, centered around mouse */
245   x = ((x / 8) - 1) * 8;
246   y = ((y / 8) - 1) * 8;
247 
248   if (api->touched(x, y))
249     {
250       return;
251     }
252 
253   for (xx = 0; xx < 16; xx = xx + 4)
254     {
255       for (yy = 0; yy < 16; yy = yy + 4)
256         {
257           /* Get avg color around the mouse */
258           total_r = total_g = total_b = 0;
259           for (xxx = 0; xxx < 4; xxx++)
260             {
261               for (yyy = 0; yyy < 4; yyy++)
262                 {
263                   SDL_GetRGB(api->getpixel(canvas_backup, x + xx + xxx, y + yy + yyy), canvas_backup->format, &r, &g,
264                              &b);
265                   total_r += r;
266                   total_g += g;
267                   total_b += b;
268                 }
269             }
270           total_r /= 16;
271           total_g /= 16;
272           total_b /= 16;
273 
274           /* Convert to CMYK values */
275           halftone_rgb2cmyk(total_r, total_g, total_b, cmyk);
276 
277           /* Draw C, M, Y and K blobs into our 'square' surface */
278           for (channel = 0; channel < NUM_CHANS; channel++)
279             {
280               r = chan_colors[channel][0];
281               g = chan_colors[channel][1];
282               b = chan_colors[channel][2];
283 
284               for (xxx = 0; xxx < 8; xxx++)
285                 {
286                   for (yyy = 0; yyy < 8; yyy++)
287                     {
288                       /* A circle blob, radius based upon channel (C, M, Y or K) strength for this color */
289 
290                       /* FIXME: Base it upon this channel's angle! -bjk 2011.07.17 */
291                       ox = xxx;
292                       oy = yyy;
293 
294                       sqx = (xx + ox) % 16;
295                       sqy = (yy + oy) % 16;
296 
297                       if (api->in_circle(xxx - 4, yyy - 4, cmyk[channel] * 6.0))
298                         {
299                           SDL_GetRGB(api->getpixel(square, sqx, sqy), square->format, &or, &og, &ob);
300 
301                           if (or == 255 && og == 255 && ob == 255)
302                             {
303                               /* If it's just white, put full color down */
304                               pixel = SDL_MapRGB(square->format, r, g, b);
305                             }
306                           else
307                             {
308                               /* Otherwise, blend a little */
309                               pixel = SDL_MapRGB(square->format, (r + or) / 2, (g + og) / 2, (b + ob) / 2);
310                             }
311 
312                           api->putpixel(square, sqx, sqy, pixel);
313                         }
314                     }
315                 }
316             }
317         }
318     }
319 
320   dest.x = x;
321   dest.y = y;
322 
323   SDL_BlitSurface(square, NULL, canvas, &dest);
324 }
325 
halftone_switchin(magic_api * api,int which ATTRIBUTE_UNUSED,int mode ATTRIBUTE_UNUSED,SDL_Surface * canvas)326 void halftone_switchin(magic_api * api, int which ATTRIBUTE_UNUSED, int mode ATTRIBUTE_UNUSED, SDL_Surface * canvas)
327 {
328   if (canvas_backup == NULL)
329     canvas_backup =
330       SDL_CreateRGBSurface(SDL_ANYFORMAT, api->canvas_w, api->canvas_h, canvas->format->BitsPerPixel,
331                            canvas->format->Rmask, canvas->format->Gmask, canvas->format->Bmask, canvas->format->Amask);
332 
333   if (square == NULL)
334     square =
335       SDL_CreateRGBSurface(SDL_ANYFORMAT, 16, 16, canvas->format->BitsPerPixel, canvas->format->Rmask,
336                            canvas->format->Gmask, canvas->format->Bmask, canvas->format->Amask);
337 
338   /* FIXME: What to do if they come back NULL!? :( */
339 
340   SDL_BlitSurface(canvas, NULL, canvas_backup, NULL);
341 }
342 
halftone_switchout(magic_api * api ATTRIBUTE_UNUSED,int which ATTRIBUTE_UNUSED,int mode ATTRIBUTE_UNUSED,SDL_Surface * canvas ATTRIBUTE_UNUSED)343 void halftone_switchout(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED,
344                         int mode ATTRIBUTE_UNUSED, SDL_Surface * canvas ATTRIBUTE_UNUSED)
345 {
346 }
347 
halftone_rgb2cmyk(Uint8 r,Uint8 g,Uint8 b,float cmyk[])348 void halftone_rgb2cmyk(Uint8 r, Uint8 g, Uint8 b, float cmyk[])
349 {
350   float mincmy, c, m, y, k;
351 
352   /* Simple RGB to CMYK math (not worrying about color profiles, etc.),
353      based on math found at http://www.javascripter.net/faq/rgb2cmyk.htm
354      by Alexei Kourbatov <alexei@kourbatov.com> */
355 
356   if (r == 0 && g == 0 && b == 0)
357     {
358       /* Black */
359       c = 0.0;
360       m = 0.0;
361       y = 0.0;
362       k = 1.0;
363     }
364   else
365     {
366       c = 1.0 - (((float)r) / 255.0);
367       m = 1.0 - (((float)g) / 255.0);
368       y = 1.0 - (((float)b) / 255.0);
369 
370       mincmy = min(c, min(m, y));
371       c = (c - mincmy) / (1.0 - mincmy);
372       m = (m - mincmy) / (1.0 - mincmy);
373       y = (y - mincmy) / (1.0 - mincmy);
374       k = mincmy;
375     }
376 
377   cmyk[0] = c;
378   cmyk[1] = m;
379   cmyk[2] = y;
380   cmyk[3] = k;
381 }
382