2011-06-24 21:41:16 +04:00
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2012-05-21 15:12:37 +04:00
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/. */
2011-06-24 21:41:16 +04:00
# include "DrawTargetCairo.h"
2012-01-10 01:50:01 +04:00
2011-06-24 21:41:16 +04:00
# include "SourceSurfaceCairo.h"
2012-01-10 02:15:10 +04:00
# include "PathCairo.h"
# include "HelpersCairo.h"
2012-01-27 22:08:46 +04:00
# include "ScaledFontBase.h"
2013-09-23 07:28:16 +04:00
# include "BorrowedContext.h"
2013-11-27 15:22:56 +04:00
# include "FilterNodeSoftware.h"
2014-05-23 04:06:16 +04:00
# include "mozilla/Scoped.h"
2014-12-19 08:23:28 +03:00
# include "mozilla/Vector.h"
2011-06-24 21:41:16 +04:00
2012-01-10 01:50:01 +04:00
# include "cairo.h"
2012-09-03 03:07:06 +04:00
# include "cairo-tee.h"
2012-07-28 01:51:53 +04:00
# include <string.h>
2012-01-10 01:50:01 +04:00
# include "Blur.h"
2012-07-24 14:18:37 +04:00
# include "Logging.h"
2012-07-24 14:18:38 +04:00
# include "Tools.h"
2012-01-10 01:50:01 +04:00
# ifdef CAIRO_HAS_QUARTZ_SURFACE
# include "cairo-quartz.h"
2015-06-12 15:48:42 +03:00
# ifdef MOZ_WIDGET_COCOA
2012-01-10 01:50:01 +04:00
# include <ApplicationServices/ApplicationServices.h>
# endif
2015-06-12 15:48:42 +03:00
# endif
2012-01-10 01:50:01 +04:00
# ifdef CAIRO_HAS_XLIB_SURFACE
# include "cairo-xlib.h"
2014-05-23 04:06:16 +04:00
# include "cairo-xlib-xrender.h"
2012-01-10 01:50:01 +04:00
# endif
2013-09-11 09:08:52 +04:00
# ifdef CAIRO_HAS_WIN32_SURFACE
# include "cairo-win32.h"
# endif
2012-01-10 01:50:01 +04:00
# include <algorithm>
2014-11-07 01:07:36 +03:00
// 2^23
# define CAIRO_COORD_MAX (Float(0x7fffff))
2011-06-24 21:41:16 +04:00
namespace mozilla {
2014-05-23 04:06:16 +04:00
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE ( ScopedCairoSurface , cairo_surface_t , cairo_surface_destroy ) ;
2011-06-24 21:41:16 +04:00
namespace gfx {
2013-09-28 18:20:24 +04:00
cairo_surface_t * DrawTargetCairo : : mDummySurface ;
2012-01-10 02:15:10 +04:00
namespace {
2011-06-24 21:41:16 +04:00
2012-01-10 02:15:10 +04:00
// An RAII class to prepare to draw a context and optional path. Saves and
// restores the context on construction/destruction.
class AutoPrepareForDrawing
2011-06-24 21:41:16 +04:00
{
2012-01-10 02:15:10 +04:00
public :
AutoPrepareForDrawing ( DrawTargetCairo * dt , cairo_t * ctx )
: mCtx ( ctx )
2011-06-24 21:41:16 +04:00
{
2012-01-10 02:15:10 +04:00
dt - > PrepareForDrawing ( ctx ) ;
cairo_save ( mCtx ) ;
2012-10-25 06:03:21 +04:00
MOZ_ASSERT ( cairo_status ( mCtx ) | | dt - > GetTransform ( ) = = GetTransform ( ) ) ;
2011-06-24 21:41:16 +04:00
}
2012-01-10 02:15:10 +04:00
AutoPrepareForDrawing ( DrawTargetCairo * dt , cairo_t * ctx , const Path * path )
: mCtx ( ctx )
2012-01-10 01:50:01 +04:00
{
2012-01-10 02:15:10 +04:00
dt - > PrepareForDrawing ( ctx , path ) ;
cairo_save ( mCtx ) ;
2012-10-25 06:03:21 +04:00
MOZ_ASSERT ( cairo_status ( mCtx ) | | dt - > GetTransform ( ) = = GetTransform ( ) ) ;
2012-01-10 01:50:01 +04:00
}
2014-08-15 06:23:22 +04:00
~ AutoPrepareForDrawing ( )
{
cairo_restore ( mCtx ) ;
cairo_status_t status = cairo_status ( mCtx ) ;
if ( status ) {
gfxWarning ( ) < < " DrawTargetCairo context in error state: " < < cairo_status_to_string ( status ) < < " ( " < < status < < " ) " ;
}
}
2012-01-10 01:50:01 +04:00
2012-01-10 02:15:10 +04:00
private :
2012-10-25 06:03:21 +04:00
# ifdef DEBUG
Matrix GetTransform ( )
{
cairo_matrix_t mat ;
cairo_get_matrix ( mCtx , & mat ) ;
return Matrix ( mat . xx , mat . yx , mat . xy , mat . yy , mat . x0 , mat . y0 ) ;
}
# endif
2012-01-10 02:15:10 +04:00
cairo_t * mCtx ;
} ;
2012-01-10 01:50:01 +04:00
2014-11-07 01:07:36 +03:00
/* Clamp r to (0,0) (2^23,2^23)
* these are to be device coordinates .
*
* Returns false if the rectangle is completely out of bounds ,
* true otherwise .
*
* This function assumes that it will be called with a rectangle being
* drawn into a surface with an identity transformation matrix ; that
* is , anything above or to the left of ( 0 , 0 ) will be offscreen .
*
* First it checks if the rectangle is entirely beyond
* CAIRO_COORD_MAX ; if so , it can ' t ever appear on the screen - -
* false is returned .
*
* Then it shifts any rectangles with x / y < 0 so that x and y are = 0 ,
* and adjusts the width and height appropriately . For example , a
* rectangle from ( 0 , - 5 ) with dimensions ( 5 , 10 ) will become a
* rectangle from ( 0 , 0 ) with dimensions ( 5 , 5 ) .
*
* If after negative x / y adjustment to 0 , either the width or height
* is negative , then the rectangle is completely offscreen , and
* nothing is drawn - - false is returned .
*
* Finally , if x + width or y + height are greater than CAIRO_COORD_MAX ,
* the width and height are clamped such x + width or y + height are equal
* to CAIRO_COORD_MAX , and true is returned .
*/
static bool
ConditionRect ( Rect & r ) {
// if either x or y is way out of bounds;
// note that we don't handle negative w/h here
if ( r . X ( ) > CAIRO_COORD_MAX | | r . Y ( ) > CAIRO_COORD_MAX )
return false ;
if ( r . X ( ) < 0.f ) {
r . width + = r . X ( ) ;
if ( r . width < 0.f )
return false ;
r . x = 0.f ;
}
if ( r . XMost ( ) > CAIRO_COORD_MAX ) {
r . width = CAIRO_COORD_MAX - r . X ( ) ;
}
if ( r . Y ( ) < 0.f ) {
r . height + = r . Y ( ) ;
if ( r . Height ( ) < 0.f )
return false ;
r . y = 0.f ;
}
if ( r . YMost ( ) > CAIRO_COORD_MAX ) {
r . height = CAIRO_COORD_MAX - r . Y ( ) ;
}
return true ;
}
2013-08-21 03:52:20 +04:00
2012-01-10 02:15:10 +04:00
} // end anonymous namespace
2012-01-10 01:50:01 +04:00
2013-10-15 05:55:16 +04:00
static bool
SupportsSelfCopy ( cairo_surface_t * surface )
{
switch ( cairo_surface_get_type ( surface ) )
{
# ifdef CAIRO_HAS_QUARTZ_SURFACE
case CAIRO_SURFACE_TYPE_QUARTZ :
return true ;
# endif
# ifdef CAIRO_HAS_WIN32_SURFACE
case CAIRO_SURFACE_TYPE_WIN32 :
case CAIRO_SURFACE_TYPE_WIN32_PRINTING :
return true ;
# endif
default :
return false ;
}
}
2012-01-10 02:19:11 +04:00
static bool
PatternIsCompatible ( const Pattern & aPattern )
{
switch ( aPattern . GetType ( ) )
{
2014-01-10 23:06:17 +04:00
case PatternType : : LINEAR_GRADIENT :
2012-01-10 02:19:11 +04:00
{
const LinearGradientPattern & pattern = static_cast < const LinearGradientPattern & > ( aPattern ) ;
2014-01-10 23:06:16 +04:00
return pattern . mStops - > GetBackendType ( ) = = BackendType : : CAIRO ;
2012-01-10 02:19:11 +04:00
}
2014-01-10 23:06:17 +04:00
case PatternType : : RADIAL_GRADIENT :
2012-01-10 02:19:11 +04:00
{
const RadialGradientPattern & pattern = static_cast < const RadialGradientPattern & > ( aPattern ) ;
2014-01-10 23:06:16 +04:00
return pattern . mStops - > GetBackendType ( ) = = BackendType : : CAIRO ;
2012-01-10 02:19:11 +04:00
}
2012-02-04 16:22:47 +04:00
default :
return true ;
2012-01-10 02:19:11 +04:00
}
}
2012-07-24 14:18:40 +04:00
static cairo_user_data_key_t surfaceDataKey ;
void
ReleaseData ( void * aData )
{
2014-07-09 05:53:48 +04:00
DataSourceSurface * data = static_cast < DataSourceSurface * > ( aData ) ;
data - > Unmap ( ) ;
data - > Release ( ) ;
2012-07-24 14:18:40 +04:00
}
2014-07-01 09:52:51 +04:00
cairo_surface_t *
CopyToImageSurface ( unsigned char * aData ,
2014-09-12 09:11:36 +04:00
const IntRect & aRect ,
2014-07-01 09:52:51 +04:00
int32_t aStride ,
SurfaceFormat aFormat )
{
2015-03-03 19:17:55 +03:00
MOZ_ASSERT ( aData ) ;
2014-07-01 09:52:51 +04:00
cairo_surface_t * surf = cairo_image_surface_create ( GfxFormatToCairoFormat ( aFormat ) ,
2014-09-12 09:11:36 +04:00
aRect . width ,
aRect . height ) ;
2014-07-01 09:52:51 +04:00
// In certain scenarios, requesting larger than 8k image fails. Bug 803568
// covers the details of how to run into it, but the full detailed
// investigation hasn't been done to determine the underlying cause. We
// will just handle the failure to allocate the surface to avoid a crash.
if ( cairo_surface_status ( surf ) ) {
return nullptr ;
}
unsigned char * surfData = cairo_image_surface_get_data ( surf ) ;
int surfStride = cairo_image_surface_get_stride ( surf ) ;
int32_t pixelWidth = BytesPerPixel ( aFormat ) ;
2014-09-12 09:11:36 +04:00
unsigned char * source = aData +
aRect . y * aStride +
aRect . x * pixelWidth ;
2014-09-23 17:44:00 +04:00
MOZ_ASSERT ( aStride > = aRect . width * pixelWidth ) ;
2014-09-12 09:11:36 +04:00
for ( int32_t y = 0 ; y < aRect . height ; + + y ) {
2014-07-01 09:52:51 +04:00
memcpy ( surfData + y * surfStride ,
2014-09-12 09:11:36 +04:00
source + y * aStride ,
aRect . width * pixelWidth ) ;
2014-07-01 09:52:51 +04:00
}
cairo_surface_mark_dirty ( surf ) ;
return surf ;
}
2014-09-12 09:11:36 +04:00
/**
* If aSurface can be represented as a surface of type
* CAIRO_SURFACE_TYPE_IMAGE then returns that surface . Does
* not add a reference .
*/
cairo_surface_t * GetAsImageSurface ( cairo_surface_t * aSurface )
{
if ( cairo_surface_get_type ( aSurface ) = = CAIRO_SURFACE_TYPE_IMAGE ) {
return aSurface ;
# ifdef CAIRO_HAS_WIN32_SURFACE
} else if ( cairo_surface_get_type ( aSurface ) = = CAIRO_SURFACE_TYPE_WIN32 ) {
return cairo_win32_surface_get_image ( aSurface ) ;
# endif
}
return nullptr ;
}
cairo_surface_t * CreateSubImageForData ( unsigned char * aData ,
const IntRect & aRect ,
int aStride ,
SurfaceFormat aFormat )
{
2015-03-03 19:17:55 +03:00
if ( ! aData ) {
gfxWarning ( ) < < " DrawTargetCairo.CreateSubImageForData null aData " ;
return nullptr ;
}
2014-09-12 09:11:36 +04:00
unsigned char * data = aData +
aRect . y * aStride +
aRect . x * BytesPerPixel ( aFormat ) ;
cairo_surface_t * image =
cairo_image_surface_create_for_data ( data ,
GfxFormatToCairoFormat ( aFormat ) ,
aRect . width ,
aRect . height ,
aStride ) ;
cairo_surface_set_device_offset ( image , - aRect . x , - aRect . y ) ;
return image ;
}
/**
* Returns a referenced cairo_surface_t representing the
* sub - image specified by aSubImage .
*/
cairo_surface_t * ExtractSubImage ( cairo_surface_t * aSurface ,
const IntRect & aSubImage ,
SurfaceFormat aFormat )
{
// No need to worry about retaining a reference to the original
// surface since the only caller of this function guarantees
// that aSurface will stay alive as long as the result
cairo_surface_t * image = GetAsImageSurface ( aSurface ) ;
if ( image ) {
image = CreateSubImageForData ( cairo_image_surface_get_data ( image ) ,
aSubImage ,
cairo_image_surface_get_stride ( image ) ,
aFormat ) ;
return image ;
}
cairo_surface_t * similar =
cairo_surface_create_similar ( aSurface ,
cairo_surface_get_content ( aSurface ) ,
aSubImage . width , aSubImage . height ) ;
cairo_t * ctx = cairo_create ( similar ) ;
cairo_set_operator ( ctx , CAIRO_OPERATOR_SOURCE ) ;
cairo_set_source_surface ( ctx , aSurface , - aSubImage . x , - aSubImage . y ) ;
cairo_paint ( ctx ) ;
cairo_destroy ( ctx ) ;
cairo_surface_set_device_offset ( similar , - aSubImage . x , - aSubImage . y ) ;
return similar ;
}
2012-07-24 14:18:40 +04:00
/**
* Returns cairo surface for the given SourceSurface .
* If possible , it will use the cairo_surface associated with aSurface ,
* otherwise , it will create a new cairo_surface .
* In either case , the caller must call cairo_surface_destroy on the
* result when it is done with it .
*/
cairo_surface_t *
2014-09-12 09:11:36 +04:00
GetCairoSurfaceForSourceSurface ( SourceSurface * aSurface ,
bool aExistingOnly = false ,
const IntRect & aSubImage = IntRect ( ) )
2012-07-24 14:18:40 +04:00
{
2014-09-12 09:11:36 +04:00
IntRect subimage = IntRect ( IntPoint ( ) , aSurface - > GetSize ( ) ) ;
if ( ! aSubImage . IsEmpty ( ) ) {
MOZ_ASSERT ( ! aExistingOnly ) ;
MOZ_ASSERT ( subimage . Contains ( aSubImage ) ) ;
subimage = aSubImage ;
}
2014-01-10 22:55:24 +04:00
if ( aSurface - > GetType ( ) = = SurfaceType : : CAIRO ) {
2012-07-24 14:18:40 +04:00
cairo_surface_t * surf = static_cast < SourceSurfaceCairo * > ( aSurface ) - > GetSurface ( ) ;
2014-09-12 09:11:36 +04:00
if ( aSubImage . IsEmpty ( ) ) {
cairo_surface_reference ( surf ) ;
} else {
surf = ExtractSubImage ( surf , subimage , aSurface - > GetFormat ( ) ) ;
}
2012-07-24 14:18:40 +04:00
return surf ;
}
2014-01-10 22:55:24 +04:00
if ( aSurface - > GetType ( ) = = SurfaceType : : CAIRO_IMAGE ) {
2012-07-24 14:18:40 +04:00
cairo_surface_t * surf =
static_cast < const DataSourceSurfaceCairo * > ( aSurface ) - > GetSurface ( ) ;
2014-09-12 09:11:36 +04:00
if ( aSubImage . IsEmpty ( ) ) {
cairo_surface_reference ( surf ) ;
} else {
surf = ExtractSubImage ( surf , subimage , aSurface - > GetFormat ( ) ) ;
}
2012-07-24 14:18:40 +04:00
return surf ;
}
2013-08-21 03:52:20 +04:00
if ( aExistingOnly ) {
return nullptr ;
}
2015-10-18 08:24:48 +03:00
RefPtr < DataSourceSurface > data = aSurface - > GetDataSurface ( ) ;
2012-08-23 00:56:03 +04:00
if ( ! data ) {
return nullptr ;
}
2014-07-09 05:53:48 +04:00
DataSourceSurface : : MappedSurface map ;
if ( ! data - > Map ( DataSourceSurface : : READ , & map ) ) {
return nullptr ;
}
2012-07-24 14:18:40 +04:00
cairo_surface_t * surf =
2014-09-12 09:11:36 +04:00
CreateSubImageForData ( map . mData , subimage ,
map . mStride , data - > GetFormat ( ) ) ;
2013-01-17 17:38:38 +04:00
// In certain scenarios, requesting larger than 8k image fails. Bug 803568
// covers the details of how to run into it, but the full detailed
// investigation hasn't been done to determine the underlying cause. We
// will just handle the failure to allocate the surface to avoid a crash.
2015-03-03 19:17:55 +03:00
if ( ! surf | | cairo_surface_status ( surf ) ) {
if ( surf & & ( cairo_surface_status ( surf ) = = CAIRO_STATUS_INVALID_STRIDE ) ) {
2014-07-01 09:52:51 +04:00
// If we failed because of an invalid stride then copy into
// a new surface with a stride that cairo chooses. No need to
// set user data since we're not dependent on the original
// data.
2014-07-09 05:53:48 +04:00
cairo_surface_t * result =
CopyToImageSurface ( map . mData ,
2014-09-12 09:11:36 +04:00
subimage ,
2014-07-09 05:53:48 +04:00
map . mStride ,
data - > GetFormat ( ) ) ;
data - > Unmap ( ) ;
return result ;
2014-07-01 09:52:51 +04:00
}
2014-07-09 05:53:48 +04:00
data - > Unmap ( ) ;
2013-01-17 17:38:38 +04:00
return nullptr ;
}
2012-07-24 14:18:40 +04:00
cairo_surface_set_user_data ( surf ,
2014-07-09 05:53:48 +04:00
& surfaceDataKey ,
2014-12-31 02:42:48 +03:00
data . forget ( ) . take ( ) ,
2014-07-09 05:53:48 +04:00
ReleaseData ) ;
2012-07-24 14:18:40 +04:00
return surf ;
}
2013-08-21 03:52:20 +04:00
// An RAII class to temporarily clear any device offset set
// on a surface. Note that this does not take a reference to the
// surface.
class AutoClearDeviceOffset
{
public :
2014-09-01 07:31:20 +04:00
explicit AutoClearDeviceOffset ( SourceSurface * aSurface )
2013-08-21 03:52:20 +04:00
: mSurface ( nullptr )
2015-03-17 18:53:46 +03:00
, mX ( 0 )
, mY ( 0 )
2013-08-21 03:52:20 +04:00
{
Init ( aSurface ) ;
}
2014-09-01 07:31:20 +04:00
explicit AutoClearDeviceOffset ( const Pattern & aPattern )
2013-08-21 03:52:20 +04:00
: mSurface ( nullptr )
{
2014-01-10 23:06:17 +04:00
if ( aPattern . GetType ( ) = = PatternType : : SURFACE ) {
2013-08-21 03:52:20 +04:00
const SurfacePattern & pattern = static_cast < const SurfacePattern & > ( aPattern ) ;
Init ( pattern . mSurface ) ;
}
}
~ AutoClearDeviceOffset ( )
{
if ( mSurface ) {
cairo_surface_set_device_offset ( mSurface , mX , mY ) ;
}
}
private :
void Init ( SourceSurface * aSurface )
{
cairo_surface_t * surface = GetCairoSurfaceForSourceSurface ( aSurface , true ) ;
if ( surface ) {
Init ( surface ) ;
cairo_surface_destroy ( surface ) ;
}
}
void Init ( cairo_surface_t * aSurface )
{
mSurface = aSurface ;
cairo_surface_get_device_offset ( mSurface , & mX , & mY ) ;
cairo_surface_set_device_offset ( mSurface , 0 , 0 ) ;
}
cairo_surface_t * mSurface ;
double mX ;
double mY ;
} ;
2015-06-24 03:50:36 +03:00
static inline void
CairoPatternAddGradientStop ( cairo_pattern_t * aPattern ,
const GradientStop & aStop ,
Float aNudge = 0 )
{
cairo_pattern_add_color_stop_rgba ( aPattern , aStop . offset + aNudge ,
aStop . color . r , aStop . color . g , aStop . color . b ,
aStop . color . a ) ;
}
2012-08-14 22:06:12 +04:00
// Never returns nullptr. As such, you must always pass in Cairo-compatible
2012-01-10 02:19:11 +04:00
// patterns, most notably gradients with a GradientStopCairo.
// The pattern returned must have cairo_pattern_destroy() called on it by the
// caller.
// As the cairo_pattern_t returned may depend on the Pattern passed in, the
// lifetime of the cairo_pattern_t returned must not exceed the lifetime of the
// Pattern passed in.
2012-01-10 02:15:10 +04:00
static cairo_pattern_t *
2015-06-24 03:50:36 +03:00
GfxPatternToCairoPattern ( const Pattern & aPattern ,
Float aAlpha ,
const Matrix & aTransform )
2012-01-10 01:50:01 +04:00
{
2012-01-10 02:19:11 +04:00
cairo_pattern_t * pat ;
2013-08-21 03:57:57 +04:00
const Matrix * matrix = nullptr ;
2012-01-10 01:50:01 +04:00
switch ( aPattern . GetType ( ) )
{
2014-01-10 23:06:17 +04:00
case PatternType : : COLOR :
2012-01-10 01:50:01 +04:00
{
Color color = static_cast < const ColorPattern & > ( aPattern ) . mColor ;
pat = cairo_pattern_create_rgba ( color . r , color . g , color . b , color . a * aAlpha ) ;
break ;
}
2014-01-10 23:06:17 +04:00
case PatternType : : SURFACE :
2012-01-10 01:50:01 +04:00
{
const SurfacePattern & pattern = static_cast < const SurfacePattern & > ( aPattern ) ;
2014-09-12 09:11:36 +04:00
cairo_surface_t * surf = GetCairoSurfaceForSourceSurface ( pattern . mSurface ,
false ,
pattern . mSamplingRect ) ;
2014-04-15 08:57:29 +04:00
if ( ! surf )
return nullptr ;
2012-01-10 01:50:01 +04:00
pat = cairo_pattern_create_for_surface ( surf ) ;
2012-09-05 05:01:57 +04:00
2013-08-21 03:57:57 +04:00
matrix = & pattern . mMatrix ;
2012-09-05 05:01:57 +04:00
2012-01-10 01:50:01 +04:00
cairo_pattern_set_filter ( pat , GfxFilterToCairoFilter ( pattern . mFilter ) ) ;
cairo_pattern_set_extend ( pat , GfxExtendToCairoExtend ( pattern . mExtendMode ) ) ;
cairo_surface_destroy ( surf ) ;
break ;
}
2014-01-10 23:06:17 +04:00
case PatternType : : LINEAR_GRADIENT :
2012-01-10 01:50:01 +04:00
{
const LinearGradientPattern & pattern = static_cast < const LinearGradientPattern & > ( aPattern ) ;
2012-01-10 02:19:11 +04:00
pat = cairo_pattern_create_linear ( pattern . mBegin . x , pattern . mBegin . y ,
pattern . mEnd . x , pattern . mEnd . y ) ;
2014-01-10 23:06:16 +04:00
MOZ_ASSERT ( pattern . mStops - > GetBackendType ( ) = = BackendType : : CAIRO ) ;
2012-10-10 14:32:36 +04:00
GradientStopsCairo * cairoStops = static_cast < GradientStopsCairo * > ( pattern . mStops . get ( ) ) ;
cairo_pattern_set_extend ( pat , GfxExtendToCairoExtend ( cairoStops - > GetExtendMode ( ) ) ) ;
2013-08-21 03:57:57 +04:00
matrix = & pattern . mMatrix ;
2012-10-10 14:32:36 +04:00
const std : : vector < GradientStop > & stops = cairoStops - > GetStops ( ) ;
2012-01-10 02:19:11 +04:00
for ( size_t i = 0 ; i < stops . size ( ) ; + + i ) {
2015-06-24 03:50:36 +03:00
CairoPatternAddGradientStop ( pat , stops [ i ] ) ;
2012-01-10 01:50:01 +04:00
}
break ;
}
2014-01-10 23:06:17 +04:00
case PatternType : : RADIAL_GRADIENT :
2012-01-10 01:50:01 +04:00
{
const RadialGradientPattern & pattern = static_cast < const RadialGradientPattern & > ( aPattern ) ;
2012-01-10 02:19:11 +04:00
pat = cairo_pattern_create_radial ( pattern . mCenter1 . x , pattern . mCenter1 . y , pattern . mRadius1 ,
pattern . mCenter2 . x , pattern . mCenter2 . y , pattern . mRadius2 ) ;
2014-01-10 23:06:16 +04:00
MOZ_ASSERT ( pattern . mStops - > GetBackendType ( ) = = BackendType : : CAIRO ) ;
2012-10-10 14:32:36 +04:00
GradientStopsCairo * cairoStops = static_cast < GradientStopsCairo * > ( pattern . mStops . get ( ) ) ;
cairo_pattern_set_extend ( pat , GfxExtendToCairoExtend ( cairoStops - > GetExtendMode ( ) ) ) ;
2013-08-21 03:57:57 +04:00
matrix = & pattern . mMatrix ;
2012-10-10 14:32:36 +04:00
const std : : vector < GradientStop > & stops = cairoStops - > GetStops ( ) ;
2012-01-10 02:19:11 +04:00
for ( size_t i = 0 ; i < stops . size ( ) ; + + i ) {
2015-06-24 03:50:36 +03:00
CairoPatternAddGradientStop ( pat , stops [ i ] ) ;
2012-01-10 01:50:01 +04:00
}
break ;
}
2012-01-10 02:19:11 +04:00
default :
{
// We should support all pattern types!
MOZ_ASSERT ( false ) ;
}
2012-01-10 01:50:01 +04:00
}
2013-08-21 03:57:57 +04:00
// The pattern matrix is a matrix that transforms the pattern into user
// space. Cairo takes a matrix that converts from user space to pattern
// space. Cairo therefore needs the inverse.
if ( matrix ) {
cairo_matrix_t mat ;
GfxMatrixToCairoMatrix ( * matrix , mat ) ;
cairo_matrix_invert ( & mat ) ;
cairo_pattern_set_matrix ( pat , & mat ) ;
}
2012-01-10 01:50:01 +04:00
return pat ;
}
2012-01-10 02:15:10 +04:00
static bool
2012-01-10 01:50:01 +04:00
NeedIntermediateSurface ( const Pattern & aPattern , const DrawOptions & aOptions )
{
// We pre-multiply colours' alpha by the global alpha, so we don't need to
// use an intermediate surface for them.
2014-01-10 23:06:17 +04:00
if ( aPattern . GetType ( ) = = PatternType : : COLOR )
2012-01-10 01:50:01 +04:00
return false ;
if ( aOptions . mAlpha = = 1.0 )
return false ;
return true ;
}
2011-06-24 21:41:16 +04:00
DrawTargetCairo : : DrawTargetCairo ( )
2012-08-14 22:06:12 +04:00
: mContext ( nullptr )
2014-10-28 17:08:25 +03:00
, mSurface ( nullptr )
2013-10-12 00:47:47 +04:00
, mLockedBits ( nullptr )
2011-06-24 21:41:16 +04:00
{
}
DrawTargetCairo : : ~ DrawTargetCairo ( )
{
cairo_destroy ( mContext ) ;
2012-07-24 14:18:38 +04:00
if ( mSurface ) {
cairo_surface_destroy ( mSurface ) ;
}
2013-10-12 00:47:47 +04:00
MOZ_ASSERT ( ! mLockedBits ) ;
2011-06-24 21:41:16 +04:00
}
2014-06-27 13:17:49 +04:00
DrawTargetType
DrawTargetCairo : : GetType ( ) const
{
if ( mContext ) {
cairo_surface_type_t type = cairo_surface_get_type ( mSurface ) ;
if ( type = = CAIRO_SURFACE_TYPE_TEE ) {
type = cairo_surface_get_type ( cairo_tee_surface_index ( mSurface , 0 ) ) ;
MOZ_ASSERT ( type ! = CAIRO_SURFACE_TYPE_TEE , " C'mon! " ) ;
MOZ_ASSERT ( type = = cairo_surface_get_type ( cairo_tee_surface_index ( mSurface , 1 ) ) ,
" What should we do here? " ) ;
}
switch ( type ) {
case CAIRO_SURFACE_TYPE_PDF :
case CAIRO_SURFACE_TYPE_PS :
case CAIRO_SURFACE_TYPE_SVG :
case CAIRO_SURFACE_TYPE_WIN32_PRINTING :
case CAIRO_SURFACE_TYPE_XML :
return DrawTargetType : : VECTOR ;
case CAIRO_SURFACE_TYPE_VG :
case CAIRO_SURFACE_TYPE_GL :
case CAIRO_SURFACE_TYPE_GLITZ :
case CAIRO_SURFACE_TYPE_QUARTZ :
case CAIRO_SURFACE_TYPE_DIRECTFB :
return DrawTargetType : : HARDWARE_RASTER ;
case CAIRO_SURFACE_TYPE_SKIA :
case CAIRO_SURFACE_TYPE_QT :
MOZ_ASSERT ( false , " Can't determine actual DrawTargetType for DrawTargetCairo - assuming SOFTWARE_RASTER " ) ;
// fallthrough
case CAIRO_SURFACE_TYPE_IMAGE :
case CAIRO_SURFACE_TYPE_XLIB :
case CAIRO_SURFACE_TYPE_XCB :
case CAIRO_SURFACE_TYPE_WIN32 :
case CAIRO_SURFACE_TYPE_BEOS :
case CAIRO_SURFACE_TYPE_OS2 :
case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE :
case CAIRO_SURFACE_TYPE_SCRIPT :
case CAIRO_SURFACE_TYPE_RECORDING :
case CAIRO_SURFACE_TYPE_DRM :
case CAIRO_SURFACE_TYPE_SUBSURFACE :
case CAIRO_SURFACE_TYPE_TEE : // included to silence warning about unhandled enum value
return DrawTargetType : : SOFTWARE_RASTER ;
2014-07-02 04:15:51 +04:00
default :
MOZ_CRASH ( " Unsupported cairo surface type " ) ;
2014-06-27 13:17:49 +04:00
}
}
MOZ_ASSERT ( false , " Could not determine DrawTargetType for DrawTargetCairo " ) ;
return DrawTargetType : : SOFTWARE_RASTER ;
}
2012-01-10 01:50:01 +04:00
IntSize
DrawTargetCairo : : GetSize ( )
{
2012-07-24 14:18:38 +04:00
return mSize ;
2012-01-10 01:50:01 +04:00
}
2015-07-07 03:19:56 +03:00
SurfaceFormat
GfxFormatForCairoSurface ( cairo_surface_t * surface )
{
cairo_surface_type_t type = cairo_surface_get_type ( surface ) ;
if ( type = = CAIRO_SURFACE_TYPE_IMAGE ) {
return CairoFormatToGfxFormat ( cairo_image_surface_get_format ( surface ) ) ;
}
# ifdef CAIRO_HAS_XLIB_SURFACE
// xlib is currently the only Cairo backend that creates 16bpp surfaces
if ( type = = CAIRO_SURFACE_TYPE_XLIB & &
cairo_xlib_surface_get_depth ( surface ) = = 16 ) {
2015-10-23 09:01:31 +03:00
return SurfaceFormat : : R5G6B5_UINT16 ;
2015-07-07 03:19:56 +03:00
}
# endif
return CairoContentToGfxFormat ( cairo_surface_get_content ( surface ) ) ;
}
2015-06-17 17:00:52 +03:00
already_AddRefed < SourceSurface >
2011-06-24 21:41:16 +04:00
DrawTargetCairo : : Snapshot ( )
{
2012-09-06 08:07:53 +04:00
if ( mSnapshot ) {
2015-10-18 08:24:48 +03:00
RefPtr < SourceSurface > snapshot ( mSnapshot ) ;
2015-05-01 16:14:16 +03:00
return snapshot . forget ( ) ;
2012-09-06 08:07:53 +04:00
}
2012-07-24 14:18:38 +04:00
IntSize size = GetSize ( ) ;
2012-09-06 08:07:53 +04:00
mSnapshot = new SourceSurfaceCairo ( mSurface ,
size ,
2014-12-18 00:49:57 +03:00
GfxFormatForCairoSurface ( mSurface ) ,
2012-09-06 08:07:53 +04:00
this ) ;
2015-10-18 08:24:48 +03:00
RefPtr < SourceSurface > snapshot ( mSnapshot ) ;
2015-05-01 16:14:16 +03:00
return snapshot . forget ( ) ;
2011-06-24 21:41:16 +04:00
}
2013-10-12 00:47:47 +04:00
bool
DrawTargetCairo : : LockBits ( uint8_t * * aData , IntSize * aSize ,
int32_t * aStride , SurfaceFormat * aFormat )
{
if ( cairo_surface_get_type ( mSurface ) = = CAIRO_SURFACE_TYPE_IMAGE ) {
WillChange ( ) ;
2015-06-26 12:34:00 +03:00
Flush ( ) ;
2013-10-12 00:47:47 +04:00
mLockedBits = cairo_image_surface_get_data ( mSurface ) ;
* aData = mLockedBits ;
* aSize = GetSize ( ) ;
* aStride = cairo_image_surface_get_stride ( mSurface ) ;
* aFormat = GetFormat ( ) ;
return true ;
}
return false ;
}
void
DrawTargetCairo : : ReleaseBits ( uint8_t * aData )
{
MOZ_ASSERT ( mLockedBits = = aData ) ;
mLockedBits = nullptr ;
2015-06-26 12:34:00 +03:00
cairo_surface_mark_dirty ( mSurface ) ;
2013-10-12 00:47:47 +04:00
}
2011-06-24 21:41:16 +04:00
void
DrawTargetCairo : : Flush ( )
{
cairo_surface_t * surf = cairo_get_target ( mContext ) ;
cairo_surface_flush ( surf ) ;
}
2012-01-10 01:50:01 +04:00
void
2012-08-14 22:06:12 +04:00
DrawTargetCairo : : PrepareForDrawing ( cairo_t * aContext , const Path * aPath /* = nullptr */ )
2012-01-10 01:50:01 +04:00
{
2012-01-10 02:15:10 +04:00
WillChange ( aPath ) ;
2012-01-10 01:50:01 +04:00
}
2013-09-28 18:20:24 +04:00
cairo_surface_t *
DrawTargetCairo : : GetDummySurface ( )
{
if ( mDummySurface ) {
return mDummySurface ;
}
mDummySurface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32 , 1 , 1 ) ;
return mDummySurface ;
}
2015-06-05 22:30:13 +03:00
static void
PaintWithAlpha ( cairo_t * aContext , const DrawOptions & aOptions )
{
if ( aOptions . mCompositionOp = = CompositionOp : : OP_SOURCE ) {
// Cairo treats the source operator like a lerp when alpha is < 1.
// Approximate the desired operator by: out = 0; out += src*alpha;
if ( aOptions . mAlpha = = 1 ) {
cairo_set_operator ( aContext , CAIRO_OPERATOR_SOURCE ) ;
cairo_paint ( aContext ) ;
} else {
cairo_set_operator ( aContext , CAIRO_OPERATOR_CLEAR ) ;
cairo_paint ( aContext ) ;
cairo_set_operator ( aContext , CAIRO_OPERATOR_ADD ) ;
cairo_paint_with_alpha ( aContext , aOptions . mAlpha ) ;
}
} else {
cairo_set_operator ( aContext , GfxOpToCairoOp ( aOptions . mCompositionOp ) ) ;
cairo_paint_with_alpha ( aContext , aOptions . mAlpha ) ;
}
}
2011-06-24 21:41:16 +04:00
void
DrawTargetCairo : : DrawSurface ( SourceSurface * aSurface ,
const Rect & aDest ,
const Rect & aSource ,
const DrawSurfaceOptions & aSurfOptions ,
const DrawOptions & aOptions )
{
2012-01-10 02:15:10 +04:00
AutoPrepareForDrawing prep ( this , mContext ) ;
2013-08-21 03:52:20 +04:00
AutoClearDeviceOffset clear ( aSurface ) ;
2012-01-10 01:50:01 +04:00
2011-06-24 21:41:16 +04:00
float sx = aSource . Width ( ) / aDest . Width ( ) ;
float sy = aSource . Height ( ) / aDest . Height ( ) ;
cairo_matrix_t src_mat ;
2012-07-24 14:18:38 +04:00
cairo_matrix_init_translate ( & src_mat , aSource . X ( ) , aSource . Y ( ) ) ;
cairo_matrix_scale ( & src_mat , sx , sy ) ;
2011-06-24 21:41:16 +04:00
2012-07-24 14:18:39 +04:00
cairo_surface_t * surf = GetCairoSurfaceForSourceSurface ( aSurface ) ;
2012-07-26 10:48:24 +04:00
cairo_pattern_t * pat = cairo_pattern_create_for_surface ( surf ) ;
2012-07-24 14:18:39 +04:00
cairo_surface_destroy ( surf ) ;
2011-06-24 21:41:16 +04:00
cairo_pattern_set_matrix ( pat , & src_mat ) ;
cairo_pattern_set_filter ( pat , GfxFilterToCairoFilter ( aSurfOptions . mFilter ) ) ;
2012-07-24 14:18:38 +04:00
cairo_pattern_set_extend ( pat , CAIRO_EXTEND_PAD ) ;
2011-06-24 21:41:16 +04:00
2013-08-21 03:56:27 +04:00
cairo_set_antialias ( mContext , GfxAntialiasToCairoAntialias ( aOptions . mAntialiasMode ) ) ;
2013-09-11 09:08:53 +04:00
// If the destination rect covers the entire clipped area, then unbounded and bounded
// operations are identical, and we don't need to push a group.
bool needsGroup = ! IsOperatorBoundByMask ( aOptions . mCompositionOp ) & &
2013-10-22 14:11:30 +04:00
! aDest . Contains ( GetUserSpaceClip ( ) ) ;
2013-09-11 09:08:53 +04:00
2012-07-26 10:48:24 +04:00
cairo_translate ( mContext , aDest . X ( ) , aDest . Y ( ) ) ;
2013-09-11 09:08:53 +04:00
if ( needsGroup ) {
2012-07-24 14:18:38 +04:00
cairo_push_group ( mContext ) ;
cairo_new_path ( mContext ) ;
cairo_rectangle ( mContext , 0 , 0 , aDest . Width ( ) , aDest . Height ( ) ) ;
cairo_set_source ( mContext , pat ) ;
cairo_fill ( mContext ) ;
cairo_pop_group_to_source ( mContext ) ;
2013-09-11 09:08:53 +04:00
} else {
cairo_new_path ( mContext ) ;
cairo_rectangle ( mContext , 0 , 0 , aDest . Width ( ) , aDest . Height ( ) ) ;
cairo_clip ( mContext ) ;
cairo_set_source ( mContext , pat ) ;
2012-07-24 14:18:38 +04:00
}
2015-06-05 22:30:13 +03:00
PaintWithAlpha ( mContext , aOptions ) ;
2012-01-10 01:50:01 +04:00
2011-06-24 21:41:16 +04:00
cairo_pattern_destroy ( pat ) ;
}
2013-11-27 15:22:56 +04:00
void
DrawTargetCairo : : DrawFilter ( FilterNode * aNode ,
const Rect & aSourceRect ,
const Point & aDestPoint ,
const DrawOptions & aOptions )
{
FilterNodeSoftware * filter = static_cast < FilterNodeSoftware * > ( aNode ) ;
filter - > Draw ( this , aSourceRect , aDestPoint , aOptions ) ;
}
2012-01-10 01:50:01 +04:00
void
DrawTargetCairo : : DrawSurfaceWithShadow ( SourceSurface * aSurface ,
const Point & aDest ,
const Color & aColor ,
const Point & aOffset ,
Float aSigma ,
CompositionOp aOperator )
{
2014-01-10 22:55:24 +04:00
if ( aSurface - > GetType ( ) ! = SurfaceType : : CAIRO ) {
2012-01-10 01:50:01 +04:00
return ;
}
2014-04-15 08:57:29 +04:00
2013-08-21 03:52:20 +04:00
AutoClearDeviceOffset clear ( aSurface ) ;
2012-01-10 01:50:01 +04:00
2012-09-28 21:21:40 +04:00
Float width = Float ( aSurface - > GetSize ( ) . width ) ;
Float height = Float ( aSurface - > GetSize ( ) . height ) ;
2012-08-27 14:27:40 +04:00
SourceSurfaceCairo * source = static_cast < SourceSurfaceCairo * > ( aSurface ) ;
2012-09-03 03:07:06 +04:00
cairo_surface_t * sourcesurf = source - > GetSurface ( ) ;
2012-09-03 03:07:06 +04:00
cairo_surface_t * blursurf ;
cairo_surface_t * surf ;
// We only use the A8 surface for blurred shadows. Unblurred shadows can just
// use the RGBA surface directly.
if ( cairo_surface_get_type ( sourcesurf ) = = CAIRO_SURFACE_TYPE_TEE ) {
blursurf = cairo_tee_surface_index ( sourcesurf , 0 ) ;
surf = cairo_tee_surface_index ( sourcesurf , 1 ) ;
MOZ_ASSERT ( cairo_surface_get_type ( blursurf ) = = CAIRO_SURFACE_TYPE_IMAGE ) ;
Rect extents ( 0 , 0 , width , height ) ;
2013-04-19 14:13:18 +04:00
AlphaBoxBlur blur ( extents ,
2012-09-03 03:07:06 +04:00
cairo_image_surface_get_stride ( blursurf ) ,
2013-11-27 15:22:36 +04:00
aSigma , aSigma ) ;
2013-04-19 14:13:18 +04:00
blur . Blur ( cairo_image_surface_get_data ( blursurf ) ) ;
2012-09-03 03:07:06 +04:00
} else {
blursurf = sourcesurf ;
surf = sourcesurf ;
}
2012-09-03 03:07:05 +04:00
WillChange ( ) ;
ClearSurfaceForUnboundedSource ( aOperator ) ;
2012-10-25 06:03:21 +04:00
2012-01-10 01:50:01 +04:00
cairo_save ( mContext ) ;
2012-07-24 14:18:38 +04:00
cairo_set_operator ( mContext , GfxOpToCairoOp ( aOperator ) ) ;
2012-01-10 01:50:01 +04:00
cairo_identity_matrix ( mContext ) ;
cairo_translate ( mContext , aDest . x , aDest . y ) ;
2012-12-05 04:02:52 +04:00
if ( IsOperatorBoundByMask ( aOperator ) ) {
cairo_set_source_rgba ( mContext , aColor . r , aColor . g , aColor . b , aColor . a ) ;
cairo_mask_surface ( mContext , blursurf , aOffset . x , aOffset . y ) ;
// Now that the shadow has been drawn, we can draw the surface on top.
cairo_set_source_surface ( mContext , surf , 0 , 0 ) ;
cairo_new_path ( mContext ) ;
cairo_rectangle ( mContext , 0 , 0 , width , height ) ;
cairo_fill ( mContext ) ;
} else {
2012-07-24 14:18:38 +04:00
cairo_push_group ( mContext ) ;
cairo_set_source_rgba ( mContext , aColor . r , aColor . g , aColor . b , aColor . a ) ;
cairo_mask_surface ( mContext , blursurf , aOffset . x , aOffset . y ) ;
2012-09-03 03:07:05 +04:00
// Now that the shadow has been drawn, we can draw the surface on top.
cairo_set_source_surface ( mContext , surf , 0 , 0 ) ;
2012-07-24 14:18:38 +04:00
cairo_new_path ( mContext ) ;
cairo_rectangle ( mContext , 0 , 0 , width , height ) ;
cairo_fill ( mContext ) ;
cairo_pop_group_to_source ( mContext ) ;
2012-09-03 03:07:05 +04:00
cairo_paint ( mContext ) ;
2012-07-24 14:18:38 +04:00
}
2012-01-10 01:50:01 +04:00
cairo_restore ( mContext ) ;
}
void
2012-01-10 02:15:10 +04:00
DrawTargetCairo : : DrawPattern ( const Pattern & aPattern ,
2012-01-10 01:50:01 +04:00
const StrokeOptions & aStrokeOptions ,
const DrawOptions & aOptions ,
2013-10-18 03:08:20 +04:00
DrawPatternType aDrawType ,
bool aPathBoundsClip )
2012-01-10 01:50:01 +04:00
{
2012-01-10 02:19:11 +04:00
if ( ! PatternIsCompatible ( aPattern ) ) {
return ;
}
2014-04-15 08:57:29 +04:00
2013-08-21 03:52:20 +04:00
AutoClearDeviceOffset clear ( aPattern ) ;
2012-01-10 02:19:11 +04:00
2015-06-24 03:50:36 +03:00
cairo_pattern_t * pat = GfxPatternToCairoPattern ( aPattern , aOptions . mAlpha , GetTransform ( ) ) ;
2014-07-04 18:55:04 +04:00
if ( ! pat ) {
return ;
}
if ( cairo_pattern_status ( pat ) ) {
cairo_pattern_destroy ( pat ) ;
gfxWarning ( ) < < " Invalid pattern " ;
2014-04-15 08:57:29 +04:00
return ;
2014-07-04 18:55:04 +04:00
}
2014-04-15 08:57:29 +04:00
2012-01-10 02:15:10 +04:00
cairo_set_source ( mContext , pat ) ;
2012-01-10 01:50:01 +04:00
2013-08-21 03:56:27 +04:00
cairo_set_antialias ( mContext , GfxAntialiasToCairoAntialias ( aOptions . mAntialiasMode ) ) ;
2012-07-24 14:18:38 +04:00
if ( NeedIntermediateSurface ( aPattern , aOptions ) | |
2013-10-18 03:08:20 +04:00
( ! IsOperatorBoundByMask ( aOptions . mCompositionOp ) & & ! aPathBoundsClip ) ) {
2012-01-10 01:50:01 +04:00
cairo_push_group_with_content ( mContext , CAIRO_CONTENT_COLOR_ALPHA ) ;
// Don't want operators to be applied twice
2012-01-10 02:15:10 +04:00
cairo_set_operator ( mContext , CAIRO_OPERATOR_OVER ) ;
2012-01-10 01:50:01 +04:00
if ( aDrawType = = DRAW_STROKE ) {
2012-01-10 02:15:10 +04:00
SetCairoStrokeOptions ( mContext , aStrokeOptions ) ;
cairo_stroke_preserve ( mContext ) ;
2012-01-10 01:50:01 +04:00
} else {
2012-01-10 02:15:10 +04:00
cairo_fill_preserve ( mContext ) ;
2012-01-10 01:50:01 +04:00
}
cairo_pop_group_to_source ( mContext ) ;
2012-01-10 02:15:10 +04:00
// Now draw the content using the desired operator
2015-06-05 22:30:13 +03:00
PaintWithAlpha ( mContext , aOptions ) ;
2012-01-10 02:15:10 +04:00
} else {
cairo_set_operator ( mContext , GfxOpToCairoOp ( aOptions . mCompositionOp ) ) ;
if ( aDrawType = = DRAW_STROKE ) {
SetCairoStrokeOptions ( mContext , aStrokeOptions ) ;
cairo_stroke_preserve ( mContext ) ;
} else {
cairo_fill_preserve ( mContext ) ;
}
2012-01-10 01:50:01 +04:00
}
2012-01-10 02:15:10 +04:00
cairo_pattern_destroy ( pat ) ;
2012-01-10 01:50:01 +04:00
}
2011-06-24 21:41:16 +04:00
void
DrawTargetCairo : : FillRect ( const Rect & aRect ,
const Pattern & aPattern ,
const DrawOptions & aOptions )
{
2012-01-10 02:15:10 +04:00
AutoPrepareForDrawing prep ( this , mContext ) ;
2014-11-07 01:07:36 +03:00
bool restoreTransform = false ;
Matrix mat ;
Rect r = aRect ;
/* Clamp coordinates to work around a design bug in cairo */
if ( r . width > CAIRO_COORD_MAX | |
r . height > CAIRO_COORD_MAX | |
r . x < - CAIRO_COORD_MAX | |
r . x > CAIRO_COORD_MAX | |
r . y < - CAIRO_COORD_MAX | |
r . y > CAIRO_COORD_MAX )
{
if ( ! mat . IsRectilinear ( ) ) {
gfxWarning ( ) < < " DrawTargetCairo::FillRect() misdrawing huge Rect "
" with non-rectilinear transform " ;
}
mat = GetTransform ( ) ;
r = mat . TransformBounds ( r ) ;
if ( ! ConditionRect ( r ) ) {
gfxWarning ( ) < < " Ignoring DrawTargetCairo::FillRect() call with "
" out-of-bounds Rect " ;
return ;
}
restoreTransform = true ;
SetTransform ( Matrix ( ) ) ;
}
2012-01-10 02:15:10 +04:00
cairo_new_path ( mContext ) ;
2014-11-07 01:07:36 +03:00
cairo_rectangle ( mContext , r . x , r . y , r . Width ( ) , r . Height ( ) ) ;
2012-01-10 01:50:01 +04:00
2013-10-18 03:08:20 +04:00
bool pathBoundsClip = false ;
2014-11-07 01:07:36 +03:00
if ( r . Contains ( GetUserSpaceClip ( ) ) ) {
2013-10-18 03:08:20 +04:00
pathBoundsClip = true ;
}
DrawPattern ( aPattern , StrokeOptions ( ) , aOptions , DRAW_FILL , pathBoundsClip ) ;
2014-11-07 01:07:36 +03:00
if ( restoreTransform ) {
SetTransform ( mat ) ;
}
2012-01-10 01:50:01 +04:00
}
2013-10-15 05:55:16 +04:00
void
DrawTargetCairo : : CopySurfaceInternal ( cairo_surface_t * aSurface ,
const IntRect & aSource ,
const IntPoint & aDest )
{
2014-07-04 18:55:04 +04:00
if ( cairo_surface_status ( aSurface ) ) {
gfxWarning ( ) < < " Invalid surface " ;
return ;
}
2013-10-15 05:55:16 +04:00
cairo_identity_matrix ( mContext ) ;
cairo_set_source_surface ( mContext , aSurface , aDest . x - aSource . x , aDest . y - aSource . y ) ;
cairo_set_operator ( mContext , CAIRO_OPERATOR_SOURCE ) ;
cairo_set_antialias ( mContext , CAIRO_ANTIALIAS_NONE ) ;
cairo_reset_clip ( mContext ) ;
cairo_new_path ( mContext ) ;
cairo_rectangle ( mContext , aDest . x , aDest . y , aSource . width , aSource . height ) ;
cairo_fill ( mContext ) ;
}
2012-01-10 01:50:01 +04:00
void
DrawTargetCairo : : CopySurface ( SourceSurface * aSurface ,
2012-07-24 14:18:37 +04:00
const IntRect & aSource ,
const IntPoint & aDest )
2012-01-10 01:50:01 +04:00
{
2012-01-10 02:15:10 +04:00
AutoPrepareForDrawing prep ( this , mContext ) ;
2013-08-21 03:52:20 +04:00
AutoClearDeviceOffset clear ( aSurface ) ;
2012-07-24 14:18:37 +04:00
2013-11-08 05:39:34 +04:00
if ( ! aSurface ) {
2012-07-24 14:18:37 +04:00
gfxWarning ( ) < < " Unsupported surface type specified " ;
return ;
}
2013-11-08 05:39:34 +04:00
cairo_surface_t * surf = GetCairoSurfaceForSourceSurface ( aSurface ) ;
if ( ! surf ) {
gfxWarning ( ) < < " Unsupported surface type specified " ;
return ;
}
2012-07-24 14:18:37 +04:00
2013-10-15 05:55:16 +04:00
CopySurfaceInternal ( surf , aSource , aDest ) ;
2013-11-08 05:39:34 +04:00
cairo_surface_destroy ( surf ) ;
2013-10-15 05:55:16 +04:00
}
2012-07-24 14:18:37 +04:00
2013-10-15 05:55:16 +04:00
void
DrawTargetCairo : : CopyRect ( const IntRect & aSource ,
const IntPoint & aDest )
{
AutoPrepareForDrawing prep ( this , mContext ) ;
2012-07-24 14:18:37 +04:00
2013-10-15 05:55:16 +04:00
IntRect source = aSource ;
cairo_surface_t * surf = mSurface ;
if ( ! SupportsSelfCopy ( mSurface ) & &
aDest . y > = aSource . y & &
aDest . y < aSource . YMost ( ) ) {
cairo_surface_t * similar = cairo_surface_create_similar ( mSurface ,
GfxFormatToCairoContent ( GetFormat ( ) ) ,
aSource . width , aSource . height ) ;
cairo_t * ctx = cairo_create ( similar ) ;
cairo_set_operator ( ctx , CAIRO_OPERATOR_SOURCE ) ;
cairo_set_source_surface ( ctx , surf , - aSource . x , - aSource . y ) ;
cairo_paint ( ctx ) ;
cairo_destroy ( ctx ) ;
source . x = 0 ;
source . y = 0 ;
surf = similar ;
}
CopySurfaceInternal ( surf , source , aDest ) ;
if ( surf ! = mSurface ) {
cairo_surface_destroy ( surf ) ;
}
2012-01-10 01:50:01 +04:00
}
void
DrawTargetCairo : : ClearRect ( const Rect & aRect )
{
2012-01-10 02:15:10 +04:00
AutoPrepareForDrawing prep ( this , mContext ) ;
2012-01-10 01:50:01 +04:00
2014-12-20 00:06:15 +03:00
if ( ! mContext | | aRect . Width ( ) < = 0 | | aRect . Height ( ) < = 0 | |
! IsFinite ( aRect . X ( ) ) | | ! IsFinite ( aRect . Width ( ) ) | |
! IsFinite ( aRect . Y ( ) ) | | ! IsFinite ( aRect . Height ( ) ) ) {
2015-10-21 15:34:00 +03:00
gfxCriticalNote < < " ClearRect with invalid argument " < < gfx : : hexa ( mContext ) < < " with " < < aRect . Width ( ) < < " x " < < aRect . Height ( ) < < " [ " < < aRect . X ( ) < < " , " < < aRect . Y ( ) < < " ] " ;
2014-12-20 00:06:15 +03:00
}
2013-08-21 03:56:27 +04:00
cairo_set_antialias ( mContext , CAIRO_ANTIALIAS_NONE ) ;
2011-06-24 21:41:16 +04:00
cairo_new_path ( mContext ) ;
2012-01-10 01:50:01 +04:00
cairo_set_operator ( mContext , CAIRO_OPERATOR_CLEAR ) ;
cairo_rectangle ( mContext , aRect . X ( ) , aRect . Y ( ) ,
aRect . Width ( ) , aRect . Height ( ) ) ;
cairo_fill ( mContext ) ;
}
void
DrawTargetCairo : : StrokeRect ( const Rect & aRect ,
const Pattern & aPattern ,
const StrokeOptions & aStrokeOptions /* = StrokeOptions() */ ,
const DrawOptions & aOptions /* = DrawOptions() */ )
{
2012-01-10 02:15:10 +04:00
AutoPrepareForDrawing prep ( this , mContext ) ;
2012-01-10 01:50:01 +04:00
2012-01-10 02:15:10 +04:00
cairo_new_path ( mContext ) ;
cairo_rectangle ( mContext , aRect . x , aRect . y , aRect . Width ( ) , aRect . Height ( ) ) ;
DrawPattern ( aPattern , aStrokeOptions , aOptions , DRAW_STROKE ) ;
2012-01-10 01:50:01 +04:00
}
void
DrawTargetCairo : : StrokeLine ( const Point & aStart ,
const Point & aEnd ,
const Pattern & aPattern ,
const StrokeOptions & aStrokeOptions /* = StrokeOptions() */ ,
const DrawOptions & aOptions /* = DrawOptions() */ )
{
2012-01-10 02:15:10 +04:00
AutoPrepareForDrawing prep ( this , mContext ) ;
2012-01-10 01:50:01 +04:00
2012-01-10 02:15:10 +04:00
cairo_new_path ( mContext ) ;
cairo_move_to ( mContext , aStart . x , aStart . y ) ;
cairo_line_to ( mContext , aEnd . x , aEnd . y ) ;
2011-06-24 21:41:16 +04:00
2012-01-10 02:15:10 +04:00
DrawPattern ( aPattern , aStrokeOptions , aOptions , DRAW_STROKE ) ;
2012-01-10 01:50:01 +04:00
}
void
DrawTargetCairo : : Stroke ( const Path * aPath ,
const Pattern & aPattern ,
const StrokeOptions & aStrokeOptions /* = StrokeOptions() */ ,
const DrawOptions & aOptions /* = DrawOptions() */ )
{
2012-01-10 02:15:10 +04:00
AutoPrepareForDrawing prep ( this , mContext , aPath ) ;
2014-01-10 23:06:16 +04:00
if ( aPath - > GetBackendType ( ) ! = BackendType : : CAIRO )
2012-01-10 02:15:10 +04:00
return ;
PathCairo * path = const_cast < PathCairo * > ( static_cast < const PathCairo * > ( aPath ) ) ;
2013-09-28 18:20:24 +04:00
path - > SetPathOnContext ( mContext ) ;
2012-01-10 02:15:10 +04:00
DrawPattern ( aPattern , aStrokeOptions , aOptions , DRAW_STROKE ) ;
2012-01-10 01:50:01 +04:00
}
void
DrawTargetCairo : : Fill ( const Path * aPath ,
const Pattern & aPattern ,
const DrawOptions & aOptions /* = DrawOptions() */ )
{
2012-01-10 02:15:10 +04:00
AutoPrepareForDrawing prep ( this , mContext , aPath ) ;
2014-01-10 23:06:16 +04:00
if ( aPath - > GetBackendType ( ) ! = BackendType : : CAIRO )
2012-01-10 02:15:10 +04:00
return ;
PathCairo * path = const_cast < PathCairo * > ( static_cast < const PathCairo * > ( aPath ) ) ;
2013-09-28 18:20:24 +04:00
path - > SetPathOnContext ( mContext ) ;
2012-01-10 02:15:10 +04:00
DrawPattern ( aPattern , StrokeOptions ( ) , aOptions , DRAW_FILL ) ;
2012-01-10 01:50:01 +04:00
}
2013-11-05 08:50:56 +04:00
void
DrawTargetCairo : : SetPermitSubpixelAA ( bool aPermitSubpixelAA )
{
DrawTarget : : SetPermitSubpixelAA ( aPermitSubpixelAA ) ;
2013-11-13 17:23:19 +04:00
# ifdef MOZ_TREE_CAIRO
2013-11-05 08:50:56 +04:00
cairo_surface_set_subpixel_antialiasing ( mSurface ,
aPermitSubpixelAA ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED ) ;
2013-11-13 17:23:19 +04:00
# endif
2013-11-05 08:50:56 +04:00
}
2012-01-10 01:50:01 +04:00
void
DrawTargetCairo : : FillGlyphs ( ScaledFont * aFont ,
const GlyphBuffer & aBuffer ,
const Pattern & aPattern ,
2012-03-19 23:20:17 +04:00
const DrawOptions & aOptions ,
const GlyphRenderingOptions * )
2012-01-10 01:50:01 +04:00
{
2012-01-10 02:15:10 +04:00
AutoPrepareForDrawing prep ( this , mContext ) ;
2013-08-21 03:52:20 +04:00
AutoClearDeviceOffset clear ( aPattern ) ;
2012-01-10 22:26:59 +04:00
2012-01-27 22:08:46 +04:00
ScaledFontBase * scaledFont = static_cast < ScaledFontBase * > ( aFont ) ;
2012-01-10 22:26:59 +04:00
cairo_set_scaled_font ( mContext , scaledFont - > GetCairoScaledFont ( ) ) ;
2015-06-24 03:50:36 +03:00
cairo_pattern_t * pat = GfxPatternToCairoPattern ( aPattern , aOptions . mAlpha , GetTransform ( ) ) ;
2014-04-15 08:57:29 +04:00
if ( ! pat )
return ;
2012-01-10 22:26:59 +04:00
cairo_set_source ( mContext , pat ) ;
cairo_pattern_destroy ( pat ) ;
2013-08-21 03:56:27 +04:00
cairo_set_antialias ( mContext , GfxAntialiasToCairoAntialias ( aOptions . mAntialiasMode ) ) ;
2014-12-19 08:23:28 +03:00
// Convert our GlyphBuffer into a vector of Cairo glyphs. This code can
// execute millions of times in short periods, so we want to avoid heap
// allocation whenever possible. So we use an inline vector capacity of 1024
// bytes (the maximum allowed by mozilla::Vector), which gives an inline
// length of 1024 / 24 = 42 elements, which is enough to typically avoid heap
// allocation in ~99% of cases.
Vector < cairo_glyph_t , 1024 / sizeof ( cairo_glyph_t ) > glyphs ;
if ( ! glyphs . resizeUninitialized ( aBuffer . mNumGlyphs ) ) {
MOZ_CRASH ( " glyphs allocation failed " ) ;
}
2012-01-10 22:26:59 +04:00
for ( uint32_t i = 0 ; i < aBuffer . mNumGlyphs ; + + i ) {
glyphs [ i ] . index = aBuffer . mGlyphs [ i ] . mIndex ;
glyphs [ i ] . x = aBuffer . mGlyphs [ i ] . mPosition . x ;
glyphs [ i ] . y = aBuffer . mGlyphs [ i ] . mPosition . y ;
}
cairo_show_glyphs ( mContext , & glyphs [ 0 ] , aBuffer . mNumGlyphs ) ;
2012-01-10 02:15:10 +04:00
}
void
DrawTargetCairo : : Mask ( const Pattern & aSource ,
const Pattern & aMask ,
const DrawOptions & aOptions /* = DrawOptions() */ )
{
AutoPrepareForDrawing prep ( this , mContext ) ;
2013-08-21 03:52:20 +04:00
AutoClearDeviceOffset clearSource ( aSource ) ;
AutoClearDeviceOffset clearMask ( aMask ) ;
2012-10-03 04:14:38 +04:00
2013-08-21 03:56:27 +04:00
cairo_set_antialias ( mContext , GfxAntialiasToCairoAntialias ( aOptions . mAntialiasMode ) ) ;
2015-06-24 03:50:36 +03:00
cairo_pattern_t * source = GfxPatternToCairoPattern ( aSource , aOptions . mAlpha , GetTransform ( ) ) ;
2014-07-04 18:55:04 +04:00
if ( ! source ) {
2014-04-15 08:57:29 +04:00
return ;
2014-07-04 18:55:04 +04:00
}
2012-10-03 04:14:38 +04:00
2015-06-24 03:50:36 +03:00
cairo_pattern_t * mask = GfxPatternToCairoPattern ( aMask , aOptions . mAlpha , GetTransform ( ) ) ;
2014-04-15 08:57:29 +04:00
if ( ! mask ) {
cairo_pattern_destroy ( source ) ;
return ;
}
2014-07-04 18:55:04 +04:00
if ( cairo_pattern_status ( source ) | | cairo_pattern_status ( mask ) ) {
cairo_pattern_destroy ( source ) ;
cairo_pattern_destroy ( mask ) ;
gfxWarning ( ) < < " Invalid pattern " ;
return ;
}
2014-04-15 08:57:29 +04:00
cairo_set_source ( mContext , source ) ;
2012-10-03 04:14:38 +04:00
cairo_mask ( mContext , mask ) ;
cairo_pattern_destroy ( mask ) ;
cairo_pattern_destroy ( source ) ;
2012-01-10 01:50:01 +04:00
}
2013-07-11 18:43:34 +04:00
void
DrawTargetCairo : : MaskSurface ( const Pattern & aSource ,
SourceSurface * aMask ,
Point aOffset ,
2013-06-13 07:57:51 +04:00
const DrawOptions & aOptions )
{
AutoPrepareForDrawing prep ( this , mContext ) ;
2013-08-21 03:52:20 +04:00
AutoClearDeviceOffset clearSource ( aSource ) ;
AutoClearDeviceOffset clearMask ( aMask ) ;
2013-06-13 07:57:51 +04:00
2013-07-11 18:43:34 +04:00
if ( ! PatternIsCompatible ( aSource ) ) {
return ;
}
2013-06-13 07:57:51 +04:00
2013-08-21 03:56:27 +04:00
cairo_set_antialias ( mContext , GfxAntialiasToCairoAntialias ( aOptions . mAntialiasMode ) ) ;
2015-06-24 03:50:36 +03:00
cairo_pattern_t * pat = GfxPatternToCairoPattern ( aSource , aOptions . mAlpha , GetTransform ( ) ) ;
2014-07-04 18:55:04 +04:00
if ( ! pat ) {
2014-04-15 08:57:29 +04:00
return ;
2014-07-04 18:55:04 +04:00
}
if ( cairo_pattern_status ( pat ) ) {
cairo_pattern_destroy ( pat ) ;
gfxWarning ( ) < < " Invalid pattern " ;
return ;
}
2014-04-15 08:57:29 +04:00
2013-07-11 18:43:34 +04:00
cairo_set_source ( mContext , pat ) ;
2013-06-13 07:57:51 +04:00
2013-07-11 18:43:34 +04:00
if ( NeedIntermediateSurface ( aSource , aOptions ) ) {
cairo_push_group_with_content ( mContext , CAIRO_CONTENT_COLOR_ALPHA ) ;
2013-06-13 07:57:51 +04:00
2013-07-11 18:43:34 +04:00
// Don't want operators to be applied twice
cairo_set_operator ( mContext , CAIRO_OPERATOR_OVER ) ;
2013-06-13 07:57:51 +04:00
2013-07-11 18:43:34 +04:00
// Now draw the content using the desired operator
cairo_paint_with_alpha ( mContext , aOptions . mAlpha ) ;
2013-06-13 07:57:51 +04:00
cairo_pop_group_to_source ( mContext ) ;
}
2013-07-11 18:43:34 +04:00
cairo_surface_t * surf = GetCairoSurfaceForSourceSurface ( aMask ) ;
2014-04-09 13:15:19 +04:00
if ( ! surf ) {
cairo_pattern_destroy ( pat ) ;
return ;
}
2013-07-11 18:43:34 +04:00
cairo_pattern_t * mask = cairo_pattern_create_for_surface ( surf ) ;
cairo_matrix_t matrix ;
2013-06-13 07:57:51 +04:00
2013-07-11 18:43:34 +04:00
cairo_matrix_init_translate ( & matrix , - aOffset . x , - aOffset . y ) ;
cairo_pattern_set_matrix ( mask , & matrix ) ;
2013-06-13 07:57:51 +04:00
2014-02-27 20:56:48 +04:00
cairo_set_operator ( mContext , GfxOpToCairoOp ( aOptions . mCompositionOp ) ) ;
2013-07-11 18:43:34 +04:00
cairo_mask ( mContext , mask ) ;
cairo_surface_destroy ( surf ) ;
cairo_pattern_destroy ( mask ) ;
2013-06-13 07:57:51 +04:00
cairo_pattern_destroy ( pat ) ;
}
2012-01-10 01:50:01 +04:00
void
DrawTargetCairo : : PushClip ( const Path * aPath )
{
2014-01-10 23:06:16 +04:00
if ( aPath - > GetBackendType ( ) ! = BackendType : : CAIRO ) {
2012-04-26 02:04:36 +04:00
return ;
}
WillChange ( aPath ) ;
cairo_save ( mContext ) ;
2012-05-18 01:54:22 +04:00
PathCairo * path = const_cast < PathCairo * > ( static_cast < const PathCairo * > ( aPath ) ) ;
2013-09-28 18:20:24 +04:00
path - > SetPathOnContext ( mContext ) ;
2012-04-26 02:04:36 +04:00
cairo_clip_preserve ( mContext ) ;
2012-01-10 01:50:01 +04:00
}
2012-01-10 02:15:10 +04:00
void
DrawTargetCairo : : PushClipRect ( const Rect & aRect )
{
2012-04-26 02:04:36 +04:00
WillChange ( ) ;
cairo_save ( mContext ) ;
2012-05-18 01:54:22 +04:00
cairo_new_path ( mContext ) ;
2012-04-26 02:04:36 +04:00
cairo_rectangle ( mContext , aRect . X ( ) , aRect . Y ( ) , aRect . Width ( ) , aRect . Height ( ) ) ;
cairo_clip_preserve ( mContext ) ;
2012-01-10 02:15:10 +04:00
}
2012-01-10 01:50:01 +04:00
void
DrawTargetCairo : : PopClip ( )
{
2012-05-18 01:54:22 +04:00
// save/restore does not affect the path, so no need to call WillChange()
2014-01-07 02:27:03 +04:00
// cairo_restore will restore the transform too and we don't want to do that
// so we'll save it now and restore it after the cairo_restore
cairo_matrix_t mat ;
cairo_get_matrix ( mContext , & mat ) ;
2012-04-26 02:04:36 +04:00
cairo_restore ( mContext ) ;
2014-01-07 02:27:03 +04:00
cairo_set_matrix ( mContext , & mat ) ;
2014-04-15 08:57:29 +04:00
MOZ_ASSERT ( cairo_status ( mContext ) | | GetTransform ( ) = = Matrix ( mat . xx , mat . yx , mat . xy , mat . yy , mat . x0 , mat . y0 ) ,
2014-01-07 02:27:03 +04:00
" Transforms are out of sync " ) ;
2012-01-10 01:50:01 +04:00
}
2015-06-17 17:00:52 +03:00
already_AddRefed < PathBuilder >
2014-01-10 23:06:17 +04:00
DrawTargetCairo : : CreatePathBuilder ( FillRule aFillRule /* = FillRule::FILL_WINDING */ ) const
2012-01-10 01:50:01 +04:00
{
2015-04-30 22:20:30 +03:00
return MakeAndAddRef < PathBuilderCairo > ( aFillRule ) ;
2012-01-10 01:50:01 +04:00
}
2012-07-24 14:18:38 +04:00
void
DrawTargetCairo : : ClearSurfaceForUnboundedSource ( const CompositionOp & aOperator )
{
2014-01-10 23:06:17 +04:00
if ( aOperator ! = CompositionOp : : OP_SOURCE )
2012-07-24 14:18:38 +04:00
return ;
cairo_set_operator ( mContext , CAIRO_OPERATOR_CLEAR ) ;
// It doesn't really matter what the source is here, since Paint
// isn't bounded by the source and the mask covers the entire clip
// region.
cairo_paint ( mContext ) ;
}
2015-06-17 17:00:52 +03:00
already_AddRefed < GradientStops >
2012-10-10 14:32:36 +04:00
DrawTargetCairo : : CreateGradientStops ( GradientStop * aStops , uint32_t aNumStops ,
ExtendMode aExtendMode ) const
2012-01-10 01:50:01 +04:00
{
2015-04-30 22:20:30 +03:00
return MakeAndAddRef < GradientStopsCairo > ( aStops , aNumStops , aExtendMode ) ;
2011-06-24 21:41:16 +04:00
}
2015-06-17 17:00:52 +03:00
already_AddRefed < FilterNode >
2013-11-27 15:22:56 +04:00
DrawTargetCairo : : CreateFilter ( FilterType aType )
{
return FilterNodeSoftware : : Create ( aType ) ;
}
2015-06-17 17:00:52 +03:00
already_AddRefed < SourceSurface >
2011-06-24 21:41:16 +04:00
DrawTargetCairo : : CreateSourceSurfaceFromData ( unsigned char * aData ,
const IntSize & aSize ,
int32_t aStride ,
SurfaceFormat aFormat ) const
{
2015-03-03 19:17:55 +03:00
if ( ! aData ) {
gfxWarning ( ) < < " DrawTargetCairo::CreateSourceSurfaceFromData null aData " ;
return nullptr ;
}
2014-09-12 09:11:36 +04:00
cairo_surface_t * surf = CopyToImageSurface ( aData , IntRect ( IntPoint ( ) , aSize ) ,
aStride , aFormat ) ;
2015-03-03 19:17:55 +03:00
if ( ! surf ) {
return nullptr ;
}
2012-10-25 06:03:21 +04:00
2015-10-18 08:24:48 +03:00
RefPtr < SourceSurfaceCairo > source_surf = new SourceSurfaceCairo ( surf , aSize , aFormat ) ;
2011-06-24 21:41:16 +04:00
cairo_surface_destroy ( surf ) ;
2012-07-24 14:18:38 +04:00
2014-06-13 20:09:23 +04:00
return source_surf . forget ( ) ;
2011-06-24 21:41:16 +04:00
}
2014-05-23 04:06:16 +04:00
# ifdef CAIRO_HAS_XLIB_SURFACE
static cairo_user_data_key_t gDestroyPixmapKey ;
struct DestroyPixmapClosure {
DestroyPixmapClosure ( Drawable d , Screen * s ) : mPixmap ( d ) , mScreen ( s ) { }
~ DestroyPixmapClosure ( ) {
XFreePixmap ( DisplayOfScreen ( mScreen ) , mPixmap ) ;
}
Drawable mPixmap ;
Screen * mScreen ;
} ;
static void
DestroyPixmap ( void * data )
{
delete static_cast < DestroyPixmapClosure * > ( data ) ;
}
# endif
2015-06-17 17:00:52 +03:00
already_AddRefed < SourceSurface >
2011-06-24 21:41:16 +04:00
DrawTargetCairo : : OptimizeSourceSurface ( SourceSurface * aSurface ) const
{
2015-10-18 08:24:48 +03:00
RefPtr < SourceSurface > surface ( aSurface ) ;
2014-05-23 04:06:16 +04:00
# ifdef CAIRO_HAS_XLIB_SURFACE
2014-10-16 01:13:14 +04:00
cairo_surface_type_t ctype = cairo_surface_get_type ( mSurface ) ;
if ( aSurface - > GetType ( ) = = SurfaceType : : CAIRO & &
cairo_surface_get_type (
static_cast < SourceSurfaceCairo * > ( aSurface ) - > GetSurface ( ) ) = = ctype ) {
2015-04-30 22:20:30 +03:00
return surface . forget ( ) ;
2014-10-16 01:13:14 +04:00
}
if ( ctype ! = CAIRO_SURFACE_TYPE_XLIB ) {
2015-04-30 22:20:30 +03:00
return surface . forget ( ) ;
2014-05-23 04:06:16 +04:00
}
IntSize size = aSurface - > GetSize ( ) ;
if ( ! size . width | | ! size . height ) {
2015-04-30 22:20:30 +03:00
return surface . forget ( ) ;
2014-05-23 04:06:16 +04:00
}
// Although the dimension parameters in the xCreatePixmapReq wire protocol are
// 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
// either dimension cannot be represented by a 16-bit *signed* integer.
# define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
if ( size . width > XLIB_IMAGE_SIDE_SIZE_LIMIT | |
size . height > XLIB_IMAGE_SIDE_SIZE_LIMIT ) {
2015-04-30 22:20:30 +03:00
return surface . forget ( ) ;
2014-05-23 04:06:16 +04:00
}
SurfaceFormat format = aSurface - > GetFormat ( ) ;
Screen * screen = cairo_xlib_surface_get_screen ( mSurface ) ;
Display * dpy = DisplayOfScreen ( screen ) ;
XRenderPictFormat * xrenderFormat = nullptr ;
switch ( format ) {
2015-10-19 07:53:40 +03:00
case SurfaceFormat : : A8R8G8B8_UINT32 :
2014-05-23 04:06:16 +04:00
xrenderFormat = XRenderFindStandardFormat ( dpy , PictStandardARGB32 ) ;
break ;
2015-10-19 07:53:40 +03:00
case SurfaceFormat : : X8R8G8B8_UINT32 :
2014-05-23 04:06:16 +04:00
xrenderFormat = XRenderFindStandardFormat ( dpy , PictStandardRGB24 ) ;
break ;
case SurfaceFormat : : A8 :
xrenderFormat = XRenderFindStandardFormat ( dpy , PictStandardA8 ) ;
break ;
default :
2015-04-30 22:20:30 +03:00
return surface . forget ( ) ;
2014-05-23 04:06:16 +04:00
}
if ( ! xrenderFormat ) {
2015-04-30 22:20:30 +03:00
return surface . forget ( ) ;
2014-05-23 04:06:16 +04:00
}
Drawable pixmap = XCreatePixmap ( dpy , RootWindowOfScreen ( screen ) ,
size . width , size . height ,
xrenderFormat - > depth ) ;
if ( ! pixmap ) {
2015-04-30 22:20:30 +03:00
return surface . forget ( ) ;
2014-05-23 04:06:16 +04:00
}
ScopedDeletePtr < DestroyPixmapClosure > closure (
new DestroyPixmapClosure ( pixmap , screen ) ) ;
ScopedCairoSurface csurf (
cairo_xlib_surface_create_with_xrender_format ( dpy , pixmap ,
screen , xrenderFormat ,
size . width , size . height ) ) ;
if ( ! csurf | | cairo_surface_status ( csurf ) ) {
2015-04-30 22:20:30 +03:00
return surface . forget ( ) ;
2014-05-23 04:06:16 +04:00
}
cairo_surface_set_user_data ( csurf , & gDestroyPixmapKey ,
closure . forget ( ) , DestroyPixmap ) ;
2015-10-18 08:24:48 +03:00
RefPtr < DrawTargetCairo > dt = new DrawTargetCairo ( ) ;
2014-05-23 04:06:16 +04:00
if ( ! dt - > Init ( csurf , size , & format ) ) {
2015-04-30 22:20:30 +03:00
return surface . forget ( ) ;
2014-05-23 04:06:16 +04:00
}
dt - > CopySurface ( aSurface ,
IntRect ( 0 , 0 , size . width , size . height ) ,
IntPoint ( 0 , 0 ) ) ;
dt - > Flush ( ) ;
2015-04-30 22:20:30 +03:00
surface = new SourceSurfaceCairo ( csurf , size , format ) ;
2014-05-23 04:06:16 +04:00
# endif
2015-04-30 22:20:30 +03:00
return surface . forget ( ) ;
2011-06-24 21:41:16 +04:00
}
2015-06-17 17:00:52 +03:00
already_AddRefed < SourceSurface >
2011-06-24 21:41:16 +04:00
DrawTargetCairo : : CreateSourceSurfaceFromNativeSurface ( const NativeSurface & aSurface ) const
{
2014-01-10 23:06:16 +04:00
if ( aSurface . mType = = NativeSurfaceType : : CAIRO_SURFACE ) {
2014-05-08 03:23:44 +04:00
if ( aSurface . mSize . width < = 0 | |
aSurface . mSize . height < = 0 ) {
gfxWarning ( ) < < " Can't create a SourceSurface without a valid size " ;
return nullptr ;
2012-01-10 01:50:01 +04:00
}
2014-05-08 03:23:44 +04:00
cairo_surface_t * surf = static_cast < cairo_surface_t * > ( aSurface . mSurface ) ;
2015-04-30 22:20:30 +03:00
return MakeAndAddRef < SourceSurfaceCairo > ( surf , aSurface . mSize , aSurface . mFormat ) ;
2013-10-26 01:25:40 +04:00
}
return nullptr ;
}
2015-06-17 17:00:52 +03:00
already_AddRefed < DrawTarget >
2012-01-10 01:50:01 +04:00
DrawTargetCairo : : CreateSimilarDrawTarget ( const IntSize & aSize , SurfaceFormat aFormat ) const
{
2014-08-28 08:07:43 +04:00
cairo_surface_t * similar = cairo_surface_create_similar ( mSurface ,
2012-01-10 01:50:01 +04:00
GfxFormatToCairoContent ( aFormat ) ,
aSize . width , aSize . height ) ;
if ( ! cairo_surface_status ( similar ) ) {
2015-10-18 08:24:48 +03:00
RefPtr < DrawTargetCairo > target = new DrawTargetCairo ( ) ;
2014-11-15 00:11:02 +03:00
if ( target - > InitAlreadyReferenced ( similar , aSize ) ) {
return target . forget ( ) ;
}
2012-01-10 01:50:01 +04:00
}
2015-07-14 22:22:29 +03:00
gfxCriticalError ( CriticalLog : : DefaultOptions ( Factory : : ReasonableSurfaceSize ( aSize ) ) ) < < " Failed to create similar cairo surface! Size: " < < aSize < < " Status: " < < cairo_surface_status ( similar ) < < " format " < < ( int ) aFormat ;
2014-09-18 01:23:09 +04:00
2012-08-14 22:06:12 +04:00
return nullptr ;
2011-06-24 21:41:16 +04:00
}
bool
2014-04-14 22:51:00 +04:00
DrawTargetCairo : : InitAlreadyReferenced ( cairo_surface_t * aSurface , const IntSize & aSize , SurfaceFormat * aFormat )
2011-06-24 21:41:16 +04:00
{
2014-09-19 02:01:24 +04:00
if ( cairo_surface_status ( aSurface ) ) {
2015-03-12 21:40:06 +03:00
gfxCriticalError ( CriticalLog : : DefaultOptions ( Factory : : ReasonableSurfaceSize ( aSize ) ) )
< < " Attempt to create DrawTarget for invalid surface. "
< < aSize < < " Cairo Status: " < < cairo_surface_status ( aSurface ) ;
2014-09-19 02:01:24 +04:00
cairo_surface_destroy ( aSurface ) ;
return false ;
}
2011-06-24 21:41:16 +04:00
mContext = cairo_create ( aSurface ) ;
2012-07-24 14:18:38 +04:00
mSurface = aSurface ;
2012-07-24 14:18:38 +04:00
mSize = aSize ;
2014-12-18 00:49:57 +03:00
mFormat = aFormat ? * aFormat : GfxFormatForCairoSurface ( aSurface ) ;
2011-06-24 21:41:16 +04:00
2014-08-15 06:23:22 +04:00
// Cairo image surface have a bug where they will allocate a mask surface (for clipping)
// the size of the clip extents, and don't take the surface extents into account.
// Add a manual clip to the surface extents to prevent this.
cairo_new_path ( mContext ) ;
cairo_rectangle ( mContext , 0 , 0 , mSize . width , mSize . height ) ;
cairo_clip ( mContext ) ;
2015-10-19 07:53:40 +03:00
if ( mFormat = = SurfaceFormat : : A8R8G8B8_UINT32 | |
2014-01-10 23:06:16 +04:00
mFormat = = SurfaceFormat : : R8G8B8A8 ) {
2013-11-05 08:50:56 +04:00
SetPermitSubpixelAA ( false ) ;
} else {
SetPermitSubpixelAA ( true ) ;
}
2011-06-24 21:41:16 +04:00
return true ;
}
2015-06-17 17:00:52 +03:00
already_AddRefed < DrawTarget >
2012-09-03 03:07:06 +04:00
DrawTargetCairo : : CreateShadowDrawTarget ( const IntSize & aSize , SurfaceFormat aFormat ,
float aSigma ) const
2012-09-03 03:07:06 +04:00
{
cairo_surface_t * similar = cairo_surface_create_similar ( cairo_get_target ( mContext ) ,
GfxFormatToCairoContent ( aFormat ) ,
aSize . width , aSize . height ) ;
if ( cairo_surface_status ( similar ) ) {
return nullptr ;
}
2012-09-03 03:07:06 +04:00
// If we don't have a blur then we can use the RGBA mask and keep all the
// operations in graphics memory.
if ( aSigma = = 0.0F ) {
2015-10-18 08:24:48 +03:00
RefPtr < DrawTargetCairo > target = new DrawTargetCairo ( ) ;
2014-11-15 00:11:02 +03:00
if ( target - > InitAlreadyReferenced ( similar , aSize ) ) {
return target . forget ( ) ;
} else {
return nullptr ;
}
2012-09-03 03:07:06 +04:00
}
2012-09-03 03:07:06 +04:00
cairo_surface_t * blursurf = cairo_image_surface_create ( CAIRO_FORMAT_A8 ,
aSize . width ,
aSize . height ) ;
if ( cairo_surface_status ( blursurf ) ) {
return nullptr ;
}
cairo_surface_t * tee = cairo_tee_surface_create ( blursurf ) ;
cairo_surface_destroy ( blursurf ) ;
if ( cairo_surface_status ( tee ) ) {
cairo_surface_destroy ( similar ) ;
return nullptr ;
}
cairo_tee_surface_add ( tee , similar ) ;
cairo_surface_destroy ( similar ) ;
2015-10-18 08:24:48 +03:00
RefPtr < DrawTargetCairo > target = new DrawTargetCairo ( ) ;
2014-11-15 00:11:02 +03:00
if ( target - > InitAlreadyReferenced ( tee , aSize ) ) {
return target . forget ( ) ;
}
return nullptr ;
2012-09-03 03:07:06 +04:00
}
2012-08-24 03:50:59 +04:00
bool
2014-04-14 22:51:00 +04:00
DrawTargetCairo : : Init ( cairo_surface_t * aSurface , const IntSize & aSize , SurfaceFormat * aFormat )
2012-08-24 03:50:59 +04:00
{
cairo_surface_reference ( aSurface ) ;
2014-04-14 22:51:00 +04:00
return InitAlreadyReferenced ( aSurface , aSize , aFormat ) ;
2012-08-24 03:50:59 +04:00
}
2013-11-04 03:57:36 +04:00
bool
DrawTargetCairo : : Init ( const IntSize & aSize , SurfaceFormat aFormat )
{
cairo_surface_t * surf = cairo_image_surface_create ( GfxFormatToCairoFormat ( aFormat ) , aSize . width , aSize . height ) ;
return InitAlreadyReferenced ( surf , aSize ) ;
}
bool
DrawTargetCairo : : Init ( unsigned char * aData , const IntSize & aSize , int32_t aStride , SurfaceFormat aFormat )
{
cairo_surface_t * surf =
cairo_image_surface_create_for_data ( aData ,
GfxFormatToCairoFormat ( aFormat ) ,
aSize . width ,
aSize . height ,
aStride ) ;
return InitAlreadyReferenced ( surf , aSize ) ;
}
2012-01-10 01:50:01 +04:00
void *
DrawTargetCairo : : GetNativeSurface ( NativeSurfaceType aType )
{
2014-01-10 23:06:16 +04:00
if ( aType = = NativeSurfaceType : : CAIRO_SURFACE ) {
2012-01-10 01:50:01 +04:00
return cairo_get_target ( mContext ) ;
}
2014-01-10 23:06:16 +04:00
if ( aType = = NativeSurfaceType : : CAIRO_CONTEXT ) {
2013-08-23 08:53:53 +04:00
return mContext ;
}
2012-01-10 01:50:01 +04:00
2012-08-14 22:06:12 +04:00
return nullptr ;
2012-01-10 01:50:01 +04:00
}
2011-06-24 21:41:16 +04:00
void
2012-09-06 08:07:53 +04:00
DrawTargetCairo : : MarkSnapshotIndependent ( )
2012-01-10 01:50:01 +04:00
{
2012-09-06 08:07:53 +04:00
if ( mSnapshot ) {
if ( mSnapshot - > refCount ( ) > 1 ) {
// We only need to worry about snapshots that someone else knows about
mSnapshot - > DrawTargetWillChange ( ) ;
}
mSnapshot = nullptr ;
2012-01-10 01:50:01 +04:00
}
}
void
2012-08-14 22:06:12 +04:00
DrawTargetCairo : : WillChange ( const Path * aPath /* = nullptr */ )
2012-01-10 01:50:01 +04:00
{
2012-09-06 08:07:53 +04:00
MarkSnapshotIndependent ( ) ;
2013-10-12 00:47:47 +04:00
MOZ_ASSERT ( ! mLockedBits ) ;
2012-01-10 01:50:01 +04:00
}
2012-01-10 02:15:10 +04:00
void
DrawTargetCairo : : SetTransform ( const Matrix & aTransform )
{
mTransform = aTransform ;
cairo_matrix_t mat ;
GfxMatrixToCairoMatrix ( mTransform , mat ) ;
cairo_set_matrix ( mContext , & mat ) ;
}
2012-01-10 01:50:01 +04:00
2013-10-22 14:11:30 +04:00
Rect
DrawTargetCairo : : GetUserSpaceClip ( )
{
double clipX1 , clipY1 , clipX2 , clipY2 ;
cairo_clip_extents ( mContext , & clipX1 , & clipY1 , & clipX2 , & clipY2 ) ;
return Rect ( clipX1 , clipY1 , clipX2 - clipX1 , clipY2 - clipY1 ) ; // Narrowing of doubles to floats
}
2013-09-20 06:00:35 +04:00
cairo_t *
BorrowedCairoContext : : BorrowCairoContextFromDrawTarget ( DrawTarget * aDT )
{
2014-06-20 00:35:33 +04:00
if ( aDT - > GetBackendType ( ) ! = BackendType : : CAIRO | |
2014-08-06 16:40:03 +04:00
aDT - > IsDualDrawTarget ( ) | |
aDT - > IsTiledDrawTarget ( ) ) {
2013-09-20 06:00:35 +04:00
return nullptr ;
}
DrawTargetCairo * cairoDT = static_cast < DrawTargetCairo * > ( aDT ) ;
cairoDT - > WillChange ( ) ;
// save the state to make it easier for callers to avoid mucking with things
cairo_save ( cairoDT - > mContext ) ;
// Neuter the DrawTarget while the context is being borrowed
cairo_t * cairo = cairoDT - > mContext ;
cairoDT - > mContext = nullptr ;
return cairo ;
}
void
BorrowedCairoContext : : ReturnCairoContextToDrawTarget ( DrawTarget * aDT ,
cairo_t * aCairo )
{
2014-06-20 00:35:33 +04:00
if ( aDT - > GetBackendType ( ) ! = BackendType : : CAIRO | |
2014-08-06 16:40:03 +04:00
aDT - > IsDualDrawTarget ( ) | |
aDT - > IsTiledDrawTarget ( ) ) {
2013-09-20 06:00:35 +04:00
return ;
}
DrawTargetCairo * cairoDT = static_cast < DrawTargetCairo * > ( aDT ) ;
cairo_restore ( aCairo ) ;
cairoDT - > mContext = aCairo ;
}
2015-05-01 21:08:04 +03:00
# ifdef MOZ_X11
bool
BorrowedXlibDrawable : : Init ( DrawTarget * aDT )
{
MOZ_ASSERT ( aDT , " Caller should check for nullptr " ) ;
MOZ_ASSERT ( ! mDT , " Can't initialize twice! " ) ;
mDT = aDT ;
mDrawable = None ;
# ifdef CAIRO_HAS_XLIB_SURFACE
if ( aDT - > GetBackendType ( ) ! = BackendType : : CAIRO | |
aDT - > IsDualDrawTarget ( ) | |
aDT - > IsTiledDrawTarget ( ) ) {
return false ;
}
DrawTargetCairo * cairoDT = static_cast < DrawTargetCairo * > ( aDT ) ;
cairo_surface_t * surf = cairoDT - > mSurface ;
if ( cairo_surface_get_type ( surf ) ! = CAIRO_SURFACE_TYPE_XLIB ) {
return false ;
}
2015-06-10 19:16:00 +03:00
cairo_surface_flush ( surf ) ;
2015-05-01 21:08:04 +03:00
cairoDT - > WillChange ( ) ;
mDisplay = cairo_xlib_surface_get_display ( surf ) ;
mDrawable = cairo_xlib_surface_get_drawable ( surf ) ;
mScreen = cairo_xlib_surface_get_screen ( surf ) ;
mVisual = cairo_xlib_surface_get_visual ( surf ) ;
mXRenderFormat = cairo_xlib_surface_get_xrender_format ( surf ) ;
return true ;
# else
return false ;
# endif
}
void
BorrowedXlibDrawable : : Finish ( )
{
2015-06-10 19:16:00 +03:00
DrawTargetCairo * cairoDT = static_cast < DrawTargetCairo * > ( mDT ) ;
cairo_surface_t * surf = cairoDT - > mSurface ;
cairo_surface_mark_dirty ( surf ) ;
2015-05-01 21:08:04 +03:00
if ( mDrawable ) {
mDrawable = None ;
}
}
# endif
2015-07-13 18:25:42 +03:00
} // namespace gfx
} // namespace mozilla