lx_exa.c revision 04007eba
1/*
2 * Copyright (c) 2007-2008 Advanced Micro Devices, Inc.
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 shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *
22 * Neither the name of the Advanced Micro Devices, Inc. nor the names of its
23 * contributors may be used to endorse or promote products derived from this
24 * software without specific prior written permission.
25 */
26
27/* TODO:
28   Support a8 as a source or destination?
29   convert !a8 or !a4 masks?
30   support multiple pass operations?
31*/
32
33/* To support PictOptAdd with a mask */
34
35#ifdef HAVE_CONFIG_H
36#include "config.h"
37#endif
38
39#include "xf86.h"
40#include "exa.h"
41
42#include "geode.h"
43#include "cim_defs.h"
44#include "cim_regs.h"
45
46#include "geode_blend.h"
47
48#define F(x)    IntToxFixed(x)
49#define I(x)    xFixedToInt(x)
50
51#define GEODE_TRACE_FALL 0
52
53#if GEODE_TRACE_FALL
54#define GEODE_FALLBACK(x)               \
55do {                                    \
56	ErrorF("%s: ", __FUNCTION__);   \
57	ErrorF x;                       \
58	return FALSE;                   \
59} while (0)
60#else
61#define GEODE_FALLBACK(x) return FALSE
62#endif
63
64static const struct exa_format_t {
65    int exa;
66    int bpp;
67    int fmt;
68    int alphabits;
69} lx_exa_formats[] = {
70    {
71    PICT_a8r8g8b8, 32, CIMGP_SOURCE_FMT_8_8_8_8, 8}, {
72    PICT_x8r8g8b8, 32, CIMGP_SOURCE_FMT_8_8_8_8, 0}, {
73    PICT_x8b8g8r8, 32, CIMGP_SOURCE_FMT_32BPP_BGR, 0}, {
74    PICT_a4r4g4b4, 16, CIMGP_SOURCE_FMT_4_4_4_4, 4}, {
75    PICT_a1r5g5b5, 16, CIMGP_SOURCE_FMT_1_5_5_5, 1}, {
76    PICT_r5g6b5, 16, CIMGP_SOURCE_FMT_0_5_6_5, 0}, {
77    PICT_b5g6r5, 16, CIMGP_SOURCE_FMT_16BPP_BGR, 0}, {
78    PICT_x1r5g5b5, 16, CIMGP_SOURCE_FMT_1_5_5_5, 0}, {
79    PICT_x1b5g5r5, 16, CIMGP_SOURCE_FMT_15BPP_BGR, 0}, {
80    PICT_r3g3b2, 8, CIMGP_SOURCE_FMT_3_3_2, 0}, {
81    PICT_a8, 32, CIMGP_SOURCE_FMT_8_8_8_8, 8}
82};
83
84/* This is a chunk of memory we use for scratch space */
85
86#define COMP_TYPE_MASK 0
87#define COMP_TYPE_ONEPASS 1
88#define COMP_TYPE_TWOPASS 3
89#define COMP_TYPE_ROTATE  5
90
91static struct {
92    int type;
93
94    unsigned int srcOffset;
95    unsigned int srcPitch;
96    unsigned int srcBpp;
97    unsigned int srcWidth, srcHeight;
98
99    unsigned int srcColor;
100    int op;
101    int repeat;
102    int maskrepeat;
103    unsigned int fourBpp;
104    unsigned int bufferOffset;
105    struct exa_format_t *srcFormat;
106    struct exa_format_t *dstFormat;
107
108    int rotate;
109    PictTransform *transform;
110
111} exaScratch;
112
113static const int SDfn[16] = {
114    0x00, 0x88, 0x44, 0xCC, 0x22, 0xAA, 0x66, 0xEE,
115    0x11, 0x99, 0x55, 0xDD, 0x33, 0xBB, 0x77, 0xFF
116};
117
118static const int SDfn_PM[16] = {
119    0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
120    0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA
121};
122
123/* These functions check to see if we can safely prefetch the memory
124 * for the blt, or if we have to wait the previous blt to complete.
125 * One function is for the fill, and the other is for the copy because
126 * they have different requirements based on ROP
127 */
128
129static int lx0 = -1, ly0 = -1, lx1 = -1, ly1 = -1;
130
131static int
132lx_fill_flags(int x0, int y0, int w, int h, int rop)
133{
134    int x1 = x0 + w, y1 = y0 + h;
135    int n = ((rop ^ (rop >> 1)) & 0x55) == 0 || /* no dst */
136        x0 >= lx1 || y0 >= ly1 ||       /* rght/below */
137        x1 <= lx0 || y1 <= ly0 ?        /* left/above */
138        0 : CIMGP_BLTFLAGS_HAZARD;
139
140    lx0 = x0;
141    ly0 = y0;
142    lx1 = x1;
143    ly1 = y1;
144
145    return n;
146}
147
148static int
149lx_copy_flags(int x0, int y0, int x1, int y1, int w, int h, int rop)
150{
151    int x2 = x1 + w, y2 = y1 + h;
152
153    /* dst not hazzard and src not hazzard */
154    int n = (((rop ^ (rop >> 1)) & 0x55) == 0 ||
155             x1 >= lx1 || y1 >= ly1 ||
156             x2 <= lx0 || y2 <= ly0) &&
157        (((rop ^ (rop >> 2)) & 0x33) == 0 ||
158         x0 >= lx1 || y0 >= ly1 ||
159         x0 + w <= lx0 || y0 + h <= ly0) ? 0 : CIMGP_BLTFLAGS_HAZARD;
160
161    lx0 = x1;
162    ly0 = y1;
163    lx1 = x2;
164    ly1 = y2;
165
166    return n;
167}
168
169/* These are borrowed from the exa engine - they should be made global
170   and available to drivers, but until then....
171*/
172
173/* exaGetPixelFromRGBA (exa_render.c) */
174
175static Bool
176_GetPixelFromRGBA(CARD32 *pixel,
177                  CARD16 red, CARD16 green, CARD16 blue, CARD16 alpha,
178                  CARD32 format)
179{
180    int rbits, bbits, gbits, abits;
181    int rshift, bshift, gshift, ashift;
182
183    *pixel = 0;
184
185    if (!PICT_FORMAT_COLOR(format))
186        return FALSE;
187
188    rbits = PICT_FORMAT_R(format);
189    gbits = PICT_FORMAT_G(format);
190    bbits = PICT_FORMAT_B(format);
191    abits = PICT_FORMAT_A(format);
192
193    if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) {
194        bshift = 0;
195        gshift = bbits;
196        rshift = gshift + gbits;
197        ashift = rshift + rbits;
198    }
199    else {                      /* PICT_TYPE_ABGR */
200        rshift = 0;
201        gshift = rbits;
202        bshift = gshift + gbits;
203        ashift = bshift + bbits;
204    }
205
206    *pixel |= (blue >> (16 - bbits)) << bshift;
207    *pixel |= (red >> (16 - rbits)) << rshift;
208    *pixel |= (green >> (16 - gbits)) << gshift;
209    *pixel |= (alpha >> (16 - abits)) << ashift;
210
211    return TRUE;
212}
213
214/* exaGetRGBAFromPixel (exa_render.c) */
215
216static Bool
217_GetRGBAFromPixel(CARD32 pixel,
218                  CARD16 *red,
219                  CARD16 *green, CARD16 *blue, CARD16 *alpha, CARD32 format)
220{
221    int rbits, bbits, gbits, abits;
222    int rshift, bshift, gshift, ashift;
223
224    if (!PICT_FORMAT_COLOR(format))
225        return FALSE;
226
227    rbits = PICT_FORMAT_R(format);
228    gbits = PICT_FORMAT_G(format);
229    bbits = PICT_FORMAT_B(format);
230    abits = PICT_FORMAT_A(format);
231
232    if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) {
233        bshift = 0;
234        gshift = bbits;
235        rshift = gshift + gbits;
236        ashift = rshift + rbits;
237    }
238    else {                      /* PICT_TYPE_ABGR */
239        rshift = 0;
240        gshift = rbits;
241        bshift = gshift + gbits;
242        ashift = bshift + bbits;
243    }
244
245    *red = ((pixel >> rshift) & ((1 << rbits) - 1)) << (16 - rbits);
246    while (rbits < 16) {
247        *red |= *red >> rbits;
248        rbits <<= 1;
249    }
250
251    *green = ((pixel >> gshift) & ((1 << gbits) - 1)) << (16 - gbits);
252    while (gbits < 16) {
253        *green |= *green >> gbits;
254        gbits <<= 1;
255    }
256
257    *blue = ((pixel >> bshift) & ((1 << bbits) - 1)) << (16 - bbits);
258    while (bbits < 16) {
259        *blue |= *blue >> bbits;
260        bbits <<= 1;
261    }
262
263    if (abits) {
264        *alpha = ((pixel >> ashift) & ((1 << abits) - 1)) << (16 - abits);
265        while (abits < 16) {
266            *alpha |= *alpha >> abits;
267            abits <<= 1;
268        }
269    }
270    else
271        *alpha = 0xffff;
272
273    return TRUE;
274}
275
276static unsigned int
277lx_get_source_color(PixmapPtr pSrc, int srcFormat, int dstFormat)
278{
279    CARD32 in, out;
280    CARD16 red = 0, green = 0, blue = 0, alpha = 0;
281
282    /* Stall to avoid a race with the upload function */
283    /* for 1.4 and newer, the problem will be resolved within
284     * exaGetPixmapFirstPixel, so this should be adjusted so
285     * the stall isn't run needlessly
286     */
287    /* FIXME: xserver-1.4 with a supposed fix for this is really old, so kill the stall? */
288
289    gp_wait_until_idle();
290    in = exaGetPixmapFirstPixel(pSrc);
291
292    _GetRGBAFromPixel(in, &red, &blue, &green, &alpha, srcFormat);
293    _GetPixelFromRGBA(&out, red, blue, green, alpha, dstFormat);
294
295    return out;
296}
297
298static Bool
299lx_prepare_solid(PixmapPtr pxMap, int alu, Pixel planemask, Pixel fg)
300{
301    int pitch = exaGetPixmapPitch(pxMap);
302    int op = (planemask == ~0U) ? SDfn[alu] : SDfn_PM[alu];
303
304    gp_declare_blt(0);
305    gp_set_bpp(pxMap->drawable.bitsPerPixel);
306
307    gp_set_raster_operation(op);
308
309    if (planemask != ~0U)
310        gp_set_solid_pattern(planemask);
311
312    exaScratch.op = op;
313
314    gp_set_solid_source(fg);
315
316    gp_set_strides(pitch, pitch);
317    gp_write_parameters();
318    return TRUE;
319}
320
321static void
322lx_do_solid(PixmapPtr pxMap, int x1, int y1, int x2, int y2)
323{
324    int bpp = (pxMap->drawable.bitsPerPixel + 7) / 8;
325    int pitch = exaGetPixmapPitch(pxMap);
326    unsigned int offset = exaGetPixmapOffset(pxMap) + (pitch * y1) + (bpp * x1);
327
328    gp_declare_blt(lx_fill_flags(x1, y1, x2 - x1, y2 - y1, exaScratch.op));
329    gp_pattern_fill(offset, x2 - x1, y2 - y1);
330}
331
332static Bool
333lx_prepare_copy(PixmapPtr pxSrc, PixmapPtr pxDst, int dx, int dy,
334                int alu, Pixel planemask)
335{
336    int dpitch = exaGetPixmapPitch(pxDst);
337    int op = (planemask == ~0U) ? SDfn[alu] : SDfn_PM[alu];
338
339    gp_declare_blt(0);
340    gp_set_bpp(pxDst->drawable.bitsPerPixel);
341
342    gp_set_raster_operation(op);
343
344    if (planemask != ~0U)
345        gp_set_solid_pattern(planemask);
346
347    exaScratch.srcOffset = exaGetPixmapOffset(pxSrc);
348    exaScratch.srcPitch = exaGetPixmapPitch(pxSrc);
349    exaScratch.srcBpp = (pxSrc->drawable.bitsPerPixel + 7) / 8;
350
351    exaScratch.op = op;
352
353    gp_set_strides(dpitch, exaScratch.srcPitch);
354    gp_write_parameters();
355    return TRUE;
356}
357
358static void
359lx_do_copy(PixmapPtr pxDst, int srcX, int srcY,
360           int dstX, int dstY, int w, int h)
361{
362    int dstBpp = (pxDst->drawable.bitsPerPixel + 7) / 8;
363    int dstPitch = exaGetPixmapPitch(pxDst);
364    unsigned int srcOffset, dstOffset;
365    int flags = 0;
366
367    gp_declare_blt(lx_copy_flags(srcX, srcY, dstX, dstY, w, h, exaScratch.op));
368
369    srcOffset = exaScratch.srcOffset + (exaScratch.srcPitch * srcY) +
370        (exaScratch.srcBpp) * srcX;
371
372    dstOffset = exaGetPixmapOffset(pxDst) + (dstPitch * dstY) + (dstBpp * dstX);
373
374    if (dstX > srcX)
375        flags |= CIMGP_NEGXDIR;
376
377    if (dstY > srcY)
378        flags |= CIMGP_NEGYDIR;
379
380    gp_screen_to_screen_blt(dstOffset, srcOffset, w, h, flags);
381}
382
383/* Composite operations
384
385These are the simplest - one pass operations - if there is no format or
386mask, the we can make these happen pretty fast
387
388                       Operation  Type  Channel   Alpha
389PictOpClear            0          2     0         3
390PictOpSrc              0          3     0         3
391PictOpDst              0          3     1         3
392PictOpOver             2          0     0         3
393PictOpOverReverse      2          0     1         3
394PictOpIn               0          1     0         3
395PictOpInReverse        0          1     1         3
396PictOpOut              1          0     0         3
397PictOpOutReverse       1          0     1         3
398PictOpAdd              2          2     0         3
399
400The following require multiple passes
401PictOpAtop
402PictOpXor
403*/
404
405struct blend_ops_t {
406    int operation;
407    int type;
408    int channel;
409} lx_alpha_ops[] = {
410    /* PictOpClear */
411    {
412    CIMGP_ALPHA_TIMES_A, CIMGP_CONSTANT_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
413    },
414        /* PictOpSrc */
415    {
416    CIMGP_ALPHA_TIMES_A, CIMGP_ALPHA_EQUALS_ONE, CIMGP_CHANNEL_A_SOURCE}, {
417    },
418        /* PictOpDst */
419    {
420    CIMGP_ALPHA_TIMES_A, CIMGP_ALPHA_EQUALS_ONE, CIMGP_CHANNEL_A_DEST}, {
421    },
422        /* PictOpOver */
423    {
424    CIMGP_A_PLUS_BETA_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
425    CIMGP_ALPHA_TIMES_A, CIMGP_CONVERTED_ALPHA, CIMGP_CHANNEL_A_SOURCE},
426        /* PictOpOverReverse */
427    {
428    CIMGP_A_PLUS_BETA_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_DEST}, {
429    CIMGP_ALPHA_TIMES_A, CIMGP_CONVERTED_ALPHA, CIMGP_CHANNEL_A_SOURCE},
430        /* PictOpIn */
431    {
432    CIMGP_ALPHA_TIMES_A, CIMGP_CHANNEL_B_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
433    CIMGP_ALPHA_TIMES_A, CIMGP_CONVERTED_ALPHA, CIMGP_CHANNEL_A_SOURCE},
434        /* PictOpInReverse */
435    {
436    CIMGP_ALPHA_TIMES_A, CIMGP_CHANNEL_B_ALPHA, CIMGP_CHANNEL_A_DEST}, {
437    CIMGP_ALPHA_TIMES_A, CIMGP_CONVERTED_ALPHA, CIMGP_CHANNEL_A_SOURCE},
438        /* PictOpOut */
439    {
440    CIMGP_BETA_TIMES_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_DEST}, {
441    CIMGP_ALPHA_TIMES_A, CIMGP_CONVERTED_ALPHA, CIMGP_CHANNEL_A_SOURCE},
442        /* PictOpOutReverse */
443    {
444    CIMGP_BETA_TIMES_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
445    CIMGP_ALPHA_TIMES_A, CIMGP_CONVERTED_ALPHA, CIMGP_CHANNEL_A_SOURCE},
446        /* SrcAtop */
447    {
448    CIMGP_ALPHA_TIMES_A, CIMGP_CHANNEL_B_ALPHA, CIMGP_CHANNEL_A_DEST}, {
449    CIMGP_BETA_TIMES_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_SOURCE},
450        /* SrcAtopReverse */
451    {
452    CIMGP_ALPHA_TIMES_A, CIMGP_CHANNEL_B_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
453    CIMGP_BETA_TIMES_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_DEST},
454        /* Xor */
455    {
456    CIMGP_BETA_TIMES_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
457    CIMGP_BETA_TIMES_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_SOURCE},
458        /* PictOpAdd */
459    {
460    CIMGP_A_PLUS_BETA_B, CIMGP_CONSTANT_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
461    }
462};
463
464#ifndef ARRAY_SIZE
465#define ARRAY_SIZE(a) (sizeof((a)) / (sizeof(*(a))))
466#endif
467
468static const struct exa_format_t *
469lx_get_format(PicturePtr p)
470{
471    int i;
472    unsigned int format = p->format;
473
474    for (i = 0; i < ARRAY_SIZE(lx_exa_formats); i++)
475        if (lx_exa_formats[i].exa == format)
476            return (&lx_exa_formats[i]);
477
478    return NULL;
479}
480
481static Bool
482lx_process_transform(PicturePtr pSrc)
483{
484    PictTransformPtr t = pSrc->transform;
485    xFixed c0 = t->matrix[0][0];
486    xFixed s0 = t->matrix[0][1];
487    xFixed s1 = t->matrix[1][0];
488    xFixed c1 = t->matrix[1][1];
489
490    /* If the transform doesn't have any rotation
491     * or scaling components, then just grab the
492     * translate coordinates */
493
494    if (t->matrix[0][0] == 0 &&
495        t->matrix[0][1] == 0 && t->matrix[1][0] == 0 && t->matrix[1][1] == 0) {
496        exaScratch.transform = pSrc->transform;
497        return TRUE;
498    }
499
500    /* Otherwise, see if this is a simple
501     * rotate transform - if it isn't, then
502     * we have to punt back to software */
503
504    if (t->matrix[2][2] != F(1))
505        return FALSE;
506
507    /* The rotate matrix looks like this:
508     * [ cos X   -sin x
509     * sin X   cos X ]
510     *
511     * Where X is the angle.  We do a simple
512     * check first - if [0,0] != [1,1], then
513     * scaling was specified too, and we can
514     * bail, and if [0,1] != -[1,1] then this
515     * isn't scaling that we can handle.
516     */
517
518    if ((c0 != c1) || (s0 != -s1))
519        return FALSE;
520
521    /* Now, figure out what angle we want - we
522     * can only accelerate right angle rotations,
523     * so this turns into an easy set of if statements */
524
525    if (c0 == F(1) && s1 == F(0))
526        exaScratch.rotate = RR_Rotate_0;
527    else if (c0 == F(0) && s1 == F(1))
528        exaScratch.rotate = RR_Rotate_90;
529    else if (c0 == F(-1) && s1 == F(0))
530        exaScratch.rotate = RR_Rotate_180;
531    else if (c0 == F(0) && s1 == F(-1))
532        exaScratch.rotate = RR_Rotate_270;
533    else
534        return FALSE;
535
536    exaScratch.transform = pSrc->transform;
537
538    return TRUE;
539}
540
541static Bool
542lx_check_composite(int op, PicturePtr pSrc, PicturePtr pMsk, PicturePtr pDst)
543{
544    GeodeRec *pGeode = GEODEPTR_FROM_PICTURE(pDst);
545    const struct exa_format_t *srcFmt, *dstFmt;
546
547    if (op > PictOpAdd)
548        GEODE_FALLBACK(("Operation %d is not supported\n", op));
549
550    /* XXX - don't know if we can do any hwaccel on solid fills or gradient types in generic cases */
551    if (pMsk && pMsk->pSourcePict)
552        GEODE_FALLBACK(("%s are not supported as a mask\n",
553                        pMsk->pSourcePict->type ==
554                        SourcePictTypeSolidFill ? "Solid pictures" :
555                        "Gradients"));
556
557    if (pSrc->pSourcePict && pSrc->pSourcePict->type != SourcePictTypeSolidFill)
558        GEODE_FALLBACK(("Gradients are not supported as the source\n"));
559
560    if (pMsk && op == PictOpAdd)
561        GEODE_FALLBACK(("PictOpAdd with mask is not supported\n"));
562
563    /* FIXME: Meet this conditions from the debug for PictOpAdd.
564     * Any Other possibilities? Add a judge for the future supplement */
565    if (op == PictOpAdd && pSrc->format == PICT_a8r8g8b8 &&
566        pDst->format == PICT_a8)
567        return TRUE;
568
569    if (op == PictOpAdd && pSrc->format == PICT_x8r8g8b8 &&
570        pDst->format == PICT_a8)
571        return TRUE;
572
573    if (op == PictOpAdd && pSrc->format == PICT_r5g6b5 &&
574        pDst->format == PICT_a8)
575        return TRUE;
576
577    if (usesPasses(op)) {
578        /* FIXME: Slightly misleading fallback msg when !pMsk */
579        if (pGeode->exaBfrOffset == 0 || !pMsk)
580            GEODE_FALLBACK(("Multipass operation requires off-screen buffer\n"));
581    }
582
583    /* Check that the filter matches what we support */
584
585    switch (pSrc->filter) {
586    case PictFilterNearest:
587    case PictFilterFast:
588    case PictFilterGood:
589    case PictFilterBest:
590        break;
591
592    default:
593        GEODE_FALLBACK(("Bilinear or convolution filters are not supported\n"));
594    }
595
596    if (pMsk && pMsk->transform)
597        GEODE_FALLBACK(("Mask transforms are not supported\n"));
598
599    /* Keep an eye out for source rotation transforms - those we can
600     * do something about */
601
602    exaScratch.rotate = RR_Rotate_0;
603    exaScratch.transform = NULL;
604
605    if (pSrc->transform && !lx_process_transform(pSrc))
606        GEODE_FALLBACK(("Transform operation is non-trivial\n"));
607
608    /* XXX - I don't understand PICT_a8 enough - so I'm punting */
609    if ((op != PictOpAdd) && (pSrc->format == PICT_a8 ||
610                              pDst->format == PICT_a8))
611        GEODE_FALLBACK(("PICT_a8 as src or dst format is unsupported\n"));
612
613    if (pMsk && op != PictOpClear) {
614        struct blend_ops_t *opPtr = &lx_alpha_ops[op * 2];
615        int direction = (opPtr->channel == CIMGP_CHANNEL_A_SOURCE) ? 0 : 1;
616
617        /* Direction 0 indicates src->dst, 1 indicates dst->src */
618        if (((direction == 0) &&
619             (pSrc->pDrawable && pSrc->pDrawable->bitsPerPixel < 16)) ||
620            ((direction == 1) && (pDst->pDrawable->bitsPerPixel < 16))) {
621            ErrorF("Mask blending unsupported with <16bpp\n");
622            return FALSE;
623        }
624        if (pMsk->format != PICT_a8 && pMsk->format != PICT_a4)
625            GEODE_FALLBACK(("Masks can be only done with a 8bpp or 4bpp depth\n"));
626
627        /* The pSrc should be 1x1 pixel if the pMsk is not zero */
628        if (pSrc->pDrawable &&
629            (pSrc->pDrawable->width != 1 || pSrc->pDrawable->height != 1))
630            GEODE_FALLBACK(("pSrc should be 1x1 pixel if pMsk is not zero\n"));
631        /* FIXME: In lx_prepare_composite, there are no variables to record the
632         * one pixel source's width and height when the mask is not zero.
633         * That will lead to bigger region to render instead of one pixel in lx
634         * _do_composite, so we should fallback currently to avoid this */
635        /* Not an issue for solid pictures, because we'll treat it as 1x1R too */
636        if (!pSrc->repeat &&
637            !(pSrc->pSourcePict &&
638              pSrc->pSourcePict->type == SourcePictTypeSolidFill)) {
639            GEODE_FALLBACK(("FIXME: unzero mask might lead to bigger rendering region than 1x1 pixels\n"));
640        }
641    }
642    else {
643        if (pSrc->pSourcePict)
644            GEODE_FALLBACK(("Solid source pictures without a mask are not supported\n"));
645    }
646
647    /* Get the formats for the source and destination */
648
649    if ((srcFmt = lx_get_format(pSrc)) == NULL)
650        GEODE_FALLBACK(("Unsupported source format %x\n", pSrc->format));
651
652    if ((dstFmt = lx_get_format(pDst)) == NULL)
653        GEODE_FALLBACK(("Unsupported destination format %x\n", pDst->format));
654
655    /* Make sure operations that need alpha bits have them */
656    /* If a mask is enabled, the alpha will come from there */
657
658    if (!pMsk && (!srcFmt->alphabits && usesSrcAlpha(op)))
659        GEODE_FALLBACK(("Operation requires src alpha, but alphabits is unset\n"));
660
661    if (!pMsk && (!dstFmt->alphabits && usesDstAlpha(op)))
662        GEODE_FALLBACK(("Operation requires dst alpha, but alphabits is unset\n"));
663
664    /* FIXME: See a way around this! */
665    if (srcFmt->alphabits == 0 && dstFmt->alphabits != 0)
666        GEODE_FALLBACK(("src_alphabits=0, dst_alphabits!=0\n"));
667
668    /* If this is a rotate operation, then make sure the src and dst
669     * formats are the same */
670    if (exaScratch.rotate != RR_Rotate_0 && srcFmt != dstFmt) {
671        ErrorF("EXA: Unable to rotate and convert formats at the same time\n");
672        return FALSE;
673    }
674    return TRUE;
675}
676
677static Bool
678lx_prepare_composite(int op, PicturePtr pSrc, PicturePtr pMsk,
679                     PicturePtr pDst, PixmapPtr pxSrc, PixmapPtr pxMsk,
680                     PixmapPtr pxDst)
681{
682    GeodeRec *pGeode = GEODEPTR_FROM_PIXMAP(pxDst);
683    const struct exa_format_t *srcFmt, *dstFmt;
684
685    /* Get the formats for the source and destination */
686
687    srcFmt = lx_get_format(pSrc);
688    dstFmt = lx_get_format(pDst);
689
690    /* Set up the scratch buffer with the information we need */
691
692    exaScratch.srcFormat = (struct exa_format_t *) srcFmt;
693    exaScratch.dstFormat = (struct exa_format_t *) dstFmt;
694    exaScratch.op = op;
695    exaScratch.repeat = pSrc->repeat;
696    exaScratch.bufferOffset = pGeode->exaBfrOffset;
697
698    if (pMsk && op != PictOpClear) {
699        /* Get the source color */
700        if (pSrc->pSourcePict) {
701            exaScratch.srcColor = pSrc->pSourcePict->solidFill.color;
702        }
703        else {
704            /* If the op is PictOpOver(or PictOpOutReverse, PictOpInReverse,
705             * PictOpIn, PictOpOut, PictOpOverReverse), we should get the
706             * ARGB32 source format */
707
708            if ((op == PictOpOver || op == PictOpOutReverse || op ==
709                 PictOpInReverse || op == PictOpIn || op == PictOpOut ||
710                 op == PictOpOverReverse) && (srcFmt->alphabits != 0))
711                exaScratch.srcColor = exaGetPixmapFirstPixel(pxSrc);
712            else if ((op == PictOpOver || op == PictOpOutReverse || op ==
713                      PictOpInReverse || op == PictOpIn || op == PictOpOut ||
714                      op == PictOpOverReverse) && (srcFmt->alphabits == 0))
715                exaScratch.srcColor = lx_get_source_color(pxSrc, pSrc->format,
716                                                          PICT_a8r8g8b8);
717            else
718                exaScratch.srcColor = lx_get_source_color(pxSrc, pSrc->format,
719                                                          pDst->format);
720        }
721
722        /* Save off the info we need (reuse the source values to save space) */
723        exaScratch.type = COMP_TYPE_MASK;
724        exaScratch.maskrepeat = pMsk->repeat;
725
726        exaScratch.srcOffset = exaGetPixmapOffset(pxMsk);
727        exaScratch.srcPitch = exaGetPixmapPitch(pxMsk);
728        exaScratch.srcBpp = (pxMsk->drawable.bitsPerPixel + 7) / 8;
729
730        exaScratch.srcWidth = pMsk->pDrawable->width;
731        exaScratch.srcHeight = pMsk->pDrawable->height;
732
733        /* Flag to indicate if this a 8BPP or a 4BPP mask */
734        exaScratch.fourBpp = (pxMsk->drawable.bitsPerPixel == 4) ? 1 : 0;
735    }
736    else {
737        if (usesPasses(op))
738            exaScratch.type = COMP_TYPE_TWOPASS;
739        else if (exaScratch.rotate != RR_Rotate_0)
740            exaScratch.type = COMP_TYPE_ROTATE;
741        else
742            exaScratch.type = COMP_TYPE_ONEPASS;
743
744        exaScratch.srcOffset = exaGetPixmapOffset(pxSrc);
745        exaScratch.srcPitch = exaGetPixmapPitch(pxSrc);
746        exaScratch.srcBpp = (pxSrc->drawable.bitsPerPixel + 7) / 8;
747
748        exaScratch.srcWidth = pSrc->pDrawable->width;
749        exaScratch.srcHeight = pSrc->pDrawable->height;
750    }
751
752    return TRUE;
753}
754
755static int
756lx_get_bpp_from_format(int format)
757{
758
759    switch (format) {
760    case CIMGP_SOURCE_FMT_8_8_8_8:
761    case CIMGP_SOURCE_FMT_32BPP_BGR:
762        return 32;
763
764    case CIMGP_SOURCE_FMT_4_4_4_4:
765        return 12;
766
767    case CIMGP_SOURCE_FMT_0_5_6_5:
768    case CIMGP_SOURCE_FMT_16BPP_BGR:
769        return 16;
770
771    case CIMGP_SOURCE_FMT_1_5_5_5:
772    case CIMGP_SOURCE_FMT_15BPP_BGR:
773        return 15;
774
775    case CIMGP_SOURCE_FMT_3_3_2:
776        return 8;
777    }
778
779    return 0;
780}
781
782/* BGR needs to be set in the source for it to take - so adjust the source
783 * to enable BGR if the two formats are different, and disable it if they
784 * are the same
785 */
786
787static void
788lx_set_source_format(int srcFormat, int dstFormat)
789{
790    if (!(srcFormat & 0x10) && (dstFormat & 0x10))
791        gp_set_source_format(srcFormat | 0x10);
792    else if ((srcFormat & 0x10) && (dstFormat & 0x10))
793        gp_set_source_format(srcFormat & ~0x10);
794    else
795        gp_set_source_format(srcFormat);
796}
797
798/* If we are converting colors and we need the channel A alpha,
799 * then use a special alpha type that preserves the alpha before
800 * converting the format
801 */
802
803static inline int
804get_op_type(struct exa_format_t *src, struct exa_format_t *dst, int type)
805{
806    return (type == CIMGP_CHANNEL_A_ALPHA &&
807            src->alphabits != dst->alphabits) ? CIMGP_CONVERTED_ALPHA : type;
808}
809
810/* Note - this is the preferred onepass method.  The other will remain
811 * ifdefed out until such time that we are sure its not needed
812 */
813
814#define GetPixmapOffset(px, x, y) ( exaGetPixmapOffset((px)) + \
815  (exaGetPixmapPitch((px)) * (y)) + \
816  ((((px)->drawable.bitsPerPixel + 7) / 8) * (x)) )
817
818#define GetSrcOffset(_x, _y) (exaScratch.srcOffset + ((_y) * exaScratch.srcPitch) + \
819			      ((_x) * exaScratch.srcBpp))
820
821static void
822lx_composite_onepass_add_a8(PixmapPtr pxDst, unsigned long dstOffset,
823                            unsigned long srcOffset, int width, int height,
824                            int opX, int opY, int srcX, int srcY)
825{
826    struct blend_ops_t *opPtr;
827    int apply, type;
828    int optempX, optempY;
829    int i, j;
830    unsigned long pixmapOffset, pixmapPitch, calBitsPixel;
831
832    pixmapOffset = exaGetPixmapOffset(pxDst);
833    pixmapPitch = exaGetPixmapPitch(pxDst);
834    calBitsPixel = (pxDst->drawable.bitsPerPixel + 7) / 8;
835
836    /* Keep this GP idle judge here. Otherwise the SW method has chance to
837     * conflict with the HW rendering method */
838    gp_wait_until_idle();
839
840    if (opX % 4 == 0 && srcX % 4 == 0) {
841        /* HW acceleration */
842        opPtr = &lx_alpha_ops[exaScratch.op * 2];
843        apply = CIMGP_APPLY_BLEND_TO_ALL;
844        gp_declare_blt(0);
845        gp_set_bpp(32);
846        gp_set_strides(exaGetPixmapPitch(pxDst), exaScratch.srcPitch);
847        gp_set_source_format(8);
848        type = opPtr->type;
849        gp_set_alpha_operation(opPtr->operation, type, opPtr->channel, apply,
850                               0);
851        gp_screen_to_screen_convert(dstOffset, srcOffset, width / 4, height, 0);
852        /* Calculate the pixels in the tail of each line */
853        for (j = srcY; j < srcY + height; j++)
854            for (i = srcX + (width / 4) * 4; i < srcX + width; i++) {
855                srcOffset = GetSrcOffset(i, j);
856                optempX = opX + i - srcX;
857                optempY = opY + j - srcY;
858                dstOffset = pixmapOffset + pixmapPitch * optempY +
859                    calBitsPixel * optempX;
860                *(cim_fb_ptr + dstOffset) = (*(cim_fb_ptr + srcOffset)
861                                             + *(cim_fb_ptr + dstOffset) <=
862                                             0xff) ? *(cim_fb_ptr + srcOffset) +
863                    *(cim_fb_ptr + dstOffset) : 0xff;
864            }
865    }
866    else {
867        for (j = srcY; j < srcY + height; j++)
868            for (i = srcX; i < srcX + width; i++) {
869                srcOffset = GetSrcOffset(i, j);
870                optempX = opX + i - srcX;
871                optempY = opY + j - srcY;
872                dstOffset = pixmapOffset + pixmapPitch * optempY +
873                    calBitsPixel * optempX;
874                *(cim_fb_ptr + dstOffset) = (*(cim_fb_ptr + srcOffset) +
875                                             *(cim_fb_ptr + dstOffset) <=
876                                             0xff) ? *(cim_fb_ptr + srcOffset) +
877                    *(cim_fb_ptr + dstOffset) : 0xff;
878            }
879    }
880}
881
882static void
883lx_composite_onepass(PixmapPtr pxDst, unsigned long dstOffset,
884                     unsigned long srcOffset, int width, int height)
885{
886    struct blend_ops_t *opPtr;
887    int apply, type;
888
889    opPtr = &lx_alpha_ops[exaScratch.op * 2];
890
891    apply = (exaScratch.dstFormat->alphabits != 0 &&
892             exaScratch.srcFormat->alphabits != 0) ?
893        CIMGP_APPLY_BLEND_TO_ALL : CIMGP_APPLY_BLEND_TO_RGB;
894
895    gp_declare_blt(0);
896    gp_set_bpp(lx_get_bpp_from_format(exaScratch.dstFormat->fmt));
897    gp_set_strides(exaGetPixmapPitch(pxDst), exaScratch.srcPitch);
898
899    lx_set_source_format(exaScratch.srcFormat->fmt, exaScratch.dstFormat->fmt);
900
901    type = get_op_type(exaScratch.srcFormat, exaScratch.dstFormat, opPtr->type);
902
903    gp_set_alpha_operation(opPtr->operation, type, opPtr->channel, apply, 0);
904
905    gp_screen_to_screen_convert(dstOffset, srcOffset, width, height, 0);
906}
907
908static void
909lx_composite_all_black(unsigned long srcOffset, int width, int height)
910{
911    struct blend_ops_t *opPtr;
912    int apply, type;
913
914    opPtr = &lx_alpha_ops[0];
915    apply = (exaScratch.srcFormat->alphabits != 0) ?
916        CIMGP_APPLY_BLEND_TO_ALL : CIMGP_APPLY_BLEND_TO_RGB;
917    gp_declare_blt(0);
918    gp_set_bpp(lx_get_bpp_from_format(exaScratch.srcFormat->fmt));
919    gp_set_strides(exaScratch.srcPitch, exaScratch.srcPitch);
920    lx_set_source_format(exaScratch.srcFormat->fmt, exaScratch.srcFormat->fmt);
921    type = get_op_type(exaScratch.srcFormat, exaScratch.srcFormat, opPtr->type);
922    gp_set_alpha_operation(opPtr->operation, type, opPtr->channel, apply, 0);
923    gp_screen_to_screen_convert(srcOffset, srcOffset, width, height, 0);
924
925}
926
927static void
928lx_composite_onepass_special(PixmapPtr pxDst, int width, int height, int opX,
929                             int opY, int srcX, int srcY)
930{
931    struct blend_ops_t *opPtr;
932    int apply, type;
933    int opWidth, opHeight;
934    int optempX, optempY;
935    unsigned int dstOffset, srcOffset = 0;
936
937    optempX = opX;
938    optempY = opY;
939
940    /* Make sure srcX and srcY are in source region */
941    srcX = ((srcX % (int) exaScratch.srcWidth) + (int) exaScratch.srcWidth)
942        % (int) exaScratch.srcWidth;
943    srcY = ((srcY % (int) exaScratch.srcHeight) + (int) exaScratch.srcHeight)
944        % (int) exaScratch.srcHeight;
945
946    opWidth = exaScratch.srcWidth - srcX;
947    opHeight = exaScratch.srcHeight - srcY;
948
949    srcOffset = GetSrcOffset(srcX, srcY);
950
951    if (width < opWidth)
952        opWidth = width;
953    if (height < opHeight)
954        opHeight = height;
955
956    while (1) {
957        gp_wait_until_idle();
958        dstOffset = GetPixmapOffset(pxDst, optempX, optempY);
959        opPtr = &lx_alpha_ops[exaScratch.op * 2];
960        apply = (exaScratch.dstFormat->alphabits != 0 &&
961                 exaScratch.srcFormat->alphabits != 0) ?
962            CIMGP_APPLY_BLEND_TO_ALL : CIMGP_APPLY_BLEND_TO_RGB;
963        gp_declare_blt(0);
964        gp_set_bpp(lx_get_bpp_from_format(exaScratch.dstFormat->fmt));
965        gp_set_strides(exaGetPixmapPitch(pxDst), exaScratch.srcPitch);
966        lx_set_source_format(exaScratch.srcFormat->fmt,
967                             exaScratch.dstFormat->fmt);
968        type = get_op_type(exaScratch.srcFormat, exaScratch.dstFormat,
969                           opPtr->type);
970        gp_set_alpha_operation(opPtr->operation, type, opPtr->channel,
971                               apply, 0);
972        gp_screen_to_screen_convert(dstOffset, srcOffset, opWidth, opHeight, 0);
973
974        optempX += opWidth;
975        if (optempX >= opX + width) {
976            optempX = opX;
977            optempY += opHeight;
978            if (optempY >= opY + height)
979                break;
980        }
981        if (optempX == opX) {
982            srcOffset = GetSrcOffset(srcX, 0);
983            opWidth = ((opX + width) - optempX) > (exaScratch.srcWidth - srcX)
984                ? (exaScratch.srcWidth - srcX) : ((opX + width) - optempX);
985            opHeight = ((opY + height) - optempY) > exaScratch.srcHeight
986                ? exaScratch.srcHeight : ((opY + height) - optempY);
987        }
988        else if (optempY == opY) {
989            srcOffset = GetSrcOffset(0, srcY);
990            opWidth = ((opX + width) - optempX) > exaScratch.srcWidth
991                ? exaScratch.srcWidth : ((opX + width) - optempX);
992            opHeight = ((opY + height) - optempY) > (exaScratch.srcHeight -
993                                                     srcY)
994                ? (exaScratch.srcHeight - srcY) : ((opY + height)
995                                                   - optempY);
996        }
997        else {
998            srcOffset = GetSrcOffset(0, 0);
999            opWidth = ((opX + width) - optempX) > exaScratch.srcWidth
1000                ? exaScratch.srcWidth : ((opX + width) - optempX);
1001            opHeight = ((opY + height) - optempY) > exaScratch.srcHeight
1002                ? exaScratch.srcHeight : ((opY + height) - optempY);
1003        }
1004    }
1005}
1006
1007/* This function handles the multipass blend functions */
1008
1009static void
1010lx_composite_multipass(PixmapPtr pxDst, unsigned long dstOffset,
1011                       unsigned long srcOffset, int width, int height)
1012{
1013    struct blend_ops_t *opPtr;
1014    int sbpp = lx_get_bpp_from_format(exaScratch.srcFormat->fmt);
1015    int apply, type;
1016
1017    /* Wait until the GP is idle - this will ensure that the scratch buffer
1018     * isn't occupied */
1019
1020    gp_wait_until_idle();
1021
1022    /* Copy the destination to the scratch buffer, and convert it to the
1023     * source format */
1024
1025    gp_declare_blt(0);
1026
1027    gp_set_bpp(sbpp);
1028    gp_set_source_format(exaScratch.dstFormat->fmt);
1029    gp_set_raster_operation(0xCC);
1030    gp_set_strides(exaScratch.srcPitch, exaGetPixmapPitch(pxDst));
1031    gp_screen_to_screen_convert(exaScratch.bufferOffset, dstOffset,
1032                                width, height, 0);
1033
1034    /* Do the first blend from the source to the scratch buffer */
1035
1036    gp_declare_blt(CIMGP_BLTFLAGS_HAZARD);
1037    gp_set_bpp(sbpp);
1038    gp_set_source_format(exaScratch.srcFormat->fmt);
1039    gp_set_strides(exaScratch.srcPitch, exaScratch.srcPitch);
1040
1041    opPtr = &lx_alpha_ops[exaScratch.op * 2];
1042
1043    apply = (exaScratch.srcFormat->alphabits == 0) ?
1044        CIMGP_APPLY_BLEND_TO_RGB : CIMGP_APPLY_BLEND_TO_ALL;
1045
1046    /* If we're destroying the source alpha bits, then make sure we
1047     * use the alpha before the color conversion
1048     */
1049
1050    gp_screen_to_screen_blt(exaScratch.bufferOffset, srcOffset, width, height,
1051                            0);
1052
1053    /* Finally, do the second blend back to the destination */
1054
1055    opPtr = &lx_alpha_ops[(exaScratch.op * 2) + 1];
1056
1057    apply = (exaScratch.dstFormat->alphabits == 0) ?
1058        CIMGP_APPLY_BLEND_TO_RGB : CIMGP_APPLY_BLEND_TO_ALL;
1059
1060    gp_declare_blt(CIMGP_BLTFLAGS_HAZARD);
1061    gp_set_bpp(lx_get_bpp_from_format(exaScratch.dstFormat->fmt));
1062
1063    lx_set_source_format(exaScratch.srcFormat->fmt, exaScratch.dstFormat->fmt);
1064
1065    type = get_op_type(exaScratch.srcFormat, exaScratch.dstFormat, opPtr->type);
1066
1067    gp_set_alpha_operation(opPtr->operation, type, opPtr->channel, apply, 0);
1068
1069    gp_screen_to_screen_convert(dstOffset, exaScratch.bufferOffset,
1070                                width, height, 0);
1071}
1072
1073static void
1074lx_composite_rotate(PixmapPtr pxDst, unsigned long dstOffset,
1075                    unsigned int srcOffset, int width, int height)
1076{
1077    int degrees = 0;
1078
1079    gp_declare_blt(0);
1080    gp_set_bpp(lx_get_bpp_from_format(exaScratch.dstFormat->fmt));
1081    gp_set_strides(exaGetPixmapPitch(pxDst), exaScratch.srcPitch);
1082
1083    lx_set_source_format(exaScratch.srcFormat->fmt, exaScratch.dstFormat->fmt);
1084
1085    gp_set_raster_operation(0xCC);
1086
1087    /* RandR rotation is counter-clockwise, our rotation
1088     * is clockwise, so adjust the numbers accordingly */
1089
1090    switch (exaScratch.rotate) {
1091    case RR_Rotate_90:
1092        degrees = 270;
1093        break;
1094    case RR_Rotate_180:
1095        degrees = 180;
1096        break;
1097    case RR_Rotate_270:
1098        degrees = 90;
1099        break;
1100    }
1101
1102    gp_rotate_blt(dstOffset, srcOffset, width, height, degrees);
1103}
1104
1105static void
1106lx_do_composite_mask(PixmapPtr pxDst, unsigned long dstOffset,
1107                     unsigned int maskOffset, int width, int height)
1108{
1109    struct blend_ops_t *opPtr = &lx_alpha_ops[exaScratch.op * 2];
1110
1111    gp_declare_blt(0);
1112
1113    gp_set_source_format(exaScratch.srcFormat->fmt);
1114    gp_set_strides(exaGetPixmapPitch(pxDst), exaScratch.srcPitch);
1115    gp_set_bpp(lx_get_bpp_from_format(exaScratch.dstFormat->fmt));
1116    gp_set_solid_source(exaScratch.srcColor);
1117
1118    gp_blend_mask_blt(dstOffset, 0, width, height, maskOffset,
1119                      exaScratch.srcPitch, opPtr->operation,
1120                      exaScratch.fourBpp);
1121}
1122
1123static void
1124lx_do_composite_mask_two_pass(PixmapPtr pxDst, unsigned long dstOffset,
1125                              unsigned int maskOffset, int width, int height,
1126                              int opX, int opY, xPointFixed srcPoint)
1127{
1128    int apply, type;
1129    struct blend_ops_t *opPtr;
1130    int opWidth, opHeight;
1131    int opoverX, opoverY;
1132
1133    opoverX = opX;
1134    opoverY = opY;
1135
1136    /* The rendering region should not be bigger than off-screen memory size
1137     * which equals to DEFAULT_EXA_SCRATCH_BFRSZ. If that happens, we split
1138     * the PictOpOver rendering region into several 256KB chunks. And because
1139     * of the Pitch(stride) parameter, so we use maximun width of mask picture.
1140     * that is to say it is a scanline rendering process */
1141    if (width * height * 4 > DEFAULT_EXA_SCRATCH_BFRSZ) {
1142        opWidth = width;
1143        opHeight = DEFAULT_EXA_SCRATCH_BFRSZ / (width * 4);
1144    }
1145    else {
1146        opWidth = width;
1147        opHeight = height;
1148    }
1149
1150    while (1) {
1151
1152        /* Wait until the GP is idle - this will ensure that the scratch buffer
1153         * isn't occupied */
1154
1155        gp_wait_until_idle();
1156
1157        /* Copy the source to the scratch buffer, and do a src * mask raster
1158         * operation */
1159
1160        gp_declare_blt(0);
1161        opPtr = &lx_alpha_ops[(exaScratch.op * 2) + 1];
1162        gp_set_source_format(CIMGP_SOURCE_FMT_8_8_8_8);
1163        gp_set_strides(opWidth * 4, exaScratch.srcPitch);
1164        gp_set_bpp(lx_get_bpp_from_format(CIMGP_SOURCE_FMT_8_8_8_8));
1165        gp_set_solid_source(exaScratch.srcColor);
1166        gp_blend_mask_blt(exaScratch.bufferOffset, 0, opWidth, opHeight,
1167                          maskOffset, exaScratch.srcPitch, opPtr->operation,
1168                          exaScratch.fourBpp);
1169
1170        /* Do a relative operation(refer rendercheck ops.c), and copy the
1171         * operation result to destination */
1172
1173        gp_declare_blt(CIMGP_BLTFLAGS_HAZARD);
1174        opPtr = &lx_alpha_ops[exaScratch.op * 2];
1175        apply = (exaScratch.dstFormat->alphabits == 0) ?
1176            CIMGP_APPLY_BLEND_TO_RGB : CIMGP_APPLY_BLEND_TO_ALL;
1177        gp_set_source_format(CIMGP_SOURCE_FMT_8_8_8_8);
1178        gp_set_strides(exaGetPixmapPitch(pxDst), opWidth * 4);
1179        gp_set_bpp(lx_get_bpp_from_format(exaScratch.dstFormat->fmt));
1180        type = CIMGP_CONVERTED_ALPHA;
1181        gp_set_alpha_operation(opPtr->operation, type, opPtr->channel,
1182                               apply, 0);
1183        gp_screen_to_screen_convert(dstOffset, exaScratch.bufferOffset,
1184                                    opWidth, opHeight, 0);
1185
1186        if (width * height * 4 > DEFAULT_EXA_SCRATCH_BFRSZ) {
1187            /* Finish the rendering */
1188            if (opoverY + opHeight == opY + height)
1189                break;
1190            /* Recalculate the Dest and Mask rendering start point */
1191            srcPoint.y = srcPoint.y + F(opHeight);
1192            opoverY = opoverY + opHeight;
1193            if (opoverY + opHeight > opY + height)
1194                opHeight = opY + height - opoverY;
1195            dstOffset = GetPixmapOffset(pxDst, opoverX, opoverY);
1196            maskOffset = GetSrcOffset(I(srcPoint.x), I(srcPoint.y));
1197        }
1198        else
1199            break;
1200    }
1201}
1202
1203static void
1204transformPoint(PictTransform * t, xPointFixed * point)
1205{
1206    PictVector v;
1207
1208    v.vector[0] = point->x;
1209    v.vector[1] = point->y;
1210    v.vector[2] = xFixed1;
1211
1212    if (t != NULL)
1213        PictureTransformPoint(t, &v);
1214
1215    point->x = v.vector[0];
1216    point->y = v.vector[1];
1217}
1218
1219static void
1220lx_do_composite(PixmapPtr pxDst, int srcX, int srcY, int maskX,
1221                int maskY, int dstX, int dstY, int width, int height)
1222{
1223    unsigned int dstOffset, srcOffset = 0;
1224
1225    xPointFixed srcPoint;
1226
1227    int opX = dstX;
1228    int opY = dstY;
1229    int opWidth = width;
1230    int opHeight = height;
1231
1232    /* Transform the source coordinates */
1233
1234    if (exaScratch.type == COMP_TYPE_MASK) {
1235        srcPoint.x = F(maskX);
1236        srcPoint.y = F(maskY);
1237    }
1238    else {
1239        srcPoint.x = F(srcX);
1240        srcPoint.y = F(srcY);
1241    }
1242
1243    /* srcX, srcY point to the upper right side of the bounding box
1244     * in the unrotated coordinate space.  Depending on the orientation,
1245     * we have to translate the coordinates to point to the origin of
1246     * the rectangle in the source pixmap */
1247
1248    switch (exaScratch.rotate) {
1249    case RR_Rotate_270:
1250        srcPoint.x += F(width);
1251
1252        opWidth = height;
1253        opHeight = width;
1254        break;
1255
1256    case RR_Rotate_180:
1257        srcPoint.x += F(width);
1258        srcPoint.y += F(height);
1259
1260        srcX += width;
1261        srcY += height;
1262        break;
1263
1264    case RR_Rotate_90:
1265        srcPoint.y += F(height);
1266
1267        opWidth = height;
1268        opHeight = width;
1269        break;
1270    }
1271
1272    transformPoint(exaScratch.transform, &srcPoint);
1273
1274    /* Adjust the point to fit into the pixmap */
1275
1276    if (I(srcPoint.x) < 0) {
1277        opWidth += I(srcPoint.x);
1278        srcPoint.x = F(0);
1279    }
1280
1281    if (I(srcPoint.y) < 0) {
1282        opHeight += I(srcPoint.y);
1283        srcPoint.y = F(0);
1284    }
1285
1286    /* Get the source point offset position */
1287
1288    srcOffset = GetSrcOffset(I(srcPoint.x), I(srcPoint.y));
1289
1290    /* When mask exists, exaScratch.srcWidth and exaScratch.srcHeight are
1291     * the source width and source height; Otherwise, they are mask width
1292     * and mask height */
1293    /* exaScratch.repeat is the source repeat attribute
1294     * exaScratch.maskrepeat is the mask repeat attribute */
1295    /* If type is COMP_TYPE_MASK, maskX and maskY are not zero, we should
1296     * subtract them to do the operation in the correct region */
1297
1298    /* FIXME:  Please add the code to handle the condition when the maskX
1299     * and maskY coordinate are negative or greater than
1300     * exaScratch.srcWidth and exaScratch.srcHeight */
1301
1302    if (exaScratch.type == COMP_TYPE_MASK) {
1303        if ((exaScratch.srcWidth - maskX) < opWidth)
1304            opWidth = exaScratch.srcWidth - maskX;
1305        if ((exaScratch.srcHeight - maskY) < opHeight)
1306            opHeight = exaScratch.srcHeight - maskY;
1307    }
1308    else {
1309        if (exaScratch.type == COMP_TYPE_ONEPASS) {
1310            /* This is the condition srcX or/and srcY is/are out of source
1311             * region */
1312            if (((srcY >= 0 && srcY >= exaScratch.srcHeight)
1313                 || (srcX >= 0 && srcX >= exaScratch.srcWidth)) &&
1314                (exaScratch.op == PictOpOver || exaScratch.op == PictOpSrc)) {
1315                if (exaScratch.repeat == 1) {
1316                    opWidth = width;
1317                    opHeight = height;
1318                }
1319                else {
1320                    if (exaScratch.op == PictOpOver)
1321                        return;
1322                    else {
1323                        exaScratch.op = PictOpClear;
1324                        opWidth = width;
1325                        opHeight = height;
1326                    }
1327                }
1328                /* This is the condition srcX or/and srcY is/are in the source
1329                 * region */
1330            }
1331            else if (srcX >= 0 && srcY >= 0 &&
1332                     (exaScratch.op == PictOpOver ||
1333                      exaScratch.op == PictOpSrc)) {
1334                if (exaScratch.repeat == 1) {
1335                    opWidth = width;
1336                    opHeight = height;
1337                }
1338                else {
1339                    if ((exaScratch.srcWidth - srcX) < opWidth)
1340                        opWidth = exaScratch.srcWidth - srcX;
1341                    if ((exaScratch.srcHeight - srcY) < opHeight)
1342                        opHeight = exaScratch.srcHeight - srcY;
1343                }
1344                /* This is the condition srcX or/and srcY is/are negative */
1345            }
1346            else if ((srcX < 0 || srcY < 0) &&
1347                     (exaScratch.op == PictOpOver ||
1348                      exaScratch.op == PictOpSrc)) {
1349                if (exaScratch.repeat == 1) {
1350                    opWidth = width;
1351                    opHeight = height;
1352                }
1353                else {
1354                    /* FIXME: We can't support negative srcX/Y for all corner cases in
1355                     * a sane way without a bit bigger refactoring. So as to avoid
1356                     * gross misrenderings (e.g missing tray icons) in current real-world
1357                     * applications, just shift destination appropriately for now and
1358                     * ignore out of bounds source pixmap zero-vector handling. This is
1359                     * actually correct for PictOpOver, but PictOpSrc out of bounds regions
1360                     * should be blacked out, but aren't - without this workaround however
1361                     * it'd be simply all black instead, which is probably worse till a full
1362                     * clean solution solves it for all cases. */
1363                    if (srcX < 0) {
1364                        opX -= srcX;
1365                        srcX = 0;
1366                    }
1367
1368                    if (srcY < 0) {
1369                        opY -= srcY;
1370                        srcY = 0;
1371                    }
1372
1373                    /* EXA has taken care of adjusting srcWidth if it gets cut on the right */
1374                    width = opWidth = exaScratch.srcWidth;
1375                    /* EXA has taken care of adjusting srcHeight if it gets cut on the bottom */
1376                    height = opHeight = exaScratch.srcHeight;
1377                }
1378            }
1379            else {
1380                if (exaScratch.srcWidth < opWidth)
1381                    opWidth = exaScratch.srcWidth;
1382                if (exaScratch.srcHeight < opHeight)
1383                    opHeight = exaScratch.srcHeight;
1384            }
1385        }
1386        else {
1387            if (exaScratch.rotate == RR_Rotate_180) {
1388            }
1389            else {
1390                if ((exaScratch.srcWidth - srcY) < opWidth)
1391                    opWidth = exaScratch.srcWidth - srcY;
1392                if ((exaScratch.srcHeight - srcX) < opHeight)
1393                    opHeight = exaScratch.srcHeight - srcX;
1394            }
1395        }
1396    }
1397
1398    while (1) {
1399
1400        dstOffset = GetPixmapOffset(pxDst, opX, opY);
1401
1402        switch (exaScratch.type) {
1403
1404        case COMP_TYPE_MASK:{
1405            if (exaScratch.op == PictOpOver || exaScratch.op ==
1406                PictOpOutReverse || exaScratch.op == PictOpInReverse ||
1407                exaScratch.op == PictOpIn || exaScratch.op == PictOpOut ||
1408                exaScratch.op == PictOpOverReverse)
1409                lx_do_composite_mask_two_pass(pxDst, dstOffset,
1410                                              srcOffset, opWidth, opHeight, opX,
1411                                              opY, srcPoint);
1412            else
1413                lx_do_composite_mask(pxDst, dstOffset, srcOffset,
1414                                     opWidth, opHeight);
1415        }
1416            break;
1417
1418        case COMP_TYPE_ONEPASS:
1419            if ((exaScratch.op == PictOpOver || exaScratch.op == PictOpSrc)
1420                && (exaScratch.repeat == 1)) {
1421                lx_composite_onepass_special(pxDst, opWidth, opHeight, opX, opY,
1422                                             srcX, srcY);
1423                return;
1424            }
1425            else if ((exaScratch.op == PictOpAdd) && (exaScratch.srcFormat->exa
1426                                                      == PICT_a8) &&
1427                     (exaScratch.dstFormat->exa == PICT_a8))
1428                lx_composite_onepass_add_a8(pxDst, dstOffset, srcOffset,
1429                                            opWidth, opHeight, opX, opY, srcX,
1430                                            srcY);
1431            else
1432                lx_composite_onepass(pxDst, dstOffset, srcOffset, opWidth,
1433                                     opHeight);
1434            break;
1435
1436        case COMP_TYPE_TWOPASS:
1437            lx_composite_multipass(pxDst, dstOffset, srcOffset, opWidth,
1438                                   opHeight);
1439
1440        case COMP_TYPE_ROTATE:
1441            lx_composite_rotate(pxDst, dstOffset, srcOffset, opWidth, opHeight);
1442            break;
1443        }
1444
1445        opX += opWidth;
1446
1447        if (opX >= dstX + width) {
1448            opX = dstX;
1449            opY += opHeight;
1450
1451            if (opY >= dstY + height)
1452                break;
1453        }
1454
1455        /* FIXME:  Please add the code to handle the condition when the maskX
1456         * and maskY coordinate are negative or greater than
1457         * exaScratch.srcWidth and exaScratch.srcHeight */
1458
1459        if (exaScratch.type == COMP_TYPE_MASK) {
1460            opWidth = ((dstX + width) - opX) > (exaScratch.srcWidth - maskX)
1461                ? (exaScratch.srcWidth - maskX) : (dstX + width) - opX;
1462            opHeight = ((dstY + height) - opY) > (exaScratch.srcHeight - maskY)
1463                ? (exaScratch.srcHeight - maskY) : (dstY + height) - opY;
1464            /* All black out of the mask */
1465            if (!exaScratch.maskrepeat)
1466                exaScratch.srcColor = 0x0;
1467        }
1468        else {
1469            if (exaScratch.type == COMP_TYPE_ONEPASS) {
1470                if (srcX >= 0 && srcY >= 0 && (exaScratch.op == PictOpOver ||
1471                                               exaScratch.op == PictOpSrc ||
1472                                               exaScratch.op == PictOpClear)) {
1473                    opWidth =
1474                        ((dstX + width) - opX) >
1475                        (exaScratch.srcWidth - srcX) ? (exaScratch.srcWidth -
1476                                                        srcX) : (dstX + width)
1477                        - opX;
1478                    opHeight = ((dstY + height) - opY) >
1479                        (exaScratch.srcHeight - srcY) ?
1480                        (exaScratch.srcHeight - srcY) : (dstY + height) - opY;
1481                }
1482                else {
1483                    opWidth = ((dstX + width) - opX) > exaScratch.srcWidth ?
1484                        exaScratch.srcWidth : (dstX + width) - opX;
1485                    opHeight = ((dstY + height) - opY) > exaScratch.srcHeight ?
1486                        exaScratch.srcHeight : (dstY + height) - opY;
1487                }
1488            }
1489            else {
1490                opWidth = ((dstX + width) - opX) > (exaScratch.srcWidth - srcY)
1491                    ? (exaScratch.srcWidth - srcY) : (dstX + width) - opX;
1492                opHeight =
1493                    ((dstY + height) - opY) >
1494                    (exaScratch.srcHeight - srcX) ? (exaScratch.srcHeight -
1495                                                     srcX) : (dstY + height) -
1496                    opY;
1497            }
1498            /* All black out of the source */
1499            if (!exaScratch.repeat && (exaScratch.type == COMP_TYPE_ONEPASS)) {
1500                /* FIXME: We black out the source here, so that any further regions
1501                 * in the loop get handled as a source that's a zero-vector (as
1502                 * defined for out-of-bounds from source pixmap for RepeatModeNone),
1503                 * but this will likely interfere with cases where srcX and/or srcY
1504                 * is negative - as opposed to e.g width being larger than srcWidth,
1505                 * which is exercised in rendercheck (always rectangle in top-left
1506                 * corner).
1507                 * Additionally it forces the drawing into tiles of srcWidth/srcHeight
1508                 * for non-repeat modes too, where we don't really need to tile it like
1509                 * this and could draw the out of bound regions all at once (or at most
1510                 * in 4 operations without the big loop). */
1511                lx_composite_all_black(srcOffset, exaScratch.srcWidth,
1512                                       exaScratch.srcHeight);
1513            }
1514            if (!exaScratch.repeat && (exaScratch.type == COMP_TYPE_ROTATE))
1515                break;
1516        }
1517    }
1518}
1519
1520static void
1521lx_wait_marker(ScreenPtr PScreen, int marker)
1522{
1523    gp_wait_until_idle();
1524}
1525
1526static void
1527lx_done(PixmapPtr ptr)
1528{
1529}
1530
1531#if 0
1532static void
1533lx_upload_to_screen(PixmapPtr pxDst, int x, int y, int w, int h,
1534                    char *src, int src_pitch)
1535{
1536    GeodeRec *pGeode = GEODEPTR_FROM_PIXMAP(pxDst);
1537    int dst_pitch = exaGetPixmapPitch(pxDst);
1538    int cpp = (pxDst->drawable.bitsPerPixel + 7) / 8;
1539
1540    char *dst;
1541    int offset = exaGetPixmapOffset(pxDst);
1542
1543    dst = (char *) (pGeode->FBBase + offset + (y * dst_pitch) + (x * cpp));
1544    int i;
1545
1546    for (i = 0; i < h; i++) {
1547        memcpy(dst, src, w * cpp);
1548        dst += dst_pitch;
1549        src += src_pitch;
1550    }
1551}
1552#endif
1553
1554#if EXA_VERSION_MAJOR > 2 || (EXA_VERSION_MAJOR == 2 && EXA_VERSION_MINOR >= 2)
1555
1556static Bool
1557lx_exa_pixmap_is_offscreen(PixmapPtr pPixmap)
1558{
1559    ScrnInfoPtr pScrni = xf86ScreenToScrn(pPixmap->drawable.pScreen);
1560    GeodeRec *pGeode = GEODEPTR(pScrni);
1561    void *start = (void *) (pGeode->FBBase);
1562    void *end =
1563        (void *) (pGeode->FBBase + pGeode->offscreenStart +
1564                  pGeode->offscreenSize);
1565
1566    if ((void *) pPixmap->devPrivate.ptr >= start &&
1567        (void *) pPixmap->devPrivate.ptr < end)
1568        return TRUE;
1569
1570    return FALSE;
1571}
1572
1573#endif
1574
1575Bool
1576LXExaInit(ScreenPtr pScreen)
1577{
1578    ScrnInfoPtr pScrni = xf86ScreenToScrn(pScreen);
1579    GeodeRec *pGeode = GEODEPTR(pScrni);
1580    ExaDriverPtr pExa = pGeode->pExa;
1581
1582    pExa->exa_major = EXA_VERSION_MAJOR;
1583    pExa->exa_minor = EXA_VERSION_MINOR;
1584
1585    pExa->WaitMarker = lx_wait_marker;
1586
1587    pExa->PrepareSolid = lx_prepare_solid;
1588    pExa->Solid = lx_do_solid;
1589    pExa->DoneSolid = lx_done;
1590
1591    pExa->PrepareCopy = lx_prepare_copy;
1592    pExa->Copy = lx_do_copy;
1593    pExa->DoneCopy = lx_done;
1594
1595    /* Composite */
1596    pExa->CheckComposite = lx_check_composite;
1597    pExa->PrepareComposite = lx_prepare_composite;
1598    pExa->Composite = lx_do_composite;
1599    pExa->DoneComposite = lx_done;
1600    //pExa->UploadToScreen =  lx_upload_to_screen;
1601
1602#if EXA_VERSION_MAJOR > 2 || (EXA_VERSION_MAJOR == 2 && EXA_VERSION_MINOR >= 2)
1603    pExa->PixmapIsOffscreen = lx_exa_pixmap_is_offscreen;
1604#endif
1605
1606    //pExa->flags = EXA_OFFSCREEN_PIXMAPS;
1607
1608    return exaDriverInit(pScreen, pGeode->pExa);
1609}
1610