note
	description: "Representation of an image that can be paste on other image."
	author: "Louis Marchand"
	date: "Sat, 28 Mar 2015 14:00:33 +0000"
	revision: "2.0"

class 
	GAME_SURFACE

create 
	make,
	share_from_image,
	make_from_image,
	share_from_other,
	make_from_other,
	make_for_window,
	make_for_display,
	make_for_display_mode,
	make_for_pixel_format,
	make_with_masks

feature {NONE} -- Initialization

	default_create
			-- Process instances of classes with no creation clause.
			-- (Default: do nothing.)
			-- (from ANY)
		do
		end
	
feature -- Access

	as_converted_to_pixel_format (a_pixel_format: GAME_PIXEL_FORMAT_READABLE): GAME_SURFACE
			-- Create a copy of Current conforming to a_pixel_format.
		require
			surface_is_video_enable: Game_library.is_video_enable
			surface_convert_is_open: is_open
			not_locked: not is_locked
		local
			l_source: GAME_IMAGE
		do
			has_error := False
			create l_source.own_from_pointer ({GAME_SDL_EXTERNAL}.sdl_convertsurfaceformat (item, a_pixel_format.internal_index, 0))
			if l_source.is_openable then
				l_source.open
				if l_source.is_open then
					create Result.share_from_image (l_source)
				else
					create Result.make_for_pixel_format (a_pixel_format, width, height)
					if Result.is_open then
						Result.draw_surface (Current, 0, 0)
					else
						has_error := True
					end
				end
			else
				create Result.make_for_pixel_format (a_pixel_format, width, height)
				if Result.is_open then
					Result.draw_surface (Current, 0, 0)
				else
					has_error := True
				end
			end
		end

	color_multiplier: TUPLE [red_multipier: NATURAL_8; green_multipier: NATURAL_8; blue_multipier: NATURAL_8]
			-- The additional color value multiplied into drawing operations
		require
			surface_is_open: is_open
			not_locked: not is_locked
		local
			l_error: INTEGER_32
			l_red, l_green, l_blue: NATURAL_8
		do
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_getsurfacecolormod (item, $l_red.to_pointer, $l_green.to_pointer, $l_blue.to_pointer)
			manage_error_code (l_error, "An error occured while retrieving the color multiplier value of the surface.")
			Result := [l_red, l_green, l_blue]
		end

	disable_blending
			-- Disable every blending mode to use for drawing operations.
			-- No blending mode:	dstRGBA = srcRGBA
			-- (from GAME_BLENDABLE)
		require -- from GAME_BLENDABLE
			blendable_is_valid: is_open
		do
			set_blend_mode ({GAME_SDL_EXTERNAL}.sdl_blendmode_none)
		end

	disable_rle_acceleration
			-- Disable the possible optimisation when using drawing with transparent_color enabled or enable_alpha_blending.
		require
			surface_is_open: is_open
			not_locked: not is_locked
		local
			l_error: INTEGER_32
		do
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_setsurfacerle (item, 0)
			manage_error_code (l_error, "An error occured when disabling RLE acceleration on the surface.")
		end

	disable_transparent
			-- Remove the transparency by color key.
		require
			surface_is_video_enable: Game_library.is_video_enable
			surface_is_open: is_open
			not_locked: not is_locked
		local
			l_error: INTEGER_32
		do
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_setcolorkey (item, {GAME_SDL_EXTERNAL}.sdl_false, 0)
			if l_error < 0 then
				manage_error_code (l_error, "An error occured while disabling the transparent color of the surface.")
			else
				disable_rle_acceleration
			end
		end

	draw_rectangle (a_color: GAME_COLOR; a_x, a_y, a_width, a_height: INTEGER_32)
			-- Draw a a_color rectangle of dimension a_width x a_height on Current at (a_x,a_y).
		require
			surface_is_video_enable: Game_library.is_video_enable
			surface_draw_is_open: is_open
			not_locked: not is_locked
		local
			l_rect_src, l_format: POINTER
			l_error: INTEGER_32
			l_color_key: NATURAL_32
			l_normalized_rectangle: TUPLE [x: INTEGER_32; y: INTEGER_32; width: INTEGER_32; height: INTEGER_32]
		do
			clear_error
			l_format := pixel_format.item
			if pixel_format.has_error then
				manage_error_boolean (False, "Cannot retreive the pixel format of the surface.")
			else
				l_normalized_rectangle := normalize_rectangle (a_x, a_y, a_width, a_height)
				l_rect_src := l_rect_src.memory_calloc (1, {GAME_SDL_EXTERNAL}.c_sizeof_sdl_rect)
				{GAME_SDL_EXTERNAL}.set_rect_struct_x (l_rect_src, l_normalized_rectangle.x)
				{GAME_SDL_EXTERNAL}.set_rect_struct_y (l_rect_src, l_normalized_rectangle.y)
				{GAME_SDL_EXTERNAL}.set_rect_struct_w (l_rect_src, l_normalized_rectangle.width)
				{GAME_SDL_EXTERNAL}.set_rect_struct_h (l_rect_src, l_normalized_rectangle.height)
				l_color_key := {GAME_SDL_EXTERNAL}.sdl_maprgba (l_format, a_color.red, a_color.green, a_color.blue, a_color.alpha)
				l_error := {GAME_SDL_EXTERNAL}.sdl_fillrect (item, l_rect_src, l_color_key)
				manage_error_code (l_error, "An error occured while drawing rectangle to the surface.");
				l_rect_src.memory_free
			end
		end

	draw_rectangles (a_color: GAME_COLOR; a_rectangles: CHAIN [TUPLE [x: INTEGER_32; y: INTEGER_32; width: INTEGER_32; height: INTEGER_32]])
			-- Drawing every a_color rectangle in a_rectangles
			-- that has it's left frontier at
			-- x, it's top frontier at y, with
			-- dimension widthxheight
		require
			surface_is_video_enable: Game_library.is_video_enable
			surface_draw_is_open: is_open
			not_locked: not is_locked
		local
			l_array_rectangles, l_rectangle, l_format: POINTER
			l_rectangle_size, l_error: INTEGER_32
			l_color_key: NATURAL_32
			l_normalized_rectangle: TUPLE [x: INTEGER_32; y: INTEGER_32; width: INTEGER_32; height: INTEGER_32]
		do
			clear_error
			l_format := pixel_format.item
			if pixel_format.has_error then
				manage_error_boolean (False, "Cannot retreive the pixel format of the surface.")
			else
				l_rectangle_size := {GAME_SDL_EXTERNAL}.c_sizeof_sdl_rect
				l_array_rectangles := l_array_rectangles.memory_alloc (l_rectangle_size * a_rectangles.count)
				l_rectangle := l_array_rectangles
				across
					a_rectangles as la_rectangles
				loop
					l_normalized_rectangle := normalize_rectangle (la_rectangles.item.x, la_rectangles.item.y, la_rectangles.item.width, la_rectangles.item.height)
					{GAME_SDL_EXTERNAL}.set_rect_struct_x (l_rectangle, l_normalized_rectangle.x)
					{GAME_SDL_EXTERNAL}.set_rect_struct_y (l_rectangle, l_normalized_rectangle.y)
					{GAME_SDL_EXTERNAL}.set_rect_struct_w (l_rectangle, l_normalized_rectangle.width)
					{GAME_SDL_EXTERNAL}.set_rect_struct_h (l_rectangle, l_normalized_rectangle.height)
					l_rectangle := l_rectangle.plus (l_rectangle_size)
				end
				l_color_key := {GAME_SDL_EXTERNAL}.sdl_maprgba (l_format, a_color.red, a_color.green, a_color.blue, a_color.alpha)
				l_error := {GAME_SDL_EXTERNAL}.sdl_fillrects (item, l_array_rectangles, a_rectangles.count, l_color_key)
				manage_error_code (l_error, "An error occured while drawing rectangles to the surface.");
				l_array_rectangles.memory_free
			end
		end

	draw_sub_surface (a_other: GAME_SURFACE; a_x_source, a_y_source, a_width, a_height, a_x_destination, a_y_destination: INTEGER_32)
			-- Draw on Current at (a_x_destination,a_y_destination) the portion of a_other
			-- starting at (a_x_source,a_y_source) with dimension a_width x a_height.
		require
			surface_is_video_enable: Game_library.is_video_enable
			surface_draw_is_open: is_open
			not_locked: not is_locked
		do
			internal_draw_surface (a_other, a_x_source, a_y_source, a_width, a_height, a_x_destination, a_y_destination, a_width, a_height, False)
		end

	draw_sub_surface_with_scale (a_other: GAME_SURFACE; a_x_source, a_y_source, a_width_source, a_height_source, a_x_destination, a_y_destination, a_width_destination, a_height_destination: INTEGER_32)
			-- Draw on Current at (a_x_destination,a_y_destination) the portion of a_other
			-- starting at (a_x_source,a_y_source) with dimension a_width x a_height.
			-- Will scale a_other using a_width_destination and a_height_destination
		require
			surface_is_video_enable: Game_library.is_video_enable
			surface_draw_is_open: is_open
			not_locked: not is_locked
		do
			internal_draw_surface (a_other, a_x_source, a_y_source, a_width_source, a_height_source, a_x_destination, a_y_destination, a_width_destination, a_height_destination, True)
		end

	draw_surface (a_other: GAME_SURFACE; a_x, a_y: INTEGER_32)
			-- Draw the whole surface a_other on Current at (a_x,a_y).
		require
			surface_is_video_enable: Game_library.is_video_enable
			surface_draw_is_open: is_open
			not_locked: not is_locked
		do
			draw_sub_surface (a_other, 0, 0, a_other.width, a_other.height, a_x, a_y)
		end

	enable_additive_blending
			-- Set the additive blending mode to use for drawing operations.
			-- Additive blending:	dstRGB = (srcRGB * srcA) + dstRGB
			--						dstA = dstA
			-- (from GAME_BLENDABLE)
		require -- from GAME_BLENDABLE
			blendable_is_valid: is_open
		do
			set_blend_mode ({GAME_SDL_EXTERNAL}.sdl_blendmode_add)
		end

	enable_alpha_blending
			-- Set the alpha blending mode to use for drawing operations.
			-- Alpha blending:	dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA))
			--					dstA = srcA + (dstA * (1-srcA))
			-- (from GAME_BLENDABLE)
		require -- from GAME_BLENDABLE
			blendable_is_valid: is_open
		do
			set_blend_mode ({GAME_SDL_EXTERNAL}.sdl_blendmode_blend)
		end

	enable_modulate_blending
			-- Set the color modulate blending mode to use for drawing operations.
			-- Color modulate:	dstRGB = srcRGB * dstRGB
			--					dstA = dstA
			-- (from GAME_BLENDABLE)
		require -- from GAME_BLENDABLE
			blendable_is_valid: is_open
		do
			set_blend_mode ({GAME_SDL_EXTERNAL}.sdl_blendmode_mod)
		end

	enable_rle_acceleration
			-- Enable possible optimisation when using drawing with transparent_color enabled or enable_alpha_blending.
		require
			surface_is_open: is_open
			not_locked: not is_locked
		local
			l_error: INTEGER_32
		do
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_setsurfacerle (item, 1)
			manage_error_code (l_error, "An error occured when enabling RLE acceleration on the surface.")
		end

	Game_library: GAME_LIBRARY_CONTROLLER
			-- The main controller of the game library
			-- (from GAME_LIBRARY_SHARED)
		once ("PROCESS")
			if attached internal_game_library as la_game_library then
				Result := la_game_library
			else
				create Result
			end
		end

	generating_type: TYPE [detachable GAME_SURFACE]
			-- Type of current object
			-- (type of which it is a direct instance)
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			generating_type_not_void: Result /= Void
		end

	generator: STRING_8
			-- Name of current object's generating class
			-- (base class of the type of which it is a direct instance)
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			generator_not_void: Result /= Void
			generator_not_empty: not Result.is_empty
		end

	has_error: BOOLEAN
			-- Is the library has generate an error
			-- (from GAME_ERROR_MANAGER)

	height: INTEGER_32
			-- The height of Current.
		require
			surface_is_open: is_open
		do
			Result := {GAME_SDL_EXTERNAL}.get_sdl_surface_struct_h (item)
		end

	image: GAME_IMAGE
			-- The GAME_IMAGE that has served for creating Current

	is_additive_blending_enabled: BOOLEAN
			-- True if the blending mode for drawing operation is additive blending.
			-- Additive blending:	dstRGB = (srcRGB * srcA) + dstRGB
			--						dstA = dstA
			-- (from GAME_BLENDABLE)
		require -- from GAME_BLENDABLE
			blendable_is_valid: is_open
		local
			l_blending_mode: INTEGER_32
		do
			l_blending_mode := blend_mode
			if not has_error then
				Result := l_blending_mode = {GAME_SDL_EXTERNAL}.sdl_blendmode_add
			else
				Result := False
			end
		end

	is_alpha_blending_enabled: BOOLEAN
			-- True if the blending mode for drawing operation is alpha blending.
			-- Alpha blending:	dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA))
			--					dstA = srcA + (dstA * (1-srcA))
			-- (from GAME_BLENDABLE)
		require -- from GAME_BLENDABLE
			blendable_is_valid: is_open
		local
			l_blending_mode: INTEGER_32
		do
			l_blending_mode := blend_mode
			if not has_error then
				Result := l_blending_mode = {GAME_SDL_EXTERNAL}.sdl_blendmode_blend
			else
				Result := False
			end
		end

	is_blending_disabled: BOOLEAN
			-- True if no blending mode is used for drawing operations.
			-- No blending mode:	dstRGBA = srcRGBA
			-- (from GAME_BLENDABLE)
		require -- from GAME_BLENDABLE
			blendable_is_valid: is_open
		local
			l_blending_mode: INTEGER_32
		do
			l_blending_mode := blend_mode
			if not has_error then
				Result := l_blending_mode = {GAME_SDL_EXTERNAL}.sdl_blendmode_none
			else
				Result := False
			end
		end

	is_locked: BOOLEAN
			-- Current is locked to access pixels. Current cannot be used until unlock is called.
		do
			Result := attached internal_pixels
		end

	is_modulate_blending_enabled: BOOLEAN
			-- True if the blending mode for drawing operation is color modulate blending.
			-- Color modulate:	dstRGB = srcRGB * dstRGB
			--					dstA = dstA
			-- (from GAME_BLENDABLE)
		require -- from GAME_BLENDABLE
			blendable_is_valid: is_open
		local
			l_blending_mode: INTEGER_32
		do
			l_blending_mode := blend_mode
			if not has_error then
				Result := l_blending_mode = {GAME_SDL_EXTERNAL}.sdl_blendmode_mod
			else
				Result := False
			end
		end

	is_open: BOOLEAN
			-- Current has been opened properly

	is_transparent_enable: BOOLEAN
			-- Is transparency by color key is enabled.
		require
			surface_is_video_enable: Game_library.is_video_enable
			surface_is_open: is_open
			not_locked: not is_locked
		local
			l_error: INTEGER_32
			l_color_key: NATURAL_32
		do
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_getcolorkey (item, $l_color_key.to_pointer)
			if l_error < -1 then
				manage_error_code (l_error, "An error occured while getting the transparent color of the surface.")
			end
			Result := l_error /= -1
		end

	last_error: READABLE_STRING_GENERAL
			-- The last error generate by the library
			-- (from GAME_SDL_ANY)
		local
			l_string: C_STRING
		do
			if is_manual_error then
				Result := Precursor {GAME_ERROR_MANAGER}
			else
				create l_string.make_by_pointer ({GAME_SDL_EXTERNAL}.sdl_geterror)
				Result := l_string.string
			end
		end

	lock
			-- Lock Current to access pixels.
			-- Must used unlock after the edition.
			-- You cannot draw on Current while locked.
		local
			l_error: INTEGER_32
		do
			clear_error
			if must_lock then
				l_error := {GAME_SDL_EXTERNAL}.sdl_locksurface (item)
				if l_error = 0 then
					create internal_pixels.make_from_pointer ({GAME_SDL_EXTERNAL}.get_sdl_surface_struct_pixels (item), pixel_format, width, height, {GAME_SDL_EXTERNAL}.get_sdl_surface_struct_pitch (item))
				else
					manage_error_code (l_error, "Cannot lock surface.")
				end
			else
				create internal_pixels.make_from_pointer ({GAME_SDL_EXTERNAL}.get_sdl_surface_struct_pixels (item), pixel_format, width, height, {GAME_SDL_EXTERNAL}.get_sdl_surface_struct_pitch (item))
			end
		ensure
			is_locked: not has_error implies is_locked
		end

	must_lock: BOOLEAN
			-- Current must be locked for pixel access
		do
			Result := {GAME_SDL_EXTERNAL}.sdl_mustlock (item)
		end

	overall_alpha: NATURAL_8 assign set_overall_alpha
			-- The Additionnal alpha value to use in drawing operation.
		require
			surface_is_open: is_open
			not_locked: not is_locked
		local
			l_error: INTEGER_32
		do
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_getsurfacealphamod (item, $Result.to_pointer)
			manage_error_code (l_error, "An error occured while retrieving the overall alpha value of the surface.")
		end

	pixel_format: GAME_PIXEL_FORMAT_READABLE
			-- The internal format of the pixel representation in memory.
		require
			surface_is_video_enable: Game_library.is_video_enable
			surface_pixel_format_is_open: is_open
		do
			create Result.share_from_structure_pointer ({GAME_SDL_EXTERNAL}.get_sdl_surface_struct_format (item))
		end

	pixels: GAME_PIXEL_READER_WRITER
			-- Used to fetch and edit pixels in Current
			-- Use lock before to access multiple pixels
		require
			locked_if_needed: must_lock implies is_locked
		do
			if attached internal_pixels as la_pixels then
				Result := la_pixels
			else
				create Result.make_from_pointer ({GAME_SDL_EXTERNAL}.get_sdl_surface_struct_pixels (item), pixel_format, width, height, {GAME_SDL_EXTERNAL}.get_sdl_surface_struct_pitch (item))
			end
		end

	save_bmp (a_filename: READABLE_STRING_GENERAL)
			-- Save Current into a BMP image file
		require
			surface_is_open: is_open
		local
			l_error: INTEGER_32
			l_filename_c: C_STRING
		do
			create l_filename_c.make (a_filename)
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_savebmp (item, l_filename_c.item)
			manage_error_code (l_error, "An error occured when saving Surface to bmp file.")
		end

	set_color_multiplier (a_red_multiplier, a_green_multiplier, a_blue_multiplier: NATURAL_8)
			-- Assign the Additionnal color_multiplier value to use into drawing operation to a_red_multiplier,
			-- a_green_multiplier, a_blue_multiplier.
		require
			surface_is_open: is_open
			not_locked: not is_locked
		local
			l_error: INTEGER_32
		do
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_setsurfacecolormod (item, a_red_multiplier, a_green_multiplier, a_blue_multiplier)
			manage_error_code (l_error, "An error occured while setting the overall alpha value on the surface.")
		end

	set_overall_alpha (a_overall_alpha: NATURAL_8)
			-- Assign the Additionnal overall_alpha value to use in drawing operation to a_overall_alpha.
		require
			surface_is_open: is_open
			not_locked: not is_locked
		local
			l_error: INTEGER_32
		do
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_setsurfacealphamod (item, a_overall_alpha)
			manage_error_code (l_error, "An error occured while setting the overall alpha value of the surface.")
		end

	set_transparent_color (a_color: GAME_COLOR_READABLE)
			-- Change all pixel of color color into transparency (and enable it). The transparency by color don't work if the surface
			-- have an alpha blending activated.
		require
			surface_is_video_enable: Game_library.is_video_enable
			surface_is_open: is_open
			not_locked: not is_locked
		local
			l_key: NATURAL_32
			l_error: INTEGER_32
		do
			l_key := {GAME_SDL_EXTERNAL}.sdl_maprgb (pixel_format.item, a_color.red, a_color.green, a_color.blue)
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_setcolorkey (item, {GAME_SDL_EXTERNAL}.sdl_true, l_key)
			if l_error < 0 then
				manage_error_code (l_error, "An error occured while setting the transparent color to the surface.")
			else
				enable_rle_acceleration
			end
		end

	transparent_color: GAME_COLOR_READABLE assign set_transparent_color
			-- The color that will be remove in the surface (the transparent color).
		require
			surface_is_video_enable: Game_library.is_video_enable
			surface_is_open: is_open
			surface_transparent_color_is_enable: is_transparent_enable
			not_locked: not is_locked
		local
			l_red, l_green, l_blue, l_alpha: NATURAL_8
			l_color_key: NATURAL_32
			l_error: INTEGER_32
		do
			clear_error
			l_error := {GAME_SDL_EXTERNAL}.sdl_getcolorkey (item, $l_color_key.to_pointer)
			if l_error < 0 then
				manage_error_code (l_error, "An error occured while getting the transparent color of the surface.")
				create Result.make (0, 0, 0, 0)
			else
				{GAME_SDL_EXTERNAL}.sdl_getrgba (l_color_key, pixel_format.item, $l_red.to_pointer, $l_green.to_pointer, $l_blue.to_pointer, $l_alpha.to_pointer)
				create Result.make (l_red, l_green, l_blue, l_alpha)
			end
		end

	unlock
			-- Unlock Current after access pixels.
		require
			is_locked: is_locked
		do
			if must_lock then
				{GAME_SDL_EXTERNAL}.sdl_unlocksurface (item)
			end
			if attached internal_pixels as la_pixels then
				la_pixels.close
			end
			internal_pixels := Void
		ensure
			not_locked: not is_locked
		end

	width: INTEGER_32
			-- The width of Current.
		require
			surface_is_open: is_open
		do
			Result := {GAME_SDL_EXTERNAL}.get_sdl_surface_struct_w (item)
		end
	
