Commit | Line | Data |
---|---|---|
a09e091a JB |
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 | ||
38 | void | |
39 | fbComposite(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 | ||
73 | static pixman_glyph_cache_t *glyphCache; | |
74 | ||
75 | void | |
76 | fbDestroyGlyphCache(void) | |
77 | { | |
78 | if (glyphCache) | |
79 | { | |
80 | pixman_glyph_cache_destroy (glyphCache); | |
81 | glyphCache = NULL; | |
82 | } | |
83 | } | |
84 | ||
85 | static void | |
86 | fbUnrealizeGlyph(ScreenPtr pScreen, | |
87 | GlyphPtr pGlyph) | |
88 | { | |
89 | if (glyphCache) | |
90 | pixman_glyph_cache_remove (glyphCache, pGlyph, NULL); | |
91 | } | |
92 | ||
93 | static void | |
94 | fbGlyphs(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 = malloc (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 + xDst, ySrc + srcYoff + 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 | ||
210 | out_free_src: | |
211 | free_pixman_pict(pSrc, srcImage); | |
212 | ||
213 | out: | |
214 | pixman_glyph_cache_thaw(glyphCache); | |
215 | if (pglyphs != stack_glyphs) | |
216 | free(pglyphs); | |
217 | } | |
218 | ||
219 | static pixman_image_t * | |
220 | create_solid_fill_image(PicturePtr pict) | |
221 | { | |
222 | PictSolidFill *solid = &pict->pSourcePict->solidFill; | |
223 | pixman_color_t color; | |
224 | CARD32 a, r, g, b; | |
225 | ||
226 | a = (solid->color & 0xff000000) >> 24; | |
227 | r = (solid->color & 0x00ff0000) >> 16; | |
228 | g = (solid->color & 0x0000ff00) >> 8; | |
229 | b = (solid->color & 0x000000ff) >> 0; | |
230 | ||
231 | color.alpha = (a << 8) | a; | |
232 | color.red = (r << 8) | r; | |
233 | color.green = (g << 8) | g; | |
234 | color.blue = (b << 8) | b; | |
235 | ||
236 | return pixman_image_create_solid_fill(&color); | |
237 | } | |
238 | ||
239 | static pixman_image_t * | |
240 | create_linear_gradient_image(PictGradient * gradient) | |
241 | { | |
242 | PictLinearGradient *linear = (PictLinearGradient *) gradient; | |
243 | pixman_point_fixed_t p1; | |
244 | pixman_point_fixed_t p2; | |
245 | ||
246 | p1.x = linear->p1.x; | |
247 | p1.y = linear->p1.y; | |
248 | p2.x = linear->p2.x; | |
249 | p2.y = linear->p2.y; | |
250 | ||
251 | return pixman_image_create_linear_gradient(&p1, &p2, | |
252 | (pixman_gradient_stop_t *) | |
253 | gradient->stops, | |
254 | gradient->nstops); | |
255 | } | |
256 | ||
257 | static pixman_image_t * | |
258 | create_radial_gradient_image(PictGradient * gradient) | |
259 | { | |
260 | PictRadialGradient *radial = (PictRadialGradient *) gradient; | |
261 | pixman_point_fixed_t c1; | |
262 | pixman_point_fixed_t c2; | |
263 | ||
264 | c1.x = radial->c1.x; | |
265 | c1.y = radial->c1.y; | |
266 | c2.x = radial->c2.x; | |
267 | c2.y = radial->c2.y; | |
268 | ||
269 | return pixman_image_create_radial_gradient(&c1, &c2, radial->c1.radius, | |
270 | radial->c2.radius, | |
271 | (pixman_gradient_stop_t *) | |
272 | gradient->stops, | |
273 | gradient->nstops); | |
274 | } | |
275 | ||
276 | static pixman_image_t * | |
277 | create_conical_gradient_image(PictGradient * gradient) | |
278 | { | |
279 | PictConicalGradient *conical = (PictConicalGradient *) gradient; | |
280 | pixman_point_fixed_t center; | |
281 | ||
282 | center.x = conical->center.x; | |
283 | center.y = conical->center.y; | |
284 | ||
285 | return pixman_image_create_conical_gradient(¢er, conical->angle, | |
286 | (pixman_gradient_stop_t *) | |
287 | gradient->stops, | |
288 | gradient->nstops); | |
289 | } | |
290 | ||
291 | static pixman_image_t * | |
292 | create_bits_picture(PicturePtr pict, Bool has_clip, int *xoff, int *yoff) | |
293 | { | |
294 | PixmapPtr pixmap; | |
295 | FbBits *bits; | |
296 | FbStride stride; | |
297 | int bpp; | |
298 | pixman_image_t *image; | |
299 | ||
300 | fbGetDrawablePixmap(pict->pDrawable, pixmap, *xoff, *yoff); | |
301 | fbGetPixmapBitsData(pixmap, bits, stride, bpp); | |
302 | ||
303 | image = pixman_image_create_bits((pixman_format_code_t) pict->format, | |
304 | pixmap->drawable.width, | |
305 | pixmap->drawable.height, (uint32_t *) bits, | |
306 | stride * sizeof(FbStride)); | |
307 | ||
308 | if (!image) | |
309 | return NULL; | |
310 | ||
311 | #ifdef FB_ACCESS_WRAPPER | |
312 | #if FB_SHIFT==5 | |
313 | ||
314 | pixman_image_set_accessors(image, | |
315 | (pixman_read_memory_func_t) wfbReadMemory, | |
316 | (pixman_write_memory_func_t) wfbWriteMemory); | |
317 | ||
318 | #else | |
319 | ||
320 | #error The pixman library only works when FbBits is 32 bits wide | |
321 | ||
322 | #endif | |
323 | #endif | |
324 | ||
325 | /* pCompositeClip is undefined for source pictures, so | |
326 | * only set the clip region for pictures with drawables | |
327 | */ | |
328 | if (has_clip) { | |
329 | if (pict->clientClipType != CT_NONE) | |
330 | pixman_image_set_has_client_clip(image, TRUE); | |
331 | ||
332 | if (*xoff || *yoff) | |
333 | pixman_region_translate(pict->pCompositeClip, *xoff, *yoff); | |
334 | ||
335 | pixman_image_set_clip_region(image, pict->pCompositeClip); | |
336 | ||
337 | if (*xoff || *yoff) | |
338 | pixman_region_translate(pict->pCompositeClip, -*xoff, -*yoff); | |
339 | } | |
340 | ||
341 | /* Indexed table */ | |
342 | if (pict->pFormat->index.devPrivate) | |
343 | pixman_image_set_indexed(image, pict->pFormat->index.devPrivate); | |
344 | ||
345 | /* Add in drawable origin to position within the image */ | |
346 | *xoff += pict->pDrawable->x; | |
347 | *yoff += pict->pDrawable->y; | |
348 | ||
349 | return image; | |
350 | } | |
351 | ||
352 | static pixman_image_t *image_from_pict_internal(PicturePtr pict, Bool has_clip, | |
353 | int *xoff, int *yoff, | |
354 | Bool is_alpha_map); | |
355 | ||
356 | static void | |
357 | set_image_properties(pixman_image_t * image, PicturePtr pict, Bool has_clip, | |
358 | int *xoff, int *yoff, Bool is_alpha_map) | |
359 | { | |
360 | pixman_repeat_t repeat; | |
361 | pixman_filter_t filter; | |
362 | ||
363 | if (pict->transform) { | |
364 | /* For source images, adjust the transform to account | |
365 | * for the drawable offset within the pixman image, | |
366 | * then set the offset to 0 as it will be used | |
367 | * to compute positions within the transformed image. | |
368 | */ | |
369 | if (!has_clip) { | |
370 | struct pixman_transform adjusted; | |
371 | ||
372 | adjusted = *pict->transform; | |
373 | pixman_transform_translate(&adjusted, | |
374 | NULL, | |
375 | pixman_int_to_fixed(*xoff), | |
376 | pixman_int_to_fixed(*yoff)); | |
377 | pixman_image_set_transform(image, &adjusted); | |
378 | *xoff = 0; | |
379 | *yoff = 0; | |
380 | } | |
381 | else | |
382 | pixman_image_set_transform(image, pict->transform); | |
383 | } | |
384 | ||
385 | switch (pict->repeatType) { | |
386 | default: | |
387 | case RepeatNone: | |
388 | repeat = PIXMAN_REPEAT_NONE; | |
389 | break; | |
390 | ||
391 | case RepeatPad: | |
392 | repeat = PIXMAN_REPEAT_PAD; | |
393 | break; | |
394 | ||
395 | case RepeatNormal: | |
396 | repeat = PIXMAN_REPEAT_NORMAL; | |
397 | break; | |
398 | ||
399 | case RepeatReflect: | |
400 | repeat = PIXMAN_REPEAT_REFLECT; | |
401 | break; | |
402 | } | |
403 | ||
404 | pixman_image_set_repeat(image, repeat); | |
405 | ||
406 | /* Fetch alpha map unless 'pict' is being used | |
407 | * as the alpha map for this operation | |
408 | */ | |
409 | if (pict->alphaMap && !is_alpha_map) { | |
410 | int alpha_xoff, alpha_yoff; | |
411 | pixman_image_t *alpha_map = | |
412 | image_from_pict_internal(pict->alphaMap, FALSE, &alpha_xoff, | |
413 | &alpha_yoff, TRUE); | |
414 | ||
415 | pixman_image_set_alpha_map(image, alpha_map, pict->alphaOrigin.x, | |
416 | pict->alphaOrigin.y); | |
417 | ||
418 | free_pixman_pict(pict->alphaMap, alpha_map); | |
419 | } | |
420 | ||
421 | pixman_image_set_component_alpha(image, pict->componentAlpha); | |
422 | ||
423 | switch (pict->filter) { | |
424 | default: | |
425 | case PictFilterNearest: | |
426 | case PictFilterFast: | |
427 | filter = PIXMAN_FILTER_NEAREST; | |
428 | break; | |
429 | ||
430 | case PictFilterBilinear: | |
431 | case PictFilterGood: | |
432 | filter = PIXMAN_FILTER_BILINEAR; | |
433 | break; | |
434 | ||
435 | case PictFilterConvolution: | |
436 | filter = PIXMAN_FILTER_CONVOLUTION; | |
437 | break; | |
438 | } | |
439 | ||
440 | pixman_image_set_filter(image, filter, | |
441 | (pixman_fixed_t *) pict->filter_params, | |
442 | pict->filter_nparams); | |
443 | pixman_image_set_source_clipping(image, TRUE); | |
444 | } | |
445 | ||
446 | static pixman_image_t * | |
447 | image_from_pict_internal(PicturePtr pict, Bool has_clip, int *xoff, int *yoff, | |
448 | Bool is_alpha_map) | |
449 | { | |
450 | pixman_image_t *image = NULL; | |
451 | ||
452 | if (!pict) | |
453 | return NULL; | |
454 | ||
455 | if (pict->pDrawable) { | |
456 | image = create_bits_picture(pict, has_clip, xoff, yoff); | |
457 | } | |
458 | else if (pict->pSourcePict) { | |
459 | SourcePict *sp = pict->pSourcePict; | |
460 | ||
461 | if (sp->type == SourcePictTypeSolidFill) { | |
462 | image = create_solid_fill_image(pict); | |
463 | } | |
464 | else { | |
465 | PictGradient *gradient = &pict->pSourcePict->gradient; | |
466 | ||
467 | if (sp->type == SourcePictTypeLinear) | |
468 | image = create_linear_gradient_image(gradient); | |
469 | else if (sp->type == SourcePictTypeRadial) | |
470 | image = create_radial_gradient_image(gradient); | |
471 | else if (sp->type == SourcePictTypeConical) | |
472 | image = create_conical_gradient_image(gradient); | |
473 | } | |
474 | *xoff = *yoff = 0; | |
475 | } | |
476 | ||
477 | if (image) | |
478 | set_image_properties(image, pict, has_clip, xoff, yoff, is_alpha_map); | |
479 | ||
480 | return image; | |
481 | } | |
482 | ||
483 | pixman_image_t * | |
484 | image_from_pict(PicturePtr pict, Bool has_clip, int *xoff, int *yoff) | |
485 | { | |
486 | return image_from_pict_internal(pict, has_clip, xoff, yoff, FALSE); | |
487 | } | |
488 | ||
489 | void | |
490 | free_pixman_pict(PicturePtr pict, pixman_image_t * image) | |
491 | { | |
492 | if (image && pixman_image_unref(image) && pict->pDrawable) | |
493 | fbFinishAccess(pict->pDrawable); | |
494 | } | |
495 | ||
496 | Bool | |
497 | fbPictureInit(ScreenPtr pScreen, PictFormatPtr formats, int nformats) | |
498 | { | |
499 | ||
500 | PictureScreenPtr ps; | |
501 | ||
502 | if (!miPictureInit(pScreen, formats, nformats)) | |
503 | return FALSE; | |
504 | ps = GetPictureScreen(pScreen); | |
505 | ps->Composite = fbComposite; | |
506 | ps->Glyphs = fbGlyphs; | |
507 | ps->UnrealizeGlyph = fbUnrealizeGlyph; | |
508 | ps->CompositeRects = miCompositeRects; | |
509 | ps->RasterizeTrapezoid = fbRasterizeTrapezoid; | |
510 | ps->Trapezoids = fbTrapezoids; | |
511 | ps->AddTraps = fbAddTraps; | |
512 | ps->AddTriangles = fbAddTriangles; | |
513 | ps->Triangles = fbTriangles; | |
514 | ||
515 | return TRUE; | |
516 | } |