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 <errno.h>
33 #include <cairo.h>
34
35 #include <wayland-client.h>
36 #include "window.h"
37
38 struct smoke {
39 struct display *display;
40 struct window *window;
41 struct widget *widget;
42 int width, height;
43 int current;
44 struct { float *d, *u, *v; } b[2];
45 };
46
diffuse(struct smoke * smoke,uint32_t time,float * source,float * dest)47 static void diffuse(struct smoke *smoke, uint32_t time,
48 float *source, float *dest)
49 {
50 float *s, *d;
51 int x, y, k, stride;
52 float t, a = 0.0002;
53
54 stride = smoke->width;
55
56 for (k = 0; k < 5; k++) {
57 for (y = 1; y < smoke->height - 1; y++) {
58 s = source + y * stride;
59 d = dest + y * stride;
60 for (x = 1; x < smoke->width - 1; x++) {
61 t = d[x - 1] + d[x + 1] +
62 d[x - stride] + d[x + stride];
63 d[x] = (s[x] + a * t) / (1 + 4 * a) * 0.995;
64 }
65 }
66 }
67 }
68
advect(struct smoke * smoke,uint32_t time,float * uu,float * vv,float * source,float * dest)69 static void advect(struct smoke *smoke, uint32_t time,
70 float *uu, float *vv, float *source, float *dest)
71 {
72 float *s, *d;
73 float *u, *v;
74 int x, y, stride;
75 int i, j;
76 float px, py, fx, fy;
77
78 stride = smoke->width;
79
80 for (y = 1; y < smoke->height - 1; y++) {
81 d = dest + y * stride;
82 u = uu + y * stride;
83 v = vv + y * stride;
84
85 for (x = 1; x < smoke->width - 1; x++) {
86 px = x - u[x];
87 py = y - v[x];
88 if (px < 0.5)
89 px = 0.5;
90 if (py < 0.5)
91 py = 0.5;
92 if (px > smoke->width - 1.5)
93 px = smoke->width - 1.5;
94 if (py > smoke->height - 1.5)
95 py = smoke->height - 1.5;
96 i = (int) px;
97 j = (int) py;
98 fx = px - i;
99 fy = py - j;
100 s = source + j * stride + i;
101 d[x] = (s[0] * (1 - fx) + s[1] * fx) * (1 - fy) +
102 (s[stride] * (1 - fx) + s[stride + 1] * fx) * fy;
103 }
104 }
105 }
106
project(struct smoke * smoke,uint32_t time,float * u,float * v,float * p,float * div)107 static void project(struct smoke *smoke, uint32_t time,
108 float *u, float *v, float *p, float *div)
109 {
110 int x, y, k, l, s;
111 float h;
112
113 h = 1.0 / smoke->width;
114 s = smoke->width;
115 memset(p, 0, smoke->height * smoke->width);
116 for (y = 1; y < smoke->height - 1; y++) {
117 l = y * s;
118 for (x = 1; x < smoke->width - 1; x++) {
119 div[l + x] = -0.5 * h * (u[l + x + 1] - u[l + x - 1] +
120 v[l + x + s] - v[l + x - s]);
121 p[l + x] = 0;
122 }
123 }
124
125 for (k = 0; k < 5; k++) {
126 for (y = 1; y < smoke->height - 1; y++) {
127 l = y * s;
128 for (x = 1; x < smoke->width - 1; x++) {
129 p[l + x] = (div[l + x] +
130 p[l + x - 1] +
131 p[l + x + 1] +
132 p[l + x - s] +
133 p[l + x + s]) / 4;
134 }
135 }
136 }
137
138 for (y = 1; y < smoke->height - 1; y++) {
139 l = y * s;
140 for (x = 1; x < smoke->width - 1; x++) {
141 u[l + x] -= 0.5 * (p[l + x + 1] - p[l + x - 1]) / h;
142 v[l + x] -= 0.5 * (p[l + x + s] - p[l + x - s]) / h;
143 }
144 }
145 }
146
render(struct smoke * smoke,cairo_surface_t * surface)147 static void render(struct smoke *smoke, cairo_surface_t *surface)
148 {
149 unsigned char *dest;
150 int x, y, width, height, stride;
151 float *s;
152 uint32_t *d, c, a;
153
154 dest = cairo_image_surface_get_data(surface);
155 width = cairo_image_surface_get_width(surface);
156 height = cairo_image_surface_get_height(surface);
157 stride = cairo_image_surface_get_stride(surface);
158
159 for (y = 1; y < height - 1; y++) {
160 s = smoke->b[smoke->current].d + y * smoke->height;
161 d = (uint32_t *) (dest + y * stride);
162 for (x = 1; x < width - 1; x++) {
163 c = (int) (s[x] * 800);
164 if (c > 255)
165 c = 255;
166 a = c;
167 if (a < 0x33)
168 a = 0x33;
169 d[x] = (a << 24) | (c << 16) | (c << 8) | c;
170 }
171 }
172 }
173
174 static void
redraw_handler(struct widget * widget,void * data)175 redraw_handler(struct widget *widget, void *data)
176 {
177 struct smoke *smoke = data;
178 uint32_t time = widget_get_last_time(smoke->widget);
179 cairo_surface_t *surface;
180
181 diffuse(smoke, time / 30, smoke->b[0].u, smoke->b[1].u);
182 diffuse(smoke, time / 30, smoke->b[0].v, smoke->b[1].v);
183 project(smoke, time / 30,
184 smoke->b[1].u, smoke->b[1].v,
185 smoke->b[0].u, smoke->b[0].v);
186 advect(smoke, time / 30,
187 smoke->b[1].u, smoke->b[1].v,
188 smoke->b[1].u, smoke->b[0].u);
189 advect(smoke, time / 30,
190 smoke->b[1].u, smoke->b[1].v,
191 smoke->b[1].v, smoke->b[0].v);
192 project(smoke, time / 30,
193 smoke->b[0].u, smoke->b[0].v,
194 smoke->b[1].u, smoke->b[1].v);
195
196 diffuse(smoke, time / 30, smoke->b[0].d, smoke->b[1].d);
197 advect(smoke, time / 30,
198 smoke->b[0].u, smoke->b[0].v,
199 smoke->b[1].d, smoke->b[0].d);
200
201 surface = window_get_surface(smoke->window);
202
203 render(smoke, surface);
204
205 cairo_surface_destroy(surface);
206
207 widget_schedule_redraw(smoke->widget);
208 }
209
210 static void
smoke_motion_handler(struct smoke * smoke,float x,float y)211 smoke_motion_handler(struct smoke *smoke, float x, float y)
212 {
213 int i, i0, i1, j, j0, j1, k, d = 5;
214
215 if (x - d < 1)
216 i0 = 1;
217 else
218 i0 = x - d;
219 if (i0 + 2 * d > smoke->width - 1)
220 i1 = smoke->width - 1;
221 else
222 i1 = i0 + 2 * d;
223
224 if (y - d < 1)
225 j0 = 1;
226 else
227 j0 = y - d;
228 if (j0 + 2 * d > smoke->height - 1)
229 j1 = smoke->height - 1;
230 else
231 j1 = j0 + 2 * d;
232
233 for (i = i0; i < i1; i++)
234 for (j = j0; j < j1; j++) {
235 k = j * smoke->width + i;
236 smoke->b[0].u[k] += 256 - (random() & 512);
237 smoke->b[0].v[k] += 256 - (random() & 512);
238 smoke->b[0].d[k] += 1;
239 }
240 }
241
242 static int
mouse_motion_handler(struct widget * widget,struct input * input,uint32_t time,float x,float y,void * data)243 mouse_motion_handler(struct widget *widget, struct input *input,
244 uint32_t time, float x, float y, void *data)
245 {
246 smoke_motion_handler(data, x, y);
247
248 return CURSOR_HAND1;
249 }
250
251 static void
touch_motion_handler(struct widget * widget,struct input * input,uint32_t time,int32_t id,float x,float y,void * data)252 touch_motion_handler(struct widget *widget, struct input *input,
253 uint32_t time, int32_t id, float x, float y, void *data)
254 {
255 smoke_motion_handler(data, x, y);
256 }
257
258 static void
resize_handler(struct widget * widget,int32_t width,int32_t height,void * data)259 resize_handler(struct widget *widget,
260 int32_t width, int32_t height, void *data)
261 {
262 struct smoke *smoke = data;
263
264 /* Don't resize me */
265 widget_set_size(smoke->widget, smoke->width, smoke->height);
266 }
267
main(int argc,char * argv[])268 int main(int argc, char *argv[])
269 {
270 struct timespec ts;
271 struct smoke smoke;
272 struct display *d;
273 int size;
274
275 d = display_create(&argc, argv);
276 if (d == NULL) {
277 fprintf(stderr, "failed to create display: %s\n",
278 strerror(errno));
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