mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
95de3783f6
--HG-- rename : gfx/thebes/src/GLContext.cpp => gfx/thebes/GLContext.cpp rename : gfx/thebes/public/GLContext.h => gfx/thebes/GLContext.h rename : gfx/thebes/public/GLContextProvider.h => gfx/thebes/GLContextProvider.h rename : gfx/thebes/src/GLContextProviderCGL.mm => gfx/thebes/GLContextProviderCGL.mm rename : gfx/thebes/src/GLContextProviderEGL.cpp => gfx/thebes/GLContextProviderEGL.cpp rename : gfx/thebes/src/GLContextProviderGLX.cpp => gfx/thebes/GLContextProviderGLX.cpp rename : gfx/thebes/src/GLContextProviderNull.cpp => gfx/thebes/GLContextProviderNull.cpp rename : gfx/thebes/src/GLContextProviderOSMesa.cpp => gfx/thebes/GLContextProviderOSMesa.cpp rename : gfx/thebes/src/GLContextProviderWGL.cpp => gfx/thebes/GLContextProviderWGL.cpp rename : gfx/thebes/public/GLDefs.h => gfx/thebes/GLDefs.h rename : gfx/thebes/public/GLXLibrary.h => gfx/thebes/GLXLibrary.h rename : gfx/thebes/public/WGLLibrary.h => gfx/thebes/WGLLibrary.h rename : gfx/thebes/src/cairo-gdk-utils.c => gfx/thebes/cairo-gdk-utils.c rename : gfx/thebes/src/cairo-gdk-utils.h => gfx/thebes/cairo-gdk-utils.h rename : gfx/thebes/src/cairo-xlib-utils.c => gfx/thebes/cairo-xlib-utils.c rename : gfx/thebes/src/cairo-xlib-utils.h => gfx/thebes/cairo-xlib-utils.h rename : gfx/thebes/src/genUnicodeScriptData.pl => gfx/thebes/genUnicodeScriptData.pl rename : gfx/thebes/public/gfx3DMatrix.h => gfx/thebes/gfx3DMatrix.h rename : gfx/thebes/src/gfxASurface.cpp => gfx/thebes/gfxASurface.cpp rename : gfx/thebes/public/gfxASurface.h => gfx/thebes/gfxASurface.h rename : gfx/thebes/src/gfxAlphaRecovery.cpp => gfx/thebes/gfxAlphaRecovery.cpp rename : gfx/thebes/public/gfxAlphaRecovery.h => gfx/thebes/gfxAlphaRecovery.h rename : gfx/thebes/src/gfxAndroidPlatform.cpp => gfx/thebes/gfxAndroidPlatform.cpp rename : gfx/thebes/public/gfxAndroidPlatform.h => gfx/thebes/gfxAndroidPlatform.h rename : gfx/thebes/src/gfxAtomList.h => gfx/thebes/gfxAtomList.h rename : gfx/thebes/src/gfxAtoms.cpp => gfx/thebes/gfxAtoms.cpp rename : gfx/thebes/src/gfxAtoms.h => gfx/thebes/gfxAtoms.h rename : gfx/thebes/src/gfxBeOSPlatform.cpp => gfx/thebes/gfxBeOSPlatform.cpp rename : gfx/thebes/public/gfxBeOSPlatform.h => gfx/thebes/gfxBeOSPlatform.h rename : gfx/thebes/src/gfxBeOSSurface.cpp => gfx/thebes/gfxBeOSSurface.cpp rename : gfx/thebes/public/gfxBeOSSurface.h => gfx/thebes/gfxBeOSSurface.h rename : gfx/thebes/public/gfxColor.h => gfx/thebes/gfxColor.h rename : gfx/thebes/src/gfxContext.cpp => gfx/thebes/gfxContext.cpp rename : gfx/thebes/public/gfxContext.h => gfx/thebes/gfxContext.h rename : gfx/thebes/src/gfxCoreTextShaper.cpp => gfx/thebes/gfxCoreTextShaper.cpp rename : gfx/thebes/src/gfxCoreTextShaper.h => gfx/thebes/gfxCoreTextShaper.h rename : gfx/thebes/src/gfxD2DSurface.cpp => gfx/thebes/gfxD2DSurface.cpp rename : gfx/thebes/public/gfxD2DSurface.h => gfx/thebes/gfxD2DSurface.h rename : gfx/thebes/src/gfxDDrawSurface.cpp => gfx/thebes/gfxDDrawSurface.cpp rename : gfx/thebes/public/gfxDDrawSurface.h => gfx/thebes/gfxDDrawSurface.h rename : gfx/thebes/src/gfxDWriteCommon.cpp => gfx/thebes/gfxDWriteCommon.cpp rename : gfx/thebes/src/gfxDWriteCommon.h => gfx/thebes/gfxDWriteCommon.h rename : gfx/thebes/src/gfxDWriteFontList.cpp => gfx/thebes/gfxDWriteFontList.cpp rename : gfx/thebes/src/gfxDWriteFontList.h => gfx/thebes/gfxDWriteFontList.h rename : gfx/thebes/src/gfxDWriteFonts.cpp => gfx/thebes/gfxDWriteFonts.cpp rename : gfx/thebes/public/gfxDWriteFonts.h => gfx/thebes/gfxDWriteFonts.h rename : gfx/thebes/src/gfxDWriteShaper.cpp => gfx/thebes/gfxDWriteShaper.cpp rename : gfx/thebes/src/gfxDWriteShaper.h => gfx/thebes/gfxDWriteShaper.h rename : gfx/thebes/src/gfxDWriteTextAnalysis.cpp => gfx/thebes/gfxDWriteTextAnalysis.cpp rename : gfx/thebes/src/gfxDWriteTextAnalysis.h => gfx/thebes/gfxDWriteTextAnalysis.h rename : gfx/thebes/src/gfxDirectFBSurface.cpp => gfx/thebes/gfxDirectFBSurface.cpp rename : gfx/thebes/public/gfxDirectFBSurface.h => gfx/thebes/gfxDirectFBSurface.h rename : gfx/thebes/src/gfxDllDeps.cpp => gfx/thebes/gfxDllDeps.cpp rename : gfx/thebes/src/gfxFT2FontBase.cpp => gfx/thebes/gfxFT2FontBase.cpp rename : gfx/thebes/public/gfxFT2FontBase.h => gfx/thebes/gfxFT2FontBase.h rename : gfx/thebes/src/gfxFT2FontList.cpp => gfx/thebes/gfxFT2FontList.cpp rename : gfx/thebes/src/gfxFT2FontList.h => gfx/thebes/gfxFT2FontList.h rename : gfx/thebes/src/gfxFT2Fonts.cpp => gfx/thebes/gfxFT2Fonts.cpp rename : gfx/thebes/public/gfxFT2Fonts.h => gfx/thebes/gfxFT2Fonts.h rename : gfx/thebes/src/gfxFT2Utils.cpp => gfx/thebes/gfxFT2Utils.cpp rename : gfx/thebes/src/gfxFT2Utils.h => gfx/thebes/gfxFT2Utils.h rename : gfx/thebes/src/gfxFont.cpp => gfx/thebes/gfxFont.cpp rename : gfx/thebes/public/gfxFont.h => gfx/thebes/gfxFont.h rename : gfx/thebes/public/gfxFontConstants.h => gfx/thebes/gfxFontConstants.h rename : gfx/thebes/src/gfxFontMissingGlyphs.cpp => gfx/thebes/gfxFontMissingGlyphs.cpp rename : gfx/thebes/src/gfxFontMissingGlyphs.h => gfx/thebes/gfxFontMissingGlyphs.h rename : gfx/thebes/src/gfxFontTest.cpp => gfx/thebes/gfxFontTest.cpp rename : gfx/thebes/public/gfxFontTest.h => gfx/thebes/gfxFontTest.h rename : gfx/thebes/src/gfxFontUtils.cpp => gfx/thebes/gfxFontUtils.cpp rename : gfx/thebes/public/gfxFontUtils.h => gfx/thebes/gfxFontUtils.h rename : gfx/thebes/src/gfxFontconfigUtils.cpp => gfx/thebes/gfxFontconfigUtils.cpp rename : gfx/thebes/src/gfxFontconfigUtils.h => gfx/thebes/gfxFontconfigUtils.h rename : gfx/thebes/src/gfxGDIFont.cpp => gfx/thebes/gfxGDIFont.cpp rename : gfx/thebes/src/gfxGDIFont.h => gfx/thebes/gfxGDIFont.h rename : gfx/thebes/src/gfxGDIFontList.cpp => gfx/thebes/gfxGDIFontList.cpp rename : gfx/thebes/src/gfxGDIFontList.h => gfx/thebes/gfxGDIFontList.h rename : gfx/thebes/src/gfxGDIShaper.cpp => gfx/thebes/gfxGDIShaper.cpp rename : gfx/thebes/src/gfxGDIShaper.h => gfx/thebes/gfxGDIShaper.h rename : gfx/thebes/src/gfxGdkNativeRenderer.cpp => gfx/thebes/gfxGdkNativeRenderer.cpp rename : gfx/thebes/public/gfxGdkNativeRenderer.h => gfx/thebes/gfxGdkNativeRenderer.h rename : gfx/thebes/public/gfxGlitzSurface.h => gfx/thebes/gfxGlitzSurface.h rename : gfx/thebes/src/gfxHarfBuzzShaper.cpp => gfx/thebes/gfxHarfBuzzShaper.cpp rename : gfx/thebes/src/gfxHarfBuzzShaper.h => gfx/thebes/gfxHarfBuzzShaper.h rename : gfx/thebes/src/gfxImageSurface.cpp => gfx/thebes/gfxImageSurface.cpp rename : gfx/thebes/public/gfxImageSurface.h => gfx/thebes/gfxImageSurface.h rename : gfx/thebes/src/gfxMacFont.cpp => gfx/thebes/gfxMacFont.cpp rename : gfx/thebes/src/gfxMacFont.h => gfx/thebes/gfxMacFont.h rename : gfx/thebes/src/gfxMacPlatformFontList.h => gfx/thebes/gfxMacPlatformFontList.h rename : gfx/thebes/src/gfxMacPlatformFontList.mm => gfx/thebes/gfxMacPlatformFontList.mm rename : gfx/thebes/src/gfxMatrix.cpp => gfx/thebes/gfxMatrix.cpp rename : gfx/thebes/public/gfxMatrix.h => gfx/thebes/gfxMatrix.h rename : gfx/thebes/src/gfxOS2Fonts.cpp => gfx/thebes/gfxOS2Fonts.cpp rename : gfx/thebes/public/gfxOS2Fonts.h => gfx/thebes/gfxOS2Fonts.h rename : gfx/thebes/src/gfxOS2Platform.cpp => gfx/thebes/gfxOS2Platform.cpp rename : gfx/thebes/public/gfxOS2Platform.h => gfx/thebes/gfxOS2Platform.h rename : gfx/thebes/src/gfxOS2Surface.cpp => gfx/thebes/gfxOS2Surface.cpp rename : gfx/thebes/public/gfxOS2Surface.h => gfx/thebes/gfxOS2Surface.h rename : gfx/thebes/src/gfxPDFSurface.cpp => gfx/thebes/gfxPDFSurface.cpp rename : gfx/thebes/public/gfxPDFSurface.h => gfx/thebes/gfxPDFSurface.h rename : gfx/thebes/src/gfxPSSurface.cpp => gfx/thebes/gfxPSSurface.cpp rename : gfx/thebes/public/gfxPSSurface.h => gfx/thebes/gfxPSSurface.h rename : gfx/thebes/src/gfxPangoFonts.cpp => gfx/thebes/gfxPangoFonts.cpp rename : gfx/thebes/public/gfxPangoFonts.h => gfx/thebes/gfxPangoFonts.h rename : gfx/thebes/src/gfxPath.cpp => gfx/thebes/gfxPath.cpp rename : gfx/thebes/public/gfxPath.h => gfx/thebes/gfxPath.h rename : gfx/thebes/src/gfxPattern.cpp => gfx/thebes/gfxPattern.cpp rename : gfx/thebes/public/gfxPattern.h => gfx/thebes/gfxPattern.h rename : gfx/thebes/src/gfxPlatform.cpp => gfx/thebes/gfxPlatform.cpp rename : gfx/thebes/public/gfxPlatform.h => gfx/thebes/gfxPlatform.h rename : gfx/thebes/src/gfxPlatformFontList.cpp => gfx/thebes/gfxPlatformFontList.cpp rename : gfx/thebes/src/gfxPlatformFontList.h => gfx/thebes/gfxPlatformFontList.h rename : gfx/thebes/src/gfxPlatformGtk.cpp => gfx/thebes/gfxPlatformGtk.cpp rename : gfx/thebes/public/gfxPlatformGtk.h => gfx/thebes/gfxPlatformGtk.h rename : gfx/thebes/src/gfxPlatformMac.cpp => gfx/thebes/gfxPlatformMac.cpp rename : gfx/thebes/public/gfxPlatformMac.h => gfx/thebes/gfxPlatformMac.h rename : gfx/thebes/public/gfxPoint.h => gfx/thebes/gfxPoint.h rename : gfx/thebes/src/gfxQPainterSurface.cpp => gfx/thebes/gfxQPainterSurface.cpp rename : gfx/thebes/public/gfxQPainterSurface.h => gfx/thebes/gfxQPainterSurface.h rename : gfx/thebes/src/gfxQtNativeRenderer.cpp => gfx/thebes/gfxQtNativeRenderer.cpp rename : gfx/thebes/public/gfxQtNativeRenderer.h => gfx/thebes/gfxQtNativeRenderer.h rename : gfx/thebes/src/gfxQtPlatform.cpp => gfx/thebes/gfxQtPlatform.cpp rename : gfx/thebes/public/gfxQtPlatform.h => gfx/thebes/gfxQtPlatform.h rename : gfx/thebes/src/gfxQuartzImageSurface.cpp => gfx/thebes/gfxQuartzImageSurface.cpp rename : gfx/thebes/public/gfxQuartzImageSurface.h => gfx/thebes/gfxQuartzImageSurface.h rename : gfx/thebes/src/gfxQuartzNativeDrawing.cpp => gfx/thebes/gfxQuartzNativeDrawing.cpp rename : gfx/thebes/public/gfxQuartzNativeDrawing.h => gfx/thebes/gfxQuartzNativeDrawing.h rename : gfx/thebes/src/gfxQuartzPDFSurface.cpp => gfx/thebes/gfxQuartzPDFSurface.cpp rename : gfx/thebes/public/gfxQuartzPDFSurface.h => gfx/thebes/gfxQuartzPDFSurface.h rename : gfx/thebes/src/gfxQuartzSurface.cpp => gfx/thebes/gfxQuartzSurface.cpp rename : gfx/thebes/public/gfxQuartzSurface.h => gfx/thebes/gfxQuartzSurface.h rename : gfx/thebes/src/gfxRect.cpp => gfx/thebes/gfxRect.cpp rename : gfx/thebes/public/gfxRect.h => gfx/thebes/gfxRect.h rename : gfx/thebes/src/gfxScriptItemizer.cpp => gfx/thebes/gfxScriptItemizer.cpp rename : gfx/thebes/src/gfxScriptItemizer.h => gfx/thebes/gfxScriptItemizer.h rename : gfx/thebes/src/gfxSharedImageSurface.cpp => gfx/thebes/gfxSharedImageSurface.cpp rename : gfx/thebes/public/gfxSharedImageSurface.h => gfx/thebes/gfxSharedImageSurface.h rename : gfx/thebes/src/gfxSkipChars.cpp => gfx/thebes/gfxSkipChars.cpp rename : gfx/thebes/public/gfxSkipChars.h => gfx/thebes/gfxSkipChars.h rename : gfx/thebes/src/gfxTextRunCache.cpp => gfx/thebes/gfxTextRunCache.cpp rename : gfx/thebes/public/gfxTextRunCache.h => gfx/thebes/gfxTextRunCache.h rename : gfx/thebes/src/gfxTextRunWordCache.cpp => gfx/thebes/gfxTextRunWordCache.cpp rename : gfx/thebes/public/gfxTextRunWordCache.h => gfx/thebes/gfxTextRunWordCache.h rename : gfx/thebes/public/gfxTypes.h => gfx/thebes/gfxTypes.h rename : gfx/thebes/src/gfxUnicodeProperties.cpp => gfx/thebes/gfxUnicodeProperties.cpp rename : gfx/thebes/src/gfxUnicodeProperties.h => gfx/thebes/gfxUnicodeProperties.h rename : gfx/thebes/src/gfxUnicodePropertyData.cpp => gfx/thebes/gfxUnicodePropertyData.cpp rename : gfx/thebes/src/gfxUniscribeShaper.cpp => gfx/thebes/gfxUniscribeShaper.cpp rename : gfx/thebes/src/gfxUniscribeShaper.h => gfx/thebes/gfxUniscribeShaper.h rename : gfx/thebes/src/gfxUserFontSet.cpp => gfx/thebes/gfxUserFontSet.cpp rename : gfx/thebes/public/gfxUserFontSet.h => gfx/thebes/gfxUserFontSet.h rename : gfx/thebes/src/gfxUtils.cpp => gfx/thebes/gfxUtils.cpp rename : gfx/thebes/public/gfxUtils.h => gfx/thebes/gfxUtils.h rename : gfx/thebes/src/gfxWindowsNativeDrawing.cpp => gfx/thebes/gfxWindowsNativeDrawing.cpp rename : gfx/thebes/public/gfxWindowsNativeDrawing.h => gfx/thebes/gfxWindowsNativeDrawing.h rename : gfx/thebes/src/gfxWindowsPlatform.cpp => gfx/thebes/gfxWindowsPlatform.cpp rename : gfx/thebes/public/gfxWindowsPlatform.h => gfx/thebes/gfxWindowsPlatform.h rename : gfx/thebes/src/gfxWindowsSurface.cpp => gfx/thebes/gfxWindowsSurface.cpp rename : gfx/thebes/public/gfxWindowsSurface.h => gfx/thebes/gfxWindowsSurface.h rename : gfx/thebes/src/gfxXlibNativeRenderer.cpp => gfx/thebes/gfxXlibNativeRenderer.cpp rename : gfx/thebes/public/gfxXlibNativeRenderer.h => gfx/thebes/gfxXlibNativeRenderer.h rename : gfx/thebes/src/gfxXlibSurface.cpp => gfx/thebes/gfxXlibSurface.cpp rename : gfx/thebes/public/gfxXlibSurface.h => gfx/thebes/gfxXlibSurface.h rename : gfx/thebes/src/ignorable.x-ccmap => gfx/thebes/ignorable.x-ccmap rename : gfx/thebes/src/nsUnicodeRange.cpp => gfx/thebes/nsUnicodeRange.cpp rename : gfx/thebes/src/nsUnicodeRange.h => gfx/thebes/nsUnicodeRange.h rename : gfx/thebes/src/woff-private.h => gfx/thebes/woff-private.h rename : gfx/thebes/src/woff.c => gfx/thebes/woff.c rename : gfx/thebes/src/woff.h => gfx/thebes/woff.h
615 lines
22 KiB
C
615 lines
22 KiB
C
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Novell code.
|
|
*
|
|
* The Initial Developer of the Original Code is Novell.
|
|
* Portions created by the Initial Developer are Copyright (C) 2006
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* rocallahan@novell.com
|
|
* Vladimir Vukicevic <vladimir@pobox.com>
|
|
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "cairo-gdk-utils.h"
|
|
|
|
#include "cairo-xlib.h"
|
|
#include <stdlib.h>
|
|
|
|
#if HAVE_STDINT_H
|
|
#include <stdint.h>
|
|
#elif HAVE_INTTYPES_H
|
|
#include <inttypes.h>
|
|
#elif HAVE_SYS_INT_TYPES_H
|
|
#include <sys/int_types.h>
|
|
#endif
|
|
|
|
#include <gdk/gdkx.h>
|
|
|
|
#if 0
|
|
#include <stdio.h>
|
|
#define CAIRO_GDK_DRAWING_NOTE(m) fprintf(stderr, m)
|
|
#else
|
|
#define CAIRO_GDK_DRAWING_NOTE(m) do {} while (0)
|
|
#endif
|
|
|
|
#define GDK_PIXMAP_SIZE_MAX 32767
|
|
|
|
static cairo_user_data_key_t pixmap_free_key;
|
|
static void pixmap_free_func (void *data)
|
|
{
|
|
GdkPixmap *pixmap = (GdkPixmap *) data;
|
|
g_object_unref(pixmap);
|
|
}
|
|
|
|
/* We have three basic strategies available:
|
|
1) 'direct': cr targets an xlib surface, and other conditions are met: we can
|
|
pass the underlying drawable directly to the callback
|
|
2) 'opaque': the image is opaque: we can create a temporary cairo xlib surface,
|
|
pass its underlying drawable to the callback, and paint the result
|
|
using cairo
|
|
3) 'default': create a temporary cairo xlib surface, fill with black, pass its
|
|
underlying drawable to the callback, copy the results to a cairo
|
|
image surface, repeat with a white background, update the on-black
|
|
image alpha values by comparing the two images, then paint the on-black
|
|
image using cairo
|
|
Sure would be nice to have an X extension to do 3 for us on the server...
|
|
*/
|
|
|
|
static cairo_bool_t
|
|
_convert_coord_to_int (double coord, int *v)
|
|
{
|
|
*v = (int)coord;
|
|
/* XXX allow some tolerance here? */
|
|
return *v == coord;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_intersect_interval (double a_begin, double a_end, double b_begin, double b_end,
|
|
double *out_begin, double *out_end)
|
|
{
|
|
*out_begin = a_begin;
|
|
if (*out_begin < b_begin) {
|
|
*out_begin = b_begin;
|
|
}
|
|
*out_end = a_end;
|
|
if (*out_end > b_end) {
|
|
*out_end = b_end;
|
|
}
|
|
return *out_begin < *out_end;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_get_rectangular_clip (cairo_t *cr,
|
|
int bounds_x, int bounds_y,
|
|
int bounds_width, int bounds_height,
|
|
cairo_bool_t *need_clip,
|
|
GdkRectangle *rectangles, int max_rectangles,
|
|
int *num_rectangles)
|
|
{
|
|
cairo_rectangle_list_t *cliplist;
|
|
cairo_rectangle_t *clips;
|
|
int i;
|
|
double b_x = bounds_x;
|
|
double b_y = bounds_y;
|
|
double b_x_most = bounds_x + bounds_width;
|
|
double b_y_most = bounds_y + bounds_height;
|
|
int rect_count = 0;
|
|
cairo_bool_t retval = True;
|
|
|
|
cliplist = cairo_copy_clip_rectangle_list (cr);
|
|
if (cliplist->status != CAIRO_STATUS_SUCCESS) {
|
|
retval = False;
|
|
goto FINISH;
|
|
}
|
|
|
|
if (cliplist->num_rectangles == 0) {
|
|
*num_rectangles = 0;
|
|
*need_clip = True;
|
|
goto FINISH;
|
|
}
|
|
|
|
clips = cliplist->rectangles;
|
|
|
|
for (i = 0; i < cliplist->num_rectangles; ++i) {
|
|
double intersect_x, intersect_y, intersect_x_most, intersect_y_most;
|
|
|
|
/* the clip is always in surface backend coordinates (i.e. native backend coords) */
|
|
if (b_x >= clips[i].x && b_x_most <= clips[i].x + clips[i].width &&
|
|
b_y >= clips[i].y && b_y_most <= clips[i].y + clips[i].height) {
|
|
/* the bounds are entirely inside the clip region so we don't need to clip. */
|
|
*need_clip = False;
|
|
goto FINISH;
|
|
}
|
|
|
|
if (_intersect_interval (b_x, b_x_most, clips[i].x, clips[i].x + clips[i].width,
|
|
&intersect_x, &intersect_x_most) &&
|
|
_intersect_interval (b_y, b_y_most, clips[i].y, clips[i].y + clips[i].height,
|
|
&intersect_y, &intersect_y_most)) {
|
|
GdkRectangle *rect = &rectangles[rect_count];
|
|
|
|
if (rect_count >= max_rectangles) {
|
|
retval = False;
|
|
goto FINISH;
|
|
}
|
|
|
|
if (!_convert_coord_to_int (intersect_x, &rect->x) ||
|
|
!_convert_coord_to_int (intersect_y, &rect->y) ||
|
|
!_convert_coord_to_int (intersect_x_most - intersect_x, &rect->width) ||
|
|
!_convert_coord_to_int (intersect_y_most - intersect_y, &rect->height))
|
|
{
|
|
retval = False;
|
|
goto FINISH;
|
|
}
|
|
|
|
++rect_count;
|
|
}
|
|
}
|
|
|
|
*need_clip = True;
|
|
*num_rectangles = rect_count;
|
|
|
|
FINISH:
|
|
cairo_rectangle_list_destroy (cliplist);
|
|
|
|
return retval;
|
|
}
|
|
|
|
#define MAX_STATIC_CLIP_RECTANGLES 50
|
|
|
|
/**
|
|
* Try the direct path.
|
|
* @param status the status returned by the callback, if we took the direct path
|
|
* @return True if we took the direct path
|
|
*/
|
|
static cairo_bool_t
|
|
_draw_with_xlib_direct (cairo_t *cr,
|
|
Display *default_display,
|
|
cairo_gdk_drawing_callback callback,
|
|
void *closure,
|
|
int bounds_width, int bounds_height,
|
|
cairo_gdk_drawing_support_t capabilities)
|
|
{
|
|
cairo_surface_t *target;
|
|
Drawable d;
|
|
cairo_matrix_t matrix;
|
|
int offset_x, offset_y;
|
|
cairo_bool_t needs_clip;
|
|
GdkRectangle rectangles[MAX_STATIC_CLIP_RECTANGLES];
|
|
int rect_count;
|
|
double device_offset_x, device_offset_y;
|
|
int max_rectangles;
|
|
Screen *screen;
|
|
Visual *visual;
|
|
cairo_bool_t have_rectangular_clip;
|
|
cairo_bool_t ret;
|
|
|
|
target = cairo_get_group_target (cr);
|
|
cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y);
|
|
d = cairo_xlib_surface_get_drawable (target);
|
|
|
|
cairo_get_matrix (cr, &matrix);
|
|
|
|
/* Check that the matrix is a pure translation */
|
|
/* XXX test some approximation to == 1.0 here? */
|
|
if (matrix.xx != 1.0 || matrix.yy != 1.0 || matrix.xy != 0.0 || matrix.yx != 0.0) {
|
|
CAIRO_GDK_DRAWING_NOTE("TAKING SLOW PATH: matrix not a pure translation\n");
|
|
return False;
|
|
}
|
|
/* Check that the matrix translation offsets (adjusted for
|
|
device offset) are integers */
|
|
if (!_convert_coord_to_int (matrix.x0 + device_offset_x, &offset_x) ||
|
|
!_convert_coord_to_int (matrix.y0 + device_offset_y, &offset_y)) {
|
|
CAIRO_GDK_DRAWING_NOTE("TAKING SLOW PATH: non-integer offset\n");
|
|
return False;
|
|
}
|
|
|
|
max_rectangles = 0;
|
|
if (capabilities & CAIRO_GDK_DRAWING_SUPPORTS_CLIP_RECT) {
|
|
max_rectangles = 1;
|
|
}
|
|
if (capabilities & CAIRO_GDK_DRAWING_SUPPORTS_CLIP_LIST) {
|
|
max_rectangles = MAX_STATIC_CLIP_RECTANGLES;
|
|
}
|
|
|
|
/* Check that the clip is rectangular and aligned on unit boundaries. */
|
|
/* Temporarily set the matrix for _get_rectangular_clip. It's basically
|
|
the identity matrix, but we must adjust for the fact that our
|
|
offset-rect is in device coordinates. */
|
|
cairo_identity_matrix (cr);
|
|
cairo_translate (cr, -device_offset_x, -device_offset_y);
|
|
have_rectangular_clip =
|
|
_get_rectangular_clip (cr,
|
|
offset_x, offset_y, bounds_width, bounds_height,
|
|
&needs_clip,
|
|
rectangles, max_rectangles, &rect_count);
|
|
cairo_set_matrix (cr, &matrix);
|
|
if (!have_rectangular_clip) {
|
|
CAIRO_GDK_DRAWING_NOTE("TAKING SLOW PATH: unsupported clip\n");
|
|
return False;
|
|
}
|
|
|
|
/* Stop now if everything is clipped out */
|
|
if (needs_clip && rect_count == 0) {
|
|
CAIRO_GDK_DRAWING_NOTE("TAKING FAST PATH: all clipped\n");
|
|
return True;
|
|
}
|
|
|
|
/* Check that the operator is OVER */
|
|
if (cairo_get_operator (cr) != CAIRO_OPERATOR_OVER) {
|
|
CAIRO_GDK_DRAWING_NOTE("TAKING SLOW PATH: non-OVER operator\n");
|
|
return False;
|
|
}
|
|
|
|
/* Check that the offset is supported */
|
|
if (!(capabilities & CAIRO_GDK_DRAWING_SUPPORTS_OFFSET) &&
|
|
(offset_x != 0 || offset_y != 0)) {
|
|
CAIRO_GDK_DRAWING_NOTE("TAKING SLOW PATH: unsupported offset\n");
|
|
return False;
|
|
}
|
|
|
|
/* Check that the target surface is an xlib surface. Do this late because
|
|
we might complete early above when when the object to be drawn is
|
|
completely clipped out. */
|
|
if (!d) {
|
|
CAIRO_GDK_DRAWING_NOTE("TAKING SLOW PATH: non-X surface\n");
|
|
return False;
|
|
}
|
|
|
|
/* Check that the display is supported */
|
|
screen = cairo_xlib_surface_get_screen (target);
|
|
if (!(capabilities & CAIRO_GDK_DRAWING_SUPPORTS_ALTERNATE_SCREEN) &&
|
|
screen != DefaultScreenOfDisplay (default_display)) {
|
|
CAIRO_GDK_DRAWING_NOTE("TAKING SLOW PATH: non-default display\n");
|
|
return False;
|
|
}
|
|
|
|
/* Check that there is a visual */
|
|
visual = cairo_xlib_surface_get_visual (target);
|
|
if (!visual) {
|
|
CAIRO_GDK_DRAWING_NOTE("TAKING SLOW PATH: no Visual for surface\n");
|
|
return False;
|
|
}
|
|
/* Check that the visual is supported */
|
|
if (!(capabilities & CAIRO_GDK_DRAWING_SUPPORTS_NONDEFAULT_VISUAL) &&
|
|
DefaultVisualOfScreen (screen) != visual) {
|
|
CAIRO_GDK_DRAWING_NOTE("TAKING SLOW PATH: non-default visual\n");
|
|
return False;
|
|
}
|
|
|
|
/* we're good to go! */
|
|
CAIRO_GDK_DRAWING_NOTE("TAKING FAST PATH\n");
|
|
cairo_surface_flush (target);
|
|
ret = callback (closure, target, offset_x, offset_y, rectangles,
|
|
needs_clip ? rect_count : 0);
|
|
if (ret) {
|
|
cairo_surface_mark_dirty (target);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_create_temp_xlib_surface (cairo_t *cr, Display *dpy, int width, int height,
|
|
cairo_gdk_drawing_support_t capabilities)
|
|
{
|
|
cairo_surface_t *result = NULL;
|
|
|
|
if (width >= GDK_PIXMAP_SIZE_MAX ||
|
|
height >= GDK_PIXMAP_SIZE_MAX)
|
|
return NULL;
|
|
|
|
/* base the temp surface on the *screen* surface, not any intermediate
|
|
* group surface, because the screen surface is more likely to have
|
|
* characteristics that the xlib-using code is likely to be happy with */
|
|
cairo_surface_t *target = cairo_get_target (cr);
|
|
Drawable target_drawable = cairo_xlib_surface_get_drawable (target);
|
|
GdkDrawable *gdk_target_drawable = GDK_DRAWABLE(gdk_xid_table_lookup(target_drawable));
|
|
|
|
GdkPixmap *pixmap = NULL;
|
|
GdkVisual *gvis = NULL;
|
|
if (gdk_target_drawable) {
|
|
gvis = gdk_drawable_get_visual(gdk_target_drawable);
|
|
if (gvis) {
|
|
pixmap = gdk_pixmap_new(gdk_target_drawable,
|
|
width, height,
|
|
-1);
|
|
}
|
|
}
|
|
|
|
if (!pixmap) {
|
|
int screen_index = DefaultScreen (dpy);
|
|
int depth = DefaultDepth (dpy, screen_index);
|
|
|
|
GdkColormap *rgb = gdk_rgb_get_colormap();
|
|
gvis = gdk_colormap_get_visual(rgb);
|
|
|
|
pixmap = gdk_pixmap_new(NULL,
|
|
width, height,
|
|
gvis->depth);
|
|
gdk_drawable_set_colormap(pixmap, rgb);
|
|
}
|
|
|
|
result = cairo_xlib_surface_create (gdk_x11_drawable_get_xdisplay(pixmap),
|
|
gdk_x11_drawable_get_xid(pixmap),
|
|
gdk_x11_visual_get_xvisual(gvis),
|
|
width, height);
|
|
if (cairo_surface_status (result) != CAIRO_STATUS_SUCCESS) {
|
|
pixmap_free_func (pixmap);
|
|
return NULL;
|
|
}
|
|
|
|
cairo_surface_set_user_data (result, &pixmap_free_key, pixmap, pixmap_free_func);
|
|
return result;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_draw_onto_temp_xlib_surface (cairo_surface_t *temp_xlib_surface,
|
|
cairo_gdk_drawing_callback callback,
|
|
void *closure,
|
|
double background_gray_value)
|
|
{
|
|
cairo_bool_t result;
|
|
|
|
cairo_t *cr = cairo_create (temp_xlib_surface);
|
|
cairo_set_source_rgb (cr, background_gray_value, background_gray_value,
|
|
background_gray_value);
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
|
cairo_paint (cr);
|
|
cairo_destroy (cr);
|
|
|
|
cairo_surface_flush (temp_xlib_surface);
|
|
/* no clipping is needed because the callback can't draw outside the native
|
|
surface anyway */
|
|
result = callback (closure, temp_xlib_surface, 0, 0, NULL, 0);
|
|
cairo_surface_mark_dirty (temp_xlib_surface);
|
|
return result;
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_copy_xlib_surface_to_image (cairo_surface_t *temp_xlib_surface,
|
|
cairo_format_t format,
|
|
int width, int height,
|
|
unsigned char **data_out)
|
|
{
|
|
unsigned char *data;
|
|
cairo_surface_t *result;
|
|
cairo_t *cr;
|
|
|
|
*data_out = data = (unsigned char*)malloc (width*height*4);
|
|
if (!data)
|
|
return NULL;
|
|
|
|
result = cairo_image_surface_create_for_data (data, format, width, height, width*4);
|
|
cr = cairo_create (result);
|
|
cairo_set_source_surface (cr, temp_xlib_surface, 0, 0);
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
|
cairo_paint (cr);
|
|
cairo_destroy (cr);
|
|
return result;
|
|
}
|
|
|
|
#define SET_ALPHA(v, a) (((v) & ~(0xFF << 24)) | ((a) << 24))
|
|
#define GREEN_OF(v) (((v) >> 8) & 0xFF)
|
|
|
|
/**
|
|
* Given the RGB data for two image surfaces, one a source image composited
|
|
* with OVER onto a black background, and one a source image composited with
|
|
* OVER onto a white background, reconstruct the original image data into
|
|
* black_data.
|
|
*
|
|
* Consider a single color channel and a given pixel. Suppose the original
|
|
* premultiplied color value was C and the alpha value was A. Let the final
|
|
* on-black color be B and the final on-white color be W. All values range
|
|
* over 0-255.
|
|
* Then B=C and W=(255*(255 - A) + C*255)/255. Solving for A, we get
|
|
* A=255 - (W - C). Therefore it suffices to leave the black_data color
|
|
* data alone and set the alpha values using that simple formula. It shouldn't
|
|
* matter what color channel we pick for the alpha computation, but we'll
|
|
* pick green because if we went through a color channel downsample the green
|
|
* bits are likely to be the most accurate.
|
|
*/
|
|
static void
|
|
_compute_alpha_values (uint32_t *black_data,
|
|
uint32_t *white_data,
|
|
int width, int height,
|
|
cairo_gdk_drawing_result_t *analysis)
|
|
{
|
|
int num_pixels = width*height;
|
|
int i;
|
|
uint32_t first;
|
|
uint32_t deltas = 0;
|
|
unsigned char first_alpha;
|
|
|
|
if (num_pixels == 0) {
|
|
if (analysis) {
|
|
analysis->uniform_alpha = True;
|
|
analysis->uniform_color = True;
|
|
/* whatever we put here will be true */
|
|
analysis->alpha = 1.0;
|
|
analysis->r = analysis->g = analysis->b = 0.0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
first_alpha = 255 - (GREEN_OF(*white_data) - GREEN_OF(*black_data));
|
|
/* set the alpha value of 'first' */
|
|
first = SET_ALPHA(*black_data, first_alpha);
|
|
|
|
for (i = 0; i < num_pixels; ++i) {
|
|
uint32_t black = *black_data;
|
|
uint32_t white = *white_data;
|
|
unsigned char pixel_alpha = 255 - (GREEN_OF(white) - GREEN_OF(black));
|
|
|
|
black = SET_ALPHA(black, pixel_alpha);
|
|
*black_data = black;
|
|
deltas |= (first ^ black);
|
|
|
|
black_data++;
|
|
white_data++;
|
|
}
|
|
|
|
if (analysis) {
|
|
analysis->uniform_alpha = (deltas >> 24) == 0;
|
|
if (analysis->uniform_alpha) {
|
|
analysis->alpha = first_alpha/255.0;
|
|
/* we only set uniform_color when the alpha is already uniform.
|
|
it's only useful in that case ... and if the alpha was nonuniform
|
|
then computing whether the color is uniform would require unpremultiplying
|
|
every pixel */
|
|
analysis->uniform_color = (deltas & ~(0xFF << 24)) == 0;
|
|
if (analysis->uniform_color) {
|
|
if (first_alpha == 0) {
|
|
/* can't unpremultiply, this is OK */
|
|
analysis->r = analysis->g = analysis->b = 0.0;
|
|
} else {
|
|
double d_first_alpha = first_alpha;
|
|
analysis->r = (first & 0xFF)/d_first_alpha;
|
|
analysis->g = ((first >> 8) & 0xFF)/d_first_alpha;
|
|
analysis->b = ((first >> 16) & 0xFF)/d_first_alpha;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
cairo_draw_with_gdk (cairo_t *cr,
|
|
cairo_gdk_drawing_callback callback,
|
|
void * closure,
|
|
unsigned int width, unsigned int height,
|
|
cairo_gdk_drawing_opacity_t is_opaque,
|
|
cairo_gdk_drawing_support_t capabilities,
|
|
cairo_gdk_drawing_result_t *result)
|
|
{
|
|
cairo_surface_t *temp_xlib_surface;
|
|
cairo_surface_t *black_image_surface;
|
|
cairo_surface_t *white_image_surface;
|
|
unsigned char *black_data;
|
|
unsigned char *white_data;
|
|
Display *dpy = gdk_x11_get_default_xdisplay();
|
|
|
|
if (result) {
|
|
result->surface = NULL;
|
|
result->uniform_alpha = False;
|
|
result->uniform_color = False;
|
|
}
|
|
|
|
/* exit early if there's no work to do. This is actually important
|
|
because we'll die with an X error if we try to create an empty temporary
|
|
pixmap */
|
|
if (width == 0 || height == 0)
|
|
return;
|
|
|
|
if (_draw_with_xlib_direct (cr, dpy, callback, closure, width, height,
|
|
capabilities))
|
|
return;
|
|
|
|
temp_xlib_surface = _create_temp_xlib_surface (cr, dpy, width, height,
|
|
capabilities);
|
|
if (temp_xlib_surface == NULL)
|
|
return;
|
|
/* update 'dpy' to refer to the display that actually got used. Might
|
|
be different now, if cr was referring to an xlib surface on a different display */
|
|
dpy = cairo_xlib_surface_get_display (temp_xlib_surface);
|
|
|
|
if (!_draw_onto_temp_xlib_surface (temp_xlib_surface, callback, closure, 0.0)) {
|
|
cairo_surface_destroy (temp_xlib_surface);
|
|
return;
|
|
}
|
|
|
|
if (is_opaque == CAIRO_GDK_DRAWING_OPAQUE) {
|
|
cairo_set_source_surface (cr, temp_xlib_surface, 0.0, 0.0);
|
|
cairo_paint (cr);
|
|
if (result) {
|
|
result->surface = temp_xlib_surface;
|
|
/* fill in the result with what we know, which is really just what our
|
|
assumption was */
|
|
result->uniform_alpha = True;
|
|
result->alpha = 1.0;
|
|
} else {
|
|
cairo_surface_destroy (temp_xlib_surface);
|
|
}
|
|
return;
|
|
}
|
|
|
|
black_image_surface =
|
|
_copy_xlib_surface_to_image (temp_xlib_surface, CAIRO_FORMAT_ARGB32,
|
|
width, height, &black_data);
|
|
|
|
_draw_onto_temp_xlib_surface (temp_xlib_surface, callback, closure, 1.0);
|
|
white_image_surface =
|
|
_copy_xlib_surface_to_image (temp_xlib_surface, CAIRO_FORMAT_RGB24,
|
|
width, height, &white_data);
|
|
|
|
cairo_surface_destroy (temp_xlib_surface);
|
|
|
|
if (black_image_surface && white_image_surface &&
|
|
cairo_surface_status (black_image_surface) == CAIRO_STATUS_SUCCESS &&
|
|
cairo_surface_status (white_image_surface) == CAIRO_STATUS_SUCCESS &&
|
|
black_data != NULL && white_data != NULL) {
|
|
cairo_surface_flush (black_image_surface);
|
|
cairo_surface_flush (white_image_surface);
|
|
_compute_alpha_values ((uint32_t*)black_data, (uint32_t*)white_data, width, height, result);
|
|
cairo_surface_mark_dirty (black_image_surface);
|
|
|
|
cairo_set_source_surface (cr, black_image_surface, 0.0, 0.0);
|
|
/* if the caller wants to retrieve the rendered image, put it into
|
|
a 'similar' surface, and use that as the source for the drawing right
|
|
now. This means we always return a surface similar to the surface
|
|
used for 'cr', which is ideal if it's going to be cached and reused.
|
|
We do not return an image if the result has uniform color and alpha. */
|
|
if (result && (!result->uniform_alpha || !result->uniform_color)) {
|
|
cairo_surface_t *target = cairo_get_group_target (cr);
|
|
cairo_surface_t *similar_surface =
|
|
cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR_ALPHA,
|
|
width, height);
|
|
cairo_t *copy_cr = cairo_create (similar_surface);
|
|
cairo_set_source_surface (copy_cr, black_image_surface, 0.0, 0.0);
|
|
cairo_set_operator (copy_cr, CAIRO_OPERATOR_SOURCE);
|
|
cairo_paint (copy_cr);
|
|
cairo_destroy (copy_cr);
|
|
|
|
cairo_set_source_surface (cr, similar_surface, 0.0, 0.0);
|
|
|
|
result->surface = similar_surface;
|
|
}
|
|
|
|
cairo_paint (cr);
|
|
}
|
|
|
|
if (black_image_surface) {
|
|
cairo_surface_destroy (black_image_surface);
|
|
}
|
|
if (white_image_surface) {
|
|
cairo_surface_destroy (white_image_surface);
|
|
}
|
|
free (black_data);
|
|
free (white_data);
|
|
}
|