note
	description: "EiffelVision drawable. GTK implementation."
	implementation_details: "[
			In Cairo, the coordinate system is not based on square pixels, but on the line
			between pixels (aka sample points).
		]"
	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

feature {NONE} -- Initialization

	init_default_values
			-- Set default values. Call during initialization.
		do
			disable_dashed_line_style
			set_drawing_mode (drawing_mode_copy)
			set_line_width (1)
			if drawable /= default_pointer then
				{CAIRO}.set_antialias (drawable, {CAIRO}.antialias_none)
				{CAIRO}.set_line_cap (drawable, {CAIRO}.line_cap_butt)
			end
			aliasing_mode := {CAIRO}.antialias_none
			line_cap_mode := {CAIRO}.line_cap_butt
		end

feature {EV_ANY_I} -- Implementation

	drawable: POINTER
			-- Pointer to the current Cairo context for `Current'.

feature {EV_DRAWABLE_IMP} -- Implementation

	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
			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
			else
					-- We default to black
			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.

	drawing_mode: INTEGER
			-- Logical operation on pixels when drawing.

	aliasing_mode: INTEGER_8
			-- Aliasing mode. Default: None

	line_cap_mode: INTEGER_8
			-- Line cap mode. Default: Butt

	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
			-- <Precursor>

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, a_color.green, a_color.blue)
			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)
			end
			internal_set_color (True, a_color.red, a_color.green, a_color.blue)
		end

	set_line_width (a_width: INTEGER)
			-- Assign `a_width' to `line_width'.
		do
			if drawable /= default_pointer then
				{CAIRO}.set_line_width (drawable, a_width)
			end
			line_width := a_width
		end

	set_drawing_mode (a_mode: INTEGER)
			-- Set drawing mode to `a_mode'.
		do
			if drawable /= default_pointer then
				{CAIRO}.set_operator (drawable, cairo_drawing_mode (a_mode))
			end
				-- Store set drawing mode.
			drawing_mode := a_mode
		end

	cairo_drawing_mode (a_drawing_mode: INTEGER): INTEGER_8
			-- Convert a vision drawing mode into the best cairo one.
		do
				-- Unfortunately there is no one to one mapping except for
				-- {EV_DRAWABLE_CONSTANTS}.drawing_mode_copy and
				-- {EV_DRAWABLE_CONSTANTS}.drawing_mode_and. All the other
				-- modes are approximation.
			inspect
				a_drawing_mode
			when {EV_DRAWABLE_CONSTANTS}.drawing_mode_copy then
				Result := {CAIRO}.operator_over
			when {EV_DRAWABLE_CONSTANTS}.drawing_mode_and then
				Result := {CAIRO}.operator_multiply
			when {EV_DRAWABLE_CONSTANTS}.drawing_mode_xor then
				Result := {CAIRO}.operator_xor
			when {EV_DRAWABLE_CONSTANTS}.drawing_mode_invert then
				Result := {CAIRO}.operator_exclusion
			when {EV_DRAWABLE_CONSTANTS}.drawing_mode_or then
				Result := {CAIRO}.operator_atop
			else
				check
					drawing_mode_exists: False
				end
			end
		end

	set_clip_area (an_area: EV_RECTANGLE)
			-- Set an area to clip to.
		local
			l_drawable: POINTER
		do
			l_drawable := get_drawable
			if l_drawable /= default_pointer then
				gc_clip_area := an_area.twin
				{CAIRO}.rectangle (l_drawable,
					an_area.x + device_x_offset + 0.5,
					an_area.y + device_y_offset + 0.5,
					an_area.width,
					an_area.height)
				{CAIRO}.clip (l_drawable)
				release_drawable (l_drawable)
			else
				check has_drawable: False end
			end
		end

	set_clip_region (a_region: EV_REGION)
			-- Set a region to clip to.
		do
			check Implemented: False end
		end

	remove_clipping
			-- Do not apply any clipping.
		local
			l_drawable: POINTER
		do
			gc_clip_area := Void
			l_drawable := get_drawable
			if l_drawable /= default_pointer then
				{CAIRO}.reset_clip (l_drawable)
				release_drawable (l_drawable)
			else
				check has_drawble: False end
			end
		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 end
		end

	remove_tile
			-- Do not apply a tile when filling.
		do
			tile := Void
		end

	enable_dashed_line_style
			-- Draw lines dashed.
		do
			dashed_line_style := True
			if drawable /= default_pointer then
				{CAIRO}.set_dashed_line_style (drawable, True)
			end
		end

	disable_dashed_line_style
			-- Draw lines solid.
		do
			dashed_line_style := False
			if drawable /= default_pointer then
				{CAIRO}.set_dashed_line_style (drawable, False)
			end
		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
			l_drawable: POINTER
		do
			l_drawable := get_drawable
			if l_drawable /= default_pointer then
				{CAIRO}.save (l_drawable)
				if attached internal_background_color as l_bg_color then
					{CAIRO}.set_source_rgb (l_drawable, l_bg_color.red, l_bg_color.green, l_bg_color.blue)
				else
						-- White
					{CAIRO}.set_source_rgb (l_drawable, 1.0, 1.0, 1.0)
				end
				{CAIRO}.rectangle (l_drawable, x + device_x_offset + 0.5, y + device_y_offset + 0.5, a_width, a_height)
				{CAIRO}.fill (l_drawable)

				{CAIRO}.restore (l_drawable)
				release_drawable (l_drawable)
			end
		end

feature -- Drawing operations

	draw_point (x, y: INTEGER)
			-- Draw point at (`x', `y').
		do
			draw_segment (x, y, x + 1, y + 1)
		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, y, 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, y, 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, y, a_text, True, clipping_width, 0.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, y, a_text, False, clipping_width, 0.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, y, a_text, False, -1, 0.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' at an `a_angle' expressed in radians counter-clockwise from horizontal plane.
			-- If `draw_from_baseline' then (`x', `y') is the baseline coordinates, otherwise it is the top-left coordinates.
			-- If `a_width' is non-negative, then `a_text' is clipped at `a_width' units.
		local
			a_cs: EV_GTK_C_STRING
			a_pango_layout, l_pango_iter: POINTER
			l_x, l_y: REAL_64
			l_ellipsize_symbol: POINTER
			l_drawable: POINTER
		do
			l_drawable := get_drawable
			if l_drawable /= default_pointer then

				{CAIRO}.save (l_drawable)

					-- Set x_orig and y_orig to be the device translated values which must be used for the rest of the routine.
				l_x := x + device_x_offset
				l_y := y + device_y_offset

				a_pango_layout := {PANGO}.cairo_create_layout (l_drawable)

				a_cs := App_implementation.c_string_from_eiffel_string (a_text)
				{PANGO}.layout_set_text (a_pango_layout, a_cs.item, a_cs.string_length)
				if internal_font_imp /= Void then
					{PANGO}.layout_set_font_description (a_pango_layout, internal_font_imp.font_description)
				end

				if a_width >= 0 then
						-- We need to perform ellipsizing on text if available, otherwise we clip.
					l_ellipsize_symbol := pango_layout_set_ellipsize_symbol
					{PANGO}.layout_set_ellipsize_call (l_ellipsize_symbol, a_pango_layout, 3)
					{PANGO}.layout_set_width (a_pango_layout, a_width * {PANGO}.scale)
				end

				if a_angle /= 0.0 then
					{CAIRO}.translate (l_drawable, l_x, l_y)

						-- Minus angle because Vision is counter-clockwise, and Cairo clockwise.
					{CAIRO}.rotate (l_drawable, -a_angle)

					if draw_from_baseline  then
						l_pango_iter := {PANGO}.layout_get_iter (a_pango_layout)
						{CAIRO}.move_to (l_drawable, 0, -({PANGO}.layout_iter_get_baseline (l_pango_iter) / {PANGO}.scale))
						{PANGO}.layout_iter_free (l_pango_iter)
					end
				else
					if draw_from_baseline  then
						l_pango_iter := {PANGO}.layout_get_iter (a_pango_layout)
						l_y := l_y -({PANGO}.layout_iter_get_baseline (l_pango_iter) / {PANGO}.scale)
						{PANGO}.layout_iter_free (l_pango_iter)
					end
					{CAIRO}.translate (l_drawable, l_x, l_y)
				end

				{PANGO}.cairo_show_layout (l_drawable, a_pango_layout)

					-- Free allocated resources
				{GTK2}.g_object_unref (a_pango_layout)

				{CAIRO}.restore (l_drawable)

				release_drawable (l_drawable)
			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

	draw_segment (x1, y1, x2, y2: INTEGER)
			-- Draw line segment from (`x1', 'y1') to (`x2', 'y2').
		local
			l_drawable: POINTER
		do
			l_drawable := get_drawable
			if l_drawable /= default_pointer then
				{CAIRO}.save (l_drawable)
				{CAIRO}.move_to (l_drawable, x1 + device_x_offset + 0.5, y1 + device_y_offset + 0.5)
				{CAIRO}.line_to (l_drawable, x2 + device_x_offset + 0.5, y2 + device_y_offset + 0.5)
				{CAIRO}.stroke (l_drawable)
				{CAIRO}.restore (l_drawable)
				release_drawable (l_drawable)
			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.
		do
			draw_ellipse_internal (x, y, a_width, a_height, a_start_angle, an_aperture, False, False)
		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
			l_drawable: POINTER
		do
			l_drawable := get_drawable
			if l_drawable /= default_pointer and then attached {EV_PIXEL_BUFFER_IMP} a_pixel_buffer.implementation as l_pixel_buffer_imp then
				{CAIRO}.save (l_drawable)
				{GDK_CAIRO}.set_source_pixbuf (l_drawable, l_pixel_buffer_imp.gdk_pixbuf, area.x, area.y)
				{CAIRO}.rectangle (l_drawable, a_x + device_x_offset + 0.5, a_y + device_y_offset + 0.5, area.width, area.height)
				{CAIRO}.fill (l_drawable)
				{CAIRO}.restore (l_drawable)
				release_drawable (l_drawable)
			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_drawable: POINTER
		do
			l_drawable := get_drawable
			if l_drawable /= default_pointer and then attached {EV_PIXMAP_IMP} a_pixmap.implementation as l_pixmap_imp then
				{CAIRO}.save (l_drawable)
				{CAIRO}.set_source_surface (l_drawable, l_pixmap_imp.cairo_surface, x - x_src, y - y_src)
				{CAIRO}.rectangle (l_drawable, x + device_x_offset + 0.5, y + device_y_offset + 0.5, src_width, src_height)
				{CAIRO}.fill (l_drawable)
				{CAIRO}.restore (l_drawable)
				release_drawable (l_drawable)
			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}.g_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
			draw_rectangle_internal (x, y, a_width, a_height, False)
		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
			draw_ellipse_internal (x, y, a_width, a_height, 0, 2 * {DOUBLE_MATH}.pi, True, False)
		end

	draw_ellipse_internal (x, y, a_width, a_height: INTEGER; a_start_angle, an_aperture: REAL_64; a_close, a_fill: BOOLEAN)
			-- Draw an ellipse bounded by top left (`x', `y') with
			-- size `a_width' and `a_height'.
			-- Fill ellipse if `a_fill'.
		local
			l_xc, l_yc, l_radius, l_y_scale: REAL_64
			l_close: BOOLEAN
			l_drawable: POINTER
		do
			l_drawable := get_drawable
			if l_drawable /= default_pointer then
				if (a_width > 0 and a_height > 0 ) then
					{CAIRO}.save (l_drawable)

					l_close := a_close and then an_aperture /= (2 * {DOUBLE_MATH}.Pi)

					l_xc := x + (a_width / 2) + device_x_offset
					l_yc := y + (a_height / 2) + device_y_offset

					l_radius := a_width / 2
					if a_width /= a_height then
						l_y_scale := a_height / a_width
						{CAIRO}.scale (l_drawable, 1.0, l_y_scale)
						l_yc := (l_yc - device_y_offset) / l_y_scale + device_y_offset
					end

					if l_close then
						{CAIRO}.move_to (l_drawable, l_xc, l_yc)
					end

					{CAIRO}.arc_negative (l_drawable, l_xc, l_yc, a_height / 2, -a_start_angle, - (a_start_angle + an_aperture))
					if a_fill then
						{CAIRO}.stroke_preserve (l_drawable)
						{CAIRO}.fill (l_drawable)
					end

					if l_close then
						{CAIRO}.close_path (l_drawable)
					end
					{CAIRO}.stroke (l_drawable)

					if a_width /= a_height then
						{CAIRO}.scale (l_drawable, 1.0, 1.0)
					end

					{CAIRO}.restore (l_drawable)
					release_drawable (l_drawable)
				end
			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'.
		do
			draw_polyline_internal (points, is_closed, False)
		end

	draw_polyline_internal (points: ARRAY [EV_COORDINATE]; is_closed, is_filled: BOOLEAN)
			-- Draw line segments between subsequent points in
			-- `points'. If `is_closed' draw line segment between first
			-- and last point in `points'.
		local
			i, l_count: INTEGER
			l_drawable: POINTER
		do
			if not points.is_empty then
				l_drawable := get_drawable
				if l_drawable /= default_pointer then
					from
						{CAIRO}.save (l_drawable)
						l_count := points.count
						{CAIRO}.move_to (l_drawable, points [1].x_precise + 0.5, points [1].y_precise + 0.5)
						i := 2
					until
						i > l_count
					loop
						{CAIRO}.line_to (l_drawable, points [i].x_precise + 0.5, points [i].y_precise + 0.5)
						i := i + 1
					end
					if is_closed then
						{CAIRO}.close_path (l_drawable)
					end
					{CAIRO}.stroke (l_drawable)
					{CAIRO}.restore (l_drawable)
					release_drawable (l_drawable)
				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
		do
			draw_ellipse_internal (x, y, a_width, a_height, a_start_angle, an_aperture, True, False)
		end

feature -- filling operations

	fill_rectangle (x, y, a_width, a_height: INTEGER)
			-- Draw rectangle with upper-left corner on (`x', `y')
		do
			draw_rectangle_internal (x, y, a_width, a_height, True)
		end

	draw_rectangle_internal (x, y, a_width, a_height: INTEGER; a_fill: BOOLEAN)
			-- Draw rectangle with upper-left corner on (`x', `y')
		local
			l_drawable: POINTER
		do
			if a_width > 0 and then a_height > 0 then
				l_drawable := get_drawable
				if l_drawable /= default_pointer then
					{CAIRO}.save (l_drawable)
						-- If width or height are zero then nothing will be rendered.
					{CAIRO}.rectangle (l_drawable, x + device_x_offset + 0.5, y + device_y_offset + 0.5, a_width - line_width, a_height - line_width)
					if a_fill then
						{CAIRO}.stroke_preserve (l_drawable)
						{CAIRO}.fill (l_drawable)
					end
					{CAIRO}.stroke (l_drawable)
					{CAIRO}.restore (l_drawable)
					release_drawable (l_drawable)
				end
			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
			draw_ellipse_internal (x, y, a_width, a_height, 0, 2 * {DOUBLE_MATH}.pi, True, True)
		end

	fill_polygon (points: ARRAY [EV_COORDINATE])
			-- Draw line segments between subsequent points in `points'.
			-- Fill all enclosed area's with `background_color'.
		do
			draw_polyline_internal (points, True, True)
		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
			draw_ellipse_internal (x, y, a_width, a_height, a_start_angle, an_aperture, True, True)
		end

feature {EV_ANY_I} -- Implementation

	get_surface: POINTER
			-- Surface that holds drawable data.
		do

		end

	get_drawable: POINTER
			-- Drawable used for rendering docking components.
		do
			Result := drawable
		end

	release_drawable (a_drawable: POINTER)
			-- Release resources of drawable `a_drawable'.
		do
			--| By default do nothing, redefined in descendents
		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
		do
			Result := {GTK}.gdk_pixbuf_new (0, True, 8, a_width, a_height)
		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}.g_object_unref (a_pixbuf)
		end

feature {NONE} -- Implementation

	device_x_offset: INTEGER
			-- Number of pixels to offset the x coord to get correct device placement
		do
		end

	device_y_offset: INTEGER
			-- Number of pixels to offset to y coord to get correct device placement.
		do
		end

	internal_set_color (a_foreground: BOOLEAN; a_red, a_green, a_blue: REAL_64)
			-- Set `gc' color to (a_red, a_green, a_blue), `a_foreground' sets foreground color, otherwise background is set.
		do
			if drawable /= default_pointer and then a_foreground then
				{CAIRO}.set_source_rgb (drawable, a_red, a_green, a_blue)
			end
		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.

	flush
			-- Force all queued expose events to be called.
		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

feature {EV_ANY, EV_ANY_I} -- Implementation

	interface: detachable EV_DRAWABLE note option: stable attribute end

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