feature -- Comparison

	frozen deep_equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void
			-- or attached to isomorphic object structures?
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.is_deep_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			shallow_implies_deep: standard_equal (a, b) implies Result
			both_or_none_void: (a = Void) implies (Result = (b = Void))
			same_type: (Result and (a /= Void)) implies (b /= Void and then a.same_type (b))
			symmetric: Result implies deep_equal (b, a)
		end

	frozen equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void or attached
			-- to objects considered equal?
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.is_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.is_equal (b))
		end

	frozen is_deep_equal alias "≡≡≡" (other: GAME_SURFACE): BOOLEAN
			-- Are Current and other attached to isomorphic object structures?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			shallow_implies_deep: standard_is_equal (other) implies Result
			same_type: Result implies same_type (other)
			symmetric: Result implies other.is_deep_equal (Current)
		end

	is_equal (other: GAME_SURFACE): BOOLEAN
			-- Is other attached to an object considered
			-- equal to current object?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			symmetric: Result implies other ~ Current
			consistent: standard_is_equal (other) implies Result
		end

	frozen standard_equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void or attached to
			-- field-by-field identical objects of the same type?
			-- Always uses default object comparison criterion.
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.standard_is_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.standard_is_equal (b))
		end

	frozen standard_is_equal alias "≜" (other: GAME_SURFACE): BOOLEAN
			-- Is other attached to an object of the same type
			-- as current object, and field-by-field identical to it?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			same_type: Result implies same_type (other)
			symmetric: Result implies other.standard_is_equal (Current)
		end
	
