1/*
2 *
3 * Copyright © 2000 SuSE, Inc.
4 * Copyright © 2007 Red Hat, Inc.
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of SuSE not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission.  SuSE makes no representations about the
13 * suitability of this software for any purpose.  It is provided "as is"
14 * without express or implied warranty.
15 *
16 * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Author:  Keith Packard, SuSE, Inc.
24 */
25
26#ifdef HAVE_DIX_CONFIG_H
27#include <dix-config.h>
28#endif
29
30#include <string.h>
31
32#include "fb.h"
33
34#include "picturestr.h"
35#include "mipict.h"
36#include "fbpict.h"
37
38void
39fbComposite(CARD8 op,
40            PicturePtr pSrc,
41            PicturePtr pMask,
42            PicturePtr pDst,
43            INT16 xSrc,
44            INT16 ySrc,
45            INT16 xMask,
46            INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height)
47{
48    pixman_image_t *src, *mask, *dest;
49    int src_xoff, src_yoff;
50    int msk_xoff, msk_yoff;
51    int dst_xoff, dst_yoff;
52
53    miCompositeSourceValidate(pSrc);
54    if (pMask)
55        miCompositeSourceValidate(pMask);
56
57    src = image_from_pict(pSrc, FALSE, &src_xoff, &src_yoff);
58    mask = image_from_pict(pMask, FALSE, &msk_xoff, &msk_yoff);
59    dest = image_from_pict(pDst, TRUE, &dst_xoff, &dst_yoff);
60
61    if (src && dest && !(pMask && !mask)) {
62        pixman_image_composite(op, src, mask, dest,
63                               xSrc + src_xoff, ySrc + src_yoff,
64                               xMask + msk_xoff, yMask + msk_yoff,
65                               xDst + dst_xoff, yDst + dst_yoff, width, height);
66    }
67
68    free_pixman_pict(pSrc, src);
69    free_pixman_pict(pMask, mask);
70    free_pixman_pict(pDst, dest);
71}
72
73static pixman_glyph_cache_t *glyphCache;
74
75void
76fbDestroyGlyphCache(void)
77{
78    if (glyphCache)
79    {
80	pixman_glyph_cache_destroy (glyphCache);
81	glyphCache = NULL;
82    }
83}
84
85static void
86fbUnrealizeGlyph(ScreenPtr pScreen,
87		 GlyphPtr pGlyph)
88{
89    if (glyphCache)
90	pixman_glyph_cache_remove (glyphCache, pGlyph, NULL);
91}
92
93void
94fbGlyphs(CARD8 op,
95	 PicturePtr pSrc,
96	 PicturePtr pDst,
97	 PictFormatPtr maskFormat,
98	 INT16 xSrc,
99	 INT16 ySrc, int nlist,
100	 GlyphListPtr list,
101	 GlyphPtr *glyphs)
102{
103#define N_STACK_GLYPHS 512
104    ScreenPtr pScreen = pDst->pDrawable->pScreen;
105    pixman_glyph_t stack_glyphs[N_STACK_GLYPHS];
106    pixman_glyph_t *pglyphs = stack_glyphs;
107    pixman_image_t *srcImage, *dstImage;
108    int srcXoff, srcYoff, dstXoff, dstYoff;
109    GlyphPtr glyph;
110    int n_glyphs;
111    int x, y;
112    int i, n;
113    int xDst = list->xOff, yDst = list->yOff;
114
115    miCompositeSourceValidate(pSrc);
116
117    n_glyphs = 0;
118    for (i = 0; i < nlist; ++i)
119	n_glyphs += list[i].len;
120
121    if (!glyphCache)
122	glyphCache = pixman_glyph_cache_create();
123
124    pixman_glyph_cache_freeze (glyphCache);
125
126    if (n_glyphs > N_STACK_GLYPHS) {
127	if (!(pglyphs = xallocarray(n_glyphs, sizeof(pixman_glyph_t))))
128	    goto out;
129    }
130
131    i = 0;
132    x = y = 0;
133    while (nlist--) {
134        x += list->xOff;
135        y += list->yOff;
136        n = list->len;
137        while (n--) {
138	    const void *g;
139
140            glyph = *glyphs++;
141
142	    if (!(g = pixman_glyph_cache_lookup (glyphCache, glyph, NULL))) {
143		pixman_image_t *glyphImage;
144		PicturePtr pPicture;
145		int xoff, yoff;
146
147		pPicture = GetGlyphPicture(glyph, pScreen);
148		if (!pPicture) {
149		    n_glyphs--;
150		    goto next;
151		}
152
153		if (!(glyphImage = image_from_pict(pPicture, FALSE, &xoff, &yoff)))
154		    goto out;
155
156		g = pixman_glyph_cache_insert(glyphCache, glyph, NULL,
157					      glyph->info.x,
158					      glyph->info.y,
159					      glyphImage);
160
161		free_pixman_pict(pPicture, glyphImage);
162
163		if (!g)
164		    goto out;
165	    }
166
167	    pglyphs[i].x = x;
168	    pglyphs[i].y = y;
169	    pglyphs[i].glyph = g;
170	    i++;
171
172	next:
173            x += glyph->info.xOff;
174            y += glyph->info.yOff;
175	}
176	list++;
177    }
178
179    if (!(srcImage = image_from_pict(pSrc, FALSE, &srcXoff, &srcYoff)))
180	goto out;
181
182    if (!(dstImage = image_from_pict(pDst, TRUE, &dstXoff, &dstYoff)))
183	goto out_free_src;
184
185    if (maskFormat) {
186	pixman_format_code_t format;
187	pixman_box32_t extents;
188
189	format = maskFormat->format | (maskFormat->depth << 24);
190
191	pixman_glyph_get_extents(glyphCache, n_glyphs, pglyphs, &extents);
192
193	pixman_composite_glyphs(op, srcImage, dstImage, format,
194				xSrc + srcXoff + extents.x1 - xDst, ySrc + srcYoff + extents.y1 - yDst,
195				extents.x1, extents.y1,
196				extents.x1 + dstXoff, extents.y1 + dstYoff,
197				extents.x2 - extents.x1,
198				extents.y2 - extents.y1,
199				glyphCache, n_glyphs, pglyphs);
200    }
201    else {
202	pixman_composite_glyphs_no_mask(op, srcImage, dstImage,
203					xSrc + srcXoff - xDst, ySrc + srcYoff - yDst,
204					dstXoff, dstYoff,
205					glyphCache, n_glyphs, pglyphs);
206    }
207
208    free_pixman_pict(pDst, dstImage);
209
210out_free_src:
211    free_pixman_pict(pSrc, srcImage);
212
213out:
214    pixman_glyph_cache_thaw(glyphCache);
215    if (pglyphs != stack_glyphs)
216	free(pglyphs);
217}
218
219static pixman_image_t *
220create_solid_fill_image(PicturePtr pict)
221{
222    PictSolidFill *solid = &pict->pSourcePict->solidFill;
223    /* pixman_color_t and xRenderColor have the same layout */
224    pixman_color_t *color = (pixman_color_t *)&solid->fullcolor;
225
226    return pixman_image_create_solid_fill(color);
227}
228
229static pixman_image_t *
230create_linear_gradient_image(PictGradient * gradient)
231{
232    PictLinearGradient *linear = (PictLinearGradient *) gradient;
233    pixman_point_fixed_t p1;
234    pixman_point_fixed_t p2;
235
236    p1.x = linear->p1.x;
237    p1.y = linear->p1.y;
238    p2.x = linear->p2.x;
239    p2.y = linear->p2.y;
240
241    return pixman_image_create_linear_gradient(&p1, &p2,
242                                               (pixman_gradient_stop_t *)
243                                               gradient->stops,
244                                               gradient->nstops);
245}
246
247static pixman_image_t *
248create_radial_gradient_image(PictGradient * gradient)
249{
250    PictRadialGradient *radial = (PictRadialGradient *) gradient;
251    pixman_point_fixed_t c1;
252    pixman_point_fixed_t c2;
253
254    c1.x = radial->c1.x;
255    c1.y = radial->c1.y;
256    c2.x = radial->c2.x;
257    c2.y = radial->c2.y;
258
259    return pixman_image_create_radial_gradient(&c1, &c2, radial->c1.radius,
260                                               radial->c2.radius,
261                                               (pixman_gradient_stop_t *)
262                                               gradient->stops,
263                                               gradient->nstops);
264}
265
266static pixman_image_t *
267create_conical_gradient_image(PictGradient * gradient)
268{
269    PictConicalGradient *conical = (PictConicalGradient *) gradient;
270    pixman_point_fixed_t center;
271
272    center.x = conical->center.x;
273    center.y = conical->center.y;
274
275    return pixman_image_create_conical_gradient(&center, conical->angle,
276                                                (pixman_gradient_stop_t *)
277                                                gradient->stops,
278                                                gradient->nstops);
279}
280
281static pixman_image_t *
282create_bits_picture(PicturePtr pict, Bool has_clip, int *xoff, int *yoff)
283{
284    PixmapPtr pixmap;
285    FbBits *bits;
286    FbStride stride;
287    int bpp;
288    pixman_image_t *image;
289
290    fbGetDrawablePixmap(pict->pDrawable, pixmap, *xoff, *yoff);
291    fbGetPixmapBitsData(pixmap, bits, stride, bpp);
292
293    image = pixman_image_create_bits((pixman_format_code_t) pict->format,
294                                     pixmap->drawable.width,
295                                     pixmap->drawable.height, (uint32_t *) bits,
296                                     stride * sizeof(FbStride));
297
298    if (!image)
299        return NULL;
300
301#ifdef FB_ACCESS_WRAPPER
302    pixman_image_set_accessors(image,
303                               (pixman_read_memory_func_t) wfbReadMemory,
304                               (pixman_write_memory_func_t) wfbWriteMemory);
305#endif
306
307    /* pCompositeClip is undefined for source pictures, so
308     * only set the clip region for pictures with drawables
309     */
310    if (has_clip) {
311        if (pict->clientClip)
312            pixman_image_set_has_client_clip(image, TRUE);
313
314        if (*xoff || *yoff)
315            pixman_region_translate(pict->pCompositeClip, *xoff, *yoff);
316
317        pixman_image_set_clip_region(image, pict->pCompositeClip);
318
319        if (*xoff || *yoff)
320            pixman_region_translate(pict->pCompositeClip, -*xoff, -*yoff);
321    }
322
323    /* Indexed table */
324    if (pict->pFormat->index.devPrivate)
325        pixman_image_set_indexed(image, pict->pFormat->index.devPrivate);
326
327    /* Add in drawable origin to position within the image */
328    *xoff += pict->pDrawable->x;
329    *yoff += pict->pDrawable->y;
330
331    return image;
332}
333
334static pixman_image_t *image_from_pict_internal(PicturePtr pict, Bool has_clip,
335                                                int *xoff, int *yoff,
336                                                Bool is_alpha_map);
337
338static void image_destroy(pixman_image_t *image, void *data)
339{
340    fbFinishAccess((DrawablePtr)data);
341}
342
343static void
344set_image_properties(pixman_image_t * image, PicturePtr pict, Bool has_clip,
345                     int *xoff, int *yoff, Bool is_alpha_map)
346{
347    pixman_repeat_t repeat;
348    pixman_filter_t filter;
349
350    if (pict->transform) {
351        /* For source images, adjust the transform to account
352         * for the drawable offset within the pixman image,
353         * then set the offset to 0 as it will be used
354         * to compute positions within the transformed image.
355         */
356        if (!has_clip) {
357            struct pixman_transform adjusted;
358
359            adjusted = *pict->transform;
360            pixman_transform_translate(&adjusted,
361                                       NULL,
362                                       pixman_int_to_fixed(*xoff),
363                                       pixman_int_to_fixed(*yoff));
364            pixman_image_set_transform(image, &adjusted);
365            *xoff = 0;
366            *yoff = 0;
367        }
368        else
369            pixman_image_set_transform(image, pict->transform);
370    }
371
372    switch (pict->repeatType) {
373    default:
374    case RepeatNone:
375        repeat = PIXMAN_REPEAT_NONE;
376        break;
377
378    case RepeatPad:
379        repeat = PIXMAN_REPEAT_PAD;
380        break;
381
382    case RepeatNormal:
383        repeat = PIXMAN_REPEAT_NORMAL;
384        break;
385
386    case RepeatReflect:
387        repeat = PIXMAN_REPEAT_REFLECT;
388        break;
389    }
390
391    pixman_image_set_repeat(image, repeat);
392
393    /* Fetch alpha map unless 'pict' is being used
394     * as the alpha map for this operation
395     */
396    if (pict->alphaMap && !is_alpha_map) {
397        int alpha_xoff, alpha_yoff;
398        pixman_image_t *alpha_map =
399            image_from_pict_internal(pict->alphaMap, FALSE, &alpha_xoff,
400                                     &alpha_yoff, TRUE);
401
402        pixman_image_set_alpha_map(image, alpha_map, pict->alphaOrigin.x,
403                                   pict->alphaOrigin.y);
404
405        free_pixman_pict(pict->alphaMap, alpha_map);
406    }
407
408    pixman_image_set_component_alpha(image, pict->componentAlpha);
409
410    switch (pict->filter) {
411    default:
412    case PictFilterNearest:
413    case PictFilterFast:
414        filter = PIXMAN_FILTER_NEAREST;
415        break;
416
417    case PictFilterBilinear:
418    case PictFilterGood:
419        filter = PIXMAN_FILTER_BILINEAR;
420        break;
421
422    case PictFilterConvolution:
423        filter = PIXMAN_FILTER_CONVOLUTION;
424        break;
425    }
426
427    if (pict->pDrawable)
428        pixman_image_set_destroy_function(image, &image_destroy,
429                                          pict->pDrawable);
430
431    pixman_image_set_filter(image, filter,
432                            (pixman_fixed_t *) pict->filter_params,
433                            pict->filter_nparams);
434    pixman_image_set_source_clipping(image, TRUE);
435}
436
437static pixman_image_t *
438image_from_pict_internal(PicturePtr pict, Bool has_clip, int *xoff, int *yoff,
439                         Bool is_alpha_map)
440{
441    pixman_image_t *image = NULL;
442
443    if (!pict)
444        return NULL;
445
446    if (pict->pDrawable) {
447        image = create_bits_picture(pict, has_clip, xoff, yoff);
448    }
449    else if (pict->pSourcePict) {
450        SourcePict *sp = pict->pSourcePict;
451
452        if (sp->type == SourcePictTypeSolidFill) {
453            image = create_solid_fill_image(pict);
454        }
455        else {
456            PictGradient *gradient = &pict->pSourcePict->gradient;
457
458            if (sp->type == SourcePictTypeLinear)
459                image = create_linear_gradient_image(gradient);
460            else if (sp->type == SourcePictTypeRadial)
461                image = create_radial_gradient_image(gradient);
462            else if (sp->type == SourcePictTypeConical)
463                image = create_conical_gradient_image(gradient);
464        }
465        *xoff = *yoff = 0;
466    }
467
468    if (image)
469        set_image_properties(image, pict, has_clip, xoff, yoff, is_alpha_map);
470
471    return image;
472}
473
474pixman_image_t *
475image_from_pict(PicturePtr pict, Bool has_clip, int *xoff, int *yoff)
476{
477    return image_from_pict_internal(pict, has_clip, xoff, yoff, FALSE);
478}
479
480void
481free_pixman_pict(PicturePtr pict, pixman_image_t * image)
482{
483    if (image)
484        pixman_image_unref(image);
485}
486
487Bool
488fbPictureInit(ScreenPtr pScreen, PictFormatPtr formats, int nformats)
489{
490
491    PictureScreenPtr ps;
492
493    if (!miPictureInit(pScreen, formats, nformats))
494        return FALSE;
495    ps = GetPictureScreen(pScreen);
496    ps->Composite = fbComposite;
497    ps->Glyphs = fbGlyphs;
498    ps->UnrealizeGlyph = fbUnrealizeGlyph;
499    ps->CompositeRects = miCompositeRects;
500    ps->RasterizeTrapezoid = fbRasterizeTrapezoid;
501    ps->Trapezoids = fbTrapezoids;
502    ps->AddTraps = fbAddTraps;
503    ps->AddTriangles = fbAddTriangles;
504    ps->Triangles = fbTriangles;
505
506    return TRUE;
507}
508