1 /*
2  * Copyright © 2010 Kristian Høgsberg
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "config.h"
25 
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 #include <math.h>
32 #include <cairo.h>
33 
34 #include <wayland-client.h>
35 #include "window.h"
36 
37 struct smoke {
38 	struct display *display;
39 	struct window *window;
40 	struct widget *widget;
41 	int width, height;
42 	int current;
43 	struct { float *d, *u, *v; } b[2];
44 };
45 
diffuse(struct smoke * smoke,uint32_t time,float * source,float * dest)46 static void diffuse(struct smoke *smoke, uint32_t time,
47 		    float *source, float *dest)
48 {
49 	float *s, *d;
50 	int x, y, k, stride;
51 	float t, a = 0.0002;
52 
53 	stride = smoke->width;
54 
55 	for (k = 0; k < 5; k++) {
56 		for (y = 1; y < smoke->height - 1; y++) {
57 			s = source + y * stride;
58 			d = dest + y * stride;
59 			for (x = 1; x < smoke->width - 1; x++) {
60 				t = d[x - 1] + d[x + 1] +
61 					d[x - stride] + d[x + stride];
62 				d[x] = (s[x] + a * t) / (1 + 4 * a) * 0.995;
63 			}
64 		}
65 	}
66 }
67 
advect(struct smoke * smoke,uint32_t time,float * uu,float * vv,float * source,float * dest)68 static void advect(struct smoke *smoke, uint32_t time,
69 		   float *uu, float *vv, float *source, float *dest)
70 {
71 	float *s, *d;
72 	float *u, *v;
73 	int x, y, stride;
74 	int i, j;
75 	float px, py, fx, fy;
76 
77 	stride = smoke->width;
78 
79 	for (y = 1; y < smoke->height - 1; y++) {
80 		d = dest + y * stride;
81 		u = uu + y * stride;
82 		v = vv + y * stride;
83 
84 		for (x = 1; x < smoke->width - 1; x++) {
85 			px = x - u[x];
86 			py = y - v[x];
87 			if (px < 0.5)
88 				px = 0.5;
89 			if (py < 0.5)
90 				py = 0.5;
91 			if (px > smoke->width - 1.5)
92 				px = smoke->width - 1.5;
93 			if (py > smoke->height - 1.5)
94 				py = smoke->height - 1.5;
95 			i = (int) px;
96 			j = (int) py;
97 			fx = px - i;
98 			fy = py - j;
99 			s = source + j * stride + i;
100 			d[x] = (s[0] * (1 - fx) + s[1] * fx) * (1 - fy) +
101 				(s[stride] * (1 - fx) + s[stride + 1] * fx) * fy;
102 		}
103 	}
104 }
105 
project(struct smoke * smoke,uint32_t time,float * u,float * v,float * p,float * div)106 static void project(struct smoke *smoke, uint32_t time,
107 		    float *u, float *v, float *p, float *div)
108 {
109 	int x, y, k, l, s;
110 	float h;
111 
112 	h = 1.0 / smoke->width;
113 	s = smoke->width;
114 	memset(p, 0, smoke->height * smoke->width);
115 	for (y = 1; y < smoke->height - 1; y++) {
116 		l = y * s;
117 		for (x = 1; x < smoke->width - 1; x++) {
118 			div[l + x] = -0.5 * h * (u[l + x + 1] - u[l + x - 1] +
119 						 v[l + x + s] - v[l + x - s]);
120 			p[l + x] = 0;
121 		}
122 	}
123 
124 	for (k = 0; k < 5; k++) {
125 		for (y = 1; y < smoke->height - 1; y++) {
126 			l = y * s;
127 			for (x = 1; x < smoke->width - 1; x++) {
128 				p[l + x] = (div[l + x] +
129 					    p[l + x - 1] +
130 					    p[l + x + 1] +
131 					    p[l + x - s] +
132 					    p[l + x + s]) / 4;
133 			}
134 		}
135 	}
136 
137 	for (y = 1; y < smoke->height - 1; y++) {
138 		l = y * s;
139 		for (x = 1; x < smoke->width - 1; x++) {
140 			u[l + x] -= 0.5 * (p[l + x + 1] - p[l + x - 1]) / h;
141 			v[l + x] -= 0.5 * (p[l + x + s] - p[l + x - s]) / h;
142 		}
143 	}
144 }
145 
render(struct smoke * smoke,cairo_surface_t * surface)146 static void render(struct smoke *smoke, cairo_surface_t *surface)
147 {
148 	unsigned char *dest;
149 	int x, y, width, height, stride;
150 	float *s;
151 	uint32_t *d, c, a;
152 
153 	dest = cairo_image_surface_get_data(surface);
154 	width = cairo_image_surface_get_width(surface);
155 	height = cairo_image_surface_get_height(surface);
156 	stride = cairo_image_surface_get_stride(surface);
157 
158 	for (y = 1; y < height - 1; y++) {
159 		s = smoke->b[smoke->current].d + y * smoke->height;
160 		d = (uint32_t *) (dest + y * stride);
161 		for (x = 1; x < width - 1; x++) {
162 			c = (int) (s[x] * 800);
163 			if (c > 255)
164 				c = 255;
165 			a = c;
166 			if (a < 0x33)
167 				a = 0x33;
168 			d[x] = (a << 24) | (c << 16) | (c << 8) | c;
169 		}
170 	}
171 }
172 
173 static void
redraw_handler(struct widget * widget,void * data)174 redraw_handler(struct widget *widget, void *data)
175 {
176 	struct smoke *smoke = data;
177 	uint32_t time = widget_get_last_time(smoke->widget);
178 	cairo_surface_t *surface;
179 
180 	diffuse(smoke, time / 30, smoke->b[0].u, smoke->b[1].u);
181 	diffuse(smoke, time / 30, smoke->b[0].v, smoke->b[1].v);
182 	project(smoke, time / 30,
183 		smoke->b[1].u, smoke->b[1].v,
184 		smoke->b[0].u, smoke->b[0].v);
185 	advect(smoke, time / 30,
186 	       smoke->b[1].u, smoke->b[1].v,
187 	       smoke->b[1].u, smoke->b[0].u);
188 	advect(smoke, time / 30,
189 	       smoke->b[1].u, smoke->b[1].v,
190 	       smoke->b[1].v, smoke->b[0].v);
191 	project(smoke, time / 30,
192 		smoke->b[0].u, smoke->b[0].v,
193 		smoke->b[1].u, smoke->b[1].v);
194 
195 	diffuse(smoke, time / 30, smoke->b[0].d, smoke->b[1].d);
196 	advect(smoke, time / 30,
197 	       smoke->b[0].u, smoke->b[0].v,
198 	       smoke->b[1].d, smoke->b[0].d);
199 
200 	surface = window_get_surface(smoke->window);
201 
202 	render(smoke, surface);
203 
204 	window_damage(smoke->window, 0, 0, smoke->width, smoke->height);
205 
206 	cairo_surface_destroy(surface);
207 
208 	widget_schedule_redraw(smoke->widget);
209 }
210 
211 static void
smoke_motion_handler(struct smoke * smoke,float x,float y)212 smoke_motion_handler(struct smoke *smoke, float x, float y)
213 {
214 	int i, i0, i1, j, j0, j1, k, d = 5;
215 
216 	if (x - d < 1)
217 		i0 = 1;
218 	else
219 		i0 = x - d;
220 	if (i0 + 2 * d > smoke->width - 1)
221 		i1 = smoke->width - 1;
222 	else
223 		i1 = i0 + 2 * d;
224 
225 	if (y - d < 1)
226 		j0 = 1;
227 	else
228 		j0 = y - d;
229 	if (j0 + 2 * d > smoke->height - 1)
230 		j1 = smoke->height - 1;
231 	else
232 		j1 = j0 + 2 * d;
233 
234 	for (i = i0; i < i1; i++)
235 		for (j = j0; j < j1; j++) {
236 			k = j * smoke->width + i;
237 			smoke->b[0].u[k] += 256 - (random() & 512);
238 			smoke->b[0].v[k] += 256 - (random() & 512);
239 			smoke->b[0].d[k] += 1;
240 		}
241 }
242 
243 static int
mouse_motion_handler(struct widget * widget,struct input * input,uint32_t time,float x,float y,void * data)244 mouse_motion_handler(struct widget *widget, struct input *input,
245 		     uint32_t time, float x, float y, void *data)
246 {
247 	smoke_motion_handler(data, x, y);
248 
249 	return CURSOR_HAND1;
250 }
251 
252 static void
touch_motion_handler(struct widget * widget,struct input * input,uint32_t time,int32_t id,float x,float y,void * data)253 touch_motion_handler(struct widget *widget, struct input *input,
254 		     uint32_t time, int32_t id, float x, float y, void *data)
255 {
256 	smoke_motion_handler(data, x, y);
257 }
258 
259 static void
resize_handler(struct widget * widget,int32_t width,int32_t height,void * data)260 resize_handler(struct widget *widget,
261 	       int32_t width, int32_t height, void *data)
262 {
263 	struct smoke *smoke = data;
264 
265 	/* Dont resize me */
266 	widget_set_size(smoke->widget, smoke->width, smoke->height);
267 }
268 
main(int argc,char * argv[])269 int main(int argc, char *argv[])
270 {
271 	struct timespec ts;
272 	struct smoke smoke;
273 	struct display *d;
274 	int size;
275 
276 	d = display_create(&argc, argv);
277 	if (d == NULL) {
278 		fprintf(stderr, "failed to create display: %m\n");
279 		return -1;
280 	}
281 
282 	smoke.width = 200;
283 	smoke.height = 200;
284 	smoke.display = d;
285 	smoke.window = window_create(d);
286 	smoke.widget = window_add_widget(smoke.window, &smoke);
287 	window_set_title(smoke.window, "smoke");
288 
289 	window_set_buffer_type(smoke.window, WINDOW_BUFFER_TYPE_SHM);
290 	clock_gettime(CLOCK_MONOTONIC, &ts);
291 	srandom(ts.tv_nsec);
292 
293 	smoke.current = 0;
294 	size = smoke.height * smoke.width;
295 	smoke.b[0].d = calloc(size, sizeof(float));
296 	smoke.b[0].u = calloc(size, sizeof(float));
297 	smoke.b[0].v = calloc(size, sizeof(float));
298 	smoke.b[1].d = calloc(size, sizeof(float));
299 	smoke.b[1].u = calloc(size, sizeof(float));
300 	smoke.b[1].v = calloc(size, sizeof(float));
301 
302 	widget_set_motion_handler(smoke.widget, mouse_motion_handler);
303 	widget_set_touch_motion_handler(smoke.widget, touch_motion_handler);
304 	widget_set_resize_handler(smoke.widget, resize_handler);
305 	widget_set_redraw_handler(smoke.widget, redraw_handler);
306 
307 	window_set_user_data(smoke.window, &smoke);
308 
309 	widget_schedule_resize(smoke.widget, smoke.width, smoke.height);
310 
311 	display_run(d);
312 
313 	widget_destroy(smoke.widget);
314 	window_destroy(smoke.window);
315 	display_destroy(d);
316 
317 	return 0;
318 }
319