2018-04-12 14:48:31 +00:00
|
|
|
/* GDK - The GIMP Drawing Kit
|
|
|
|
*
|
|
|
|
* Copyright © 2018 Benjamin Otte
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library 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
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "gdkconfig.h"
|
|
|
|
|
|
|
|
#include "gdkcairocontext-win32.h"
|
2018-04-24 15:14:23 +00:00
|
|
|
#include "gdkprivate-win32.h"
|
|
|
|
#include "gdksurface-win32.h"
|
|
|
|
#include "gdkwin32misc.h"
|
|
|
|
|
|
|
|
#include <cairo-win32.h>
|
|
|
|
|
|
|
|
#include <Windows.h>
|
2018-04-12 14:48:31 +00:00
|
|
|
|
|
|
|
G_DEFINE_TYPE (GdkWin32CairoContext, gdk_win32_cairo_context, GDK_TYPE_CAIRO_CONTEXT)
|
|
|
|
|
2018-04-24 15:14:23 +00:00
|
|
|
static cairo_surface_t *
|
2019-05-19 03:09:05 +00:00
|
|
|
create_cairo_surface_for_layered_window (GdkWin32Surface *impl,
|
2020-07-24 13:54:49 +00:00
|
|
|
int width,
|
|
|
|
int height,
|
|
|
|
int scale)
|
2018-04-24 15:14:23 +00:00
|
|
|
{
|
|
|
|
if (width > impl->dib_width ||
|
|
|
|
height > impl->dib_height)
|
|
|
|
{
|
|
|
|
cairo_surface_t *new_cache;
|
|
|
|
|
|
|
|
impl->dib_width = MAX (impl->dib_width, MAX (width, 1));
|
|
|
|
impl->dib_height = MAX (impl->dib_height, MAX (height, 1));
|
|
|
|
/* Create larger cache surface, copy old cache surface over it */
|
|
|
|
new_cache = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32,
|
|
|
|
impl->dib_width,
|
|
|
|
impl->dib_height);
|
|
|
|
|
|
|
|
if (impl->cache_surface)
|
|
|
|
{
|
|
|
|
cairo_t *cr = cairo_create (new_cache);
|
|
|
|
cairo_set_source_surface (cr, impl->cache_surface, 0, 0);
|
|
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
|
|
|
cairo_paint (cr);
|
|
|
|
cairo_destroy (cr);
|
|
|
|
cairo_surface_flush (new_cache);
|
|
|
|
|
|
|
|
cairo_surface_destroy (impl->cache_surface);
|
|
|
|
}
|
|
|
|
|
|
|
|
impl->cache_surface = new_cache;
|
|
|
|
|
|
|
|
cairo_surface_set_device_scale (impl->cache_surface,
|
|
|
|
scale,
|
|
|
|
scale);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cairo_surface_reference (impl->cache_surface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static cairo_surface_t *
|
|
|
|
create_cairo_surface_for_surface (GdkSurface *surface,
|
|
|
|
int scale)
|
|
|
|
{
|
|
|
|
cairo_surface_t *cairo_surface;
|
|
|
|
HDC hdc;
|
|
|
|
|
|
|
|
hdc = GetDC (GDK_SURFACE_HWND (surface));
|
|
|
|
if (!hdc)
|
|
|
|
{
|
|
|
|
WIN32_GDI_FAILED ("GetDC");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
cairo_surface = cairo_win32_surface_create_with_format (hdc, CAIRO_FORMAT_ARGB32);
|
|
|
|
cairo_surface_set_device_scale (cairo_surface, scale, scale);
|
|
|
|
|
|
|
|
return cairo_surface;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gdk_win32_cairo_context_begin_frame (GdkDrawContext *draw_context,
|
|
|
|
cairo_region_t *region)
|
|
|
|
{
|
|
|
|
GdkWin32CairoContext *self = GDK_WIN32_CAIRO_CONTEXT (draw_context);
|
|
|
|
GdkSurface *surface;
|
2019-05-19 03:09:05 +00:00
|
|
|
GdkWin32Surface *impl;
|
2018-04-24 15:14:23 +00:00
|
|
|
int scale;
|
|
|
|
cairo_t *cr;
|
2020-07-24 13:54:49 +00:00
|
|
|
int width, height;
|
2018-04-24 15:14:23 +00:00
|
|
|
RECT queued_window_rect;
|
|
|
|
|
|
|
|
surface = gdk_draw_context_get_surface (draw_context);
|
2019-05-19 03:09:05 +00:00
|
|
|
impl = GDK_WIN32_SURFACE (surface);
|
2018-04-24 15:14:23 +00:00
|
|
|
scale = gdk_surface_get_scale_factor (surface);
|
|
|
|
|
2020-08-05 02:43:11 +00:00
|
|
|
queued_window_rect = gdk_win32_surface_handle_queued_move_resize (draw_context);
|
2018-04-24 15:14:23 +00:00
|
|
|
|
|
|
|
width = queued_window_rect.right - queued_window_rect.left;
|
|
|
|
height = queued_window_rect.bottom - queued_window_rect.top;
|
|
|
|
width = MAX (width, 1);
|
|
|
|
height = MAX (height, 1);
|
|
|
|
|
|
|
|
if (self->layered)
|
|
|
|
self->window_surface = create_cairo_surface_for_layered_window (impl, width, height, scale);
|
|
|
|
else
|
|
|
|
self->window_surface = create_cairo_surface_for_surface (surface, scale);
|
|
|
|
|
|
|
|
if (self->layered ||
|
|
|
|
!self->double_buffered)
|
|
|
|
{
|
|
|
|
/* Layered windows paint on the window_surface (which is itself
|
|
|
|
* an in-memory cache that the window maintains, since layered windows
|
|
|
|
* do not support incremental redraws.
|
|
|
|
* Non-double-buffered windows paint on the window surface directly
|
|
|
|
* as well.
|
|
|
|
*/
|
|
|
|
self->paint_surface = cairo_surface_reference (self->window_surface);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (width > self->db_width ||
|
|
|
|
height > self->db_height)
|
|
|
|
{
|
|
|
|
self->db_width = MAX (width, self->db_width);
|
|
|
|
self->db_height = MAX (height, self->db_height);
|
|
|
|
|
|
|
|
g_clear_pointer (&self->db_surface, cairo_surface_destroy);
|
|
|
|
|
|
|
|
self->db_surface = gdk_surface_create_similar_surface (surface,
|
|
|
|
cairo_surface_get_content (self->window_surface),
|
|
|
|
self->db_width,
|
|
|
|
self->db_height);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Double-buffered windows paint on a DB surface.
|
|
|
|
* Due to performance concerns we don't recreate it unless forced to.
|
|
|
|
*/
|
|
|
|
self->paint_surface = cairo_surface_reference (self->db_surface);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear the paint region.
|
|
|
|
* For non-double-buffered and for layered rendering we must
|
|
|
|
* clear it, otherwise semi-transparent pixels will "add up"
|
|
|
|
* with each repaint.
|
|
|
|
* For double-buffered rendering we must clear the old pixels
|
|
|
|
* from the DB cache surface that we're going to use as a buffer.
|
|
|
|
*/
|
|
|
|
cr = cairo_create (self->paint_surface);
|
|
|
|
cairo_set_source_rgba (cr, 0, 0, 0, 00);
|
|
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
|
|
|
gdk_cairo_region (cr, region);
|
|
|
|
cairo_clip (cr);
|
|
|
|
cairo_paint (cr);
|
|
|
|
cairo_destroy (cr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gdk_win32_cairo_context_end_frame (GdkDrawContext *draw_context,
|
2018-06-10 20:53:25 +00:00
|
|
|
cairo_region_t *painted)
|
2018-04-24 15:14:23 +00:00
|
|
|
{
|
|
|
|
GdkWin32CairoContext *self = GDK_WIN32_CAIRO_CONTEXT (draw_context);
|
|
|
|
GdkSurface *surface;
|
2020-07-24 13:54:49 +00:00
|
|
|
int scale;
|
2018-04-24 15:14:23 +00:00
|
|
|
|
|
|
|
surface = gdk_draw_context_get_surface (draw_context);
|
|
|
|
scale = gdk_surface_get_scale_factor (surface);
|
|
|
|
|
|
|
|
/* The code to resize double-buffered windows immediately
|
|
|
|
* before blitting the buffer contents onto them used
|
|
|
|
* to be here.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Layered windows have their own, special copying section
|
|
|
|
* further down. For double-buffered windows we need to blit
|
|
|
|
* the DB buffer contents into the window itself.
|
|
|
|
*/
|
|
|
|
if (!self->layered &&
|
|
|
|
self->double_buffered)
|
|
|
|
{
|
|
|
|
cairo_t *cr;
|
|
|
|
|
|
|
|
cr = cairo_create (self->window_surface);
|
|
|
|
|
|
|
|
cairo_set_source_surface (cr, self->paint_surface, 0, 0);
|
|
|
|
gdk_cairo_region (cr, painted);
|
|
|
|
cairo_clip (cr);
|
|
|
|
|
|
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
|
|
|
cairo_paint (cr);
|
|
|
|
|
|
|
|
cairo_destroy (cr);
|
|
|
|
}
|
|
|
|
|
|
|
|
cairo_surface_flush (self->window_surface);
|
|
|
|
|
|
|
|
/* Update layered window, updating its contents, size and position
|
|
|
|
* in one call.
|
|
|
|
*/
|
|
|
|
if (self->layered)
|
|
|
|
{
|
|
|
|
RECT client_rect;
|
|
|
|
|
|
|
|
/* Get the position/size of the window that GDK wants. */
|
|
|
|
_gdk_win32_get_window_client_area_rect (surface, scale, &client_rect);
|
|
|
|
_gdk_win32_update_layered_window_from_cache (surface, &client_rect, TRUE, TRUE, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_clear_pointer (&self->paint_surface, cairo_surface_destroy);
|
|
|
|
g_clear_pointer (&self->window_surface, cairo_surface_destroy);
|
|
|
|
}
|
|
|
|
|
|
|
|
static cairo_t *
|
|
|
|
gdk_win32_cairo_context_cairo_create (GdkCairoContext *context)
|
|
|
|
{
|
|
|
|
GdkWin32CairoContext *self = GDK_WIN32_CAIRO_CONTEXT (context);
|
|
|
|
|
|
|
|
return cairo_create (self->paint_surface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gdk_win32_cairo_context_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
GdkWin32CairoContext *self = GDK_WIN32_CAIRO_CONTEXT (object);
|
|
|
|
|
|
|
|
g_clear_pointer (&self->db_surface, cairo_surface_destroy);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (gdk_win32_cairo_context_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
2018-04-12 14:48:31 +00:00
|
|
|
static void
|
|
|
|
gdk_win32_cairo_context_class_init (GdkWin32CairoContextClass *klass)
|
|
|
|
{
|
2018-04-24 15:14:23 +00:00
|
|
|
GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);
|
|
|
|
GdkCairoContextClass *cairo_context_class = GDK_CAIRO_CONTEXT_CLASS (klass);
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
object_class->finalize = gdk_win32_cairo_context_finalize;
|
|
|
|
|
|
|
|
draw_context_class->begin_frame = gdk_win32_cairo_context_begin_frame;
|
|
|
|
draw_context_class->end_frame = gdk_win32_cairo_context_end_frame;
|
|
|
|
|
|
|
|
cairo_context_class->cairo_create = gdk_win32_cairo_context_cairo_create;
|
2018-04-12 14:48:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gdk_win32_cairo_context_init (GdkWin32CairoContext *self)
|
|
|
|
{
|
2018-04-24 15:14:23 +00:00
|
|
|
self->double_buffered = g_strcmp0 (g_getenv ("GDK_WIN32_CAIRO_DB"), "1") == 0;
|
|
|
|
self->db_width = -1;
|
|
|
|
self->db_height = -1;
|
2018-04-12 14:48:31 +00:00
|
|
|
}
|
|
|
|
|