Module: Termpix::Protocols::Kitty
- Defined in:
- lib/termpix/protocols.rb
Overview
Kitty Graphics Protocol Uses Unicode placeholder mode (U=1) for curses/TUI compatibility Images are tied to placeholder characters that curses manages as text
Class Method Summary collapse
- .clear ⇒ Object
- .display(image_path, x:, y:, max_width:, max_height:) ⇒ Object
- .get_cell_size ⇒ Object
- .get_dimensions(image_path) ⇒ Object
- .scale_dimensions(w, h, max_w, max_h) ⇒ Object
Class Method Details
.clear ⇒ Object
70 71 72 73 74 75 76 77 |
# File 'lib/termpix/protocols.rb', line 70 def self.clear @active_image_ids.each do |id| print "\e_Ga=d,d=i,i=#{id}\e\\" end $stdout.flush unless @active_image_ids.empty? @active_image_ids.clear true end |
.display(image_path, x:, y:, max_width:, max_height:) ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/termpix/protocols.rb', line 13 def self.display(image_path, x:, y:, max_width:, max_height:) return false unless File.exist?(image_path) # Get terminal cell size in pixels cell_width, cell_height = get_cell_size return false unless cell_width && cell_height # Calculate pixel dimensions for the image area pixel_width = max_width * cell_width pixel_height = max_height * cell_height # Check if we have this image cached (include mtime to detect overwrites) mtime = File.mtime(image_path).to_i rescue 0 cache_key = "#{image_path}:#{pixel_width}x#{pixel_height}:#{mtime}" image_id = @image_cache[cache_key] unless image_id # Generate new image ID (1-4294967295) image_id = (Time.now.to_f * 1000).to_i % 4294967295 image_id = 1 if image_id == 0 end unless @image_cache[cache_key] # Transmit the image with scaling escaped = Shellwords.escape(image_path) # Create scaled PNG data using ImageMagick png_data = `convert #{escaped}[0] -auto-orient -resize #{pixel_width}x#{pixel_height}\\> PNG:- 2>/dev/null` return false if png_data.empty? # Encode as base64 and chunk it (max 4096 bytes per chunk) encoded = Base64.strict_encode64(png_data) chunks = encoded.scan(/.{1,4096}/) # Transmit image in chunks chunks.each_with_index do |chunk, idx| more = idx < chunks.length - 1 ? 1 : 0 if idx == 0 print "\e_Ga=t,f=100,i=#{image_id},m=#{more};#{chunk}\e\\" else print "\e_Gm=#{more};#{chunk}\e\\" end end $stdout.flush @image_cache[cache_key] = image_id end # Position cursor and place the image (keep existing images) print "\e[#{y};#{x}H" print "\e_Ga=p,i=#{image_id},C=1\e\\" $stdout.flush @active_image_ids << image_id unless @active_image_ids.include?(image_id) true end |
.get_cell_size ⇒ Object
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/termpix/protocols.rb', line 79 def self.get_cell_size # Query terminal for cell size using XTWINOPS # Or estimate from terminal size begin require 'io/console' rows, cols = IO.console.winsize # Get pixel size if available if IO.console.respond_to?(:winsize_pixels) prows, pcols = IO.console.winsize_pixels rescue nil if prows && pcols && prows > 0 && pcols > 0 return [pcols / cols, prows / rows] end end # Fall back to common defaults (10x20 pixels per cell) [10, 20] rescue LoadError, IOError, StandardError [10, 20] end end |
.get_dimensions(image_path) ⇒ Object
101 102 103 104 105 106 |
# File 'lib/termpix/protocols.rb', line 101 def self.get_dimensions(image_path) escaped = Shellwords.escape(image_path) dimensions = `identify -format "%wx%h" #{escaped}[0] 2>/dev/null`.strip return nil if dimensions.empty? || !dimensions.match?(/\A\d+x\d+\z/) dimensions.split('x').map(&:to_i) end |
.scale_dimensions(w, h, max_w, max_h) ⇒ Object
108 109 110 111 112 113 114 115 |
# File 'lib/termpix/protocols.rb', line 108 def self.scale_dimensions(w, h, max_w, max_h) if w > max_w || h > max_h scale = [max_w.to_f / w, max_h.to_f / h].min w = (w * scale).to_i h = (h * scale).to_i end [w, h] end |