note
description: "EiffelVision drawable. GTK implementation."
legal: "See notice at end of class."
status: "See notice at end of class."
keywords: figures, primitives, drawing, line, point, ellipse
date: "$Date$"
revision: "$Revision$"
deferred class
EV_DRAWABLE_IMP
inherit
EV_DRAWABLE_I
redefine
interface
end
EV_DRAWABLE_CONSTANTS
DISPOSABLE
undefine
copy,
default_create
end
PLATFORM
undefine
copy,
default_create
end
MATH_CONST
feature {NONE} -- Initialization
init_default_values
-- Set default values. Call during initialization.
local
l_mem: INTEGER_16
do
l_mem := {INTEGER_16} 3 | ({INTEGER_16} 3 |<< integer_8_bits)
set_dashes_pattern (gc, $l_mem)
line_style := {GTK}.Gdk_line_solid_enum
set_drawing_mode (drawing_mode_copy)
set_line_width (1)
end
feature {EV_ANY_I} -- Implementation
gc: POINTER
-- Pointer to GdkGC struct.
-- The graphics context applied to the primitives.
-- Line style, width, colors, etc. are defined in here.
gcvalues: POINTER
-- Pointer to GdkGCValues struct.
-- Is allocated during creation but has to be updated
-- every time it is accessed.
drawable: POINTER
-- Pointer to the GdkWindow of `c_object'.
deferred
end
mask: POINTER
-- Pointer to the mask used by `Current'
deferred
end
feature {EV_DRAWABLE_IMP} -- Implementation
line_style: INTEGER
-- Dash-style used when drawing lines.
cap_style: INTEGER
-- Style used for drawing end of lines.
do
Result := {GTK}.gdk_cap_round_enum
end
join_style: INTEGER
-- Way in which lines are joined together.
do
Result := {GTK}.Gdk_join_bevel_enum
end
gc_clip_area: detachable EV_RECTANGLE
-- Clip area currently used by `gc'.
height: INTEGER
-- Needed by `draw_straight_line'.
deferred
end
width: INTEGER
-- Needed by `draw_straight_line'.
deferred
end
feature -- Access
font: EV_FONT
-- Font used for drawing text.
do
if attached internal_font_imp as l_internal_font_imp then
Result := l_internal_font_imp.attached_interface.twin
else
create Result
end
end
foreground_color_internal: EV_COLOR
-- Color used to draw primitives.
local
l_red, l_green, l_blue: INTEGER
do
-- Default to black if `internal_foreground_color` is not set.
if attached internal_foreground_color as l_internal_foreground_color then
l_red := l_internal_foreground_color.red_8_bit
l_green := l_internal_foreground_color.green_8_bit
l_blue := l_internal_foreground_color.blue_8_bit
end
create Result.make_with_8_bit_rgb (l_red, l_green, l_blue)
end
background_color_internal: EV_COLOR
-- Color used for erasing of canvas.
-- Default: white.
local
l_red, l_green, l_blue: INTEGER
do
if attached internal_background_color as l_internal_background_color then
l_red := l_internal_background_color.red_8_bit
l_green := l_internal_background_color.green_8_bit
l_blue := l_internal_background_color.blue_8_bit
else
-- We default to white
l_red := 255
l_green := 255
l_blue := 255
end
create Result.make_with_8_bit_rgb (l_red, l_green, l_blue)
end
line_width: INTEGER
-- Line thickness.
do
gcvalues := {GTK}.c_gdk_gcvalues_struct_allocate
{GTK}.gdk_gc_get_values (gc, gcvalues)
Result := {GTK}.gdk_gcvalues_struct_line_width (gcvalues)
gcvalues.memory_free
end
drawing_mode: INTEGER
-- Logical operation on pixels when drawing.
local
gdk_drawing_mode: INTEGER
do
gcvalues := {GTK}.c_gdk_gcvalues_struct_allocate
{GTK}.gdk_gc_get_values (gc, gcvalues)
gdk_drawing_mode := {GTK}.gdk_gcvalues_struct_function (gcvalues)
gcvalues.memory_free
if gdk_drawing_mode = {GTK}.Gdk_copy_enum then
Result := drawing_mode_copy
elseif gdk_drawing_mode = {GTK}.Gdk_xor_enum then
Result := drawing_mode_xor
elseif gdk_drawing_mode = {GTK}.Gdk_invert_enum then
Result := drawing_mode_invert
elseif gdk_drawing_mode = {GTK}.Gdk_and_enum then
Result := drawing_mode_and
elseif gdk_drawing_mode = {GTK}.Gdk_or_enum then
Result := drawing_mode_or
else
check
drawing_mode_existent: False
end
end
end
clip_area: detachable EV_RECTANGLE
-- Clip area used to clip drawing.
-- If set to Void, no clipping is applied.
do
if attached gc_clip_area as l_gc_clip_area then
Result := l_gc_clip_area.twin
end
end
tile: detachable EV_PIXMAP
-- Pixmap that is used to fill instead of background_color.
-- If set to Void, `background_color' is used to fill.
dashed_line_style: BOOLEAN
-- Are lines drawn dashed?
local
style: INTEGER
do
gcvalues := {GTK}.c_gdk_gcvalues_struct_allocate
{GTK}.gdk_gc_get_values (gc, gcvalues)
style := {GTK}.gdk_gcvalues_struct_line_style (gcvalues)
gcvalues.memory_free
Result := style = {GTK}.Gdk_line_on_off_dash_enum
end
feature -- Element change
set_font (a_font: EV_FONT)
-- Set `font' to `a_font'.
do
if attached {EV_FONT_IMP} a_font.implementation as l_font_imp then
internal_font_imp := l_font_imp
else
check False end
end
end
set_background_color (a_color: EV_COLOR)
-- Assign `a_color' to `background_color'.
local
l_internal_background_color: detachable like internal_background_color
do
l_internal_background_color := internal_background_color
if l_internal_background_color = Void then
create l_internal_background_color.make_with_8_bit_rgb (255, 255, 255)
internal_background_color := l_internal_background_color
end
if not l_internal_background_color.is_equal (a_color) then
l_internal_background_color.set_red_with_8_bit (a_color.red_8_bit)
l_internal_background_color.set_green_with_8_bit (a_color.green_8_bit)
l_internal_background_color.set_blue_with_8_bit (a_color.blue_8_bit)
internal_set_color (False, a_color.red_16_bit, a_color.green_16_bit, a_color.blue_16_bit)
end
end
set_foreground_color (a_color: EV_COLOR)
-- Assign `a_color' to `foreground_color'
local
l_internal_foreground_color: detachable like internal_foreground_color
do
l_internal_foreground_color := internal_foreground_color
if l_internal_foreground_color = Void then
create l_internal_foreground_color
internal_foreground_color := l_internal_foreground_color
end
if not l_internal_foreground_color.is_equal (a_color) then
l_internal_foreground_color.set_red_with_8_bit (a_color.red_8_bit)
l_internal_foreground_color.set_green_with_8_bit (a_color.green_8_bit)
l_internal_foreground_color.set_blue_with_8_bit (a_color.blue_8_bit)
internal_set_color (True, a_color.red_16_bit, a_color.green_16_bit, a_color.blue_16_bit)
end
end
set_line_width (a_width: INTEGER)
-- Assign `a_width' to `line_width'.
do
{GTK}.gdk_gc_set_line_attributes (gc, a_width,
line_style, cap_style, join_style)
end
set_drawing_mode (a_mode: INTEGER)
-- Set drawing mode to `a_mode'.
local
l_gc: like gc
do
l_gc := gc
inspect
a_mode
when drawing_mode_copy then
{GTK}.gdk_gc_set_function (l_gc, {GTK}.Gdk_copy_enum)
when drawing_mode_xor then
{GTK}.gdk_gc_set_function (l_gc, {GTK}.Gdk_xor_enum)
when drawing_mode_invert then
{GTK}.gdk_gc_set_function (l_gc, {GTK}.Gdk_invert_enum)
when drawing_mode_and then
{GTK}.gdk_gc_set_function (l_gc, {GTK}.Gdk_and_enum)
when drawing_mode_or then
{GTK}.gdk_gc_set_function (l_gc, {GTK}.Gdk_or_enum)
else
check
drawing_mode_existent: False
end
end
end
set_clip_area (an_area: EV_RECTANGLE)
-- Set an area to clip to.
local
rectangle_struct: POINTER
do
gc_clip_area := an_area.twin
rectangle_struct := {GTK}.c_gdk_rectangle_struct_allocate
{GTK2}.set_gdk_rectangle_struct_x (rectangle_struct, an_area.x + device_x_offset)
{GTK2}.set_gdk_rectangle_struct_y (rectangle_struct, an_area.y + device_y_offset)
{GTK2}.set_gdk_rectangle_struct_width (rectangle_struct, an_area.width)
{GTK2}.set_gdk_rectangle_struct_height (rectangle_struct, an_area.height)
{GTK}.gdk_gc_set_clip_region (gc, default_pointer)
{GTK}.gdk_gc_set_clip_rectangle (gc, rectangle_struct)
rectangle_struct.memory_free
end
set_clip_region (a_region: EV_REGION)
-- Set a region to clip to.
local
a_region_imp: detachable EV_REGION_IMP
rectangle_struct: POINTER
do
rectangle_struct := {GTK}.c_gdk_rectangle_struct_allocate
a_region_imp ?= a_region.implementation
check a_region_imp /= Void then end
{GTK}.gdk_region_get_clipbox (a_region_imp.gdk_region, rectangle_struct)
-- Set the gc clip area.
create gc_clip_area.make (
{GTK}.gdk_rectangle_struct_x (rectangle_struct) + device_x_offset,
{GTK}.gdk_rectangle_struct_y (rectangle_struct) + device_y_offset,
{GTK}.gdk_rectangle_struct_width (rectangle_struct),
{GTK}.gdk_rectangle_struct_height (rectangle_struct)
)
{GTK}.gdk_gc_set_clip_rectangle (gc, default_pointer)
{GTK}.gdk_gc_set_clip_region (gc, a_region_imp.gdk_region)
rectangle_struct.memory_free
end
remove_clipping
-- Do not apply any clipping.
do
gc_clip_area := Void
{GTK}.gdk_gc_set_clip_rectangle (gc, default_pointer)
{GTK}.gdk_gc_set_clip_region (gc, default_pointer)
end
set_tile (a_pixmap: EV_PIXMAP)
-- Set tile used to fill figures.
-- Set to Void to use `background_color' to fill.
local
tile_imp: detachable EV_PIXMAP_IMP
l_tile: like tile
do
create l_tile
tile := l_tile
l_tile.copy (a_pixmap)
tile_imp ?= l_tile.implementation
check tile_imp /= Void then end
{GTK}.gdk_gc_set_tile (gc, tile_imp.drawable)
end
remove_tile
-- Do not apply a tile when filling.
do
tile := Void
end
enable_dashed_line_style
-- Draw lines dashed.
do
line_style := {GTK}.Gdk_line_on_off_dash_enum
{GTK}.gdk_gc_set_line_attributes (gc, line_width,
line_style, cap_style, join_style)
end
disable_dashed_line_style
-- Draw lines solid.
do
line_style := {GTK}.Gdk_line_solid_enum
{GTK}.gdk_gc_set_line_attributes (gc, line_width,
line_style, cap_style, join_style)
end
set_anti_aliasing (value: BOOLEAN)
-- <Precursor>
do
-- TODO: provide implementation.
end
feature -- Clearing operations
clear
-- Erase `Current' with `background_color'.
do
clear_rectangle (0, 0, width, height)
end
clear_rectangle (x, y, a_width, a_height: INTEGER)
-- Erase rectangle specified with `background_color'.
local
tmp_fg_color, tmp_bg_color: detachable EV_COLOR
do
if drawable /= default_pointer then
tmp_fg_color := internal_foreground_color
if tmp_fg_color = Void then
tmp_fg_color := foreground_color
end
tmp_bg_color := internal_background_color
if tmp_bg_color = Void then
tmp_bg_color := background_color
end
internal_set_color (True, tmp_bg_color.red_16_bit, tmp_bg_color.green_16_bit, tmp_bg_color.blue_16_bit)
{GTK}.gdk_draw_rectangle (drawable, gc, 1,
(x + device_x_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
(y + device_y_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_width,
a_height)
internal_set_color (True, tmp_fg_color.red_16_bit, tmp_fg_color.green_16_bit, tmp_fg_color.blue_16_bit)
update_if_needed
end
end
feature -- Drawing operations
draw_point (x, y: INTEGER)
-- Draw point at (`x', `y').
do
if drawable /= default_pointer then
{GTK}.gdk_draw_point (
drawable,
gc,
(x + device_x_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
(y + device_y_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value)
)
update_if_needed
end
end
draw_text (x, y: INTEGER; a_text: READABLE_STRING_GENERAL)
-- Draw `a_text' with left of baseline at (`x', `y') using `font'.
do
draw_text_internal (
x.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
y.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_text,
True,
-1,
0
)
end
draw_rotated_text (x, y: INTEGER; angle: REAL; a_text: READABLE_STRING_GENERAL)
-- Draw rotated text `a_text' with left of baseline at (`x', `y') using `font'.
-- Rotation is number of radians counter-clockwise from horizontal plane.
do
draw_text_internal (
x.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
y.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_text,
True,
-1,
angle
)
end
draw_ellipsed_text (x, y: INTEGER; a_text: READABLE_STRING_GENERAL; clipping_width: INTEGER)
-- Draw `a_text' with left of baseline at (`x', `y') using `font'.
-- Text is clipped to `clipping_width' in pixels and ellipses are displayed
-- to show truncated characters if any.
do
draw_text_internal (
x.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
y.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_text,
True,
clipping_width,
0
)
end
draw_ellipsed_text_top_left (x, y: INTEGER; a_text: READABLE_STRING_GENERAL; clipping_width: INTEGER)
-- Draw `a_text' with top left corner at (`x', `y') using `font'.
-- Text is clipped to `clipping_width' in pixels and ellipses are displayed
-- to show truncated characters if any.
do
draw_text_internal (
x.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
y.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_text,
False,
clipping_width,
0
)
end
draw_text_top_left (x, y: INTEGER; a_text: READABLE_STRING_GENERAL)
-- Draw `a_text' with top left corner at (`x', `y') using `font'.
do
draw_text_internal (
x.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
y.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_text,
False,
-1,
0
)
end
draw_text_internal (x, y: INTEGER; a_text: READABLE_STRING_GENERAL; draw_from_baseline: BOOLEAN; a_width: INTEGER; a_angle: REAL)
-- Draw `a_text' at (`x', `y') using `font'.
local
a_cs: EV_GTK_C_STRING
a_pango_layout: POINTER
x_orig, y_orig, a_x, a_y: INTEGER
a_clip_area: detachable EV_RECTANGLE
a_pango_matrix, a_pango_context: POINTER
l_app_imp: like App_implementation
l_pango_renderer: POINTER
l_ellipsize_symbol: POINTER
do
if drawable /= default_pointer then
l_app_imp := App_implementation
-- Set x_orig and y_orig to be the device translated values which must be used for the rest of the routine.
x_orig := x + device_x_offset
y_orig := y + device_y_offset
a_x := x_orig
if draw_from_baseline then
if internal_font_imp /= Void then
a_y := y_orig - internal_font_imp.ascent
else
a_y := y_orig - l_app_imp.default_font_ascent
end
else
a_y := y_orig
end
a_cs := l_app_imp.c_string_from_eiffel_string (a_text)
-- Replace when we have UTF16 support
a_pango_layout := l_app_imp.pango_layout
{GTK2}.pango_layout_set_text (a_pango_layout, a_cs.item, a_cs.string_length)
if internal_font_imp /= Void then
{GTK2}.pango_layout_set_font_description (a_pango_layout, internal_font_imp.font_description)
end
if a_width /= -1 then
-- We need to perform ellipsizing on text if available, otherwise we clip.
l_ellipsize_symbol := pango_layout_set_ellipsize_symbol
if l_ellipsize_symbol = default_pointer then
-- Previous code for gtk 2.4 that set a clip area for text rendering.
a_clip_area := gc_clip_area
set_clip_area (create {EV_RECTANGLE}.make (x_orig, y_orig, a_width, 10000))
{GTK2}.pango_layout_set_width (a_pango_layout, 10000 * {GTK2}.pango_scale)
else
pango_layout_set_ellipsize_call (l_ellipsize_symbol, a_pango_layout, 3)
{GTK2}.pango_layout_set_width (a_pango_layout, a_width * {GTK2}.pango_scale)
end
end
if a_angle = 0 then
{GTK2}.gdk_draw_layout (
drawable,
gc,
a_x.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_y.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_pango_layout
)
else
l_pango_renderer := {GTK2}.gdk_pango_renderer_get_default ({GTK2}.gdk_screen_get_default)
-- This is reusable so do not free the renderer.
-- Renderer is needed to rotate text without a bounding box
{GTK2}.gdk_pango_renderer_set_drawable (l_pango_renderer, drawable)
{GTK2}.gdk_pango_renderer_set_gc (l_pango_renderer, gc)
a_pango_context := {GTK2}.pango_layout_get_context (a_pango_layout)
{GTK2}.pango_matrix_init ($a_pango_matrix)
{GTK2}.pango_matrix_translate (
a_pango_matrix,
x_orig.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
y_orig.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value)
)
{GTK2}.pango_matrix_rotate (a_pango_matrix, a_angle / Pi.truncated_to_real * 180)
{GTK2}.pango_matrix_translate (a_pango_matrix, 0, -(y_orig - a_y))
{GTK2}.pango_context_set_matrix (a_pango_context, a_pango_matrix)
{GTK2}.pango_renderer_draw_layout (l_pango_renderer, a_pango_layout, 0, 0)
-- Clean up Pango renderer.
{GTK2}.gdk_pango_renderer_set_drawable (l_pango_renderer, default_pointer)
{GTK2}.gdk_pango_renderer_set_gc (l_pango_renderer, default_pointer)
end
-- Reset all changed values.
if a_width /= -1 then
if l_ellipsize_symbol /= default_pointer then
pango_layout_set_ellipsize_call (l_ellipsize_symbol, a_pango_layout, 0)
elseif attached a_clip_area then
-- Restore clip area (used for gtk 2.4 implementation)
set_clip_area (a_clip_area)
else
remove_clipping
end
end
{GTK2}.pango_layout_set_width (a_pango_layout, -1)
{GTK2}.pango_layout_set_font_description (a_pango_layout, default_pointer)
if a_pango_context /= default_pointer then
{GTK2}.pango_context_set_matrix (a_pango_context, default_pointer)
{GTK2}.pango_matrix_free (a_pango_matrix)
end
end
end
pango_layout_set_ellipsize_symbol: POINTER
-- Symbol for `pango_layout_set_ellipsize'.
once
Result := app_implementation.symbol_from_symbol_name ("pango_layout_set_ellipsize")
end
pango_layout_set_ellipsize_call (a_function: POINTER; a_layout: POINTER; a_ellipsize_mode: INTEGER)
external
"C inline use <ev_gtk.h>"
alias
"(FUNCTION_CAST(void, (PangoLayout*, gint)) $a_function)((PangoLayout*) $a_layout, (gint) $a_ellipsize_mode);"
end
draw_segment (x1, y1, x2, y2: INTEGER)
-- Draw line segment from (`x1', 'y1') to (`x2', 'y2').
do
if drawable /= default_pointer then
{GTK}.gdk_draw_line (
drawable,
gc,
(x1 + device_x_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
(y1 + device_y_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
(x2 + device_x_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
(y2 + device_y_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value)
)
update_if_needed
end
end
draw_arc (x, y, a_width, a_height: INTEGER; a_start_angle, an_aperture: REAL)
-- Draw a part of an ellipse bounded by top left (`x', `y') with
-- size `a_width' and `a_height'.
-- Start at `a_start_angle' and stop at `a_start_angle' + `an_aperture'.
-- Angles are measured in radians.
local
a_radians: INTEGER
do
if drawable /= default_pointer then
a_radians := radians_to_gdk_angle
{GTK}.gdk_draw_arc (
drawable,
gc,
0,
(x + device_x_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
(y + device_y_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_width,
a_height,
(a_start_angle * a_radians + 0.5).truncated_to_integer,
(an_aperture * a_radians + 0.5).truncated_to_integer
)
update_if_needed
end
end
draw_sub_pixel_buffer (a_x, a_y: INTEGER; a_pixel_buffer: EV_PIXEL_BUFFER; area: EV_RECTANGLE)
-- Draw `area' of `a_pixel_buffer' with upper-left corner on (`a_x', `a_y').
local
a_pixbuf_imp: detachable EV_PIXEL_BUFFER_IMP
l_pixels: POINTER
l_rowstride: NATURAL
l_back_buffer: POINTER
l_x, l_y: INTEGER
l_rect: EV_RECTANGLE
do
-- We can only draw the visible area of `a_pixel_buffer' in `area'.
l_rect := area.intersection (a_pixel_buffer.area)
if l_rect.width > 0 and l_rect.height > 0 then
-- Shift the destination (x,y) where `a_pixel_buffer' will be drawn in
-- case the source `area' is not entirely within `a_pixel_buffer'.
l_x := a_x + l_rect.x - area.x
l_y := a_y + l_rect.y - area.y
a_pixbuf_imp ?= a_pixel_buffer.implementation
check a_pixbuf_imp /= Void then end
if supports_pixbuf_alpha then
{GTK2}.gdk_draw_pixbuf (drawable, gc, a_pixbuf_imp.gdk_pixbuf, l_rect.x, l_rect.y, l_x + device_x_offset, l_y + device_y_offset, l_rect.width, l_rect.height, 0, 0, 0)
else
-- We need to retrieve the source pixmap, composite and then reblit to the same area.
l_back_buffer := pixbuf_from_drawable_at_position (l_x, l_y, 0, 0, l_rect.width, l_rect.height)
{GTK2}.gdk_pixbuf_composite (a_pixbuf_imp.gdk_pixbuf, l_back_buffer, 0, 0, l_rect.width, l_rect.height, 0, 0, 1, 1, 0, 255)
l_pixels := {GTK}.gdk_pixbuf_get_pixels (l_back_buffer)
l_rowstride := {GTK}.gdk_pixbuf_get_rowstride (l_back_buffer)
{GTK}.gdk_draw_rgb_32_image (drawable, gc, l_x + device_x_offset, l_y + device_y_offset, l_rect.width, l_rect.height, 0, l_pixels, l_rowstride.as_integer_32)
{GTK2}.object_unref (l_back_buffer)
end
end
end
supports_pixbuf_alpha: BOOLEAN
-- Does drawable support direct GdkPixbuf alpha blending?
do
-- For the moment EV_SCREEN doesn't support direct alpha blending.
Result := True
end
draw_pixmap (x, y: INTEGER; a_pixmap: EV_PIXMAP)
-- Draw `a_pixmap' with upper-left corner on (`x', `y').
do
draw_full_pixmap (x, y, a_pixmap, 0, 0, a_pixmap.width, a_pixmap.height)
end
draw_full_pixmap (x, y: INTEGER; a_pixmap: EV_PIXMAP; x_src, y_src, src_width, src_height: INTEGER)
local
l_source_full, l_source_clip, l_source_intersection: EV_RECTANGLE
l_dest_full, l_dest_clip, l_dest_intersection: EV_RECTANGLE
pixmap_imp: detachable EV_PIXMAP_IMP
-- l_visible_region, l_visible_rectangle: POINTER
do
if drawable /= default_pointer then
-- Get visible region of drawable
--| FIXME IEK Optimize for visible regions with corruptable data.
-- l_visible_rectangle := {EV_GTK_EXTERNALS}.c_gdk_rectangle_struct_allocate
-- l_visible_region := {EV_GTK_EXTERNALS}.gdk_drawable_get_visible_region (drawable)
-- if corruptable_onscreen and l_visible_region /= default_pointer then
-- {EV_GTK_EXTERNALS}.gdk_region_get_clipbox (l_visible_region, l_visible_rectangle)
-- {EV_GTK_EXTERNALS}.gdk_region_destroy (l_visible_region)
-- l_visible_region := default_pointer
-- create l_source_full.make (
-- {EV_GTK_EXTERNALS}.gdk_rectangle_struct_x (l_visible_rectangle),
-- {EV_GTK_EXTERNALS}.gdk_rectangle_struct_y (l_visible_rectangle),
-- {EV_GTK_EXTERNALS}.gdk_rectangle_struct_width (l_visible_rectangle),
-- {EV_GTK_EXTERNALS}.gdk_rectangle_struct_height (l_visible_rectangle)
-- )
-- else
create l_source_full.make (0, 0, a_pixmap.width, a_pixmap.height)
-- end
-- l_visible_rectangle.memory_free
create l_source_clip.make (x_src, y_src, src_width, src_height)
l_source_intersection := l_source_full.intersection (l_source_clip)
-- The source intersection dimensions are used as the initial size of the clip
-- The source intersection origin is used as the initial origin of the clip
if l_source_intersection.width > 0 and then l_source_intersection.height > 0 then
create l_dest_full.make (0, 0, width, height)
-- Account for any source clipping in the destination
create l_dest_clip.make (x + l_source_intersection.x - l_source_clip.x, y + l_source_intersection.y - l_source_clip.y, l_source_intersection.width, l_source_intersection.height)
-- Move the source clip to the intersection position and dimensions
l_source_clip.move (l_source_intersection.x, l_source_intersection.y)
l_source_clip.resize (l_source_intersection.width, l_source_intersection.height)
l_dest_intersection := l_dest_full.intersection (l_dest_clip)
if l_dest_intersection.width > 0 and then l_dest_intersection.height > 0 then
l_source_clip.resize (l_dest_intersection.width, l_dest_intersection.height)
-- We need to account for any destination position clipping by updating the source clip accordingly.
l_source_clip.set_x (l_source_clip.x + (l_dest_intersection.x - l_dest_clip.x))
l_source_clip.set_y (l_source_clip.y + (l_dest_intersection.y - l_dest_clip.y))
-- The dimensions of the destination intersection are now the new dimensions of the source clip.
l_dest_clip.move (l_dest_intersection.x, l_dest_intersection.y)
l_dest_clip.resize (l_dest_intersection.width, l_dest_intersection.y)
pixmap_imp ?= a_pixmap.implementation
check pixmap_imp /= Void then end
if pixmap_imp.mask /= default_pointer then
{GTK}.gdk_gc_set_clip_mask (gc, pixmap_imp.mask)
{GTK}.gdk_gc_set_clip_origin (gc, l_dest_clip.x + device_x_offset - l_source_clip.x, l_dest_clip.y + device_y_offset - l_source_clip.y)
end
{GTK2}.gdk_draw_drawable (
drawable,
gc,
pixmap_imp.drawable,
l_source_clip.x,
l_source_clip.y,
l_dest_clip.x + device_x_offset,
l_dest_clip.y + device_y_offset,
l_source_clip.width,
l_source_clip.height
)
update_if_needed
if pixmap_imp.mask /= default_pointer then
{GTK}.gdk_gc_set_clip_mask (gc, default_pointer)
{GTK}.gdk_gc_set_clip_origin (gc, 0, 0)
end
end
end
end
end
sub_pixmap (area: EV_RECTANGLE): EV_PIXMAP
-- Pixmap region of `Current' represented by rectangle `area'
local
pix_imp: detachable EV_PIXMAP_IMP
a_pix: POINTER
do
create Result
pix_imp ?= Result.implementation
check pix_imp /= Void then end
a_pix := pixbuf_from_drawable_at_position (area.x, area.y, 0, 0, area.width, area.height)
pix_imp.set_pixmap_from_pixbuf (a_pix)
{GTK2}.object_unref (a_pix)
end
draw_sub_pixmap (x, y: INTEGER; a_pixmap: EV_PIXMAP; area: EV_RECTANGLE)
-- Draw `area' of `a_pixmap' with upper-left corner on (`x', `y').
do
draw_full_pixmap (x, y, a_pixmap, area.x, area.y, area.width, area.height)
end
draw_rectangle (x, y, a_width, a_height: INTEGER)
-- Draw rectangle with upper-left corner on (`x', `y')
-- with size `a_width' and `a_height'.
do
if
drawable /= default_pointer and then
a_width > 0 and then a_height > 0
then
-- If width or height are zero then nothing will be rendered.
{GTK}.gdk_draw_rectangle (
drawable,
gc,
0,
(x + device_x_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
(y + device_y_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_width - 1,
a_height - 1
)
update_if_needed
end
end
draw_ellipse (x, y, a_width, a_height: INTEGER)
-- Draw an ellipse bounded by top left (`x', `y') with
-- size `a_width' and `a_height'.
do
if
drawable /= default_pointer and then
a_width > 0 and a_height > 0
then
{GTK}.gdk_draw_arc (
drawable,
gc,
0,
(x + device_x_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
(y + device_y_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_width - 1,
a_height - 1,
0,
whole_circle
)
update_if_needed
end
end
draw_polyline (points: ARRAY [EV_COORDINATE]; is_closed: BOOLEAN)
-- Draw line segments between subsequent points in
-- `points'. If `is_closed' draw line segment between first
-- and last point in `points'.
local
tmp: SPECIAL [INTEGER]
do
if drawable /= default_pointer then
tmp := coord_array_to_gdkpoint_array (points).area
if is_closed then
{GTK}.gdk_draw_polygon (drawable, gc, 0, $tmp, points.count)
update_if_needed
else
{GTK}.gdk_draw_lines (drawable, gc, $tmp, points.count)
update_if_needed
end
end
end
draw_pie_slice (x, y, a_width, a_height: INTEGER; a_start_angle, an_aperture: REAL)
-- Draw a part of an ellipse bounded by top left (`x', `y') with
-- size `a_width' and `a_height'.
-- Start at `a_start_angle' and stop at `a_start_angle' + `an_aperture'.
-- The arc is then closed by two segments through (`x', `y').
-- Angles are measured in radians
local
left, top: INTEGER
x_start_arc, y_start_arc, x_end_arc, y_end_arc: INTEGER
semi_width, semi_height: REAL
tang_start, tang_end: REAL
x_tmp, y_tmp: REAL
do
left := x.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value)
top := y.max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value)
semi_width := (a_width / 2).truncated_to_real
semi_height := (a_height / 2).truncated_to_real
tang_start := tangent (a_start_angle)
tang_end := tangent (a_start_angle + an_aperture)
x_tmp := semi_height / sqrt (tang_start * tang_start + (semi_height * semi_height) / (semi_width * semi_width))
y_tmp := semi_height / sqrt (1 + (semi_height * semi_height) / (semi_width * semi_width * tang_start * tang_start))
if sine (a_start_angle) > 0 then
y_tmp := -y_tmp
end
if cosine (a_start_angle) < 0 then
x_tmp := -x_tmp
end
x_start_arc := (x_tmp + left + semi_width).rounded
y_start_arc := (y_tmp + top + semi_height).rounded
x_tmp := semi_height / sqrt (tang_end * tang_end + (semi_height * semi_height) / (semi_width * semi_width))
y_tmp := semi_height / sqrt (1 + (semi_height * semi_height) / (semi_width * semi_width * tang_end * tang_end))
if sine (a_start_angle + an_aperture) > 0 then
y_tmp := -y_tmp
end
if cosine (a_start_angle + an_aperture) < 0 then
x_tmp := -x_tmp
end
x_end_arc := (x_tmp + left + semi_width).rounded
y_end_arc := (y_tmp + top + semi_height).rounded
draw_arc (x, y, a_width, a_height, a_start_angle, an_aperture)
draw_segment (x + (a_width // 2), y + (a_height // 2), x_start_arc, y_start_arc)
draw_segment (x + (a_width // 2), y + (a_height // 2), x_end_arc, y_end_arc)
update_if_needed
end
feature -- filling operations
fill_rectangle (x, y, a_width, a_height: INTEGER)
-- Draw rectangle with upper-left corner on (`x', `y')
-- with size `a_width' and `a_height'. Fill with `background_color'.
do
if drawable /= default_pointer then
if tile /= Void then
{GTK}.gdk_gc_set_fill (gc, {GTK}.Gdk_tiled_enum)
end
{GTK}.gdk_draw_rectangle (
drawable,
gc,
1,
(x + device_x_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
(y + device_y_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_width,
a_height
)
{GTK}.gdk_gc_set_fill (gc, {GTK}.Gdk_solid_enum)
update_if_needed
end
end
fill_ellipse (x, y, a_width, a_height: INTEGER)
-- Draw an ellipse bounded by top left (`x', `y') with
-- size `a_width' and `a_height'.
-- Fill with `background_color'.
do
if drawable /= default_pointer then
if tile /= Void then
{GTK}.gdk_gc_set_fill (gc, {GTK}.Gdk_tiled_enum)
end
{GTK}.gdk_draw_arc (drawable, gc, 1, (x + device_x_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
(y + device_y_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value), a_width,
a_height, 0, whole_circle)
update_if_needed
{GTK}.gdk_gc_set_fill (gc, {GTK}.Gdk_solid_enum)
end
end
fill_polygon (points: ARRAY [EV_COORDINATE])
-- Draw line segments between subsequent points in `points'.
-- Fill all enclosed area's with `background_color'.
local
tmp: SPECIAL [INTEGER]
do
if drawable /= default_pointer then
tmp := coord_array_to_gdkpoint_array (points).area
if tile /= Void then
{GTK}.gdk_gc_set_fill (gc, {GTK}.Gdk_tiled_enum)
end
{GTK}.gdk_draw_polygon (drawable, gc, 1, $tmp, points.count)
{GTK}.gdk_gc_set_fill (gc, {GTK}.Gdk_solid_enum)
update_if_needed
end
end
fill_pie_slice (x, y, a_width, a_height: INTEGER; a_start_angle, an_aperture: REAL)
-- Draw a part of an ellipse bounded by top left (`x', `y') with
-- size `a_width' and `a_height'.
-- Start at `a_start_angle' and stop at `a_start_angle' + `an_aperture'.
-- The arc is then closed by two segments through (`x', `y').
-- Angles are measured in radians.
do
if drawable /= default_pointer then
if tile /= Void then
{GTK}.gdk_gc_set_fill (gc, {GTK}.Gdk_tiled_enum)
end
{GTK}.gdk_draw_arc (
drawable,
gc,
1,
(x + device_x_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
(y + device_y_offset).max ({INTEGER_16}.min_value).min ({INTEGER_16}.max_value),
a_width,
a_height,
(a_start_angle * radians_to_gdk_angle).truncated_to_integer,
(an_aperture * radians_to_gdk_angle).truncated_to_integer
)
{GTK}.gdk_gc_set_fill (gc, {GTK}.Gdk_solid_enum)
update_if_needed
end
end
feature {NONE} -- Implemention
coord_array_to_gdkpoint_array (pts: ARRAY [EV_COORDINATE]): ARRAY [INTEGER]
-- Low-level conversion.
require
pts_exists: pts /= Void
local
i, j, array_count: INTEGER
l_area: SPECIAL [INTEGER]
l_coord_area: SPECIAL [EV_COORDINATE]
do
from
array_count := pts.count
create Result.make_filled (0, 1, array_count * 2)
l_coord_area := pts.area
l_area := Result.area
i := array_count - 1
j := array_count * 2 - 1
-- Iterate backwards as comparing against zero generates faster C code.
until
i < 0
loop
l_area [j] := (l_coord_area @ i).y + device_y_offset
j := j - 1
l_area [j] := (l_coord_area @ i).x + device_x_offset
i := i - 1
j := j - 1
end
ensure
Result_exists: Result /= Void
same_size: pts.count = Result.count / 2
end
feature {EV_GTK_DEPENDENT_APPLICATION_IMP, EV_ANY_I} -- Implementation
pixbuf_from_drawable: POINTER
-- Return a GdkPixbuf object from the current Gdkpixbuf structure
do
Result := pixbuf_from_drawable_at_position (0, 0, 0, 0, width, height)
end
pixbuf_from_drawable_at_position (src_x, src_y, dest_x, dest_y, a_width, a_height: INTEGER): POINTER
-- Return a GdkPixbuf object from the current Gdkpixbuf structure
local
l_mask_pix, l_temp_pix: POINTER
l_null: POINTER
do
Result := {GTK2}.gdk_pixbuf_get_from_drawable (l_null, drawable, l_null, src_x + device_x_offset, src_y + device_y_offset, dest_x, dest_y, a_width, a_height)
if Result = l_null then
-- Cannot get pixbuf from `drawable', just create an empty one
-- to ensure that there is no failure, just a bad display.
Result := {GTK}.gdk_pixbuf_new (0, True, 8, a_width, a_height)
elseif mask /= l_null then
l_mask_pix := {GTK2}.gdk_pixbuf_get_from_drawable (l_null, mask, l_null, src_x, src_y, dest_x, dest_y, a_width, a_height)
-- If mask could not be allocated, keep `Result` without masking.
if l_mask_pix /= l_null then
-- Add alpha channel to `l_mask_pix' as required by
-- `draw_mask_on_pixbuf' since the creation of `l_mask_pix'
-- above will only create a RGB image without alpha channel
l_temp_pix := l_mask_pix
l_mask_pix := {GTK2}.gdk_pixbuf_add_alpha (l_mask_pix, True, 255, 255, 255)
-- Free the original `l_mask_pix' as it was replaced by a new
-- one with alpha channel.
{GTK2}.object_unref (l_temp_pix)
-- Add alpha channel to Result as required by
-- `draw_mask_on_pixbuf' since the creation of `Result'
-- above will only create a RGB image without alpha channel
l_temp_pix := Result
Result := {GTK2}.gdk_pixbuf_add_alpha (Result, False, 0, 0, 0)
-- Free the original `Result' as it was replaced by a new
-- one with alpha channel.
{GTK2}.object_unref (l_temp_pix)
-- Draw mask on top of pixbuf.
{GTK2}.gdk_pixbuf_composite (l_mask_pix, Result, 0, 0, a_width, a_height, 0.0, 0.0, 1.0, 1.0, {GTK2}.gdk_interp_bilinear, 255)
draw_mask_on_pixbuf (Result, l_mask_pix)
-- Clean up
{GTK2}.object_unref (l_mask_pix)
end
end
end
pixbuf_from_drawable_with_size (a_width, a_height: INTEGER): POINTER
-- Return a GdkPixbuf object from the current Gdkpixbuf structure with dimensions `a_width' * `a_height'
local
a_pixbuf: POINTER
do
a_pixbuf := pixbuf_from_drawable
Result := {GTK2}.gdk_pixbuf_scale_simple (a_pixbuf, a_width, a_height, {GTK2}.gdk_interp_bilinear)
{GTK2}.object_unref (a_pixbuf)
end
feature {NONE} -- Implementation
device_x_offset: INTEGER_16
-- Number of pixels to offset the x coord to get correct device placement
device_y_offset: INTEGER_16
-- Number of pixels to offset to y coord to get correct device placement.
internal_set_color (a_foreground: BOOLEAN; a_red, a_green, a_blue: INTEGER_32)
-- Set `gc' color to (a_red, a_green, a_blue), `a_foreground' sets foreground color, otherwise background is set.
local
l_color_struct: POINTER
do
l_color_struct := App_implementation.reusable_color_struct
{GTK}.set_gdk_color_struct_red (l_color_struct, a_red)
{GTK}.set_gdk_color_struct_green (l_color_struct, a_green)
{GTK}.set_gdk_color_struct_blue (l_color_struct, a_blue)
if a_foreground then
{GTK2}.gdk_gc_set_rgb_fg_color (gc, l_color_struct)
else
{GTK2}.gdk_gc_set_rgb_bg_color (gc, l_color_struct)
end
end
fg_color: POINTER
-- Default allocated background color.
once
Result := {GTK}.c_gdk_color_struct_allocate
{GTK}.gdk_colormap_alloc_color ({GTK2}.gdk_screen_get_rgb_colormap ({GTK2}.gdk_screen_get_default), Result, False, True).do_nothing
end
bg_color: POINTER
-- Default allocate foreground color.
once
Result := {GTK}.c_gdk_color_struct_allocate
{GTK}.set_gdk_color_struct_red (Result, 65535)
{GTK}.set_gdk_color_struct_green (Result, 65535)
{GTK}.set_gdk_color_struct_blue (Result, 65535)
{GTK}.gdk_colormap_alloc_color ({GTK2}.gdk_screen_get_rgb_colormap ({GTK2}.gdk_screen_get_default), Result, False, True).do_nothing
end
draw_mask_on_pixbuf (a_pixbuf_ptr, a_mask_ptr: POINTER)
require
a_pixbuf_ptr_has_alpha: {GTK2}.gdk_pixbuf_get_has_alpha (a_pixbuf_ptr)
a_mask_ptr_has_alpha: {GTK2}.gdk_pixbuf_get_has_alpha (a_mask_ptr)
external
"C inline use <ev_gtk.h>"
alias
"[
{
guint32 x, y;
guint32 l_pix_height,l_pix_width;
guint32 l_pix_row_stride;
guint32 l_mask_row_stride;
GdkPixbuf *pixbuf, *mask;
guchar *l_mask_pixels, *l_pixbuf_pixels;
pixbuf = (GdkPixbuf*) arg1;
mask = (GdkPixbuf*) arg2;
l_pix_height = gdk_pixbuf_get_height (pixbuf);
l_pix_width = gdk_pixbuf_get_width (pixbuf);
l_pix_row_stride = gdk_pixbuf_get_rowstride(pixbuf);
l_mask_row_stride = gdk_pixbuf_get_rowstride(mask);
l_mask_pixels = gdk_pixbuf_get_pixels (mask);
l_pixbuf_pixels = gdk_pixbuf_get_pixels (pixbuf);
for (y = 0; y < l_pix_height; y++) {
guchar *src, *dest;
src = (l_mask_pixels + (y * l_mask_row_stride));
dest = (l_pixbuf_pixels + (y * l_pix_row_stride));
for (x = 0; x < l_pix_width; x++) {
if (src [0] == (guchar)0) {
dest [3] = (guchar)0;
}
src += 4;
dest += 4;
}
}
}
]"
end
app_implementation: EV_APPLICATION_IMP
-- Return the instance of EV_APPLICATION_IMP.
deferred
end
internal_foreground_color: detachable EV_COLOR
-- Color used to draw primitives.
internal_background_color: detachable EV_COLOR
-- Color used for erasing of canvas.
-- Default: white.
corruptable_onscreen: BOOLEAN
-- Is pixel data corruptable if displayed on screen.
-- True if drawing area or screen.
do
Result := True
end
flush
-- Force all queued expose events to be called.
deferred
end
update_if_needed
-- Force update of `Current' if needed
deferred
end
whole_circle: INTEGER = 23040
-- Number of 1/64 th degrees in a full circle (360 * 64)
radians_to_gdk_angle: INTEGER = 3667 --
-- Multiply radian by this to get no of (1/64) degree segments.
internal_font_imp: detachable EV_FONT_IMP
note
option: stable
attribute
end
gdk_gc_unref (a_gc: POINTER)
-- void gdk_gc_unref (GdkGC *gc);
external
"C (GdkGC*) | <ev_gtk.h>"
end
set_dashes_pattern (a_gc, dash_pattern: POINTER)
-- Set the dashes pattern for gc `a_gc', `dash_pattern' is a pointer to a two count gint8[]] denoting the pattern.
external
"C inline use <ev_gtk.h>"
alias
"gdk_gc_set_dashes ((GdkGC*) $a_gc, 0, (gint8*) $dash_pattern, 2)"
end
feature {EV_ANY, EV_ANY_I} -- Implementation
interface: detachable EV_DRAWABLE note option: stable attribute end;
invariant
gc_not_void: is_usable implies gc /= default_pointer
note
copyright: "Copyright (c) 1984-2019, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end