1 /*
2  * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <time.h>
21 #include <math.h>
22 #include <cairo/cairo.h>
23 
rounded_rectangle(cairo_t * cr,double x,double y,double w,double h,double r)24 static void rounded_rectangle (cairo_t* cr, double x, double y, double w, double h, double r) {
25 	double degrees = M_PI / 180.0;
26 
27 	cairo_new_sub_path (cr);
28 	cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees);
29 	cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees);
30 	cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees);
31 	cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees);
32 	cairo_close_path (cr);
33 }
34 
random1()35 static float random1 () {
36 	return rand () / (float)RAND_MAX;
37 }
38 
random2()39 static float random2 () {
40 	return 1.f - 2.f * random1 ();
41 }
42 
make_box(cairo_t * cr,int w,int h)43 static void make_box (cairo_t* cr, int w, int h) {
44 	int b = 16;
45 	int t = 6;
46 	int r0 = 17;
47 	int r1 = 6;
48 
49 	// outer box (borders/shade)
50 	rounded_rectangle (cr, 0.5, 0.5, w - 1, h - 1, r0);
51 	cairo_set_source_rgba (cr, .93, .93, .93, 1.0);
52 	cairo_fill_preserve (cr);
53 	cairo_clip (cr);
54 
55 	// payload
56 	rounded_rectangle (cr, b, t, w - 2 * b, h - 2 * b, r1);
57 	cairo_set_source_rgba (cr, .95, .95, .95, 1.0);
58 	cairo_fill (cr);
59 
60 	cairo_pattern_t* pat;
61 
62 	// right side shadow
63 	pat = cairo_pattern_create_linear (0.0, 0.0, b, 0.0);
64 	cairo_pattern_add_color_stop_rgba (pat, 0.00, .9, .9, .9, 0.0);
65 	cairo_pattern_add_color_stop_rgba (pat, 0.20, .0, .0, .0, 0.1);
66 	cairo_pattern_add_color_stop_rgba (pat, 0.60, .0, .0, .0, 0.4);
67 	cairo_pattern_add_color_stop_rgba (pat, 1.00, .0, .0, .0, 0.6);
68 
69 	cairo_save (cr);
70 	cairo_translate (cr, w - b, 0);
71 	cairo_rectangle (cr, 0, 0, b, h);
72 	cairo_set_source (cr, pat);
73 	cairo_fill (cr);
74 	cairo_restore (cr);
75 	cairo_pattern_destroy (pat);
76 
77 	// bottom shadow
78 	int bb = 2 * b - t;
79 	pat = cairo_pattern_create_linear (0.0, 0.0, 0, bb);
80 	cairo_pattern_add_color_stop_rgba (pat, 0.00, .9, .9, .9, 0.0);
81 	cairo_pattern_add_color_stop_rgba (pat, 0.10, .0, .0, .0, 0.1);
82 	cairo_pattern_add_color_stop_rgba (pat, 0.30, .0, .0, .0, 0.4);
83 	cairo_pattern_add_color_stop_rgba (pat, 1.00, .0, .0, .0, 0.7);
84 
85 	cairo_save (cr);
86 	cairo_translate (cr, 0, h - bb);
87 	cairo_rectangle (cr, 0, 0, w, bb);
88 	cairo_set_source (cr, pat);
89 	cairo_fill (cr);
90 	cairo_restore (cr);
91 	cairo_pattern_destroy (pat);
92 
93 	// bottom edge
94 	pat = cairo_pattern_create_linear (0.0, 0.0, 0, bb);
95 	cairo_pattern_add_color_stop_rgba (pat, 0.00,  .0, .0, .0, 0.0);
96 	cairo_pattern_add_color_stop_rgba (pat, 0.20,  .0, .0, .0, 0.15);
97 	cairo_pattern_add_color_stop_rgba (pat, 0.30,  .0, .0, .0, 0.0);
98 	cairo_pattern_add_color_stop_rgba (pat, 1.00,  .0, .0, .0, 0.0);
99 
100 	cairo_save (cr);
101 	cairo_translate (cr, 0, h - bb - 4); // TODO unhardcode
102 	cairo_rectangle (cr, b + 1.5, 0, w - b, bb);
103 	cairo_set_source (cr, pat);
104 	cairo_fill (cr);
105 	cairo_restore (cr);
106 	cairo_pattern_destroy (pat);
107 
108 	// left light
109 	pat = cairo_pattern_create_linear (0.0, 0.0, b, 0.0);
110 	cairo_pattern_add_color_stop_rgba (pat, 0.00,  1, 1, 1, 0.9);
111 	cairo_pattern_add_color_stop_rgba (pat, 1.00,  1, 1, 1, 0.0);
112 
113 	cairo_rectangle (cr, 0, 0, b, h);
114 	cairo_set_source (cr, pat);
115 	cairo_fill (cr);
116 	cairo_pattern_destroy (pat);
117 
118 	// top light
119 	pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, t);
120 	cairo_pattern_add_color_stop_rgba (pat, 0.00,  1, 1, 1, 0.9);
121 	cairo_pattern_add_color_stop_rgba (pat, 1.00,  1, 1, 1, 0.0);
122 
123 	cairo_rectangle (cr, 0, 0, w, t);
124 	cairo_set_source (cr, pat);
125 	cairo_fill (cr);
126 	cairo_pattern_destroy (pat);
127 
128 	// top-edge highlight
129 	pat = cairo_pattern_create_linear (0.0, 0.0, 0, t);
130 	cairo_pattern_add_color_stop_rgba (pat, 0.00,  1, 1, 1, 0.0);
131 	cairo_pattern_add_color_stop_rgba (pat, 0.50,  1, 1, 1, 1.0);
132 	cairo_pattern_add_color_stop_rgba (pat, 1.00,  1, 1, 1, 0.0);
133 
134 	cairo_rectangle (cr, b + 0.5, 4, w - 2 * b - 1, t);
135 	cairo_set_source (cr, pat);
136 	cairo_fill (cr);
137 	cairo_pattern_destroy (pat);
138 
139 	// bottom left corner
140 	cairo_arc_negative (cr, 0.5 + r0, 0.5 + h - bb - r0 - 3, r0 + 3, M_PI, 0.5 * M_PI);
141 	cairo_set_line_width (cr, 3.0);
142 	cairo_set_source_rgba (cr, 0, 0, 0, 0.07);
143 	cairo_stroke (cr);
144 
145 	cairo_arc_negative (cr, 0.5 + r0, 0.5 + h - bb - r0 + 6, r0 + 1, M_PI, 0.5 * M_PI);
146 	cairo_set_line_width (cr, 3.0);
147 	cairo_set_source_rgba (cr, 0, 0, 0, 0.1);
148 	cairo_stroke (cr);
149 
150 	// bottom left corner shade
151 	cairo_arc (cr, 0.5 + r0, 0.5 + h - r0, r0, 0.5 * M_PI, M_PI);
152 	cairo_arc_negative (cr, 0.5 + r0, 0.5 + h - bb - r0, r0 + 4, M_PI, 0.5 * M_PI);
153 
154 	pat = cairo_pattern_create_linear (b, h - bb - r0, b * .5, h);
155 	cairo_pattern_add_color_stop_rgba (pat, 0.50,  1, 1, 1, 0.0);
156 	cairo_pattern_add_color_stop_rgba (pat, 1.00,  0, 0, 0, 0.4);
157 	cairo_set_source (cr, pat);
158 	cairo_fill (cr);
159 }
160 
scratch_pattern(float x,float y,float dx,float dy,float m)161 static cairo_pattern_t* scratch_pattern (float x, float y, float dx, float dy, float m) {
162 	int j;
163 	cairo_pattern_t* pat;
164 	pat = cairo_pattern_create_linear (x, y, x + dx, y + dy);
165 	cairo_pattern_add_color_stop_rgba (pat, 0.00, .9, .9, .9, 0.0);
166 	for (j = 1 ; j < 9; ++j) {
167 		cairo_pattern_add_color_stop_rgba (pat, j/10.f, .6, .6, .6, .01 + m * random1 ());
168 	}
169 	cairo_pattern_add_color_stop_rgba (pat, 1.00, .9, .9, .9, 0.0);
170 	return pat;
171 }
172 
173 
add_scratches(cairo_t * cr,int w,int h)174 static void add_scratches (cairo_t* cr, int w, int h) {
175 	int b = 16;
176 	int t = 6;
177 	int r1 = 6;
178 	rounded_rectangle (cr, b, t, w - 2 * b, h - 2 * b, r1);
179 	cairo_clip (cr);
180 
181 	float ww = w - b * 2;
182 	float hh = h - b * 2;
183 
184 	float ww2 = ww * .4;
185 	float hh2 = hh * .4;
186 	float ww4 = ww2 * .5;
187 	float hh4 = hh2 * .5;
188 
189 	cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
190 
191 	float maxlen = sqrt (ww2 * ww2 + hh2 * hh2);
192 	int deep = 0;
193 	fprintf (stderr, "D: %.1f\n", maxlen);
194 
195 	cairo_pattern_t* pat;
196 	int i, j;
197 
198 	for (i = 0; i < ceil (maxlen); ++i) {
199 		float x = b + ww * random1 ();
200 		float y = t + hh * random1 ();
201 		float dx = ww4 - ww2 * random1 ();
202 		float dy = hh4 - hh2 * random1 ();
203 
204 
205 		cairo_move_to (cr, x, y);
206 		if (random1 () > .5) {
207 			cairo_rel_line_to (cr, dx, dy);
208 		} else {
209 			cairo_rel_curve_to (cr, 0, 0, dx * .5 * random2 (), dy * .5 * random2 (), dx, dy);
210 		}
211 
212 		pat = scratch_pattern (x, y, dx, dy, .07);
213 		cairo_set_source (cr, pat);
214 		cairo_set_line_width (cr, 0.5 + 1.5 * random1 ());
215 		cairo_stroke (cr);
216 		cairo_pattern_destroy (pat);
217 	}
218 
219 	// short & deep
220 	for (i = 0; i < ceil (maxlen * .1); ++i) {
221 		float x = b + ww * random1 ();
222 		float y = t + hh * random1 ();
223 		float dx = .7 * ww4 * random2 ();
224 		float dy = .7 * ww4 * random2 ();
225 
226 		float len = sqrt (dx * dx + dy * dy);
227 
228 		pat = scratch_pattern (x, y, dx, dy, .2);
229 
230 		cairo_move_to (cr, x, y);
231 		if (random1 () > .5) {
232 			cairo_rel_line_to (cr, dx, dy);
233 		} else {
234 			cairo_rel_curve_to (cr, 0, 0, dx * .5 * random2 (), dy * .5 * random2 (), dx, dy);
235 		}
236 
237 		cairo_set_source (cr, pat);
238 		cairo_set_line_width (cr, 0.5 + 1.5 * random1 ());
239 
240 		if (len < maxlen * .04) {
241 			++deep;
242 			for (j = 0 ; j < 4; ++j) {
243 				cairo_stroke_preserve (cr);
244 				cairo_stroke_preserve (cr);
245 				cairo_pattern_destroy (pat);
246 				pat = scratch_pattern (x, y, dx, dy, .9);
247 				cairo_set_line_width (cr, 2.5 + 1.5 * random1 ());
248 			}
249 			cairo_stroke_preserve (cr);
250 		}
251 		cairo_stroke (cr);
252 		cairo_pattern_destroy (pat);
253 	}
254 	fprintf (stderr, "ADDED %d deep bump(s)\n", deep);
255 }
256 
main(int argc,char ** argv)257 int main (int argc, char **argv) {
258 	int n_steps = 8;
259 	int n_notes = 8;
260 	srand (time (NULL));
261 	char* fn = "../modgui/box.png";
262 
263 	if (argc > 1) {
264 	 fn = argv[1];
265 	}
266 	if (argc > 2) {
267 		n_steps = atoi (argv[2]);
268 	}
269 	if (argc > 3) {
270 		n_notes = atoi (argv[3]);
271 	}
272 
273 	/* see gridgen.sh */
274 	int w = 250 + 46 * n_steps;
275 	int h = 192 + 46 * n_notes;
276 	fprintf (stderr, "G: %d x %d F: %s\n", w, h, fn);
277 
278 	cairo_surface_t* cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
279 	cairo_t* cr = cairo_create (cs);
280 
281 	make_box (cr, w, h);
282 	add_scratches (cr, w, h);
283 
284 	cairo_surface_write_to_png (cs, fn);
285 	cairo_destroy (cr);
286 	cairo_surface_destroy (cs);
287 	return 0;
288 }
289