forked from AuroraMiddleware/gtk
1000 lines
26 KiB
C
1000 lines
26 KiB
C
/* GDK - The GIMP Drawing Kit
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
* Copyright (C) 1998-2004 Tor Lillqvist
|
|
* Copyright (C) 2001-2005 Hans Breuer
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/*
|
|
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
|
|
* file for a list of people on the GTK+ Team. See the ChangeLog
|
|
* files for a list of changes. These files are distributed with
|
|
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <glib.h>
|
|
|
|
#include <pango/pangowin32.h>
|
|
#include <cairo-win32.h>
|
|
|
|
#include "gdkscreen.h" /* gdk_screen_get_default() */
|
|
#include "gdkprivate-win32.h"
|
|
|
|
#define ROP3_D 0x00AA0029
|
|
#define ROP3_DSna 0x00220326
|
|
#define ROP3_DSPDxax 0x00E20746
|
|
|
|
#define LINE_ATTRIBUTES (GDK_GC_LINE_WIDTH|GDK_GC_LINE_STYLE| \
|
|
GDK_GC_CAP_STYLE|GDK_GC_JOIN_STYLE)
|
|
|
|
#define MUST_RENDER_DASHES_MANUALLY(gcwin32) \
|
|
(gcwin32->line_style == GDK_LINE_DOUBLE_DASH || \
|
|
(gcwin32->line_style == GDK_LINE_ON_OFF_DASH && gcwin32->pen_dash_offset))
|
|
|
|
static void gdk_win32_draw_drawable (GdkDrawable *drawable,
|
|
GdkGC *gc,
|
|
GdkPixmap *src,
|
|
gint xsrc,
|
|
gint ysrc,
|
|
gint xdest,
|
|
gint ydest,
|
|
gint width,
|
|
gint height,
|
|
GdkDrawable *original_src);
|
|
|
|
static cairo_surface_t *gdk_win32_ref_cairo_surface (GdkDrawable *drawable);
|
|
|
|
static void gdk_win32_set_colormap (GdkDrawable *drawable,
|
|
GdkColormap *colormap);
|
|
|
|
static GdkColormap* gdk_win32_get_colormap (GdkDrawable *drawable);
|
|
|
|
static gint gdk_win32_get_depth (GdkDrawable *drawable);
|
|
|
|
static GdkScreen * gdk_win32_get_screen (GdkDrawable *drawable);
|
|
|
|
static GdkVisual* gdk_win32_get_visual (GdkDrawable *drawable);
|
|
|
|
static void gdk_drawable_impl_win32_finalize (GObject *object);
|
|
|
|
static const cairo_user_data_key_t gdk_win32_cairo_key;
|
|
|
|
G_DEFINE_TYPE (GdkDrawableImplWin32, _gdk_drawable_impl_win32, GDK_TYPE_DRAWABLE)
|
|
|
|
|
|
static void
|
|
_gdk_drawable_impl_win32_class_init (GdkDrawableImplWin32Class *klass)
|
|
{
|
|
GdkDrawableClass *drawable_class = GDK_DRAWABLE_CLASS (klass);
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = gdk_drawable_impl_win32_finalize;
|
|
|
|
drawable_class->create_gc = _gdk_win32_gc_new;
|
|
drawable_class->draw_drawable_with_src = gdk_win32_draw_drawable;
|
|
|
|
drawable_class->ref_cairo_surface = gdk_win32_ref_cairo_surface;
|
|
|
|
drawable_class->set_colormap = gdk_win32_set_colormap;
|
|
drawable_class->get_colormap = gdk_win32_get_colormap;
|
|
|
|
drawable_class->get_depth = gdk_win32_get_depth;
|
|
drawable_class->get_screen = gdk_win32_get_screen;
|
|
drawable_class->get_visual = gdk_win32_get_visual;
|
|
}
|
|
|
|
static void
|
|
_gdk_drawable_impl_win32_init (GdkDrawableImplWin32 *impl)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gdk_drawable_impl_win32_finalize (GObject *object)
|
|
{
|
|
gdk_drawable_set_colormap (GDK_DRAWABLE (object), NULL);
|
|
|
|
G_OBJECT_CLASS (_gdk_drawable_impl_win32_parent_class)->finalize (object);
|
|
}
|
|
|
|
/*****************************************************
|
|
* Win32 specific implementations of generic functions *
|
|
*****************************************************/
|
|
|
|
static GdkColormap*
|
|
gdk_win32_get_colormap (GdkDrawable *drawable)
|
|
{
|
|
return GDK_DRAWABLE_IMPL_WIN32 (drawable)->colormap;
|
|
}
|
|
|
|
static void
|
|
gdk_win32_set_colormap (GdkDrawable *drawable,
|
|
GdkColormap *colormap)
|
|
{
|
|
GdkDrawableImplWin32 *impl = GDK_DRAWABLE_IMPL_WIN32 (drawable);
|
|
|
|
if (impl->colormap == colormap)
|
|
return;
|
|
|
|
if (impl->colormap)
|
|
g_object_unref (impl->colormap);
|
|
impl->colormap = colormap;
|
|
if (impl->colormap)
|
|
g_object_ref (impl->colormap);
|
|
}
|
|
|
|
/* Drawing
|
|
*/
|
|
|
|
static int
|
|
rop2_to_rop3 (int rop2)
|
|
{
|
|
switch (rop2)
|
|
{
|
|
/* Oh, Microsoft's silly names for binary and ternary rops. */
|
|
#define CASE(rop2,rop3) case R2_##rop2: return rop3
|
|
CASE (BLACK, BLACKNESS);
|
|
CASE (NOTMERGEPEN, NOTSRCERASE);
|
|
CASE (MASKNOTPEN, 0x00220326);
|
|
CASE (NOTCOPYPEN, NOTSRCCOPY);
|
|
CASE (MASKPENNOT, SRCERASE);
|
|
CASE (NOT, DSTINVERT);
|
|
CASE (XORPEN, SRCINVERT);
|
|
CASE (NOTMASKPEN, 0x007700E6);
|
|
CASE (MASKPEN, SRCAND);
|
|
CASE (NOTXORPEN, 0x00990066);
|
|
CASE (NOP, 0x00AA0029);
|
|
CASE (MERGENOTPEN, MERGEPAINT);
|
|
CASE (COPYPEN, SRCCOPY);
|
|
CASE (MERGEPENNOT, 0x00DD0228);
|
|
CASE (MERGEPEN, SRCPAINT);
|
|
CASE (WHITE, WHITENESS);
|
|
#undef CASE
|
|
default: return SRCCOPY;
|
|
}
|
|
}
|
|
|
|
static int
|
|
rop2_to_patblt_rop (int rop2)
|
|
{
|
|
switch (rop2)
|
|
{
|
|
#define CASE(rop2,patblt_rop) case R2_##rop2: return patblt_rop
|
|
CASE (COPYPEN, PATCOPY);
|
|
CASE (XORPEN, PATINVERT);
|
|
CASE (NOT, DSTINVERT);
|
|
CASE (BLACK, BLACKNESS);
|
|
CASE (WHITE, WHITENESS);
|
|
#undef CASE
|
|
default:
|
|
g_warning ("Unhandled rop2 in GC to be used in PatBlt: %#x", rop2);
|
|
return PATCOPY;
|
|
}
|
|
}
|
|
|
|
static inline int
|
|
align_with_dash_offset (int a, DWORD *dashes, int num_dashes, GdkGCWin32 *gcwin32)
|
|
{
|
|
int n = 0;
|
|
int len_sum = 0;
|
|
/*
|
|
* We can't simply add the dashoffset, it can be an arbitrary larger
|
|
* or smaller value not even between x1 and x2. It just says use the
|
|
* dash pattern aligned to the offset. So ensure x1 is smaller _x1
|
|
* and we start with the appropriate dash.
|
|
*/
|
|
for (n = 0; n < num_dashes; n++)
|
|
len_sum += dashes[n];
|
|
if ( len_sum > 0 /* pathological api usage? */
|
|
&& gcwin32->pen_dash_offset > a)
|
|
a -= (((gcwin32->pen_dash_offset/len_sum - a/len_sum) + 1) * len_sum);
|
|
else
|
|
a = gcwin32->pen_dash_offset;
|
|
|
|
return a;
|
|
}
|
|
|
|
/* Render a dashed line 'by hand'. Used for all dashes on Win9x (where
|
|
* GDI is way too limited), and for double dashes on all Windowses.
|
|
*/
|
|
static inline gboolean
|
|
render_line_horizontal (GdkGCWin32 *gcwin32,
|
|
int x1,
|
|
int x2,
|
|
int y)
|
|
{
|
|
int n = 0;
|
|
const int pen_width = MAX (gcwin32->pen_width, 1);
|
|
const int _x1 = x1;
|
|
|
|
g_assert (gcwin32->pen_dashes);
|
|
|
|
x1 = align_with_dash_offset (x1, gcwin32->pen_dashes, gcwin32->pen_num_dashes, gcwin32);
|
|
|
|
for (n = 0; x1 < x2; n++)
|
|
{
|
|
int len = gcwin32->pen_dashes[n % gcwin32->pen_num_dashes];
|
|
if (x1 + len > x2)
|
|
len = x2 - x1;
|
|
|
|
if (n % 2 == 0 && x1 + len > _x1)
|
|
if (!GDI_CALL (PatBlt, (gcwin32->hdc,
|
|
x1 < _x1 ? _x1 : x1,
|
|
y - pen_width / 2,
|
|
len, pen_width,
|
|
rop2_to_patblt_rop (gcwin32->rop2))))
|
|
return FALSE;
|
|
|
|
x1 += gcwin32->pen_dashes[n % gcwin32->pen_num_dashes];
|
|
}
|
|
|
|
if (gcwin32->line_style == GDK_LINE_DOUBLE_DASH)
|
|
{
|
|
HBRUSH hbr;
|
|
|
|
if ((hbr = SelectObject (gcwin32->hdc, gcwin32->pen_hbrbg)) == HGDI_ERROR)
|
|
return FALSE;
|
|
x1 = _x1;
|
|
x1 += gcwin32->pen_dash_offset;
|
|
for (n = 0; x1 < x2; n++)
|
|
{
|
|
int len = gcwin32->pen_dashes[n % gcwin32->pen_num_dashes];
|
|
if (x1 + len > x2)
|
|
len = x2 - x1;
|
|
|
|
if (n % 2)
|
|
if (!GDI_CALL (PatBlt, (gcwin32->hdc, x1, y - pen_width / 2,
|
|
len, pen_width,
|
|
rop2_to_patblt_rop (gcwin32->rop2))))
|
|
return FALSE;
|
|
|
|
x1 += gcwin32->pen_dashes[n % gcwin32->pen_num_dashes];
|
|
}
|
|
if (SelectObject (gcwin32->hdc, hbr) == HGDI_ERROR)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
render_line_vertical (GdkGCWin32 *gcwin32,
|
|
int x,
|
|
int y1,
|
|
int y2)
|
|
{
|
|
int n;
|
|
const int pen_width = MAX (gcwin32->pen_width, 1);
|
|
const int _y1 = y1;
|
|
|
|
g_assert (gcwin32->pen_dashes);
|
|
|
|
y1 = align_with_dash_offset (y1, gcwin32->pen_dashes, gcwin32->pen_num_dashes, gcwin32);
|
|
for (n = 0; y1 < y2; n++)
|
|
{
|
|
int len = gcwin32->pen_dashes[n % gcwin32->pen_num_dashes];
|
|
if (y1 + len > y2)
|
|
len = y2 - y1;
|
|
if (n % 2 == 0 && y1 + len > _y1)
|
|
if (!GDI_CALL (PatBlt, (gcwin32->hdc, x - pen_width / 2,
|
|
y1 < _y1 ? _y1 : y1,
|
|
pen_width, len,
|
|
rop2_to_patblt_rop (gcwin32->rop2))))
|
|
return FALSE;
|
|
|
|
y1 += gcwin32->pen_dashes[n % gcwin32->pen_num_dashes];
|
|
}
|
|
|
|
if (gcwin32->line_style == GDK_LINE_DOUBLE_DASH)
|
|
{
|
|
HBRUSH hbr;
|
|
|
|
if ((hbr = SelectObject (gcwin32->hdc, gcwin32->pen_hbrbg)) == HGDI_ERROR)
|
|
return FALSE;
|
|
y1 = _y1;
|
|
y1 += gcwin32->pen_dash_offset;
|
|
for (n = 0; y1 < y2; n++)
|
|
{
|
|
int len = gcwin32->pen_dashes[n % gcwin32->pen_num_dashes];
|
|
if (y1 + len > y2)
|
|
len = y2 - y1;
|
|
if (n % 2)
|
|
if (!GDI_CALL (PatBlt, (gcwin32->hdc, x - pen_width / 2, y1,
|
|
pen_width, len,
|
|
rop2_to_patblt_rop (gcwin32->rop2))))
|
|
return FALSE;
|
|
|
|
y1 += gcwin32->pen_dashes[n % gcwin32->pen_num_dashes];
|
|
}
|
|
if (SelectObject (gcwin32->hdc, hbr) == HGDI_ERROR)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
draw_tiles_lowlevel (HDC dest,
|
|
HDC tile,
|
|
int rop3,
|
|
gint dest_x,
|
|
gint dest_y,
|
|
gint tile_x_origin,
|
|
gint tile_y_origin,
|
|
gint width,
|
|
gint height,
|
|
gint tile_width,
|
|
gint tile_height)
|
|
{
|
|
gint x, y;
|
|
|
|
GDK_NOTE (DRAW, g_print ("draw_tiles_lowlevel: %p %+d%+d tile=%p:%dx%d@%+d%+d %dx%d\n",
|
|
dest,
|
|
dest_x, dest_y,
|
|
tile, tile_width, tile_height,
|
|
tile_x_origin, tile_y_origin,
|
|
width, height));
|
|
|
|
y = tile_y_origin % tile_height;
|
|
if (y > 0)
|
|
y -= tile_height;
|
|
while (y < dest_y + height)
|
|
{
|
|
if (y + tile_height >= dest_y)
|
|
{
|
|
x = tile_x_origin % tile_width;
|
|
if (x > 0)
|
|
x -= tile_width;
|
|
while (x < dest_x + width)
|
|
{
|
|
if (x + tile_width >= dest_x)
|
|
{
|
|
gint src_x = MAX (0, dest_x - x);
|
|
gint src_y = MAX (0, dest_y - y);
|
|
|
|
if (!GDI_CALL (BitBlt, (dest, x + src_x, y + src_y,
|
|
MIN (tile_width, dest_x + width - (x + src_x)),
|
|
MIN (tile_height, dest_y + height - (y + src_y)),
|
|
tile,
|
|
src_x, src_y,
|
|
rop3)))
|
|
return;
|
|
}
|
|
x += tile_width;
|
|
}
|
|
}
|
|
y += tile_height;
|
|
}
|
|
}
|
|
|
|
static void
|
|
draw_tiles (GdkDrawable *drawable,
|
|
GdkGC *gc,
|
|
int rop3,
|
|
GdkPixmap *tile,
|
|
gint dest_x,
|
|
gint dest_y,
|
|
gint tile_x_origin,
|
|
gint tile_y_origin,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
const GdkGCValuesMask mask = GDK_GC_FOREGROUND;
|
|
gint tile_width, tile_height;
|
|
GdkGC *gc_copy;
|
|
HDC dest_hdc, tile_hdc;
|
|
|
|
gc_copy = gdk_gc_new (tile);
|
|
gdk_gc_copy (gc_copy, gc);
|
|
dest_hdc = gdk_win32_hdc_get (drawable, gc, mask);
|
|
tile_hdc = gdk_win32_hdc_get (tile, gc_copy, mask);
|
|
|
|
gdk_drawable_get_size (tile, &tile_width, &tile_height);
|
|
|
|
draw_tiles_lowlevel (dest_hdc, tile_hdc, rop3,
|
|
dest_x, dest_y, tile_x_origin, tile_y_origin,
|
|
width, height, tile_width, tile_height);
|
|
|
|
gdk_win32_hdc_release (drawable, gc, mask);
|
|
gdk_win32_hdc_release (tile, gc_copy, mask);
|
|
g_object_unref (gc_copy);
|
|
}
|
|
|
|
static cairo_region_t *
|
|
widen_bounds (GdkRectangle *bounds,
|
|
gint pen_width)
|
|
{
|
|
if (pen_width == 0)
|
|
pen_width = 1;
|
|
|
|
bounds->x -= pen_width;
|
|
bounds->y -= pen_width;
|
|
bounds->width += 2 * pen_width;
|
|
bounds->height += 2 * pen_width;
|
|
|
|
return cairo_region_create_rectangle (bounds);
|
|
}
|
|
|
|
static void
|
|
gdk_win32_draw_drawable (GdkDrawable *drawable,
|
|
GdkGC *gc,
|
|
GdkPixmap *src,
|
|
gint xsrc,
|
|
gint ysrc,
|
|
gint xdest,
|
|
gint ydest,
|
|
gint width,
|
|
gint height,
|
|
GdkDrawable *original_src)
|
|
{
|
|
g_assert (GDK_IS_DRAWABLE_IMPL_WIN32 (drawable));
|
|
|
|
_gdk_win32_blit (FALSE, (GdkDrawableImplWin32 *) drawable,
|
|
gc, src, xsrc, ysrc,
|
|
xdest, ydest, width, height);
|
|
}
|
|
|
|
static void
|
|
blit_from_pixmap (gboolean use_fg_bg,
|
|
GdkDrawableImplWin32 *dest,
|
|
HDC hdc,
|
|
GdkPixmapImplWin32 *src,
|
|
GdkGC *gc,
|
|
gint xsrc,
|
|
gint ysrc,
|
|
gint xdest,
|
|
gint ydest,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
GdkGCWin32 *gcwin32 = GDK_GC_WIN32 (gc);
|
|
HDC srcdc;
|
|
HBITMAP holdbitmap;
|
|
RGBQUAD oldtable[256], newtable[256];
|
|
COLORREF bg, fg;
|
|
|
|
gint newtable_size = 0, oldtable_size = 0;
|
|
gboolean ok = TRUE;
|
|
|
|
GDK_NOTE (DRAW, g_print ("blit_from_pixmap\n"));
|
|
|
|
srcdc = _gdk_win32_drawable_acquire_dc (GDK_DRAWABLE (src));
|
|
if (!srcdc)
|
|
return;
|
|
|
|
if (!(holdbitmap = SelectObject (srcdc, ((GdkDrawableImplWin32 *) src)->handle)))
|
|
WIN32_GDI_FAILED ("SelectObject");
|
|
else
|
|
{
|
|
if (GDK_PIXMAP_OBJECT (src->parent_instance.wrapper)->depth <= 8)
|
|
{
|
|
/* Blitting from a 1, 4 or 8-bit pixmap */
|
|
|
|
if ((oldtable_size = GetDIBColorTable (srcdc, 0, 256, oldtable)) == 0)
|
|
WIN32_GDI_FAILED ("GetDIBColorTable");
|
|
else if (GDK_PIXMAP_OBJECT (src->parent_instance.wrapper)->depth == 1)
|
|
{
|
|
/* Blitting from an 1-bit pixmap */
|
|
|
|
gint bgix, fgix;
|
|
|
|
if (use_fg_bg)
|
|
{
|
|
bgix = _gdk_gc_get_bg_pixel (gc);
|
|
fgix = _gdk_gc_get_fg_pixel (gc);
|
|
}
|
|
else
|
|
{
|
|
bgix = 0;
|
|
fgix = 1;
|
|
}
|
|
|
|
if (GDK_IS_PIXMAP_IMPL_WIN32 (dest) &&
|
|
GDK_PIXMAP_OBJECT (dest->wrapper)->depth <= 8)
|
|
{
|
|
/* Destination is also pixmap, get fg and bg from
|
|
* its palette. Either use the foreground and
|
|
* background pixel values in the GC, or 0
|
|
* and 1 to index the palette.
|
|
*/
|
|
if (!GDI_CALL (GetDIBColorTable, (hdc, bgix, 1, newtable)) ||
|
|
!GDI_CALL (GetDIBColorTable, (hdc, fgix, 1, newtable+1)))
|
|
ok = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* Destination is a window, get fg and bg from its
|
|
* colormap
|
|
*/
|
|
|
|
bg = _gdk_win32_colormap_color (dest->colormap, bgix);
|
|
fg = _gdk_win32_colormap_color (dest->colormap, fgix);
|
|
newtable[0].rgbBlue = GetBValue (bg);
|
|
newtable[0].rgbGreen = GetGValue (bg);
|
|
newtable[0].rgbRed = GetRValue (bg);
|
|
newtable[0].rgbReserved = 0;
|
|
newtable[1].rgbBlue = GetBValue (fg);
|
|
newtable[1].rgbGreen = GetGValue (fg);
|
|
newtable[1].rgbRed = GetRValue (fg);
|
|
newtable[1].rgbReserved = 0;
|
|
}
|
|
if (ok)
|
|
GDK_NOTE (DRAW, g_print ("bg: %02x %02x %02x "
|
|
"fg: %02x %02x %02x\n",
|
|
newtable[0].rgbRed,
|
|
newtable[0].rgbGreen,
|
|
newtable[0].rgbBlue,
|
|
newtable[1].rgbRed,
|
|
newtable[1].rgbGreen,
|
|
newtable[1].rgbBlue));
|
|
newtable_size = 2;
|
|
}
|
|
else if (GDK_IS_PIXMAP_IMPL_WIN32 (dest))
|
|
{
|
|
/* Destination is pixmap, get its color table */
|
|
|
|
if ((newtable_size = GetDIBColorTable (hdc, 0, 256, newtable)) == 0)
|
|
WIN32_GDI_FAILED ("GetDIBColorTable"), ok = FALSE;
|
|
}
|
|
|
|
/* If blitting between pixmaps, set source's color table */
|
|
if (ok && newtable_size > 0)
|
|
{
|
|
GDK_NOTE (MISC_OR_COLORMAP,
|
|
g_print ("blit_from_pixmap: set color table"
|
|
" hdc=%p count=%d\n",
|
|
srcdc, newtable_size));
|
|
if (!GDI_CALL (SetDIBColorTable, (srcdc, 0, newtable_size, newtable)))
|
|
ok = FALSE;
|
|
}
|
|
}
|
|
|
|
if (ok)
|
|
if (!BitBlt (hdc, xdest, ydest, width, height,
|
|
srcdc, xsrc, ysrc, rop2_to_rop3 (gcwin32->rop2)) &&
|
|
GetLastError () != ERROR_INVALID_HANDLE)
|
|
WIN32_GDI_FAILED ("BitBlt");
|
|
|
|
/* Restore source's color table if necessary */
|
|
if (ok && newtable_size > 0 && oldtable_size > 0)
|
|
{
|
|
GDK_NOTE (MISC_OR_COLORMAP,
|
|
g_print ("blit_from_pixmap: reset color table"
|
|
" hdc=%p count=%d\n",
|
|
srcdc, oldtable_size));
|
|
GDI_CALL (SetDIBColorTable, (srcdc, 0, oldtable_size, oldtable));
|
|
}
|
|
|
|
GDI_CALL (SelectObject, (srcdc, holdbitmap));
|
|
}
|
|
|
|
_gdk_win32_drawable_release_dc (GDK_DRAWABLE (src));
|
|
}
|
|
|
|
static void
|
|
blit_inside_drawable (HDC hdc,
|
|
GdkGCWin32 *gcwin32,
|
|
gint xsrc,
|
|
gint ysrc,
|
|
gint xdest,
|
|
gint ydest,
|
|
gint width,
|
|
gint height)
|
|
|
|
{
|
|
GDK_NOTE (DRAW, g_print ("blit_inside_drawable\n"));
|
|
|
|
GDI_CALL (BitBlt, (hdc, xdest, ydest, width, height,
|
|
hdc, xsrc, ysrc, rop2_to_rop3 (gcwin32->rop2)));
|
|
}
|
|
|
|
static void
|
|
blit_from_window (HDC hdc,
|
|
GdkGCWin32 *gcwin32,
|
|
GdkDrawableImplWin32 *src,
|
|
gint xsrc,
|
|
gint ysrc,
|
|
gint xdest,
|
|
gint ydest,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
HDC srcdc;
|
|
HPALETTE holdpal = NULL;
|
|
GdkColormap *cmap = gdk_colormap_get_system ();
|
|
|
|
GDK_NOTE (DRAW, g_print ("blit_from_window\n"));
|
|
|
|
if ((srcdc = GetDC (src->handle)) == NULL)
|
|
{
|
|
WIN32_GDI_FAILED ("GetDC");
|
|
return;
|
|
}
|
|
|
|
if (cmap->visual->type == GDK_VISUAL_PSEUDO_COLOR ||
|
|
cmap->visual->type == GDK_VISUAL_STATIC_COLOR)
|
|
{
|
|
gint k;
|
|
|
|
if (!(holdpal = SelectPalette (srcdc, GDK_WIN32_COLORMAP_DATA (cmap)->hpal, FALSE)))
|
|
WIN32_GDI_FAILED ("SelectPalette");
|
|
else if ((k = RealizePalette (srcdc)) == GDI_ERROR)
|
|
WIN32_GDI_FAILED ("RealizePalette");
|
|
else if (k > 0)
|
|
GDK_NOTE (MISC_OR_COLORMAP,
|
|
g_print ("blit_from_window: realized %d\n", k));
|
|
}
|
|
|
|
GDI_CALL (BitBlt, (hdc, xdest, ydest, width, height,
|
|
srcdc, xsrc, ysrc, rop2_to_rop3 (gcwin32->rop2)));
|
|
|
|
if (holdpal != NULL)
|
|
GDI_CALL (SelectPalette, (srcdc, holdpal, FALSE));
|
|
|
|
GDI_CALL (ReleaseDC, (src->handle, srcdc));
|
|
}
|
|
|
|
void
|
|
_gdk_win32_blit (gboolean use_fg_bg,
|
|
GdkDrawableImplWin32 *draw_impl,
|
|
GdkGC *gc,
|
|
GdkDrawable *src,
|
|
gint xsrc,
|
|
gint ysrc,
|
|
gint xdest,
|
|
gint ydest,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
HDC hdc;
|
|
HRGN src_rgn, draw_rgn, outside_rgn;
|
|
RECT r;
|
|
GdkDrawableImplWin32 *src_impl = NULL;
|
|
gint src_width, src_height;
|
|
|
|
GDK_NOTE (DRAW, g_print ("_gdk_win32_blit: src:%s %dx%d@%+d%+d\n"
|
|
" dst:%s @%+d%+d use_fg_bg=%d\n",
|
|
_gdk_win32_drawable_description (src),
|
|
width, height, xsrc, ysrc,
|
|
_gdk_win32_drawable_description (&draw_impl->parent_instance),
|
|
xdest, ydest,
|
|
use_fg_bg));
|
|
|
|
/* If blitting from the root window, take the multi-monitor offset
|
|
* into account.
|
|
*/
|
|
if (src == ((GdkWindowObject *)_gdk_root)->impl)
|
|
{
|
|
GDK_NOTE (DRAW, g_print ("... offsetting src coords\n"));
|
|
xsrc -= _gdk_offset_x;
|
|
ysrc -= _gdk_offset_y;
|
|
}
|
|
|
|
if (GDK_IS_DRAWABLE_IMPL_WIN32 (src))
|
|
src_impl = (GdkDrawableImplWin32 *) src;
|
|
else if (GDK_IS_WINDOW (src))
|
|
src_impl = (GdkDrawableImplWin32 *) GDK_WINDOW_OBJECT (src)->impl;
|
|
else if (GDK_IS_PIXMAP (src))
|
|
src_impl = (GdkDrawableImplWin32 *) GDK_PIXMAP_OBJECT (src)->impl;
|
|
else
|
|
g_assert_not_reached ();
|
|
|
|
if (GDK_IS_WINDOW_IMPL_WIN32 (draw_impl) &&
|
|
GDK_IS_PIXMAP_IMPL_WIN32 (src_impl))
|
|
{
|
|
GdkPixmapImplWin32 *src_pixmap = GDK_PIXMAP_IMPL_WIN32 (src_impl);
|
|
|
|
if (xsrc < 0)
|
|
{
|
|
width += xsrc;
|
|
xdest -= xsrc;
|
|
xsrc = 0;
|
|
}
|
|
|
|
if (ysrc < 0)
|
|
{
|
|
height += ysrc;
|
|
ydest -= ysrc;
|
|
ysrc = 0;
|
|
}
|
|
|
|
if (xsrc + width > src_pixmap->width)
|
|
width = src_pixmap->width - xsrc;
|
|
if (ysrc + height > src_pixmap->height)
|
|
height = src_pixmap->height - ysrc;
|
|
}
|
|
|
|
hdc = gdk_win32_hdc_get (&draw_impl->parent_instance, gc, GDK_GC_FOREGROUND);
|
|
|
|
gdk_drawable_get_size (src_impl->wrapper, &src_width, &src_height);
|
|
|
|
if ((src_rgn = CreateRectRgn (0, 0, src_width + 1, src_height + 1)) == NULL)
|
|
{
|
|
WIN32_GDI_FAILED ("CreateRectRgn");
|
|
}
|
|
else if ((draw_rgn = CreateRectRgn (xsrc, ysrc,
|
|
xsrc + width + 1,
|
|
ysrc + height + 1)) == NULL)
|
|
{
|
|
WIN32_GDI_FAILED ("CreateRectRgn");
|
|
}
|
|
else
|
|
{
|
|
if (GDK_IS_WINDOW_IMPL_WIN32 (draw_impl))
|
|
{
|
|
int comb;
|
|
|
|
/* If we are drawing on a window, calculate the region that is
|
|
* outside the source pixmap, and invalidate that, causing it to
|
|
* be cleared. Not completely sure whether this is always needed. XXX
|
|
*/
|
|
SetRectEmpty (&r);
|
|
outside_rgn = CreateRectRgnIndirect (&r);
|
|
|
|
if ((comb = CombineRgn (outside_rgn,
|
|
draw_rgn, src_rgn,
|
|
RGN_DIFF)) == ERROR)
|
|
WIN32_GDI_FAILED ("CombineRgn");
|
|
else if (comb != NULLREGION)
|
|
{
|
|
OffsetRgn (outside_rgn, xdest, ydest);
|
|
GDK_NOTE (DRAW, (GetRgnBox (outside_rgn, &r),
|
|
g_print ("... InvalidateRgn "
|
|
"bbox: %ldx%ld@%+ld%+ld\n",
|
|
r.right - r.left - 1, r.bottom - r.top - 1,
|
|
r.left, r.top)));
|
|
InvalidateRgn (draw_impl->handle, outside_rgn, TRUE);
|
|
}
|
|
GDI_CALL (DeleteObject, (outside_rgn));
|
|
}
|
|
|
|
#if 1 /* Don't know if this is necessary XXX */
|
|
if (CombineRgn (draw_rgn, draw_rgn, src_rgn, RGN_AND) == COMPLEXREGION)
|
|
g_warning ("gdk_win32_blit: CombineRgn returned a COMPLEXREGION");
|
|
|
|
GetRgnBox (draw_rgn, &r);
|
|
if (r.left != xsrc || r.top != ysrc ||
|
|
r.right != xsrc + width + 1 || r.bottom != ysrc + height + 1)
|
|
{
|
|
xdest += r.left - xsrc;
|
|
xsrc = r.left;
|
|
ydest += r.top - ysrc;
|
|
ysrc = r.top;
|
|
width = r.right - xsrc - 1;
|
|
height = r.bottom - ysrc - 1;
|
|
|
|
GDK_NOTE (DRAW, g_print ("... restricted to src: %dx%d@%+d%+d, "
|
|
"dest: @%+d%+d\n",
|
|
width, height, xsrc, ysrc,
|
|
xdest, ydest));
|
|
}
|
|
#endif
|
|
|
|
GDI_CALL (DeleteObject, (src_rgn));
|
|
GDI_CALL (DeleteObject, (draw_rgn));
|
|
}
|
|
|
|
if (draw_impl->handle == src_impl->handle)
|
|
blit_inside_drawable (hdc, GDK_GC_WIN32 (gc), xsrc, ysrc, xdest, ydest, width, height);
|
|
else if (GDK_IS_PIXMAP_IMPL_WIN32 (src_impl))
|
|
blit_from_pixmap (use_fg_bg, draw_impl, hdc,
|
|
(GdkPixmapImplWin32 *) src_impl, gc,
|
|
xsrc, ysrc, xdest, ydest, width, height);
|
|
else
|
|
blit_from_window (hdc, GDK_GC_WIN32 (gc), src_impl, xsrc, ysrc, xdest, ydest, width, height);
|
|
|
|
gdk_win32_hdc_release (&draw_impl->parent_instance, gc, GDK_GC_FOREGROUND);
|
|
}
|
|
|
|
/**
|
|
* _gdk_win32_drawable_acquire_dc
|
|
* @drawable: a Win32 #GdkDrawable implementation
|
|
*
|
|
* Gets a DC with the given drawable selected into
|
|
* it.
|
|
*
|
|
* Return value: The DC, on success. Otherwise
|
|
* %NULL. If this function succeeded
|
|
* _gdk_win32_drawable_release_dc() must be called
|
|
* release the DC when you are done using it.
|
|
**/
|
|
HDC
|
|
_gdk_win32_drawable_acquire_dc (GdkDrawable *drawable)
|
|
{
|
|
GdkDrawableImplWin32 *impl = GDK_DRAWABLE_IMPL_WIN32 (drawable);
|
|
|
|
if (GDK_IS_WINDOW_IMPL_WIN32 (drawable) &&
|
|
GDK_WINDOW_DESTROYED (impl->wrapper))
|
|
return NULL;
|
|
|
|
if (!impl->hdc)
|
|
{
|
|
if (GDK_IS_PIXMAP_IMPL_WIN32 (impl))
|
|
{
|
|
impl->hdc = CreateCompatibleDC (NULL);
|
|
if (!impl->hdc)
|
|
WIN32_GDI_FAILED ("CreateCompatibleDC");
|
|
|
|
if (impl->hdc)
|
|
{
|
|
impl->saved_dc_bitmap = SelectObject (impl->hdc,
|
|
impl->handle);
|
|
if (!impl->saved_dc_bitmap)
|
|
{
|
|
WIN32_GDI_FAILED ("CreateCompatibleDC");
|
|
DeleteDC (impl->hdc);
|
|
impl->hdc = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
impl->hdc = GetDC (impl->handle);
|
|
if (!impl->hdc)
|
|
WIN32_GDI_FAILED ("GetDC");
|
|
}
|
|
}
|
|
|
|
if (impl->hdc)
|
|
{
|
|
impl->hdc_count++;
|
|
return impl->hdc;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* _gdk_win32_drawable_release_dc
|
|
* @drawable: a Win32 #GdkDrawable implementation
|
|
*
|
|
* Releases the reference count for the DC
|
|
* from _gdk_win32_drawable_acquire_dc()
|
|
**/
|
|
void
|
|
_gdk_win32_drawable_release_dc (GdkDrawable *drawable)
|
|
{
|
|
GdkDrawableImplWin32 *impl = GDK_DRAWABLE_IMPL_WIN32 (drawable);
|
|
|
|
g_return_if_fail (impl->hdc_count > 0);
|
|
|
|
impl->hdc_count--;
|
|
if (impl->hdc_count == 0)
|
|
{
|
|
if (impl->saved_dc_bitmap)
|
|
{
|
|
GDI_CALL (SelectObject, (impl->hdc, impl->saved_dc_bitmap));
|
|
impl->saved_dc_bitmap = NULL;
|
|
}
|
|
|
|
if (impl->hdc)
|
|
{
|
|
if (GDK_IS_PIXMAP_IMPL_WIN32 (impl))
|
|
GDI_CALL (DeleteDC, (impl->hdc));
|
|
else
|
|
GDI_CALL (ReleaseDC, (impl->handle, impl->hdc));
|
|
impl->hdc = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
cairo_surface_t *
|
|
_gdk_windowing_create_cairo_surface (GdkDrawable *drawable,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
/* width and height are determined from the DC */
|
|
return gdk_win32_ref_cairo_surface (drawable);
|
|
}
|
|
|
|
static void
|
|
gdk_win32_cairo_surface_destroy (void *data)
|
|
{
|
|
GdkDrawableImplWin32 *impl = data;
|
|
|
|
_gdk_win32_drawable_release_dc (GDK_DRAWABLE (impl));
|
|
impl->cairo_surface = NULL;
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
gdk_win32_ref_cairo_surface (GdkDrawable *drawable)
|
|
{
|
|
GdkDrawableImplWin32 *impl = GDK_DRAWABLE_IMPL_WIN32 (drawable);
|
|
|
|
if (GDK_IS_WINDOW_IMPL_WIN32 (drawable) &&
|
|
GDK_WINDOW_DESTROYED (impl->wrapper))
|
|
return NULL;
|
|
|
|
if (!impl->cairo_surface)
|
|
{
|
|
HDC hdc = _gdk_win32_drawable_acquire_dc (drawable);
|
|
if (!hdc)
|
|
return NULL;
|
|
|
|
impl->cairo_surface = cairo_win32_surface_create (hdc);
|
|
|
|
cairo_surface_set_user_data (impl->cairo_surface, &gdk_win32_cairo_key,
|
|
drawable, gdk_win32_cairo_surface_destroy);
|
|
}
|
|
else
|
|
cairo_surface_reference (impl->cairo_surface);
|
|
|
|
return impl->cairo_surface;
|
|
}
|
|
|
|
void
|
|
_gdk_windowing_set_cairo_surface_size (cairo_surface_t *surface,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
// Do nothing. The surface size is determined by the DC
|
|
}
|
|
|
|
static gint
|
|
gdk_win32_get_depth (GdkDrawable *drawable)
|
|
{
|
|
/* This is a bit bogus but I'm not sure the other way is better */
|
|
|
|
return gdk_drawable_get_depth (GDK_DRAWABLE_IMPL_WIN32 (drawable)->wrapper);
|
|
}
|
|
|
|
static GdkScreen*
|
|
gdk_win32_get_screen (GdkDrawable *drawable)
|
|
{
|
|
return gdk_screen_get_default ();
|
|
}
|
|
|
|
static GdkVisual*
|
|
gdk_win32_get_visual (GdkDrawable *drawable)
|
|
{
|
|
return gdk_drawable_get_visual (GDK_DRAWABLE_IMPL_WIN32 (drawable)->wrapper);
|
|
}
|
|
|
|
HGDIOBJ
|
|
gdk_win32_drawable_get_handle (GdkDrawable *drawable)
|
|
{
|
|
return GDK_DRAWABLE_HANDLE (drawable);
|
|
}
|
|
|
|
/**
|
|
* _gdk_win32_drawable_finish
|
|
* @drawable: a Win32 #GdkDrawable implementation
|
|
*
|
|
* Releases any resources allocated internally for the drawable.
|
|
* This is called when the drawable becomes unusable
|
|
* (gdk_window_destroy() for a window, or the refcount going to
|
|
* zero for a pixmap.)
|
|
**/
|
|
void
|
|
_gdk_win32_drawable_finish (GdkDrawable *drawable)
|
|
{
|
|
GdkDrawableImplWin32 *impl = GDK_DRAWABLE_IMPL_WIN32 (drawable);
|
|
|
|
if (impl->cairo_surface)
|
|
{
|
|
cairo_surface_finish (impl->cairo_surface);
|
|
cairo_surface_set_user_data (impl->cairo_surface, &gdk_win32_cairo_key, NULL, NULL);
|
|
}
|
|
|
|
g_assert (impl->hdc_count == 0);
|
|
}
|
|
|