feature -- Status report

	conforms_to (other: ANY): BOOLEAN
			-- Does type of current object conform to type
			-- of other (as per Eiffel: The Language, chapter 13)?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		end

	same_type (other: ANY): BOOLEAN
			-- Is type of current object identical to type of other?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			definition: Result = (conforms_to (other) and other.conforms_to (Current))
		end
	
feature -- Duplication

	frozen clone (other: detachable ANY): like other
		obsolete "Use `twin' instead. [2017-05-31]"
			-- Void if other is void; otherwise new object
			-- equal to other
			--
			-- For non-void other, clone calls copy;
			-- to change copying/cloning semantics, redefine copy.
			-- (from ANY)
		do
			if other /= Void then
				Result := other.twin
			end
		ensure -- from ANY
			instance_free: class
			equal: Result ~ other
		end

	copy (other: GAME_SURFACE)
			-- Update current object using fields of object attached
			-- to other, so as to yield equal objects.
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
			type_identity: same_type (other)
		external
			"built_in"
		ensure -- from ANY
			is_equal: Current ~ other
		end

	frozen deep_clone (other: detachable ANY): like other
		obsolete "Use `deep_twin' instead. [2017-05-31]"
			-- Void if other is void: otherwise, new object structure
			-- recursively duplicated from the one attached to other
			-- (from ANY)
		do
			if other /= Void then
				Result := other.deep_twin
			end
		ensure -- from ANY
			instance_free: class
			deep_equal: deep_equal (other, Result)
		end

	frozen deep_copy (other: GAME_SURFACE)
			-- Effect equivalent to that of:
			--		copy (other . deep_twin)
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		do
			copy (other.deep_twin)
		ensure -- from ANY
			deep_equal: deep_equal (Current, other)
		end

	frozen deep_twin: GAME_SURFACE
			-- New object structure recursively duplicated from Current.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			deep_twin_not_void: Result /= Void
			deep_equal: deep_equal (Current, Result)
		end

	frozen standard_clone (other: detachable ANY): like other
		obsolete "Use `standard_twin' instead. [2017-05-31]"
			-- Void if other is void; otherwise new object
			-- field-by-field identical to other.
			-- Always uses default copying semantics.
			-- (from ANY)
		do
			if other /= Void then
				Result := other.standard_twin
			end
		ensure -- from ANY
			instance_free: class
			equal: standard_equal (Result, other)
		end

	frozen standard_copy (other: GAME_SURFACE)
			-- Copy every field of other onto corresponding field
			-- of current object.
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
			type_identity: same_type (other)
		external
			"built_in"
		ensure -- from ANY
			is_standard_equal: standard_is_equal (other)
		end

	frozen standard_twin: GAME_SURFACE
			-- New object field-by-field identical to other.
			-- Always uses default copying semantics.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			standard_twin_not_void: Result /= Void
			equal: standard_equal (Result, Current)
		end

	frozen twin: GAME_SURFACE
			-- New object equal to Current
			-- twin calls copy; to change copying/twinning semantics, redefine copy.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			twin_not_void: Result /= Void
			is_equal: Result ~ Current
		end
	
