File indexing completed on 2024-04-28 05:32:10

0001 /*
0002     this file is part of the oxygen gtk engine
0003     SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "oxygencairoutils.h"
0009 #include "oxygencairocontext.h"
0010 #include "oxygenrgba.h"
0011 
0012 #ifdef GDK_WINDOWING_X11
0013 #include <cairo/cairo-xlib.h>
0014 #endif
0015 
0016 #include <cmath>
0017 namespace Oxygen
0018 {
0019 
0020     //__________________________________________________________________
0021     void cairo_arc_qt(cairo_t* context, double x, double y, double diam, double a, double alen)
0022     {
0023 
0024         const double xc( x+diam/2 );
0025         const double yc( y+diam/2 );
0026         const double radius( diam/2 );
0027 
0028         double angle1( -a );
0029         double angle2( -(alen+a) );
0030 
0031         if( angle1 > angle2 ) std::swap( angle1, angle2 );
0032         cairo_arc(context, xc,yc,radius,angle1,angle2);
0033 
0034     }
0035 
0036     //__________________________________________________________________
0037     void cairo_pattern_add_color_stop( cairo_pattern_t* pattern, double x, const ColorUtils::Rgba& color )
0038     {
0039         /*
0040         for some reason passing 1.0 for the upperbound on some gradients
0041         breaks the gradient definition internally with some compilers
0042         this is fixed by passing a value close to (but smaller than) unity.
0043         */
0044         cairo_pattern_add_color_stop_rgba( pattern, x, color.red(), color.green(), color.blue(), color.alpha() );
0045     }
0046 
0047     //__________________________________________________________________
0048     ColorStop::List cairo_pattern_get_color_stops( cairo_pattern_t* pattern )
0049     {
0050 
0051         ColorStop::List out;
0052         int count(0);
0053         if( cairo_pattern_get_color_stop_count( pattern, &count ) != CAIRO_STATUS_SUCCESS ) return out;
0054 
0055         for( int i = 0; i < count; ++i )
0056         {
0057             double x(0);
0058             double r(0), g(0), b(0), a(0);
0059             assert( cairo_pattern_get_color_stop_rgba( pattern, i, &x, &r, &g, &b, &a ) == CAIRO_STATUS_SUCCESS );
0060             out.push_back( ColorStop( x, ColorUtils::Rgba( r, g, b, a ) ) );
0061         }
0062 
0063         return out;
0064 
0065     }
0066 
0067     //__________________________________________________________________
0068     void cairo_set_source( cairo_t* context, const ColorUtils::Rgba& color )
0069     { cairo_set_source_rgba( context, color.red(), color.green(), color.blue(), color.alpha() ); }
0070 
0071     //__________________________________________________________________
0072     void cairo_rounded_rectangle( cairo_t* context, double x, double y, double w, double h, double r, Corners corners )
0073     {
0074         if(corners==CornersAll)
0075         {
0076             if(w<2*r)
0077             {
0078                 double r0=r;
0079                 r=0.5*w;
0080                 y+=r0-r;
0081                 h-=2*(r0-r);
0082             }
0083             if(h<2*r)
0084             {
0085                 double r0=r;
0086                 r=0.5*h;
0087                 x+=r0-r;
0088                 w-=2*(r0-r);
0089             }
0090         }
0091 
0092         if( corners == CornersNone )
0093         {
0094             cairo_rectangle( context, x, y, w, h );
0095             return;
0096         }
0097 
0098         if( corners & CornersTopLeft )
0099         {
0100             cairo_move_to( context, x, y+r );
0101             cairo_arc( context, x+r, y+r, r, M_PI, 3.0*M_PI/2 );
0102         } else cairo_move_to( context, x, y );
0103 
0104         if( corners & CornersTopRight )
0105         {
0106             cairo_line_to( context, x+w-r, y );
0107             cairo_arc( context, x+w-r, y+r, r, -M_PI/2, 0 );
0108         } else cairo_line_to( context, x+w, y );
0109 
0110         if( corners & CornersBottomRight )
0111         {
0112             cairo_line_to( context, x+w, y+h-r );
0113             cairo_arc( context, x+w-r, y+h-r, r, 0, M_PI/2 );
0114         } else cairo_line_to( context, x+w, y+h );
0115 
0116         if( corners & CornersBottomLeft )
0117         {
0118             cairo_line_to( context, x+r, y+h );
0119             cairo_arc( context, x+r, y+h-r, r, M_PI/2, M_PI );
0120         } else cairo_line_to( context, x, y+h );
0121 
0122         cairo_close_path( context );
0123 
0124     }
0125 
0126     //__________________________________________________________________
0127     void cairo_rounded_rectangle_negative( cairo_t* context, double x, double y, double w, double h, double r, Corners corners )
0128     {
0129 
0130         if( corners == CornersNone )
0131         {
0132             cairo_rectangle_negative( context, x, y, w, h );
0133             return;
0134         }
0135 
0136         if( corners & CornersTopRight )
0137         {
0138             cairo_move_to( context, x+w, y+r );
0139             cairo_arc_negative( context, x+w-r, y+r, r, 0, -M_PI/2 );
0140         } else cairo_move_to( context, x+w, y );
0141 
0142         if( corners & CornersTopLeft )
0143         {
0144             cairo_line_to( context, x+r, y );
0145             cairo_arc_negative( context, x+r, y+r, r, -M_PI/2, -M_PI );
0146         } else cairo_line_to( context, x, y );
0147 
0148         if( corners & CornersBottomLeft )
0149         {
0150             cairo_line_to( context, x, y+h-r );
0151             cairo_arc_negative( context, x+r, y+h-r, r, -M_PI, -3.0*M_PI/2 );
0152         } else cairo_line_to( context, x, y+h );
0153 
0154         if( corners & CornersBottomRight )
0155         {
0156             cairo_line_to( context, x+w-r, y+h );
0157             cairo_arc_negative( context, x+w-r, y+h-r, r, M_PI/2, 0 );
0158         } else cairo_line_to( context, x+w, y+h );
0159 
0160         cairo_close_path( context );
0161 
0162     }
0163 
0164     //__________________________________________________________________
0165     void cairo_rectangle_negative( cairo_t* context, double x, double y, double w, double h )
0166     {
0167 
0168         cairo_move_to( context, x+w, y );
0169         cairo_line_to( context, x, y );
0170         cairo_line_to( context, x, y+h );
0171         cairo_line_to( context, x+w, y+h );
0172         cairo_close_path( context );
0173 
0174     }
0175 
0176     //__________________________________________________________________
0177     void cairo_ellipse( cairo_t* context, double x, double y, double w, double h )
0178     {
0179         cairo_save( context );
0180         cairo_translate( context, x+w/2, y+h/2 );
0181         cairo_scale( context, w/2, h/2 );
0182         cairo_arc( context, 0, 0, 1, 0, 2.0*M_PI);
0183         cairo_restore( context );
0184     }
0185 
0186     //___________________________________________________________
0187     void cairo_ellipse_negative( cairo_t* context, double x, double y, double w, double h )
0188     {
0189         cairo_save( context );
0190         cairo_translate( context, x+w/2, y+h/2 );
0191         cairo_scale( context, w/2, h/2 );
0192         cairo_arc_negative( context, 0, 0, 1, 2.0*M_PI, 0);
0193         cairo_restore( context );
0194     }
0195 
0196     //__________________________________________________________________
0197     void cairo_polygon( cairo_t* context, const Polygon& polygon )
0198     {
0199         for( Polygon::const_iterator iter = polygon.begin(); iter != polygon.end(); ++iter )
0200         {
0201             if( iter == polygon.begin() ) cairo_move_to( context, iter->x(), iter->y() );
0202             else cairo_line_to( context, iter->x(), iter->y() );
0203         }
0204     }
0205 
0206     //_____________________________________________________
0207     int cairo_surface_get_width( cairo_surface_t* surface )
0208     {
0209         const cairo_surface_type_t type( cairo_surface_get_type( surface ) );
0210         switch( type )
0211         {
0212 
0213             case CAIRO_SURFACE_TYPE_IMAGE:
0214             return cairo_image_surface_get_width( surface );
0215 
0216             #if CAIRO_HAS_XLIB_SURFACE
0217             case CAIRO_SURFACE_TYPE_XLIB:
0218             return cairo_xlib_surface_get_width( surface );
0219             #endif
0220 
0221             default:
0222             {
0223                 // This is less efficient, still makes rendering correct
0224                 Cairo::Context context(surface);
0225                 double x1,x2,dummy;
0226                 cairo_clip_extents(context,&x1,&dummy,&x2,&dummy);
0227                 return int(x2-x1);
0228             }
0229         }
0230 
0231     }
0232 
0233     //_____________________________________________________
0234     int cairo_surface_get_height( cairo_surface_t* surface )
0235     {
0236         const cairo_surface_type_t type( cairo_surface_get_type( surface ) );
0237         switch( type )
0238         {
0239             case CAIRO_SURFACE_TYPE_IMAGE:
0240             return cairo_image_surface_get_height( surface );
0241 
0242             #if CAIRO_HAS_XLIB_SURFACE
0243             case CAIRO_SURFACE_TYPE_XLIB:
0244             return cairo_xlib_surface_get_height( surface );
0245             #endif
0246 
0247             default:
0248             {
0249                 // This is less efficient, still makes rendering correct
0250                 Cairo::Context context(surface);
0251                 double y1,y2,dummy;
0252                 cairo_clip_extents(context,&dummy,&y1,&dummy,&y2);
0253                 return int(y2-y1);
0254             }
0255         }
0256 
0257     }
0258 
0259 
0260     //_____________________________________________________
0261     void cairo_surface_get_size( cairo_surface_t* surface, int& width, int& height )
0262     {
0263 
0264         const cairo_surface_type_t type( cairo_surface_get_type( surface ) );
0265         switch( type )
0266         {
0267             case CAIRO_SURFACE_TYPE_IMAGE:
0268             width = cairo_image_surface_get_width( surface );
0269             height = cairo_image_surface_get_height( surface );
0270             return;
0271 
0272             #if CAIRO_HAS_XLIB_SURFACE
0273             case CAIRO_SURFACE_TYPE_XLIB:
0274             width = cairo_xlib_surface_get_width( surface );
0275             height = cairo_xlib_surface_get_height( surface );
0276             return;
0277             #endif
0278 
0279             default:
0280             {
0281                 // This is less efficient, still makes rendering correct
0282                 Cairo::Context context(surface);
0283                 double x1, x2, y1, y2;
0284                 cairo_clip_extents( context, &x1, &y1, &x2, &y2 );
0285                 width = int(  x2 - x1 );
0286                 height = int( y2 - y1 );
0287                 return;
0288             }
0289         }
0290 
0291     }
0292 
0293     //__________________________________________________________________
0294     cairo_surface_t* cairo_surface_copy( cairo_surface_t* source )
0295     {
0296 
0297         const int width( cairo_surface_get_width( source ) );
0298         const int height( cairo_surface_get_height( source ) );
0299         cairo_surface_t* dest = cairo_surface_create_similar( source, CAIRO_CONTENT_COLOR_ALPHA, width, height );
0300 
0301         // create context on surface
0302         cairo_t* context( cairo_create( dest ) );
0303         cairo_set_source_surface( context, source, 0, 0 );
0304         cairo_rectangle( context, 0, 0, width, height );
0305         cairo_fill( context );
0306 
0307         cairo_destroy( context );
0308         return dest;
0309 
0310     }
0311 
0312     //__________________________________________________________________
0313     void cairo_surface_add_alpha( cairo_surface_t* surface, double alpha )
0314     {
0315 
0316         // create context on surface
0317         cairo_t* context( cairo_create( surface ) );
0318         cairo_set_operator( context, CAIRO_OPERATOR_DEST_IN );
0319         cairo_set_source_rgba( context, 1, 1, 1, alpha );
0320         cairo_rectangle( context, 0, 0, cairo_surface_get_width( surface ), cairo_surface_get_height( surface ) );
0321         cairo_fill( context );
0322         cairo_destroy( context );
0323         return;
0324 
0325     }
0326 
0327     //__________________________________________________________________
0328     void cairo_image_surface_saturate( cairo_surface_t* surface, double saturation )
0329     {
0330 
0331         // make sure right type is used
0332         assert( cairo_surface_get_type( surface ) == CAIRO_SURFACE_TYPE_IMAGE );
0333         assert( cairo_image_surface_get_format( surface ) == CAIRO_FORMAT_ARGB32 );
0334 
0335 
0336         // dimensions and stride
0337         const int width( cairo_image_surface_get_width( surface ) );
0338         const int height( cairo_image_surface_get_width( surface ) );
0339         const int stride( cairo_image_surface_get_stride( surface ) );
0340         const int bytesPerPixel(4);
0341 
0342         // data
0343         unsigned char* data( cairo_image_surface_get_data( surface ) );
0344         assert( data );
0345 
0346         int t;
0347         #define INTENSITY(r, g, b) ((unsigned char)((r) * 0.30 + (g) * 0.59 + (b) * 0.11))
0348         #define CLAMP_UCHAR(v) (t = (v), CLAMP (t, 0, 255))
0349         #define SATURATE(v) int(((1.0 - saturation) * intensity + saturation * (v)))
0350 
0351         unsigned char* line(data);
0352         unsigned char* pixel(data);
0353 
0354         for( int i = 0 ; i < height ; i++ )
0355         {
0356 
0357             pixel = line;
0358             line += stride;
0359 
0360             for( int j = 0 ; j < width ; j++ )
0361             {
0362                 unsigned char intensity = INTENSITY( pixel[0], pixel[1], pixel[2] );
0363                 pixel[0] = CLAMP_UCHAR( SATURATE( pixel[0] ) );
0364                 pixel[1] = CLAMP_UCHAR( SATURATE( pixel[1] ) );
0365                 pixel[2] = CLAMP_UCHAR( SATURATE( pixel[2] ) );
0366                 pixel += bytesPerPixel;
0367 
0368             }
0369 
0370         }
0371 
0372     }
0373 
0374 }