1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Novell code.
16 : *
17 : * The Initial Developer of the Original Code is Novell.
18 : * Portions created by the Initial Developer are Copyright (C) 2006
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * rocallahan@novell.com
23 : * Vladimir Vukicevic <vladimir@pobox.com>
24 : * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "gfxXlibNativeRenderer.h"
41 :
42 : #include "gfxXlibSurface.h"
43 : #include "gfxImageSurface.h"
44 : #include "gfxContext.h"
45 : #include "gfxAlphaRecovery.h"
46 : #include "cairo-xlib.h"
47 : #include "cairo-xlib-xrender.h"
48 :
49 : #if 0
50 : #include <stdio.h>
51 : #define NATIVE_DRAWING_NOTE(m) fprintf(stderr, m)
52 : #else
53 : #define NATIVE_DRAWING_NOTE(m) do {} while (0)
54 : #endif
55 :
56 : /* We have four basic strategies available:
57 :
58 : 1) 'direct': If the target is an xlib surface, and other conditions are met,
59 : we can pass the underlying drawable directly to the callback.
60 :
61 : 2) 'simple': If the drawing is opaque, or we can draw to a surface with an
62 : alpha channel, then we can create a temporary xlib surface, pass its
63 : underlying drawable to the callback, and composite the result using
64 : cairo.
65 :
66 : 3) 'copy-background': If the drawing is not opaque but the target is
67 : opaque, and we can draw to a surface with format such that pixel
68 : conversion to and from the target format is exact, we can create a
69 : temporary xlib surface, copy the background from the target, pass the
70 : underlying drawable to the callback, and copy back to the target.
71 :
72 : This strategy is not used if the pixel format conversion is not exact,
73 : because that would mean that drawing intended to be very transparent
74 : messes with other content.
75 :
76 : The strategy is prefered over simple for non-opaque drawing and opaque
77 : targets on the same screen as compositing without alpha is a simpler
78 : operation.
79 :
80 : 4) 'alpha-extraction': create a temporary xlib surface, fill with black,
81 : pass its underlying drawable to the callback, copy the results to a
82 : cairo image surface, repeat with a white background, update the on-black
83 : image alpha values by comparing the two images, then paint the on-black
84 : image using cairo.
85 :
86 : Sure would be nice to have an X extension or GL to do this for us on the
87 : server...
88 : */
89 :
90 : static cairo_bool_t
91 0 : _convert_coord_to_int (double coord, PRInt32 *v)
92 : {
93 0 : *v = (PRInt32)coord;
94 : /* XXX allow some tolerance here? */
95 0 : return *v == coord;
96 : }
97 :
98 : static bool
99 0 : _get_rectangular_clip (cairo_t *cr,
100 : const nsIntRect& bounds,
101 : bool *need_clip,
102 : nsIntRect *rectangles, int max_rectangles,
103 : int *num_rectangles)
104 : {
105 : cairo_rectangle_list_t *cliplist;
106 : cairo_rectangle_t *clips;
107 : int i;
108 0 : bool retval = true;
109 :
110 0 : cliplist = cairo_copy_clip_rectangle_list (cr);
111 0 : if (cliplist->status != CAIRO_STATUS_SUCCESS) {
112 0 : retval = false;
113 : NATIVE_DRAWING_NOTE("FALLBACK: non-rectangular clip");
114 0 : goto FINISH;
115 : }
116 :
117 : /* the clip is always in surface backend coordinates (i.e. native backend coords) */
118 0 : clips = cliplist->rectangles;
119 :
120 0 : for (i = 0; i < cliplist->num_rectangles; ++i) {
121 :
122 0 : nsIntRect rect;
123 0 : if (!_convert_coord_to_int (clips[i].x, &rect.x) ||
124 0 : !_convert_coord_to_int (clips[i].y, &rect.y) ||
125 0 : !_convert_coord_to_int (clips[i].width, &rect.width) ||
126 0 : !_convert_coord_to_int (clips[i].height, &rect.height))
127 : {
128 0 : retval = false;
129 : NATIVE_DRAWING_NOTE("FALLBACK: non-integer clip");
130 0 : goto FINISH;
131 : }
132 :
133 0 : if (rect.IsEqualInterior(bounds)) {
134 : /* the bounds are entirely inside the clip region so we don't need to clip. */
135 0 : *need_clip = false;
136 0 : goto FINISH;
137 : }
138 :
139 0 : NS_ASSERTION(bounds.Contains(rect),
140 : "Was expecting to be clipped to bounds");
141 :
142 0 : if (i >= max_rectangles) {
143 0 : retval = false;
144 : NATIVE_DRAWING_NOTE("FALLBACK: unsupported clip rectangle count");
145 0 : goto FINISH;
146 : }
147 :
148 0 : rectangles[i] = rect;
149 : }
150 :
151 0 : *need_clip = true;
152 0 : *num_rectangles = cliplist->num_rectangles;
153 :
154 : FINISH:
155 0 : cairo_rectangle_list_destroy (cliplist);
156 :
157 0 : return retval;
158 : }
159 :
160 : #define MAX_STATIC_CLIP_RECTANGLES 50
161 :
162 : /**
163 : * Try the direct path.
164 : * @return True if we took the direct path
165 : */
166 : bool
167 0 : gfxXlibNativeRenderer::DrawDirect(gfxContext *ctx, nsIntSize size,
168 : PRUint32 flags,
169 : Screen *screen, Visual *visual)
170 : {
171 0 : cairo_t *cr = ctx->GetCairo();
172 :
173 : /* Check that the target surface is an xlib surface. */
174 0 : cairo_surface_t *target = cairo_get_group_target (cr);
175 0 : if (cairo_surface_get_type (target) != CAIRO_SURFACE_TYPE_XLIB) {
176 : NATIVE_DRAWING_NOTE("FALLBACK: non-X surface");
177 0 : return false;
178 : }
179 :
180 : cairo_matrix_t matrix;
181 0 : cairo_get_matrix (cr, &matrix);
182 : double device_offset_x, device_offset_y;
183 0 : cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y);
184 :
185 : /* Draw() checked that the matrix contained only a very-close-to-integer
186 : translation. Here (and in several other places and thebes) device
187 : offsets are assumed to be integer. */
188 0 : NS_ASSERTION(PRInt32(device_offset_x) == device_offset_x &&
189 : PRInt32(device_offset_y) == device_offset_y,
190 : "Expected integer device offsets");
191 : nsIntPoint offset(NS_lroundf(matrix.x0 + device_offset_x),
192 0 : NS_lroundf(matrix.y0 + device_offset_y));
193 :
194 0 : int max_rectangles = 0;
195 0 : if (flags & DRAW_SUPPORTS_CLIP_RECT) {
196 0 : max_rectangles = 1;
197 : }
198 0 : if (flags & DRAW_SUPPORTS_CLIP_LIST) {
199 0 : max_rectangles = MAX_STATIC_CLIP_RECTANGLES;
200 : }
201 :
202 : /* The client won't draw outside the surface so consider this when
203 : analysing clip rectangles. */
204 0 : nsIntRect bounds(offset, size);
205 : bounds.IntersectRect(bounds,
206 : nsIntRect(0, 0,
207 : cairo_xlib_surface_get_width(target),
208 0 : cairo_xlib_surface_get_height(target)));
209 :
210 0 : bool needs_clip = true;
211 0 : nsIntRect rectangles[MAX_STATIC_CLIP_RECTANGLES];
212 0 : int rect_count = 0;
213 :
214 : /* Check that the clip is rectangular and aligned on unit boundaries. */
215 : /* Temporarily set the matrix for _get_rectangular_clip. It's basically
216 : the identity matrix, but we must adjust for the fact that our
217 : offset-rect is in device coordinates. */
218 0 : cairo_identity_matrix (cr);
219 0 : cairo_translate (cr, -device_offset_x, -device_offset_y);
220 : bool have_rectangular_clip =
221 : _get_rectangular_clip (cr, bounds, &needs_clip,
222 0 : rectangles, max_rectangles, &rect_count);
223 0 : cairo_set_matrix (cr, &matrix);
224 0 : if (!have_rectangular_clip)
225 0 : return false;
226 :
227 : /* Stop now if everything is clipped out */
228 0 : if (needs_clip && rect_count == 0)
229 0 : return true;
230 :
231 : /* Check that the screen is supported.
232 : Visuals belong to screens, so, if alternate visuals are not supported,
233 : then alternate screens cannot be supported. */
234 : bool supports_alternate_visual =
235 0 : (flags & DRAW_SUPPORTS_ALTERNATE_VISUAL) != 0;
236 : bool supports_alternate_screen = supports_alternate_visual &&
237 0 : (flags & DRAW_SUPPORTS_ALTERNATE_SCREEN);
238 0 : if (!supports_alternate_screen &&
239 0 : cairo_xlib_surface_get_screen (target) != screen) {
240 : NATIVE_DRAWING_NOTE("FALLBACK: non-default screen");
241 0 : return false;
242 : }
243 :
244 : /* Check that there is a visual */
245 0 : Visual *target_visual = cairo_xlib_surface_get_visual (target);
246 0 : if (!target_visual) {
247 : NATIVE_DRAWING_NOTE("FALLBACK: no Visual for surface");
248 0 : return false;
249 : }
250 : /* Check that the visual is supported */
251 0 : if (!supports_alternate_visual && target_visual != visual) {
252 : // Only the format of the visual is important (not the GLX properties)
253 : // for Xlib or XRender drawing.
254 : XRenderPictFormat *target_format =
255 0 : cairo_xlib_surface_get_xrender_format (target);
256 0 : if (!target_format ||
257 : (target_format !=
258 0 : XRenderFindVisualFormat (DisplayOfScreen(screen), visual))) {
259 : NATIVE_DRAWING_NOTE("FALLBACK: unsupported Visual");
260 0 : return false;
261 : }
262 : }
263 :
264 : /* we're good to go! */
265 : NATIVE_DRAWING_NOTE("TAKING FAST PATH\n");
266 0 : cairo_surface_flush (target);
267 0 : nsRefPtr<gfxASurface> surface = gfxASurface::Wrap(target);
268 0 : nsresult rv = DrawWithXlib(static_cast<gfxXlibSurface*>(surface.get()),
269 : offset, rectangles,
270 0 : needs_clip ? rect_count : 0);
271 0 : if (NS_SUCCEEDED(rv)) {
272 0 : cairo_surface_mark_dirty (target);
273 0 : return true;
274 : }
275 0 : return false;
276 : }
277 :
278 : static bool
279 0 : VisualHasAlpha(Screen *screen, Visual *visual) {
280 : // There may be some other visuals format with alpha but usually this is
281 : // the only one we care about.
282 : return visual->c_class == TrueColor &&
283 : visual->bits_per_rgb == 8 &&
284 : visual->red_mask == 0xff0000 &&
285 : visual->green_mask == 0xff00 &&
286 : visual->blue_mask == 0xff &&
287 0 : gfxXlibSurface::DepthOfVisual(screen, visual) == 32;
288 : }
289 :
290 : // Returns whether pixel conversion between visual and format is exact (in
291 : // both directions).
292 : static bool
293 0 : FormatConversionIsExact(Screen *screen, Visual *visual, XRenderPictFormat *format) {
294 0 : if (!format ||
295 : visual->c_class != TrueColor ||
296 : format->type != PictTypeDirect ||
297 0 : gfxXlibSurface::DepthOfVisual(screen, visual) != format->depth)
298 0 : return false;
299 :
300 : XRenderPictFormat *visualFormat =
301 0 : XRenderFindVisualFormat(DisplayOfScreen(screen), visual);
302 :
303 0 : if (visualFormat->type != PictTypeDirect )
304 0 : return false;
305 :
306 0 : const XRenderDirectFormat& a = visualFormat->direct;
307 0 : const XRenderDirectFormat& b = format->direct;
308 : return a.redMask == b.redMask &&
309 : a.greenMask == b.greenMask &&
310 0 : a.blueMask == b.blueMask;
311 : }
312 :
313 : // The 3 non-direct strategies described above.
314 : // The surface format and strategy are inter-dependent.
315 : enum DrawingMethod {
316 : eSimple,
317 : eCopyBackground,
318 : eAlphaExtraction
319 : };
320 :
321 : static already_AddRefed<gfxXlibSurface>
322 0 : CreateTempXlibSurface (gfxASurface *destination, nsIntSize size,
323 : bool canDrawOverBackground,
324 : PRUint32 flags, Screen *screen, Visual *visual,
325 : DrawingMethod *method)
326 : {
327 0 : bool drawIsOpaque = (flags & gfxXlibNativeRenderer::DRAW_IS_OPAQUE) != 0;
328 : bool supportsAlternateVisual =
329 0 : (flags & gfxXlibNativeRenderer::DRAW_SUPPORTS_ALTERNATE_VISUAL) != 0;
330 : bool supportsAlternateScreen = supportsAlternateVisual &&
331 0 : (flags & gfxXlibNativeRenderer::DRAW_SUPPORTS_ALTERNATE_SCREEN);
332 :
333 0 : cairo_surface_t *target = destination->CairoSurface();
334 0 : cairo_surface_type_t target_type = cairo_surface_get_type (target);
335 0 : cairo_content_t target_content = cairo_surface_get_content (target);
336 :
337 : Screen *target_screen = target_type == CAIRO_SURFACE_TYPE_XLIB ?
338 0 : cairo_xlib_surface_get_screen (target) : screen;
339 :
340 : // When the background has an alpha channel, we need to draw with an alpha
341 : // channel anyway, so there is no need to copy the background. If
342 : // doCopyBackground is set here, we'll also need to check below that the
343 : // background can copied without any loss in format conversions.
344 0 : bool doCopyBackground = !drawIsOpaque && canDrawOverBackground &&
345 0 : target_content == CAIRO_CONTENT_COLOR;
346 :
347 0 : if (supportsAlternateScreen && screen != target_screen && drawIsOpaque) {
348 : // Prefer a visual on the target screen.
349 : // (If !drawIsOpaque, we'll need doCopyBackground or an alpha channel.)
350 0 : visual = DefaultVisualOfScreen(target_screen);
351 0 : screen = target_screen;
352 :
353 0 : } else if (doCopyBackground || (supportsAlternateVisual && drawIsOpaque)) {
354 : // Analyse the pixel formats either to check whether we can
355 : // doCopyBackground or to see if we can find a better visual for
356 : // opaque drawing.
357 0 : Visual *target_visual = NULL;
358 0 : XRenderPictFormat *target_format = NULL;
359 0 : switch (target_type) {
360 : case CAIRO_SURFACE_TYPE_XLIB:
361 0 : target_visual = cairo_xlib_surface_get_visual (target);
362 0 : target_format = cairo_xlib_surface_get_xrender_format (target);
363 0 : break;
364 : case CAIRO_SURFACE_TYPE_IMAGE: {
365 : gfxASurface::gfxImageFormat imageFormat =
366 0 : static_cast<gfxImageSurface*>(destination)->Format();
367 0 : target_visual = gfxXlibSurface::FindVisual(screen, imageFormat);
368 0 : Display *dpy = DisplayOfScreen(screen);
369 0 : if (target_visual) {
370 0 : target_format = XRenderFindVisualFormat(dpy, target_visual);
371 : } else {
372 : target_format =
373 0 : gfxXlibSurface::FindRenderFormat(dpy, imageFormat);
374 : }
375 0 : break;
376 : }
377 : default:
378 0 : break;
379 : }
380 :
381 0 : if (supportsAlternateVisual &&
382 : (supportsAlternateScreen || screen == target_screen)) {
383 0 : if (target_visual) {
384 0 : visual = target_visual;
385 0 : screen = target_screen;
386 : }
387 : }
388 : // Could try harder to match formats across screens for background
389 : // copying when !supportsAlternateScreen, if we cared. Preferably
390 : // we'll find a visual below with an alpha channel anyway; if so, the
391 : // background won't need to be copied.
392 :
393 0 : if (doCopyBackground && visual != target_visual &&
394 0 : !FormatConversionIsExact(screen, visual, target_format)) {
395 0 : doCopyBackground = false;
396 : }
397 : }
398 :
399 0 : if (supportsAlternateVisual && !drawIsOpaque &&
400 : (screen != target_screen ||
401 0 : !(doCopyBackground || VisualHasAlpha(screen, visual)))) {
402 : // Try to find a visual with an alpha channel.
403 : Screen *visualScreen =
404 0 : supportsAlternateScreen ? target_screen : screen;
405 : Visual *argbVisual =
406 : gfxXlibSurface::FindVisual(visualScreen,
407 0 : gfxASurface::ImageFormatARGB32);
408 0 : if (argbVisual) {
409 0 : visual = argbVisual;
410 0 : screen = visualScreen;
411 0 : } else if (!doCopyBackground &&
412 0 : gfxXlibSurface::DepthOfVisual(screen, visual) != 24) {
413 : // Will need to do alpha extraction; prefer a 24-bit visual.
414 : // No advantage in using the target screen.
415 : Visual *rgb24Visual =
416 : gfxXlibSurface::FindVisual(screen,
417 0 : gfxASurface::ImageFormatRGB24);
418 0 : if (rgb24Visual) {
419 0 : visual = rgb24Visual;
420 : }
421 : }
422 : }
423 :
424 : Drawable drawable =
425 : (screen == target_screen && target_type == CAIRO_SURFACE_TYPE_XLIB) ?
426 0 : cairo_xlib_surface_get_drawable (target) : RootWindowOfScreen(screen);
427 :
428 : nsRefPtr<gfxXlibSurface> surface =
429 : gfxXlibSurface::Create(screen, visual,
430 : gfxIntSize(size.width, size.height),
431 0 : drawable);
432 :
433 0 : if (drawIsOpaque ||
434 0 : surface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA) {
435 : NATIVE_DRAWING_NOTE(drawIsOpaque ?
436 : ", SIMPLE OPAQUE\n" : ", SIMPLE WITH ALPHA");
437 0 : *method = eSimple;
438 0 : } else if (doCopyBackground) {
439 : NATIVE_DRAWING_NOTE(", COPY BACKGROUND\n");
440 0 : *method = eCopyBackground;
441 : } else {
442 : NATIVE_DRAWING_NOTE(", SLOW ALPHA EXTRACTION\n");
443 0 : *method = eAlphaExtraction;
444 : }
445 :
446 0 : return surface.forget();
447 : }
448 :
449 : bool
450 0 : gfxXlibNativeRenderer::DrawOntoTempSurface(gfxXlibSurface *tempXlibSurface,
451 : nsIntPoint offset)
452 : {
453 0 : tempXlibSurface->Flush();
454 : /* no clipping is needed because the callback can't draw outside the native
455 : surface anyway */
456 0 : nsresult rv = DrawWithXlib(tempXlibSurface, offset, NULL, 0);
457 0 : tempXlibSurface->MarkDirty();
458 0 : return NS_SUCCEEDED(rv);
459 : }
460 :
461 : static already_AddRefed<gfxImageSurface>
462 0 : CopyXlibSurfaceToImage(gfxXlibSurface *tempXlibSurface,
463 : gfxASurface::gfxImageFormat format)
464 : {
465 : nsRefPtr<gfxImageSurface> result =
466 0 : new gfxImageSurface(tempXlibSurface->GetSize(), format);
467 :
468 0 : gfxContext copyCtx(result);
469 0 : copyCtx.SetSource(tempXlibSurface);
470 0 : copyCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
471 0 : copyCtx.Paint();
472 :
473 0 : return result.forget();
474 : }
475 :
476 : void
477 0 : gfxXlibNativeRenderer::Draw(gfxContext* ctx, nsIntSize size,
478 : PRUint32 flags, Screen *screen, Visual *visual,
479 : DrawOutput* result)
480 : {
481 0 : if (result) {
482 0 : result->mSurface = NULL;
483 0 : result->mUniformAlpha = false;
484 0 : result->mUniformColor = false;
485 : }
486 :
487 0 : bool drawIsOpaque = (flags & DRAW_IS_OPAQUE) != 0;
488 0 : gfxMatrix matrix = ctx->CurrentMatrix();
489 :
490 : // We can only draw direct or onto a copied background if pixels align and
491 : // native drawing is compatible with the current operator. (The matrix is
492 : // actually also pixel-exact for flips and right-angle rotations, which
493 : // would permit copying the background but not drawing direct.)
494 0 : bool matrixIsIntegerTranslation = !matrix.HasNonIntegerTranslation();
495 : bool canDrawOverBackground = matrixIsIntegerTranslation &&
496 0 : ctx->CurrentOperator() == gfxContext::OPERATOR_OVER;
497 :
498 : // The padding of 0.5 for non-pixel-exact transformations used here is
499 : // the same as what _cairo_pattern_analyze_filter uses.
500 0 : const gfxFloat filterRadius = 0.5;
501 0 : gfxRect affectedRect(0.0, 0.0, size.width, size.height);
502 0 : if (!matrixIsIntegerTranslation) {
503 : // The filter footprint means that the affected rectangle is a
504 : // little larger than the drawingRect;
505 0 : affectedRect.Inflate(filterRadius);
506 :
507 : NATIVE_DRAWING_NOTE("FALLBACK: matrix not integer translation");
508 0 : } else if (!canDrawOverBackground) {
509 : NATIVE_DRAWING_NOTE("FALLBACK: unsupported operator");
510 : }
511 :
512 : // Clipping to the region affected by drawing allows us to consider only
513 : // the portions of the clip region that will be affected by drawing.
514 0 : gfxRect clipExtents;
515 : {
516 0 : gfxContextAutoSaveRestore autoSR(ctx);
517 0 : ctx->Clip(affectedRect);
518 :
519 0 : clipExtents = ctx->GetClipExtents();
520 0 : if (clipExtents.IsEmpty())
521 : return; // nothing to do
522 :
523 0 : if (canDrawOverBackground &&
524 0 : DrawDirect(ctx, size, flags, screen, visual))
525 : return;
526 : }
527 :
528 0 : nsIntRect drawingRect(nsIntPoint(0, 0), size);
529 : // Drawing need only be performed within the clip extents
530 : // (and padding for the filter).
531 0 : if (!matrixIsIntegerTranslation) {
532 : // The source surface may need to be a little larger than the clip
533 : // extents due to the filter footprint.
534 0 : clipExtents.Inflate(filterRadius);
535 : }
536 0 : clipExtents.RoundOut();
537 :
538 0 : nsIntRect intExtents(PRInt32(clipExtents.X()),
539 0 : PRInt32(clipExtents.Y()),
540 0 : PRInt32(clipExtents.Width()),
541 0 : PRInt32(clipExtents.Height()));
542 0 : drawingRect.IntersectRect(drawingRect, intExtents);
543 0 : gfxPoint offset(drawingRect.x, drawingRect.y);
544 :
545 : DrawingMethod method;
546 0 : nsRefPtr<gfxASurface> target = ctx->CurrentSurface();
547 : nsRefPtr<gfxXlibSurface> tempXlibSurface =
548 : CreateTempXlibSurface(target, drawingRect.Size(),
549 : canDrawOverBackground, flags, screen, visual,
550 0 : &method);
551 0 : if (!tempXlibSurface)
552 : return;
553 :
554 0 : if (drawingRect.Size() != size || method == eCopyBackground) {
555 : // Only drawing a portion, or copying background,
556 : // so won't return a result.
557 0 : result = NULL;
558 : }
559 :
560 0 : nsRefPtr<gfxContext> tmpCtx;
561 0 : if (!drawIsOpaque) {
562 0 : tmpCtx = new gfxContext(tempXlibSurface);
563 0 : if (method == eCopyBackground) {
564 0 : tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
565 0 : tmpCtx->SetSource(target, -(offset + matrix.GetTranslation()));
566 : // The copy from the tempXlibSurface to the target context should
567 : // use operator SOURCE, but that would need a mask to bound the
568 : // operation. Here we only copy opaque backgrounds so operator
569 : // OVER will behave like SOURCE masked by the surface.
570 0 : NS_ASSERTION(tempXlibSurface->GetContentType()
571 : == gfxASurface::CONTENT_COLOR,
572 : "Don't copy background with a transparent surface");
573 : } else {
574 0 : tmpCtx->SetOperator(gfxContext::OPERATOR_CLEAR);
575 : }
576 0 : tmpCtx->Paint();
577 : }
578 :
579 0 : if (!DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft())) {
580 : return;
581 : }
582 :
583 0 : if (method != eAlphaExtraction) {
584 0 : ctx->SetSource(tempXlibSurface, offset);
585 0 : ctx->Paint();
586 0 : if (result) {
587 0 : result->mSurface = tempXlibSurface;
588 : /* fill in the result with what we know, which is really just what our
589 : assumption was */
590 0 : result->mUniformAlpha = true;
591 0 : result->mColor.a = 1.0;
592 : }
593 : return;
594 : }
595 :
596 : nsRefPtr<gfxImageSurface> blackImage =
597 0 : CopyXlibSurfaceToImage(tempXlibSurface, gfxASurface::ImageFormatARGB32);
598 :
599 0 : tmpCtx->SetDeviceColor(gfxRGBA(1.0, 1.0, 1.0));
600 0 : tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
601 0 : tmpCtx->Paint();
602 0 : DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft());
603 : nsRefPtr<gfxImageSurface> whiteImage =
604 0 : CopyXlibSurfaceToImage(tempXlibSurface, gfxASurface::ImageFormatRGB24);
605 :
606 0 : if (blackImage->CairoStatus() == CAIRO_STATUS_SUCCESS &&
607 0 : whiteImage->CairoStatus() == CAIRO_STATUS_SUCCESS) {
608 : gfxAlphaRecovery::Analysis analysis;
609 0 : if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage,
610 0 : result ? &analysis : nsnull))
611 : return;
612 :
613 0 : ctx->SetSource(blackImage, offset);
614 :
615 : /* if the caller wants to retrieve the rendered image, put it into
616 : a 'similar' surface, and use that as the source for the drawing right
617 : now. This means we always return a surface similar to the surface
618 : used for 'cr', which is ideal if it's going to be cached and reused.
619 : We do not return an image if the result has uniform color (including
620 : alpha). */
621 0 : if (result) {
622 0 : if (analysis.uniformAlpha) {
623 0 : result->mUniformAlpha = true;
624 0 : result->mColor.a = analysis.alpha;
625 : }
626 0 : if (analysis.uniformColor) {
627 0 : result->mUniformColor = true;
628 0 : result->mColor.r = analysis.r;
629 0 : result->mColor.g = analysis.g;
630 0 : result->mColor.b = analysis.b;
631 : } else {
632 0 : result->mSurface = target->
633 : CreateSimilarSurface(gfxASurface::CONTENT_COLOR_ALPHA,
634 0 : gfxIntSize(size.width, size.height));
635 :
636 0 : gfxContext copyCtx(result->mSurface);
637 0 : copyCtx.SetSource(blackImage);
638 0 : copyCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
639 0 : copyCtx.Paint();
640 :
641 0 : ctx->SetSource(result->mSurface);
642 : }
643 : }
644 :
645 0 : ctx->Paint();
646 : }
647 : }
|