feature -- Basic operations

	frozen as_attached: attached GAME_SURFACE
		obsolete "Remove calls to this feature. [2017-05-31]"
			-- Attached version of Current.
			-- (Can be used during transitional period to convert
			-- non-void-safe classes to void-safe ones.)
			-- (from ANY)
		do
			Result := Current
		end

	frozen default: detachable GAME_SURFACE
			-- Default value of object's type
			-- (from ANY)
		do
		end

	frozen default_pointer: POINTER
			-- Default value of type POINTER
			-- (Avoid the need to write p.default for
			-- some p of type POINTER.)
			-- (from ANY)
		do
		ensure -- from ANY
			instance_free: class
		end

	default_rescue
			-- Process exception for routines with no Rescue clause.
			-- (Default: do nothing.)
			-- (from ANY)
		do
		end

	frozen do_nothing
			-- Execute a null action.
			-- (from ANY)
		do
		ensure -- from ANY
			instance_free: class
		end
	
feature {GAME_SDL_ANY} -- Implementation

	item: POINTER
			-- The internal pointer to the image
		do
			Result := image.item
		end
	
feature {NONE} -- Implementation

	blend_mode: INTEGER_32 assign set_blend_mode
			-- The alpha blending mode of Current
			-- (from GAME_BLENDABLE)
		local
			l_error, l_blending_mode: INTEGER_32
		do
			clear_error
			l_error := c_get_blend_mode (item, $l_blending_mode.to_pointer)
			if l_error < 0 then
				manage_error_code (l_error, "An error occured while retrieving the blending mode.")
				Result := 0
			end
			Result := l_blending_mode
		end

	clear_error
			-- Remove error pending in Current
			-- (from GAME_SDL_ANY)
		require -- from  GAME_ERROR_MANAGER
			True
		do
			{GAME_SDL_EXTERNAL}.sdl_clearerror
			Precursor {GAME_ERROR_MANAGER}
			is_manual_error := False
		ensure -- from GAME_ERROR_MANAGER
			no_error: not has_error
		ensure then -- from GAME_SDL_ANY
			no_error: not is_manual_error
		end

	disable_print_on_error
			-- Desactive the print_on_error functionnality.
			-- (from GAME_ERROR_MANAGER)
		do
			Print_on_error_internal.put (False)
		end

	enable_print_on_error
			-- Active the print_on_error functionnality.
			-- (from GAME_ERROR_MANAGER)
		do
			Print_on_error_internal.put (True)
		end

	internal_draw_surface (a_other: GAME_SURFACE; a_x_source, a_y_source, a_width_source, a_height_source, a_x_destination, a_y_destination, a_width_destination, a_height_destination: INTEGER_32; a_must_scale: BOOLEAN)
			-- Draw on Current at (a_x_destination,a_y_destination) the portion of a_other
			-- starting at (a_x_source,a_y_source) with dimension a_width x a_height.
			-- If a_must_scale is set, will scale using a_width_destination and a_height_destination
		require
			surface_is_video_enable: Game_library.is_video_enable
			surface_draw_is_open: is_open
			not_locked: not is_locked
		local
			l_rect_src, l_rect_dst: POINTER
			l_error: INTEGER_32
			l_normalized_rectangle_source, l_normalized_rectangle_destination: TUPLE [x: INTEGER_32; y: INTEGER_32; width: INTEGER_32; height: INTEGER_32]
		do
			l_normalized_rectangle_source := normalize_rectangle (a_x_source, a_y_source, a_width_source, a_height_source)
			l_normalized_rectangle_destination := normalize_rectangle (a_x_destination, a_y_destination, a_width_destination, a_height_destination)
			l_rect_src := l_rect_src.memory_calloc (1, {GAME_SDL_EXTERNAL}.c_sizeof_sdl_rect)
			l_rect_dst := l_rect_dst.memory_calloc (1, {GAME_SDL_EXTERNAL}.c_sizeof_sdl_rect)
			{GAME_SDL_EXTERNAL}.set_rect_struct_x (l_rect_src, l_normalized_rectangle_source.x)
			{GAME_SDL_EXTERNAL}.set_rect_struct_y (l_rect_src, l_normalized_rectangle_source.y)
			{GAME_SDL_EXTERNAL}.set_rect_struct_w (l_rect_src, l_normalized_rectangle_source.width)
			{GAME_SDL_EXTERNAL}.set_rect_struct_h (l_rect_src, l_normalized_rectangle_source.height)
			{GAME_SDL_EXTERNAL}.set_rect_struct_x (l_rect_dst, l_normalized_rectangle_destination.x)
			{GAME_SDL_EXTERNAL}.set_rect_struct_y (l_rect_dst, l_normalized_rectangle_destination.y)
			{GAME_SDL_EXTERNAL}.set_rect_struct_w (l_rect_dst, l_normalized_rectangle_destination.width)
			{GAME_SDL_EXTERNAL}.set_rect_struct_h (l_rect_dst, l_normalized_rectangle_destination.height)
			clear_error
			if a_must_scale then
				l_error := {GAME_SDL_EXTERNAL}.sdl_blitscaled (a_other.image.item, l_rect_src, item, l_rect_dst)
			else
				l_error := {GAME_SDL_EXTERNAL}.sdl_blitsurface (a_other.image.item, l_rect_src, item, l_rect_dst)
			end
			manage_error_code (l_error, "An error occured while drawing to the surface.");
			l_rect_dst.memory_free;
			l_rect_src.memory_free
		end

	internal_game_library: detachable GAME_LIBRARY_CONTROLLER
			-- Assign to this attribute prior to use Game_library to inject a specific GAME_LIBRARY_CONTROLLER singleton.
			-- (from GAME_LIBRARY_SHARED)

	internal_pixels: detachable GAME_PIXEL_READER_WRITER
			-- Internal representation of the pixels feature

	is_manual_error: BOOLEAN
			-- Is the current pending error is a manual error (using manual_error as message)
			-- (from GAME_SDL_ANY)

	manage_error_boolean (a_boolean: BOOLEAN; a_message: READABLE_STRING_GENERAL)
			-- Create an error if a_boolean is false.
			-- If there is an error, append a_message to the error message
			-- on the SDL2 library
			-- (from GAME_SDL_ANY)
		do
			if not a_boolean then
				if Print_on_error_internal.item then
					Io.Error.put_string (a_message.to_string_8 + "%N");
					Io.Error.put_string (last_error.to_string_8 + "%N")
				end
				has_error := True
			end
		ensure -- from GAME_SDL_ANY
				not a_boolean implies has_error
		end

	manage_error_code (a_error_code: INTEGER_32; a_message: READABLE_STRING_GENERAL)
			-- If needed create an error depending of the error code a_code.
			-- If there is an error, append a_message to the error message
			-- on the SDL2 library
			-- (from GAME_SDL_ANY)
		do
			if a_error_code < 0 then
				if Print_on_error_internal.item then
					Io.Error.put_string (a_message.to_string_8 + "%N");
					Io.Error.put_string (last_error.to_string_8 + "%N")
				end
				has_error := True
			end
		end

	manage_error_pointer (a_pointer: POINTER; a_message: READABLE_STRING_GENERAL)
			-- Create an error if a_pointer is not valid.
			-- If there is an error, append a_message to the error message
			-- on the SDL2 library
			-- (from GAME_SDL_ANY)
		do
			if a_pointer.is_default_pointer then
				if Print_on_error_internal.item then
					Io.Error.put_string (a_message.to_string_8 + "%N");
					Io.Error.put_string (last_error.to_string_8 + "%N")
				end
				has_error := True
			end
		ensure -- from GAME_SDL_ANY
				a_pointer.is_default_pointer implies has_error
		end

	manual_error: detachable READABLE_STRING_GENERAL
			-- The specific message for the last error
			-- (from GAME_ERROR_MANAGER)

	normalize_rectangle (a_x, a_y, a_width, a_height: INTEGER_32): TUPLE [x: INTEGER_32; y: INTEGER_32; width: INTEGER_32; height: INTEGER_32]
			-- Adjust the rectangle delimited started at (a_x,a_y) and having
			-- dimensions a_widthxa_height so that the resulting rectangle
			-- never have a negative width or height
			-- (from GAME_DRAWING_TOOLS)
		local
			l_x, l_y: INTEGER_32
		do
			if a_width < 0 then
				l_x := a_x + a_width
			else
				l_x := a_x
			end
			if a_height < 0 then
				l_y := a_y + a_height
			else
				l_y := a_y
			end
			Result := [l_x, l_y, a_width.abs, a_height.abs]
		end

	print_on_error: BOOLEAN
			-- When an error occured, the library will print
			-- informations about the error on the error console
			-- output (default is True).
			-- (from GAME_ERROR_MANAGER)
		do
			Result := Print_on_error_internal.item
		end

	Print_on_error_internal: CELL [BOOLEAN]
			-- True when an error occured,
			-- The library will print it right away.
			-- (from GAME_ERROR_MANAGER)
		once ("PROCESS")
			create Result.put (True)
		end

	put_manual_error (a_general_message, a_specific_error: READABLE_STRING_GENERAL)
			-- Create an error using a_general_error for the debug information
			-- and a_specific_error for the lasting information
			-- (from GAME_SDL_ANY)
		do
			is_manual_error := True
			Precursor {GAME_ERROR_MANAGER} (a_general_message, a_specific_error)
		ensure -- from GAME_ERROR_MANAGER
				has_error
		end

	set_blend_mode (a_blend_mode: INTEGER_32)
			-- The alpha blending mode of Current
			-- (from GAME_BLENDABLE)
		local
			l_error: INTEGER_32
		do
			clear_error
			l_error := c_set_blend_mode (item, a_blend_mode)
			manage_error_code (l_error, "An error occured while enabling color modulate blending on the renderer.")
		end

	set_print_on_error (a_value: BOOLEAN)
			-- Assign to print_on_error the value of a_value
			-- (from GAME_ERROR_MANAGER)
		do
			if a_value then
				enable_print_on_error
			else
				disable_print_on_error
			end
		ensure -- from GAME_ERROR_MANAGER
			is_assign: print_on_error ~ a_value
		end
	
