LCOV - code coverage report
Current view: directory - gfx/thebes - gfxXlibNativeRenderer.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 239 0 0.0 %
Date: 2012-06-02 Functions: 9 0 0.0 %

       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                 : }

Generated by: LCOV version 1.7