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