feature {NONE} -- External

	c_get_blend_mode (a_item, a_blend_mode: POINTER): INTEGER_32
			-- Internal getter for blend mode
		do
			Result := {GAME_SDL_EXTERNAL}.sdl_getsurfaceblendmode (a_item, a_blend_mode)
		end

	c_set_blend_mode (a_item: POINTER; a_blend_mode: INTEGER_32): INTEGER_32
			-- Internal setter for blend mode
		do
			Result := {GAME_SDL_EXTERNAL}.sdl_setsurfaceblendmode (a_item, a_blend_mode)
		end
	
feature {NONE} -- Initialisation

	make (a_width, a_height: INTEGER_32)
			-- Create an empty Current of dimension a_width x a_height
			-- conforming to the first founded GAME_DISPLAY.
		require
			surface_make_video_enabled: Game_library.is_video_enable
		do
			if not Game_library.displays.is_empty then
				make_for_display_mode (Game_library.displays.first.current_mode, a_width, a_height)
			else
				create image.own_from_pointer (create {POINTER})
			end
		ensure
			surface_make_is_open: has_error or is_open
		end

	make_for_display (a_display: GAME_DISPLAY; a_width, a_height: INTEGER_32)
			-- Create an empty Current of dimension a_width x a_height
			-- conforming to a_display.
		require
			surface_make_video_enabled: Game_library.is_video_enable
		do
			make_for_display_mode (a_display.current_mode, a_width, a_height)
		ensure
			surface_make_is_open: has_error or is_open
		end

	make_for_display_mode (a_display_mode: GAME_DISPLAY_MODE; a_width, a_height: INTEGER_32)
			-- Create an empty Current of dimension a_width x a_height
			-- conforming to a_display_mode.
		require
			surface_make_video_enabled: Game_library.is_video_enable
		do
			make_for_pixel_format (a_display_mode.pixel_format, a_width, a_height)
		ensure
			surface_make_is_open: has_error or is_open
		end

	make_for_pixel_format (a_pixel_format: GAME_PIXEL_FORMAT_READABLE; a_width, a_height: INTEGER_32)
			-- Create an empty Current of dimension a_width x a_height
			-- conforming to a_pixel_format.
		require
			surface_make_video_enabled: Game_library.is_video_enable
		local
			l_bpp: INTEGER_32
			l_masks: TUPLE [red_mask: NATURAL_32; green_mask: NATURAL_32; blue_mask: NATURAL_32; alpha_mask: NATURAL_32]
		do
			l_bpp := a_pixel_format.bits_per_pixel
			if a_pixel_format.has_error then
				Io.Error.put_string ("An error occured while creating the surface.%N");
				Io.Error.put_string (last_error.to_string_8 + "%N")
				has_error := True
				create image.own_from_pointer (create {POINTER})
			else
				l_masks := a_pixel_format.masks
				if a_pixel_format.has_error then
					Io.Error.put_string ("An error occured while creating the surface.%N");
					Io.Error.put_string (last_error.to_string_8 + "%N")
					has_error := True
					create image.own_from_pointer (create {POINTER})
				else
					make_with_masks (a_width, a_height, l_bpp, l_masks.red_mask, l_masks.green_mask, l_masks.blue_mask, l_masks.alpha_mask)
				end
			end
			is_open := not has_error
		ensure
			surface_make_is_open: has_error or is_open
		end

	make_for_window (a_window: GAME_WINDOW; a_width, a_height: INTEGER_32)
			-- Create an empty Current of dimension a_width x a_height
			-- conforming to a_window.
		require
			surface_make_video_enabled: Game_library.is_video_enable
		do
			make_for_pixel_format (a_window.pixel_format, a_width, a_height)
		ensure
			surface_make_is_open: has_error or is_open
		end

	make_from_image (a_image: GAME_IMAGE)
			-- Create a Current from a_image.
			-- The image source in memory is copied.
			-- Slower than share_from_image and use more memory.
			-- If multiple surface is done with the same a_image,
			-- every modification to surface will affect all.
		require
			surface_make_video_enabled: Game_library.is_video_enable
			surface_make_from_image_source_is_open: a_image.is_open
		local
			l_source: GAME_IMAGE
		do
			has_error := False
			create l_source.make_from_other (a_image)
			if l_source.is_openable then
				l_source.open
				if l_source.is_open then
					share_from_image (l_source)
				else
					image := create {GAME_IMAGE}.own_from_pointer (create {POINTER})
					has_error := True
				end
			else
				image := create {GAME_IMAGE}.own_from_pointer (create {POINTER})
				has_error := True
			end
		ensure
			surface_make_is_open: has_error or is_open
		end

	make_from_other (a_other: GAME_SURFACE)
			-- Create a Current from a_other.
			-- The image source in memory will be copied.
			-- Slower than share_from_other and use more memory.
		require
			surface_make_video_enabled: Game_library.is_video_enable
			surface_make_other_opened: a_other.is_open
		do
			make_from_image (a_other.image)
		ensure
			surface_make_is_open: has_error or is_open
		end

	make_with_masks (a_width, a_height, a_bits_per_pixel: INTEGER_32; a_rmask, a_gmask, a_bmask, a_amask: NATURAL_32)
			-- Initialization for Current.
			-- Create a new empty surface with RGBA mask and flags.
		require
			surface_make_video_enabled: Game_library.is_video_enable
		local
			l_surface_pointer: POINTER
			l_image_source: GAME_IMAGE
		do
			clear_error
			l_surface_pointer := {GAME_SDL_EXTERNAL}.sdl_creatergbsurface (0, a_width, a_height, a_bits_per_pixel, a_rmask, a_gmask, a_bmask, a_amask)
			if l_surface_pointer.is_default_pointer then
				manage_error_pointer (l_surface_pointer, "An error occured while creating the surface.")
				create image.own_from_pointer (create {POINTER})
			else
				create l_image_source.own_from_pointer (l_surface_pointer)
				if l_image_source.is_openable then
					l_image_source.open
					share_from_image (l_image_source)
				else
					put_manual_error ("An error occured while creating the surface.", "Cannot read file.")
					create image.own_from_pointer (create {POINTER})
				end
			end
		ensure
			surface_make_is_open: has_error or is_open
		end

	share_from_image (a_image: GAME_IMAGE)
			-- Create a Current from a_image_source.
			-- The image source in memory is not copied.
			-- If multiple surface is done with the same a_image,
			-- every modification to surface will affect all.
		require
			surface_make_video_enabled: Game_library.is_video_enable
			surface_make_from_image_source_is_open: a_image.is_open
		do
			image := a_image
			is_open := not has_error
		ensure
			surface_make_is_open: has_error or is_open
		end

	share_from_other (a_other: GAME_SURFACE)
			-- Create a Current from a_other.
			-- The image source in memory is not copied.
			-- If multiple surface is done with the same image,
			-- every modification to surface will affect all.
		require
			surface_make_video_enabled: Game_library.is_video_enable
			surface_make_other_opened: a_other.is_open
		do
			share_from_image (a_other.image)
		ensure
			surface_make_is_open: has_error or is_open
		end
	
