ruby_spriter 0.6.5 → 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+ require 'fileutils'
5
+
6
+ module RubySpriter
7
+ # Manages PNG compression with metadata preservation
8
+ class CompressionManager
9
+ # Compress PNG file using ImageMagick with maximum compression
10
+ # @param input_file [String] Source PNG file
11
+ # @param output_file [String] Destination PNG file
12
+ # @param debug [Boolean] Enable debug output
13
+ def self.compress(input_file, output_file, debug: false)
14
+ Utils::FileHelper.validate_readable!(input_file)
15
+
16
+ cmd = build_compression_command(input_file, output_file)
17
+
18
+ if debug
19
+ Utils::OutputFormatter.indent("DEBUG: Compression command: #{cmd}")
20
+ end
21
+
22
+ stdout, stderr, status = Open3.capture3(cmd)
23
+
24
+ unless status.success?
25
+ raise ProcessingError, "Failed to compress PNG: #{stderr}"
26
+ end
27
+
28
+ Utils::FileHelper.validate_exists!(output_file)
29
+ end
30
+
31
+ # Compress PNG file while preserving embedded metadata
32
+ # @param input_file [String] Source PNG file
33
+ # @param output_file [String] Destination PNG file
34
+ # @param debug [Boolean] Enable debug output
35
+ def self.compress_with_metadata(input_file, output_file, debug: false)
36
+ # Read metadata before compression
37
+ metadata = MetadataManager.read(input_file)
38
+
39
+ # Compress the file
40
+ temp_file = output_file.gsub('.png', '_compress_temp.png')
41
+ compress(input_file, temp_file, debug: debug)
42
+
43
+ # Re-embed metadata if it existed
44
+ if metadata
45
+ MetadataManager.embed(
46
+ temp_file,
47
+ output_file,
48
+ columns: metadata[:columns],
49
+ rows: metadata[:rows],
50
+ frames: metadata[:frames],
51
+ debug: debug
52
+ )
53
+
54
+ # Clean up temp file
55
+ FileUtils.rm_f(temp_file) if File.exist?(temp_file)
56
+ else
57
+ # No metadata, just move temp to output
58
+ FileUtils.mv(temp_file, output_file)
59
+ end
60
+ end
61
+
62
+ # Get compression statistics
63
+ # @param original_file [String] Original file path
64
+ # @param compressed_file [String] Compressed file path
65
+ # @return [Hash] Statistics including sizes and reduction percentage
66
+ def self.compression_stats(original_file, compressed_file)
67
+ original_size = File.size(original_file)
68
+ compressed_size = File.size(compressed_file)
69
+ saved_bytes = original_size - compressed_size
70
+ reduction_percent = (saved_bytes.to_f / original_size * 100.0)
71
+
72
+ {
73
+ original_size: original_size,
74
+ compressed_size: compressed_size,
75
+ saved_bytes: saved_bytes,
76
+ reduction_percent: reduction_percent
77
+ }
78
+ end
79
+
80
+ private_class_method def self.build_compression_command(input_file, output_file)
81
+ magick_cmd = Platform.imagemagick_convert_cmd
82
+
83
+ # Use maximum PNG compression settings:
84
+ # - compression-level=9: Maximum zlib compression
85
+ # - compression-filter=5: Paeth filter (best for most images)
86
+ # - compression-strategy=1: Filtered strategy
87
+ # - quality=95: High quality
88
+ # - strip: Remove all metadata (we'll re-add it later)
89
+ [
90
+ magick_cmd,
91
+ Utils::PathHelper.quote_path(input_file),
92
+ '-strip',
93
+ '-define', 'png:compression-level=9',
94
+ '-define', 'png:compression-filter=5',
95
+ '-define', 'png:compression-strategy=1',
96
+ '-quality', '95',
97
+ Utils::PathHelper.quote_path(output_file)
98
+ ].join(' ')
99
+ end
100
+ end
101
+ end
@@ -58,6 +58,39 @@ module RubySpriter
58
58
  }
59
59
  end
60
60
 
61
+ # Find all PNG files with spritesheet metadata in a directory
62
+ # @param directory [String] Directory path to scan
63
+ # @return [Array<String>] Sorted array of spritesheet file paths
64
+ def find_spritesheets_in_directory(directory)
65
+ # Validate directory exists
66
+ unless File.directory?(directory)
67
+ raise ValidationError, "Directory not found: #{directory}"
68
+ end
69
+
70
+ # Find all PNG files
71
+ pattern = File.join(directory, '*.png')
72
+ png_files = Dir.glob(pattern)
73
+
74
+ # Filter to only files with metadata
75
+ spritesheets = png_files.select do |file|
76
+ metadata = MetadataManager.read(file)
77
+ !metadata.nil?
78
+ end
79
+
80
+ # Validate we found at least one
81
+ if spritesheets.empty?
82
+ raise ValidationError, "No PNG files with spritesheet metadata found in directory: #{directory}"
83
+ end
84
+
85
+ # Validate we have at least 2
86
+ if spritesheets.length < 2
87
+ raise ValidationError, "Found only #{spritesheets.length} spritesheet, need at least 2 for consolidation"
88
+ end
89
+
90
+ # Sort alphabetically by filename
91
+ spritesheets.sort
92
+ end
93
+
61
94
  private
62
95
 
63
96
  def validate_files!(files)
@@ -68,7 +68,8 @@ module RubySpriter
68
68
 
69
69
  def scale_image(input_file)
70
70
  percent = options[:scale_percent]
71
- output_file = Utils::FileHelper.output_filename(input_file, "scaled-#{percent}pct")
71
+ desired_output = Utils::FileHelper.output_filename(input_file, "scaled-#{percent}pct")
72
+ output_file = Utils::FileHelper.ensure_unique_output(desired_output, overwrite: options[:overwrite])
72
73
 
73
74
  Utils::OutputFormatter.indent("Scaling to #{percent}%...")
74
75
 
@@ -85,7 +86,8 @@ module RubySpriter
85
86
 
86
87
  def remove_background(input_file)
87
88
  method = options[:fuzzy_select] ? 'fuzzy' : 'global'
88
- output_file = Utils::FileHelper.output_filename(input_file, "nobg-#{method}")
89
+ desired_output = Utils::FileHelper.output_filename(input_file, "nobg-#{method}")
90
+ output_file = Utils::FileHelper.ensure_unique_output(desired_output, overwrite: options[:overwrite])
89
91
 
90
92
  Utils::OutputFormatter.indent("Removing background (#{method} select)...")
91
93
 
@@ -621,7 +623,8 @@ module RubySpriter
621
623
  gain = options[:sharpen_gain] || 0.5
622
624
  threshold = options[:sharpen_threshold] || 0.03
623
625
 
624
- output_file = Utils::FileHelper.output_filename(input_file, "sharpened")
626
+ desired_output = Utils::FileHelper.output_filename(input_file, "sharpened")
627
+ output_file = Utils::FileHelper.ensure_unique_output(desired_output, overwrite: options[:overwrite])
625
628
 
626
629
  Utils::OutputFormatter.indent("Applying unsharp mask (ImageMagick)...")
627
630
  Utils::OutputFormatter.indent(" radius=#{radius}, gain=#{gain}, threshold=#{threshold}")