note description: "Gtk implementation for EV_PIXEL_BUFFER_I." legal: "See notice at end of class." status: "See notice at end of class." keywords: drawable, primitives, figures, buffer, bitmap, picture date: "$Date$" revision: "$Revision$" class EV_PIXEL_BUFFER_IMP inherit EV_PIXEL_BUFFER_I DISPOSABLE create make feature -- Initialization make_with_size (a_width, a_height: INTEGER) -- Create with size. do if {GTK}.gtk_maj_ver >= 2 then set_gdkpixbuf ({GDK}.gdk_pixbuf_new ({GDK}.gdk_colorspace_rgb_enum, True, 8, a_width, a_height)) {GDK}.gdk_pixbuf_fill (gdk_pixbuf, 0) else create internal_pixmap.make_with_size (a_width, a_height) end end old_make (an_interface: EV_PIXEL_BUFFER) -- Creation method. do assign_interface (an_interface) make_with_size (1, 1) end make_with_pixmap (a_pixmap: EV_PIXMAP) -- Create with `a_pixmap''s image data. local -- l_pixbuf: POINTER -- l_window: POINTER do if attached {EV_PIXMAP_IMP} a_pixmap.implementation as l_pixmap_imp then -- FIXME JV -- find a way to replace gdk_pixbuf_get_from_drawable -- options gdk_pixbuf_get_from_window or --l_pixbuf := {GDK}.gdk_pixbuf_get_from_drawable (default_pointer, l_pixmap_imp.drawable, default_pointer, 0, 0, 0, 0, l_pixmap_imp.width, l_pixmap_imp.height) --l_window := {GDK}.gdk_screen_get_root_window ({GDK}.gdk_screen_get_default) --l_pixbuf := {GDK}.gdk_pixbuf_new (0, False, 8, l_pixmap_imp.width, l_pixmap_imp.height) --l_pixbuf := {GDK}.gdk_pixbuf_get_from_window (l_window, 0, 0, l_pixmap_imp.width, l_pixmap_imp.height) set_gdkpixbuf (l_pixmap_imp.pixbuf) end end make -- Initialize `Current'. do -- Creating managed pointer used for inspecting RGBA data. create reusable_managed_pointer.share_from_pointer (default_pointer, 0) set_is_initialized (True) end feature -- Command set_with_named_path (a_file_name: PATH) -- Load pixel data file `a_file_name'. local l_cs: EV_GTK_C_STRING ge: POINTER filepixbuf: POINTER e: EV_GLIB_ERROR m: like {EV_GLIB_ERROR}.message do if {GTK}.gtk_maj_ver >= 2 then create l_cs.make_from_path (a_file_name) filepixbuf := {GDK}.gdk_pixbuf_new_from_file (l_cs.item, $ge) if not ge.is_default_pointer then -- The image cannot be loaded, raise an exception. create e.make_from_pointer (ge) m := e.message e.free {EXCEPTIONS}.raise (m) else set_gdkpixbuf (filepixbuf) end elseif attached internal_pixmap as l_internal_pixmap then l_internal_pixmap.set_with_named_path (a_file_name) end end set_with_pointer (a_pointer: POINTER; a_size: INTEGER) -- Load pixel data from `a_pointer' -- `a_size' size in bytes local l_pixel_buf: POINTER ge: POINTER l_stream: EV_G_INPUT_STREAM e: EV_GLIB_ERROR m: like {EV_GLIB_ERROR}.message do create l_stream.new_from_data (a_pointer, a_size) l_pixel_buf := {GDK}.gdk_pixbuf_new_from_stream (l_stream.item, default_pointer, $ge) if not ge.is_default_pointer then -- The image cannot be loaded, raise an exception. create e.make_from_pointer (ge) m := e.message e.free {EXCEPTIONS}.raise (m) else set_gdkpixbuf (l_pixel_buf) end end save_to_named_path (a_file_name: PATH) -- Save pixel data to file `a_file_name'. local l_cs, l_file_type: EV_GTK_C_STRING ge: POINTER l_writeable_formats: ARRAYED_LIST [STRING_32] l_extension: READABLE_STRING_32 l_format: detachable READABLE_STRING_32 l_dep: EV_GTK_ENVIRONMENT i: INTEGER e: EV_GLIB_ERROR m: like {EV_GLIB_ERROR}.message do create l_dep l_writeable_formats := l_dep.writeable_pixbuf_formats if a_file_name.has_extension ("jpeg") then l_extension := {STRING_32} "jpg" elseif attached a_file_name.extension as l_ext then l_extension := l_ext else l_extension := {STRING_32} "png" end from i := 1 until i > l_writeable_formats.count loop if l_writeable_formats [i].is_case_insensitive_equal (l_extension) then l_format := l_extension end i := i + 1 end if l_format /= Void then if l_format.is_case_insensitive_equal ({STRING_32} "jpg") then l_format := {STRING_32} "jpeg" end if {GTK}.gtk_maj_ver >= 2 then create l_cs.make_from_path (a_file_name) create l_file_type.set_with_eiffel_string (l_format) {GDK}.gdk_pixbuf_save (gdk_pixbuf, l_cs.item, l_file_type.item, $ge) if not ge.is_default_pointer then -- The image cannot be saved, raise an exception. create e.make_from_pointer (ge) m := e.message e.free {EXCEPTIONS}.raise (m) end else if l_format.is_case_insensitive_equal ({STRING_32} "jpg") and then attached internal_pixmap as l_internal_pixmap then l_internal_pixmap.save_to_named_path (create {EV_PNG_FORMAT}, a_file_name) else (create {EXCEPTIONS}).raise ("Could not save image file.") end end else (create {EXCEPTIONS}).raise ("Could not save image file.") end end save_to_pointer: detachable MANAGED_POINTER -- <Precursor> local l_result: INTEGER l_file_type: EV_GTK_C_STRING l_buffer_size: INTEGER ge: POINTER l_pointer: POINTER e: EV_GLIB_ERROR m: like {EV_GLIB_ERROR}.message do -- Same as Windows {EV_PIXEL_BUFFER_IMP} implementation, using PNG format as default create l_file_type.set_with_eiffel_string ("png") l_result := {GDK}.gdk_pixbuf_save_to_buffer (gdk_pixbuf, $l_pointer, $l_buffer_size, l_file_type.item, $ge) if not ge.is_default_pointer then -- The image cannot be saved, raise an exception. create e.make_from_pointer (ge) m := e.message e.free elseif l_result /= 0 then create Result.own_from_pointer (l_pointer, l_buffer_size) end end sub_pixmap (a_rect: EV_RECTANGLE): EV_PIXMAP -- Draw Current to `a_drawable' local l_pixmap_imp: detachable EV_PIXMAP_IMP l_pixbuf: POINTER l_internal_pixmap: like internal_pixmap do if {GTK}.gtk_maj_ver >= 2 then create Result l_pixmap_imp ?= Result.implementation check l_pixmap_imp /= Void then end l_pixbuf := {GDK}.gdk_pixbuf_new_subpixbuf (gdk_pixbuf, a_rect.x, a_rect.y, a_rect.width, a_rect.height) l_pixmap_imp.set_pixmap_from_pixbuf (l_pixbuf) {GDK}.g_object_unref (l_pixbuf) else l_internal_pixmap := internal_pixmap check l_internal_pixmap /= Void then end Result := l_internal_pixmap.sub_pixmap (a_rect) end end stretched (a_width, a_height: INTEGER): EV_PIXEL_BUFFER -- <Precursor> local l_imp: detachable EV_PIXEL_BUFFER_IMP l_pixbuf: POINTER l_scale_type: INTEGER_32 do create Result l_imp ?= Result.implementation check l_imp /= Void then end -- The code below was taken from `{EV_PIXMAP_IMP}.stretch'. if a_width <= 16 and then a_height <= 16 then l_scale_type := {GDK}.gdk_interp_nearest else l_scale_type := {GDK}.gdk_interp_bilinear end l_pixbuf := {GDK}.gdk_pixbuf_scale_simple (gdk_pixbuf, a_width, a_height, l_scale_type) -- We need to pass in a copy of the pixbuf as subpixbuf shares the pixels. if not {GDK}.gdk_is_pixbuf (l_pixbuf) then debug ("gtk_log") print (generator + ".stretched gdk_is_pixbuf is False" ) end end l_imp.set_gdkpixbuf ({GDK}.gdk_pixbuf_copy (l_pixbuf)) end sub_pixel_buffer (a_rect: EV_RECTANGLE): EV_PIXEL_BUFFER -- Create a new sub pixel buffer object. local l_subpixbuf, l_pixbuf: POINTER l_rect: EV_RECTANGLE do l_rect := area.intersection (a_rect) l_subpixbuf := {GDK}.gdk_pixbuf_new_subpixbuf (gdk_pixbuf, l_rect.x, l_rect.y, l_rect.width, l_rect.height) if area.contains (a_rect) then -- We need to pass in a copy of the pixbuf as subpixbuf shares the pixels. if not {GDK}.gdk_is_pixbuf (l_subpixbuf) then print (generator + ".sub_pixel_buffer gdk_is_pixbuf is False" ) end create Result Result.actual_implementation.set_gdkpixbuf ({GDK}.gdk_pixbuf_copy (l_subpixbuf)) else -- We create a new pixbuf of the right size, copy the subpart of Current in it via -- scaling. We could have used `gdk_pixbuf_copy_area' but it is already a wrapper -- to `gdk_pixbuf_scale'. create Result.make_with_size (a_rect.width, a_rect.height) if l_rect.has_area then l_pixbuf := Result.actual_implementation.gdk_pixbuf {GDK}.gdk_pixbuf_scale (l_subpixbuf, l_pixbuf, (-a_rect.x).max (0), (-a_rect.y).max (0), l_rect.width, l_rect.height, (-a_rect.x).max (0), (-a_rect.x).max (0), 1.0, 1.0, {GDK}.gdk_interp_nearest) end end {GDK}.g_object_unref (l_subpixbuf) end get_pixel (a_x, a_y: NATURAL_32): NATURAL_32 -- Get RGBA value at `a_y', `a_y'. local byte_pos: INTEGER_32 l_managed_pointer: detachable MANAGED_POINTER l_n_channels: NATURAL l_row_stride: NATURAL_32 l_bytes_per_sample: NATURAL do l_n_channels := {GDK}.gdk_pixbuf_get_n_channels (gdk_pixbuf) l_bytes_per_sample := {GDK}.gdk_pixbuf_get_bits_per_sample (gdk_pixbuf) // 8 l_row_stride := {GDK}.gdk_pixbuf_get_rowstride (gdk_pixbuf) byte_pos := (a_y * l_row_stride + (a_x * l_n_channels * l_bytes_per_sample)).as_integer_32 l_managed_pointer := reusable_managed_pointer -- Share with a size big enough to read at `byte_pos'. l_managed_pointer.set_from_pointer ({GDK}.gdk_pixbuf_get_pixels (gdk_pixbuf), byte_pos + {PLATFORM}.natural_32_bytes) -- Data is stored at a byte level of R G B A which is big endian, so we need to read big endian. Result := l_managed_pointer.read_natural_32_be (byte_pos) end set_pixel (a_x, a_y, rgba: NATURAL_32) -- Set RGBA value at `a_x', `a_y' to `rgba'. local byte_pos: INTEGER_32 l_managed_pointer: detachable MANAGED_POINTER l_n_channels: NATURAL l_row_stride: NATURAL_32 l_bytes_per_sample: NATURAL do l_n_channels := {GDK}.gdk_pixbuf_get_n_channels (gdk_pixbuf) l_bytes_per_sample := {GDK}.gdk_pixbuf_get_bits_per_sample (gdk_pixbuf) // 8 l_row_stride := {GDK}.gdk_pixbuf_get_rowstride (gdk_pixbuf) byte_pos := (a_y * l_row_stride + (a_x * l_n_channels * l_bytes_per_sample)).as_integer_32 l_managed_pointer := reusable_managed_pointer l_managed_pointer.set_from_pointer ({GDK}.gdk_pixbuf_get_pixels (gdk_pixbuf), byte_pos + (l_bytes_per_sample * l_n_channels).to_integer_32) -- Data is stored at a byte level of R G B A which is big endian, so we need to set big endian. l_managed_pointer.put_natural_32_be (rgba, byte_pos) end draw_text (a_text: READABLE_STRING_GENERAL; a_font: EV_FONT; a_point: EV_COORDINATE) -- Draw `a_text' using `a_font' at `a_point'. local l_x, l_y, l_width, l_height: INTEGER l_string_size: TUPLE [width: INTEGER; height: INTEGER; left_offset: INTEGER; right_offset: INTEGER] l_pixmap: EV_PIXMAP l_pixmap_imp: detachable EV_PIXMAP_IMP l_pixbuf_ptr, l_pixbuf_ptr2: POINTER l_pixbuf: EV_PIXEL_BUFFER l_pixbuf_imp: detachable EV_PIXEL_BUFFER_IMP l_color: EV_COLOR l_grey_value, l_composite_alpha: NATURAL_8 do l_x := a_point.x l_y := a_point.y l_string_size := a_font.string_size (a_text) l_width := (l_x + l_string_size.width).min (width) - l_x l_height := (l_y + l_string_size.height).min (height) - l_y create l_pixmap.make_with_size (l_width, l_height) l_pixmap.set_font (a_font) l_grey_value := 75 l_composite_alpha := 200 create l_color.make_with_8_bit_rgb (l_grey_value, l_grey_value, l_grey_value) -- Create a pixmap with a grey background so that anti-aliasing is not so harsh. l_pixmap.set_background_color (l_color) l_pixmap.clear l_pixmap.draw_text_top_left (0, 0, a_text) l_pixmap_imp ?= l_pixmap.implementation check l_pixmap_imp /= Void then end create l_pixbuf l_pixbuf_imp ?= l_pixbuf.implementation check l_pixbuf_imp /= Void then end -- Retrieve pixbuf from drawable and set the previous background color 'l_grey_value' to transparent alpha. -- TODO check how to replace gdk_pixbuf_get_from_drawable -- l_pixbuf_ptr := {GDK}.gdk_pixbuf_get_from_drawable (default_pointer, l_pixmap_imp.drawable, default_pointer, 0, 0, 0, 0, l_width, l_height) l_pixbuf_ptr := {GDK}.gdk_pixbuf_get_from_window ({GDK}.gdk_screen_get_root_window ({GDK}.gdk_screen_get_default), 0, 0, l_width, l_height) l_pixbuf_ptr2 := {GDK}.gdk_pixbuf_add_alpha (l_pixbuf_ptr, True, l_grey_value, l_grey_value, l_grey_value) -- Clean up {GDK}.g_object_unref (l_pixbuf_ptr) l_pixbuf_ptr := default_pointer -- Composite pixbuf with alpha on to `Current' {GDK}.gdk_pixbuf_composite (l_pixbuf_ptr2, gdk_pixbuf, l_x, l_y, l_width, l_height, 0, 0, 1, 1, 0, l_composite_alpha) -- Clean up {GDK}.g_object_unref (l_pixbuf_ptr2) l_pixbuf_ptr2 := default_pointer end draw_pixel_buffer_with_x_y (a_x, a_y: INTEGER; a_pixel_buffer: EV_PIXEL_BUFFER) -- Draw `a_pixel_buffer' at `a_x', `a_y'. local l_pixel_buffer_imp: detachable EV_PIXEL_BUFFER_IMP l_dest_width, l_dest_height: INTEGER l_x, l_y: INTEGER do l_pixel_buffer_imp ?= a_pixel_buffer.implementation check l_pixel_buffer_imp /= Void then end -- We must make sure dest rectangle not larger than source rectangle -- http://library.gnome.org/devel/gdk-pixbuf/stable/gdk-pixbuf-scaling.html#gdk-pixbuf-composite -- They say: -- When the destination rectangle contains parts not in the source image, the data at the edges -- of the source image is replicated to infinity. -- We modify `l_dest_width' and `l_dest_height' to avoid it, so it will consistent with Windows implementation l_x := a_x l_y := a_y l_dest_width := l_pixel_buffer_imp.width l_dest_height := l_pixel_buffer_imp.height if l_x < 0 then l_x := 0 l_dest_width := l_dest_width + a_x if l_dest_width < 0 then l_dest_width := 0 end end if l_y < 0 then l_dest_height := l_dest_height + a_y l_y := 0 if l_dest_height < 0 then l_dest_height := 0 end end -- We also have to make sure, dest rectangle not larger than source image, otherwise the API will not draw the image and this is -- not consitent with Windows implementation if l_x + l_dest_width > width then l_dest_width := width - l_x if l_x > width then l_x := width end if l_dest_width < 0 then l_dest_width := 0 end end if l_y + l_dest_height > height then l_dest_height := height - l_y if l_y > height then l_y := height end if l_dest_height < 0 then l_dest_height := 0 end end {GDK}.gdk_pixbuf_composite (l_pixel_buffer_imp.gdk_pixbuf, gdk_pixbuf, l_x, l_y, l_dest_width, l_dest_height, a_x, a_y, 1, 1, {GDK}.gdk_interp_hyper, 255) end feature -- Query width: INTEGER -- Width of buffer in pixels. do if {GTK}.gtk_maj_ver > 1 then Result := {GDK}.gdk_pixbuf_get_width (gdk_pixbuf) elseif attached internal_pixmap as l_internal_pixmap then Result := l_internal_pixmap.width end end height: INTEGER -- Height of buffer in pixels. do if {GTK}.gtk_maj_ver > 1 then Result := {GDK}.gdk_pixbuf_get_height (gdk_pixbuf) elseif attached internal_pixmap as l_internal_pixmap then Result := l_internal_pixmap.height end end data_ptr: POINTER --Memory acess point to image data. -- This feature is NOT platform independent. do Result := {GDK}.gdk_pixbuf_get_pixels (gdk_pixbuf) end feature {EV_STOCK_PIXMAPS_IMP} -- Implementation set_from_stock (a_stock_id: EV_GTK_C_STRING) -- Pixmap symbolizing a piece of information. local a_cs: EV_GTK_C_STRING l_string: STRING_32 do a_cs := a_stock_id set_from_stock_id (a_cs.item) if gdk_pixbuf.is_default_pointer then l_string := a_cs.string l_string.to_lower if l_string.same_string ({STRING_32} "information") or else l_string.same_string ({STRING_32} "dialog-information") then set_gdkpixbuf ({GDK}.gdk_pixbuf_new_from_xpm_data ({EV_STOCK_PIXMAPS_IMP}.information_pixmap_xpm)) elseif l_string.same_string ({STRING_32} "dialog-error") then set_gdkpixbuf ({GDK}.gdk_pixbuf_new_from_xpm_data ({EV_STOCK_PIXMAPS_IMP}.error_pixmap_xpm)) elseif l_string.same_string ({STRING_32} "dialog-question") then set_gdkpixbuf ({GDK}.gdk_pixbuf_new_from_xpm_data ({EV_STOCK_PIXMAPS_IMP}.question_pixmap_xpm)) elseif l_string.same_string ({STRING_32} "dialog-warning") then set_gdkpixbuf ({GDK}.gdk_pixbuf_new_from_xpm_data ({EV_STOCK_PIXMAPS_IMP}.warning_pixmap_xpm)) else -- If no stock pixbuf is found, default to empty 48x48 pixbuf. make_with_size (48, 48) end end end set_from_stock_id (a_stock_id: POINTER) -- Pixmap symbolizing a piece of information require a_stock_id_not_null: a_stock_id /= default_pointer local stock_pixbuf: POINTER l_label: POINTER l_theme: POINTER ge: POINTER l_screen: POINTER e: EV_GLIB_ERROR do l_label := {GTK}.gtk_label_new (default_pointer) -- Floating ref -- Using GDK instead of GTK2 l_label := {GDK}.g_object_ref_sink (l_label) -- adopt floating ref if {GTK2}.gtk_widget_has_screen (l_label) then l_screen:= {GTK2}.gtk_widget_get_screen (l_label) l_theme := {GTK2}.gtk_icon_theme_get_for_screen (l_screen) else l_theme := {GTK2}.gtk_icon_theme_get_default end stock_pixbuf := {GTK2}.gtk_icon_theme_load_icon (l_theme, a_stock_id, 48, 0, $ge) if not ge.is_default_pointer then create e.make_from_pointer (ge) debug ("gtk_log") print ({STRING_32} "Could not load icon: " + e.message) end e.free end {GDK}.g_object_unref (l_label) l_label := default_pointer if stock_pixbuf /= default_pointer then -- If a stock pixmap can be found then set it, else do nothing. if not {GDK}.gdk_is_pixbuf (stock_pixbuf) then debug ("gtk_log") print (generator + ".set_from_stock_id gdk_is_pixbuf is False" ) end end set_gdkpixbuf ({GDK}.gdk_pixbuf_copy (stock_pixbuf)) {GDK}.g_object_unref (stock_pixbuf) end end feature {EV_PIXEL_BUFFER_IMP, EV_POINTER_STYLE_IMP, EV_DRAWABLE_IMP} -- Implementation reusable_managed_pointer: MANAGED_POINTER -- Managed pointer used for inspecting current. internal_pixmap: detachable EV_PIXMAP -- Pixmap used for fallback implementation on gtk 1.2 set_gdkpixbuf (a_pixbuf: POINTER) -- Set `gdk_pixbuf' to `a_pixbuf'. local l_pixbuf: POINTER do if gdk_pixbuf /= default_pointer then -- Unref previous gdkpixbuf {GDK}.g_object_unref (gdk_pixbuf) end if a_pixbuf /= default_pointer then l_pixbuf := {GDK}.g_object_ref (a_pixbuf) -- incr ref (GdkPixbuf is not a GInitiallyUnowned) if not {GDK}.gdk_pixbuf_get_has_alpha (a_pixbuf) then -- Make sure that the pixel data is internally stored as R G B A gdk_pixbuf := {GDK}.gdk_pixbuf_add_alpha (l_pixbuf, False, 0, 0, 0) {GDK}.g_object_unref (a_pixbuf) -- gdk_pixbuf_add_alpha is creating a newly pixbuf, so unref previous one. gdk_pixbuf := {GDK}.g_object_ref (gdk_pixbuf) -- incr ref for this newly pixbuf else gdk_pixbuf := a_pixbuf end else gdk_pixbuf := default_pointer end end set_internal_pixmap (a_pixmap: like internal_pixmap) -- Set `internal_pixmap' to `a_pixmap'. require a_pixmap_not_void: a_pixmap /= Void do internal_pixmap := a_pixmap end gdk_pixbuf: POINTER -- Pointer to the GdkPixbuf structure. destroy -- Destroy `Current'. do set_is_in_destroy (True) set_gdkpixbuf (default_pointer) set_is_destroyed (True) end feature {NONE} -- Dispose dispose -- Dispose current. do set_gdkpixbuf (default_pointer) -- indirectly call g_object_unref on gdkpixbuf is any. end feature -- Obsolete draw_pixel_buffer (a_pixel_buffer: EV_PIXEL_BUFFER; a_rect: EV_RECTANGLE) -- Draw `a_pixel_buffer' to current at `a_rect'. obsolete "Use draw_pixel_buffer_with_x_y instead [2017-05-31]" local l_pixel_buffer_imp: detachable EV_PIXEL_BUFFER_IMP do l_pixel_buffer_imp ?= a_pixel_buffer.implementation check l_pixel_buffer_imp /= Void then end {GDK}.gdk_pixbuf_copy_area (l_pixel_buffer_imp.gdk_pixbuf, 0, 0, a_rect.width, a_rect.height, gdk_pixbuf, a_rect.x, a_rect.y) end note copyright: "Copyright (c) 1984-2023, 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