feature -- Output

	Io: STD_FILES
			-- Handle to standard file setup
			-- (from ANY)
		once
			create Result;
			Result.set_output_default
		ensure -- from ANY
			instance_free: class
			io_not_void: Result /= Void
		end

	out: STRING_8
			-- New string containing terse printable representation
			-- of current object
			-- (from ANY)
		do
			Result := tagged_out
		ensure -- from ANY
			out_not_void: Result /= Void
		end

	print (o: detachable ANY)
			-- Write terse external representation of o
			-- on standard output.
			-- (from ANY)
		local
			s: READABLE_STRING_8
		do
			if attached o then
				s := o.out
				if attached {READABLE_STRING_32} s as s32 then
					Io.put_string_32 (s32)
				elseif attached {READABLE_STRING_8} s as s8 then
					Io.put_string (s8)
				else
					Io.put_string_32 (s.as_string_32)
				end
			end
		ensure -- from ANY
			instance_free: class
		end

	frozen tagged_out: STRING_8
			-- New string containing terse printable representation
			-- of current object
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			tagged_out_not_void: Result /= Void
		end
	
feature -- Platform

	Operating_environment: OPERATING_ENVIRONMENT
			-- Objects available from the operating system
			-- (from ANY)
		once
			create Result
		ensure -- from ANY
			instance_free: class
			operating_environment_not_void: Result /= Void
		end
	
feature {NONE} -- Retrieval

	frozen internal_correct_mismatch
			-- Called from runtime to perform a proper dynamic dispatch on correct_mismatch
			-- from MISMATCH_CORRECTOR.
			-- (from ANY)
		local
			l_msg: STRING_32
			l_exc: EXCEPTIONS
		do
			if attached {MISMATCH_CORRECTOR} Current as l_corrector then
				l_corrector.correct_mismatch
			else
				create l_msg.make_from_string ("Mismatch: ".as_string_32)
				create l_exc;
				l_msg.append (generating_type.name_32);
				l_exc.raise_retrieval_exception (l_msg)
			end
		end
	
invariant
	surface_valid: is_open implies image.is_open

		-- from ANY
	reflexive_equality: standard_is_equal (Current)
	reflexive_conformance: conforms_to (Current)

end -- class GAME_SURFACE

Generated by ISE EiffelStudio