indexing description: "[ GUI_PROPORTIONAL_FONT draws text onto an ESDL surface. text can be written on one line or on multiple lines within a bounding box including automated word wrapping. left, centered and right alignment is possible. bitmap fonts with charcter size information are used for drawing. alpha channels are fully supported. This class is part of the XAE Extended Adventure Engine Project. ]" author: "Ralph Wiedemeier, ralphw@student.ethz.ch" date: "$Date$" revision: "$Revision$" class GUI_PROPORTIONAL_FONT inherit EM_SHARED_SUBSYSTEMS export {NONE} all {ANY} video_subsystem end EM_SHARED_BITMAP_FACTORY export {NONE} all undefine default_create end EM_GL_CONSTANTS export {NONE} all undefine default_create end -- GL_FUNCTIONS -- export {NONE} all -- undefine default_create end EMGL_SETTINGS export {NONE} all undefine default_create end EMGL_BASIC_RENDERER export {NONE} all undefine default_create end creation make_with_font_resource feature -- Initialization make_with_font_resource (a_filepath: STRING) is -- Create an EM_BITMAPFONT out of `a_drawable' that contains the font data. local font_bitmap_path, font_info_path: STRING file: PLAIN_TEXT_FILE line: STRING i, sep: INTEGER do font_bitmap_path := a_filepath + ".tga" font_info_path := a_filepath + ".ini" bitmap_factory.create_bitmap_from_image (font_bitmap_path) font_map := bitmap_factory.last_bitmap create file.make_open_read (font_info_path) file.read_line create character_width_table.make (0, 255) from i := 0 until i > 255 loop file.read_line line := file.last_string sep := line.index_of ('=', 1) character_width_table.put ((line.substring (sep + 1, line.count)).to_integer, i) i := i + 1 end file.close tile_width := font_map.width // 16 tile_height := font_map.height // 16 gl_scaling := 1 line_spacing := 1 set_max_characters (10000) create text_color.make_white ensure font_map_loaded_successfully: font_map /= Void end feature -- Status character_width (a_character: CHARACTER): DOUBLE is -- Width of 'a_character' including scaling. do Result := character_width_table.item (a_character.code) * gl_scaling end line_height: DOUBLE is -- Height of 'a_character'. All characters have same height. do Result := tile_height * gl_scaling end last_text_width: DOUBLE is -- Total width of last text drawn do result := text_width (last_text) end last_text_height: DOUBLE -- Total height of last text drawn text_width (a_string: STRING): DOUBLE is -- Width of 'a_string' including spacing offset and scaling. require a_string_not_void: a_string /= Void a_string_not_empty: a_string.count > 0 local i, n: INTEGER do from i := 1 n := a_string.count Result := 0 until i > n loop Result := Result + character_width (a_string.item (i)) i := i + 1 end Result := Result + (n - 1) * spacing_offset end text_height (a_string: STRING; a_width: INTEGER): DOUBLE is -- Height of multiline text 'a_string' drawn within 'a_width' -- including spacing offset and scaling. do aux_rect.set_width (a_width) aux_rect.set_height (10000) draw_text_multiline (a_string, aux_rect, Void) Result := last_text_height end set_gl_scaling (a_value: DOUBLE) is -- Set scaling factor. -- Scaling on fonts only takes effect in gl mode. require factor_valid: a_value > 0.2 and then a_value <= 2 do gl_scaling := a_value end gl_scaling: DOUBLE set_spacing_offset (an_offset: DOUBLE) is -- set spacing offset in pixels do spacing_offset := an_offset end spacing_offset: DOUBLE set_line_spacing (a_spacing: DOUBLE) is -- set line spacing do line_spacing := a_spacing end line_spacing: DOUBLE set_max_characters (a_number: INTEGER) is -- Set maximum number of displayed characters do if a_number >= 0 then max_characters := a_number else max_characters := 0 end end max_characters: INTEGER set_alignment_left is -- left align text in framed output do alignment := align_left end set_alignment_centered is -- center text in framed output do alignment := align_center end set_alignment_right is -- right align text in framed output do alignment := align_right end set_text_color (a_color: EM_COLOR) is -- set text color do text_color := a_color end text_color: EM_COLOR feature -- Commands draw_text_multiline (a_string: STRING; a_box: EM_RECT; a_surface: EM_VIDEO_SURFACE) is -- Draws 'a_string' to 'a_surface' into the specified box -- using automatic word wrapping -- draw multiple lines of text local x: INTEGER y: DOUBLE c, n: INTEGER paragraphs: LIST[STRING] words: LIST[STRING] line: STRING exit: BOOLEAN do paragraphs := a_string.split ('%N') y := a_box.y n := max_characters if alignment = align_center then x := a_box.x + a_box.width // 2 elseif alignment = align_right then x := a_box.x + a_box.width else x := a_box.x end from paragraphs.start until paragraphs.after or else y + line_height > a_box.y + a_box.height or else c >= n loop words := paragraphs.item.split (' ') if words.count > 0 then from words.start exit := False create line.make_empty until exit loop if words.after or else (text_width (line + words.item)).truncated_to_integer > a_box.width then max_characters := n - c if a_surface /= Void then draw_text_line (line, a_surface, x, y.truncated_to_integer) end y := y + line_height c := c + line.count create line.make_empty end if words.after or else y + line_height > a_box.y + a_box.height then exit := True else line := line + words.item + " " words.forth end end y := y + line_height * (line_spacing - 1) paragraphs.forth end end last_text_height := y - a_box.y max_characters := n end draw_text_line (a_string: STRING; a_surface: EM_VIDEO_SURFACE; x, y: DOUBLE) is -- Draws 'a_string' to 'a_surface' at the specified position do last_text := a_string -- CHANGES -- if a_surface = a_surface.video_subsystem.video_surface then -- CHANGES -> io --io.putstring (a_string + " x: " + x.out + " y: " + y.out) --io.put_new_line --draw_sdl_string (a_string, a_surface, x, y) if a_surface = video_subsystem.video_surface then -- io.put_string ("system_test") draw_gl_string (a_string, x, y) else draw_sdl_string (a_string, a_surface, x, y) end end draw_character (a_character: CHARACTER; a_surface: EM_VIDEO_SURFACE; x, y: DOUBLE) is -- Draws 'a_character' to 'a_surface' at the specified position do draw_text_line (a_character.out, a_surface, x, y) end feature {NONE} -- Text drawing routines draw_sdl_string (a_string: STRING; a_surface: EM_VIDEO_SURFACE; x, y: DOUBLE) is -- Draw 'a_string' onto 'a_surface' using standard sdl blitting local i, char_width, dst_x: INTEGER string_length: INTEGER char: CHARACTER do string_length := a_string.count if alignment = align_center then dst_x := (x - text_width (a_string) / 2).truncated_to_integer elseif alignment = align_right then dst_x := (x - text_width (a_string)).truncated_to_integer else dst_x := x.truncated_to_integer end from i := 1 until i > string_length or else i > max_characters loop char := a_string.item (i) char_width := character_width (char).truncated_to_integer src_rect.set_x ((char.code \\ 16) * tile_width + tile_width // 2 - char_width // 2) src_rect.set_width (char_width) src_rect.set_y ((char.code // 16) * tile_height) src_rect.set_height (tile_height) -- src_rect.set_x ((char.code \\ 16) * tile_width + tile_width // 2 - char_width // 2) -- src_rect.set_width (char_width) -- src_rect.set_y ((char.code // 16) * tile_height) -- src_rect.set_height (tile_height) --src_rect2.set_x_y ((char.code \\ 16) * tile_width + tile_width // 2 - char_width // 2, (char.code // 16) * tile_height) --src_rect2.set_size (char_width, tile_height) font_map.set_x (dst_x) font_map.set_y (y.truncated_to_integer) -- CHANGES -- font_map.draw_part (src_rect, a_surface) font_map.draw_part(src_rect, a_surface) --font_map.draw_surface_stretched(a_surface, src_rect2) i := i + 1 dst_x := dst_x + char_width + spacing_offset.truncated_to_integer end end draw_gl_string (a_string: STRING; x: DOUBLE; y: DOUBLE) is -- Draw 'a_string' onto video surface using openGL texture mapping local i: INTEGER src_x, src_y, src_center, src_w, src_h: DOUBLE dst_x, dst_y, dst_w, dst_h: DOUBLE char_width_scaled: DOUBLE char_width: INTEGER string_length: INTEGER char: CHARACTER do if not gl_texture_valid then -- CHANGES -- font_map_texture_id := font_map.gl_texture -- font_map_texture_id := font_map.texture.id create font_map_texture.make_mipmap_from_surface ( font_map ) --font_map.texture.do_not_free_texture -- font_map.texture.save gl_texture_valid := True end string_length := a_string.count if alignment = align_center then dst_x := x - text_width (a_string) / 2 elseif alignment = align_right then dst_x := x - text_width (a_string) else dst_x := x end dst_y := y dst_h := line_height emgl_push_attrib ( EM_gl_enable_bit | EM_gl_color_buffer_bit) emgl_enable (EM_gl_texture_2d) -- gl_tex_envi (EM_gl_texture_env, EM_gl_texture_env_mode, EM_gl_modulate) emgl_tex_envi (EM_gl_texture_env_mode, EM_gl_modulate) -- gl_tex_parameteri (EM_gl_texture_2d, EM_gl_texture_min_filter, EM_gl_linear) -- gl_tex_parameteri (EM_gl_texture_2d, EM_gl_texture_mag_filter, EM_gl_linear) -- gl_bind_texture (EM_gl_texture_2d, font_map_texture_id) font_map_texture.set_texture_min_filter (em_gl_linear) font_map_texture.set_texture_mag_filter (em_gl_linear) font_map_texture.bind emgl_begin (EM_gl_quads) emgl_color3f (text_color.red / 255, text_color.green / 255, text_color.blue / 255) from i := 1 until i > string_length or else i > max_characters loop char := a_string.item (i) char_width_scaled := character_width (char) char_width := character_width_table.item (char.code) src_center := ((char.code \\ 16) + 0.5) / 16 src_x := src_center - char_width / font_map.width * 0.5 src_w := char_width / font_map.width src_y := (char.code // 16) / 16 src_h := 1 / 16 dst_w := char_width_scaled * 1.005 emgl_tex_coord2d (src_x, src_y) emgl_vertex2d (dst_x, dst_y) emgl_tex_coord2d (src_x + src_w, src_y) emgl_vertex2d (dst_x + dst_w, dst_y) emgl_tex_coord2d (src_x + src_w, src_y + src_h) emgl_vertex2d (dst_x + dst_w, dst_y + dst_h) emgl_tex_coord2d (src_x, src_y + src_h) emgl_vertex2d (dst_x, dst_y + dst_h) i := i + 1 dst_x := dst_x + dst_w + spacing_offset end emgl_end font_map_texture.unbind emgl_pop_attrib end gl_mode_valid (a_surface: EM_VIDEO_SURFACE): BOOLEAN is -- Check if we are in openGL 2d mode and if drawing target -- is the main video surface. If so, we can use the openGL -- texture mapping mode. local screen: EM_VIDEO_SURFACE do screen := video_subsystem.video_surface -- CHANGES -- Result := screen.gl_2d_mode and then a_surface = screen Result := screen.exists and then a_surface = screen end feature {NONE} -- Implementation font_map: EM_BITMAP -- Bitmap containing the font data -- font_map_texture_id: INTEGER font_map_texture: EM3D_TEXTURE_2D[ EMGL_FORMAT_RGBA ] -- ID of openGL texture generated from font bitmap gl_texture_valid: BOOLEAN -- Valid openGL texture of the font bitmap created? character_width_table: ARRAY[INTEGER] -- Array containing width of each character tile_width, tile_height: INTEGER -- Size of one character tile in the font map -- CHANGES -- src_rect: EM_RECT is src_rect: EM_BLITTING_RECTANGLE is -- Rectangle specifing part of font map to be drawn once create result.make (0, 0, 0, 0) end src_rect2: EM_RECTANGLE is -- Rectangle specifing part of font map to be drawn once create result.make_from_coordinates (0.0, 0.0, 0.0, 0.0) end aux_rect: EM_RECT is -- Auxiliary rectangle once create result.make (0, 0, 0, 0) end last_text: STRING -- Least recent drawn text alignment: INTEGER -- information about how framed text should be aligned align_left: INTEGER is 0 align_center: INTEGER is 1 align_right: INTEGER is 2 -- alignment options end -- class GUI_PROPORTIONAL_FONT