1/*
2 * Copyright © 2010 Intel Corporation
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 FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 *    Chris Wilson <chris@chris-wilson.co.uk>
25 *
26 */
27
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#include "sna.h"
33#include "sna_render.h"
34
35#define xFixedToDouble(f) pixman_fixed_to_double(f)
36
37bool
38sna_gradient_is_opaque(const PictGradient *gradient)
39{
40	int n;
41
42	for (n = 0; n < gradient->nstops; n++) {
43		 if (gradient->stops[n].color.alpha < 0xff00)
44			 return false;
45	}
46
47	return true;
48}
49
50static int
51sna_gradient_sample_width(PictGradient *gradient)
52{
53	int n, width;
54
55	width = 0;
56	for (n = 1; n < gradient->nstops; n++) {
57		xFixed dx = gradient->stops[n].x - gradient->stops[n-1].x;
58		int delta, max, ramp;
59
60		if (dx == 0)
61			return 1024;
62
63		max = gradient->stops[n].color.red -
64			gradient->stops[n-1].color.red;
65		if (max < 0)
66			max = -max;
67
68		delta = gradient->stops[n].color.green -
69			gradient->stops[n-1].color.green;
70		if (delta < 0)
71			delta = -delta;
72		if (delta > max)
73			max = delta;
74
75		delta = gradient->stops[n].color.blue -
76			gradient->stops[n-1].color.blue;
77		if (delta < 0)
78			delta = -delta;
79		if (delta > max)
80			max = delta;
81
82		delta = gradient->stops[n].color.alpha -
83			gradient->stops[n-1].color.alpha;
84		if (delta < 0)
85			delta = -delta;
86		if (delta > max)
87			max = delta;
88
89		ramp = 256 * max / dx;
90		if (ramp > width)
91			width = ramp;
92	}
93
94	if (width == 0)
95		return 1;
96
97	width = (width + 7) & -8;
98	return min(width, 1024);
99}
100
101static bool
102_gradient_color_stops_equal(PictGradient *pattern,
103			    struct sna_gradient_cache *cache)
104{
105    if (cache->nstops != pattern->nstops)
106	    return false;
107
108    return memcmp(cache->stops,
109		  pattern->stops,
110		  sizeof(PictGradientStop)*cache->nstops) == 0;
111}
112
113struct kgem_bo *
114sna_render_get_gradient(struct sna *sna,
115			PictGradient *pattern)
116{
117	struct sna_render *render = &sna->render;
118	struct sna_gradient_cache *cache;
119	pixman_image_t *gradient, *image;
120	pixman_point_fixed_t p1, p2;
121	int i, width;
122	struct kgem_bo *bo;
123
124	DBG(("%s: %dx[%f:%x ... %f:%x ... %f:%x]\n", __FUNCTION__,
125	     pattern->nstops,
126	     pattern->stops[0].x / 65536.,
127	     pattern->stops[0].color.alpha >> 8 << 24 |
128	     pattern->stops[0].color.red   >> 8 << 16 |
129	     pattern->stops[0].color.green >> 8 << 8 |
130	     pattern->stops[0].color.blue  >> 8 << 0,
131	     pattern->stops[pattern->nstops/2].x / 65536.,
132	     pattern->stops[pattern->nstops/2].color.alpha >> 8 << 24 |
133	     pattern->stops[pattern->nstops/2].color.red   >> 8 << 16 |
134	     pattern->stops[pattern->nstops/2].color.green >> 8 << 8 |
135	     pattern->stops[pattern->nstops/2].color.blue  >> 8 << 0,
136	     pattern->stops[pattern->nstops-1].x / 65536.,
137	     pattern->stops[pattern->nstops-1].color.alpha >> 8 << 24 |
138	     pattern->stops[pattern->nstops-1].color.red   >> 8 << 16 |
139	     pattern->stops[pattern->nstops-1].color.green >> 8 << 8 |
140	     pattern->stops[pattern->nstops-1].color.blue  >> 8 << 0));
141
142	for (i = 0; i < render->gradient_cache.size; i++) {
143		cache = &render->gradient_cache.cache[i];
144		if (_gradient_color_stops_equal(pattern, cache)) {
145			DBG(("%s: old --> %d\n", __FUNCTION__, i));
146			return kgem_bo_reference(cache->bo);
147		}
148	}
149
150	width = sna_gradient_sample_width(pattern);
151	DBG(("%s: sample width = %d\n", __FUNCTION__, width));
152	if (width == 0)
153		return NULL;
154
155	p1.x = 0;
156	p1.y = 0;
157	p2.x = width << 16;
158	p2.y = 0;
159
160	gradient = pixman_image_create_linear_gradient(&p1, &p2,
161						       (pixman_gradient_stop_t *)pattern->stops,
162						       pattern->nstops);
163	if (gradient == NULL)
164		return NULL;
165
166	pixman_image_set_filter(gradient, PIXMAN_FILTER_BILINEAR, NULL, 0);
167	pixman_image_set_repeat(gradient, PIXMAN_REPEAT_PAD);
168
169	image = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, 1, NULL, 0);
170	if (image == NULL) {
171		pixman_image_unref(gradient);
172		return NULL;
173	}
174
175	pixman_image_composite(PIXMAN_OP_SRC,
176			       gradient, NULL, image,
177			       0, 0,
178			       0, 0,
179			       0, 0,
180			       width, 1);
181	pixman_image_unref(gradient);
182
183	DBG(("%s: [0]=%x, [%d]=%x [%d]=%x\n", __FUNCTION__,
184	     pixman_image_get_data(image)[0],
185	     width/2, pixman_image_get_data(image)[width/2],
186	     width-1, pixman_image_get_data(image)[width-1]));
187
188	bo = kgem_create_linear(&sna->kgem, width*4, 0);
189	if (!bo) {
190		pixman_image_unref(image);
191		return NULL;
192	}
193
194	bo->pitch = 4*width;
195	kgem_bo_write(&sna->kgem, bo, pixman_image_get_data(image), 4*width);
196
197	pixman_image_unref(image);
198
199	if (render->gradient_cache.size < GRADIENT_CACHE_SIZE)
200		i = render->gradient_cache.size++;
201	else
202		i = rand () % GRADIENT_CACHE_SIZE;
203
204	cache = &render->gradient_cache.cache[i];
205	if (cache->nstops < pattern->nstops) {
206		PictGradientStop *newstops;
207
208		newstops = malloc(sizeof(PictGradientStop) * pattern->nstops);
209		if (newstops == NULL)
210			return bo;
211
212		free(cache->stops);
213		cache->stops = newstops;
214	}
215
216	memcpy(cache->stops, pattern->stops,
217	       sizeof(PictGradientStop) * pattern->nstops);
218	cache->nstops = pattern->nstops;
219
220	if (cache->bo)
221		kgem_bo_destroy(&sna->kgem, cache->bo);
222	cache->bo = kgem_bo_reference(bo);
223
224	return bo;
225}
226
227void
228sna_render_flush_solid(struct sna *sna)
229{
230	struct sna_solid_cache *cache = &sna->render.solid_cache;
231
232	DBG(("sna_render_flush_solid(size=%d)\n", cache->size));
233	assert(cache->dirty);
234	assert(cache->size);
235	assert(cache->size <= 1024);
236
237	kgem_bo_write(&sna->kgem, cache->cache_bo,
238		      cache->color, cache->size*sizeof(uint32_t));
239	cache->dirty = 0;
240}
241
242static void
243sna_render_finish_solid(struct sna *sna, bool force)
244{
245	struct sna_solid_cache *cache = &sna->render.solid_cache;
246	struct kgem_bo *old;
247	int i;
248
249	DBG(("sna_render_finish_solid(force=%d, domain=%d, busy=%d, dirty=%d, size=%d)\n",
250	     force, cache->cache_bo->domain, cache->cache_bo->rq != NULL, cache->dirty, cache->size));
251
252	if (!force && cache->cache_bo->domain != DOMAIN_GPU)
253		return;
254
255	if (cache->dirty)
256		sna_render_flush_solid(sna);
257
258	for (i = 0; i < cache->size; i++) {
259		if (cache->bo[i] == NULL)
260			continue;
261
262		kgem_bo_destroy(&sna->kgem, cache->bo[i]);
263		cache->bo[i] = NULL;
264	}
265
266	DBG(("sna_render_finish_solid reset\n"));
267	old = cache->cache_bo;
268	cache->cache_bo = kgem_create_linear(&sna->kgem, sizeof(cache->color), 0);
269	if (cache->cache_bo == NULL) {
270		cache->cache_bo = old;
271		old = NULL;
272	}
273
274	if (force)
275		cache->size = 0;
276	if (cache->last < cache->size) {
277		cache->bo[cache->last] = kgem_create_proxy(&sna->kgem, cache->cache_bo,
278							   cache->last*sizeof(uint32_t), sizeof(uint32_t));
279		if (cache->bo[cache->last])
280			cache->bo[cache->last]->pitch = 4;
281		else
282			cache->last = 1024;
283	}
284
285	if (old)
286		kgem_bo_destroy(&sna->kgem, old);
287}
288
289struct kgem_bo *
290sna_render_get_solid(struct sna *sna, uint32_t color)
291{
292	struct sna_solid_cache *cache = &sna->render.solid_cache;
293	int i;
294
295	DBG(("%s: %08x\n", __FUNCTION__, color));
296
297	if ((color & 0xffffff) == 0) /* alpha only */
298		return kgem_bo_reference(sna->render.alpha_cache.bo[color>>24]);
299
300	if (color == 0xffffffff) {
301		DBG(("%s(white)\n", __FUNCTION__));
302		return kgem_bo_reference(sna->render.alpha_cache.bo[255+7]);
303	}
304
305	if ((color >> 24) == 0xff) {
306		int v = 0;
307
308		if (((color >> 16) & 0xff) == 0)
309			v |= 0;
310		else if (((color >> 16) & 0xff) == 0xff)
311			v |= 1 << 2;
312		else
313			v = -1;
314
315		if (((color >> 8) & 0xff) == 0)
316			v |= 0;
317		else if (((color >> 8) & 0xff) == 0xff)
318			v |= 1 << 1;
319		else
320			v = -1;
321
322		if (((color >> 0) & 0xff) == 0)
323			v |= 0;
324		else if (((color >> 0) & 0xff) == 0xff)
325			v |= 1 << 0;
326		else
327			v = -1;
328
329		if (v >= 0) {
330			DBG(("%s(primary (%d,%d,%d): %d)\n",
331			     __FUNCTION__, v & 4, v & 2, v & 1, v));
332			return kgem_bo_reference(sna->render.alpha_cache.bo[255+v]);
333		}
334	}
335
336	if (cache->color[cache->last] == color) {
337		DBG(("sna_render_get_solid(%d) = %x (last)\n",
338		     cache->last, color));
339		return kgem_bo_reference(cache->bo[cache->last]);
340	}
341
342	for (i = 0; i < cache->size; i++) {
343		if (cache->color[i] == color) {
344			if (cache->bo[i] == NULL) {
345				DBG(("sna_render_get_solid(%d) = %x (recreate)\n",
346				     i, color));
347				goto create;
348			} else {
349				DBG(("sna_render_get_solid(%d) = %x (old)\n",
350				     i, color));
351				goto done;
352			}
353		}
354	}
355
356	sna_render_finish_solid(sna, i == ARRAY_SIZE(cache->color));
357
358	i = cache->size++;
359	assert(i < ARRAY_SIZE(cache->color));
360	cache->color[i] = color;
361	cache->dirty = 1;
362	DBG(("sna_render_get_solid(%d) = %x (new)\n", i, color));
363
364create:
365	cache->bo[i] = kgem_create_proxy(&sna->kgem, cache->cache_bo,
366					 i*sizeof(uint32_t), sizeof(uint32_t));
367	cache->bo[i]->pitch = 4;
368
369done:
370	cache->last = i;
371	return kgem_bo_reference(cache->bo[i]);
372}
373
374static bool sna_alpha_cache_init(struct sna *sna)
375{
376	struct sna_alpha_cache *cache = &sna->render.alpha_cache;
377	uint32_t color[256 + 7];
378	int i;
379
380	DBG(("%s\n", __FUNCTION__));
381
382	cache->cache_bo = kgem_create_linear(&sna->kgem, sizeof(color), 0);
383	if (!cache->cache_bo)
384		return false;
385
386	for (i = 0; i < 256; i++) {
387		color[i] = i << 24;
388		cache->bo[i] = kgem_create_proxy(&sna->kgem,
389						 cache->cache_bo,
390						 sizeof(uint32_t)*i,
391						 sizeof(uint32_t));
392		if (cache->bo[i] == NULL)
393			return false;
394
395		cache->bo[i]->pitch = 4;
396	}
397
398	/* primary */
399	for (i = 1; i < 8; i++) {
400		int j = 255+i;
401
402		color[j] = 0xff << 24;
403		if (i & 1)
404			color[j] |= 0xff << 0;
405		if (i & 2)
406			color[j] |= 0xff << 8;
407		if (i & 4)
408			color[j] |= 0xff << 16;
409		cache->bo[j] = kgem_create_proxy(&sna->kgem,
410						 cache->cache_bo,
411						 sizeof(uint32_t)*j,
412						 sizeof(uint32_t));
413		if (cache->bo[j] == NULL)
414			return false;
415
416		cache->bo[j]->pitch = 4;
417	}
418
419	return kgem_bo_write(&sna->kgem, cache->cache_bo, color, sizeof(color));
420}
421
422static bool sna_solid_cache_init(struct sna *sna)
423{
424	struct sna_solid_cache *cache = &sna->render.solid_cache;
425
426	DBG(("%s\n", __FUNCTION__));
427
428	cache->cache_bo =
429		kgem_create_linear(&sna->kgem, 4096, 0);
430	if (!cache->cache_bo)
431		return false;
432
433	cache->last = 0;
434	cache->color[cache->last] = 0;
435	cache->dirty = 0;
436	cache->size = 0;
437
438	return true;
439}
440
441bool sna_gradients_create(struct sna *sna)
442{
443	DBG(("%s\n", __FUNCTION__));
444
445	if (unlikely(sna->kgem.wedged))
446		return true;
447
448	if (!sna_alpha_cache_init(sna))
449		return false;
450
451	if (!sna_solid_cache_init(sna))
452		return false;
453
454	return true;
455}
456
457void sna_gradients_close(struct sna *sna)
458{
459	int i;
460
461	DBG(("%s\n", __FUNCTION__));
462
463	for (i = 0; i < 256; i++) {
464		if (sna->render.alpha_cache.bo[i]) {
465			kgem_bo_destroy(&sna->kgem, sna->render.alpha_cache.bo[i]);
466			sna->render.alpha_cache.bo[i] = NULL;
467		}
468	}
469	if (sna->render.alpha_cache.cache_bo) {
470		kgem_bo_destroy(&sna->kgem, sna->render.alpha_cache.cache_bo);
471		sna->render.alpha_cache.cache_bo = NULL;
472	}
473
474	if (sna->render.solid_cache.cache_bo)
475		kgem_bo_destroy(&sna->kgem, sna->render.solid_cache.cache_bo);
476	for (i = 0; i < sna->render.solid_cache.size; i++) {
477		if (sna->render.solid_cache.bo[i])
478			kgem_bo_destroy(&sna->kgem, sna->render.solid_cache.bo[i]);
479	}
480	sna->render.solid_cache.cache_bo = 0;
481	sna->render.solid_cache.size = 0;
482	sna->render.solid_cache.dirty = 0;
483
484	for (i = 0; i < sna->render.gradient_cache.size; i++) {
485		struct sna_gradient_cache *cache =
486			&sna->render.gradient_cache.cache[i];
487
488		if (cache->bo)
489			kgem_bo_destroy(&sna->kgem, cache->bo);
490
491		free(cache->stops);
492		cache->stops = NULL;
493		cache->nstops = 0;
494	}
495	sna->render.gradient_cache.size = 0;
496}
497