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,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module RubySpriter
6
+ # Processes multiple videos in batch mode
7
+ class BatchProcessor
8
+ attr_reader :options
9
+
10
+ def initialize(options = {})
11
+ @options = options
12
+ validate_directory!
13
+ end
14
+
15
+ # Find all MP4 files in the directory
16
+ # @return [Array<String>] List of MP4 file paths
17
+ def find_videos
18
+ pattern = File.join(options[:dir], '*.mp4')
19
+ videos = Dir.glob(pattern)
20
+
21
+ if videos.empty?
22
+ raise ValidationError, "No MP4 files found in directory: #{options[:dir]}"
23
+ end
24
+
25
+ if options[:debug]
26
+ Utils::OutputFormatter.note("Found #{videos.length} video(s) to process")
27
+ videos.each { |v| Utils::OutputFormatter.indent(File.basename(v)) }
28
+ end
29
+
30
+ videos
31
+ end
32
+
33
+ # Process all videos in the directory
34
+ # @return [Hash] Processing results with outputs and errors
35
+ def process
36
+ videos = find_videos
37
+ output_dir = determine_output_directory
38
+ ensure_output_directory_exists(output_dir)
39
+
40
+ results = {
41
+ processed_count: 0,
42
+ outputs: [],
43
+ errors: []
44
+ }
45
+
46
+ Utils::OutputFormatter.header("Batch Processing: #{videos.length} video(s)")
47
+
48
+ videos.each_with_index do |video, index|
49
+ begin
50
+ puts "\nProcessing [#{index + 1}/#{videos.length}]: #{File.basename(video)}"
51
+
52
+ output_file = determine_output_file(video, output_dir)
53
+ output_file = Utils::FileHelper.ensure_unique_output(output_file, overwrite: options[:overwrite])
54
+
55
+ # Process video
56
+ video_result = process_video(video, output_file)
57
+
58
+ # Apply max compression if requested
59
+ if options[:max_compress] && video_result[:output_file]
60
+ video_result[:output_file] = apply_compression(video_result[:output_file])
61
+ end
62
+
63
+ results[:outputs] << video_result[:output_file]
64
+ results[:processed_count] += 1
65
+
66
+ Utils::OutputFormatter.success("Output: #{File.basename(video_result[:output_file])}")
67
+ rescue StandardError => e
68
+ error_msg = "#{File.basename(video)}: #{e.message}"
69
+ results[:errors] << error_msg
70
+ Utils::OutputFormatter.error(error_msg)
71
+ end
72
+ end
73
+
74
+ # Consolidate if requested
75
+ if options[:batch_consolidate] && results[:outputs].any?
76
+ consolidated = consolidate_results(results[:outputs])
77
+ results[:consolidated] = consolidated if consolidated
78
+ end
79
+
80
+ display_summary(results)
81
+
82
+ results
83
+ end
84
+
85
+ # Consolidate all resulting spritesheets
86
+ # @param outputs [Array<String>] List of spritesheet file paths
87
+ # @return [Hash, nil] Consolidation result or nil if not requested
88
+ def consolidate_results(outputs)
89
+ return nil unless options[:batch_consolidate]
90
+ return nil if outputs.empty?
91
+
92
+ Utils::OutputFormatter.header("Consolidating #{outputs.length} spritesheets")
93
+
94
+ output_dir = options[:outputdir] || options[:dir]
95
+ consolidated_file = File.join(output_dir, 'batch_consolidated_spritesheet.png')
96
+ consolidated_file = Utils::FileHelper.ensure_unique_output(consolidated_file, overwrite: options[:overwrite])
97
+
98
+ consolidator = Consolidator.new(options)
99
+ result = consolidator.consolidate(outputs, consolidated_file)
100
+
101
+ Utils::OutputFormatter.success("Consolidated output: #{File.basename(consolidated_file)}")
102
+
103
+ result
104
+ end
105
+
106
+ private
107
+
108
+ def validate_directory!
109
+ dir = options[:dir]
110
+
111
+ raise ValidationError, "Must specify --dir for batch processing" unless dir
112
+ raise ValidationError, "Directory not found: #{dir}" unless File.directory?(dir)
113
+ end
114
+
115
+ def determine_output_directory
116
+ options[:outputdir] || options[:dir]
117
+ end
118
+
119
+ def ensure_output_directory_exists(dir)
120
+ return if File.directory?(dir)
121
+
122
+ if options[:debug]
123
+ Utils::OutputFormatter.note("Creating output directory: #{dir}")
124
+ end
125
+
126
+ FileUtils.mkdir_p(dir)
127
+ end
128
+
129
+ def determine_output_file(video_file, output_dir)
130
+ basename = File.basename(video_file, '.*')
131
+ File.join(output_dir, "#{basename}_spritesheet.png")
132
+ end
133
+
134
+ def process_video(video_file, output_file)
135
+ video_processor = VideoProcessor.new(options)
136
+ result = video_processor.create_spritesheet(video_file, output_file)
137
+
138
+ working_file = result[:output_file]
139
+
140
+ # Apply GIMP processing if requested
141
+ if needs_gimp_processing?
142
+ working_file = process_with_gimp(working_file, result)
143
+ end
144
+
145
+ # Update result with final file
146
+ result[:output_file] = working_file
147
+ result
148
+ end
149
+
150
+ def needs_gimp_processing?
151
+ options[:scale_percent] || options[:remove_bg] || options[:sharpen]
152
+ end
153
+
154
+ def process_with_gimp(input_file, video_result)
155
+ # Get GIMP path from dependency checker
156
+ checker = DependencyChecker.new(verbose: false)
157
+ results = checker.check_all
158
+ gimp_path = checker.gimp_path
159
+
160
+ unless gimp_path
161
+ raise DependencyError, "GIMP not found but required for processing"
162
+ end
163
+
164
+ gimp_processor = GimpProcessor.new(gimp_path, options)
165
+ output_file = gimp_processor.process(input_file)
166
+
167
+ # Clean up intermediate file if different
168
+ if output_file != input_file && File.exist?(input_file)
169
+ File.delete(input_file) unless options[:keep_temp]
170
+ end
171
+
172
+ output_file
173
+ end
174
+
175
+ def apply_compression(file)
176
+ Utils::OutputFormatter.indent("Applying maximum compression...")
177
+
178
+ temp_file = file.gsub('.png', '_temp.png')
179
+ CompressionManager.compress_with_metadata(file, temp_file)
180
+
181
+ # Show compression stats
182
+ if options[:debug]
183
+ stats = CompressionManager.compression_stats(file, temp_file)
184
+ Utils::OutputFormatter.indent("Saved #{Utils::FileHelper.format_size(stats[:saved_bytes])} (#{stats[:reduction_percent].round(1)}% reduction)")
185
+ end
186
+
187
+ # Replace original with compressed
188
+ FileUtils.mv(temp_file, file)
189
+
190
+ file
191
+ end
192
+
193
+ def display_summary(results)
194
+ Utils::OutputFormatter.header("Batch Processing Summary")
195
+
196
+ puts "Total videos: #{results[:processed_count] + results[:errors].length}"
197
+ Utils::OutputFormatter.success("Successfully processed: #{results[:processed_count]}")
198
+
199
+ if results[:errors].any?
200
+ Utils::OutputFormatter.error("Failed: #{results[:errors].length}")
201
+ results[:errors].each do |error|
202
+ Utils::OutputFormatter.indent("- #{error}")
203
+ end
204
+ end
205
+
206
+ if results[:consolidated]
207
+ puts "\nConsolidated spritesheet:"
208
+ Utils::OutputFormatter.indent(results[:consolidated][:output_file])
209
+ end
210
+ end
211
+ end
212
+ end
@@ -18,6 +18,12 @@ module RubySpriter
18
18
 
19
19
  def parse_and_run(args)
20
20
  options = {}
21
+
22
+ # Handle context-sensitive help before validation
23
+ if args.include?('--help') || args.include?('-h')
24
+ handle_context_sensitive_help(args)
25
+ end
26
+
21
27
  parser = build_option_parser(options)
22
28
 
23
29
  parser.parse!(args)
@@ -29,6 +35,16 @@ module RubySpriter
29
35
  exit(checker.all_satisfied? ? 0 : 1)
30
36
  end
31
37
 
38
+ # Validate mutually exclusive options
39
+ if options[:extract] && options[:split]
40
+ raise ValidationError, "--extract and --split are mutually exclusive"
41
+ end
42
+
43
+ # Validate --add-meta cannot be combined with processing options
44
+ if options[:add_meta] && (options[:scale_percent] || options[:remove_bg] || options[:sharpen])
45
+ raise ValidationError, "--add-meta cannot be combined with processing options (--scale, --remove-bg, --sharpen)"
46
+ end
47
+
32
48
  # Run processor
33
49
  processor = Processor.new(options)
34
50
  processor.run
@@ -57,14 +73,21 @@ module RubySpriter
57
73
 
58
74
  def add_header(opts)
59
75
  opts.separator ""
60
- opts.separator "Ruby Spriter v#{VERSION} - MP4 to Spritesheet + GIMP Processing"
76
+ opts.separator "Ruby Spriter v#{VERSION} - Professional MP4 to Spritesheet Converter with Advanced Image Processing"
61
77
  opts.separator "Platform: #{Platform.current.to_s.capitalize}"
62
78
  opts.separator ""
79
+ opts.separator "Get mode-specific help:"
80
+ opts.separator " ruby_spriter --video --help"
81
+ opts.separator " ruby_spriter --image --help"
82
+ opts.separator " ruby_spriter --consolidate --help"
83
+ opts.separator " ruby_spriter --batch --help"
84
+ opts.separator " ruby_spriter --split --help"
85
+ opts.separator ""
63
86
  end
64
87
 
65
88
  def add_input_options(opts, options)
66
89
  opts.separator "Input Options:"
67
-
90
+
68
91
  opts.on("-v", "--video FILE", "Input video file (MP4)") do |v|
69
92
  options[:video] = v
70
93
  end
@@ -73,14 +96,55 @@ module RubySpriter
73
96
  options[:image] = i
74
97
  end
75
98
 
76
- opts.on("--consolidate FILES", Array, "Consolidate multiple spritesheets (comma-separated)") do |c|
77
- options[:consolidate] = c
99
+ opts.on("--batch", "Batch process all MP4 files in directory") do
100
+ options[:batch] = true
101
+ end
102
+
103
+ opts.on("--dir DIRECTORY", "Directory for batch processing") do |d|
104
+ options[:dir] = d
105
+ end
106
+
107
+ opts.on("--outputdir DIRECTORY", "Output directory for batch processing") do |d|
108
+ options[:outputdir] = d
109
+ end
110
+
111
+ opts.on("--batch-consolidate", "Consolidate all spritesheets after batch processing") do
112
+ options[:batch_consolidate] = true
113
+ end
114
+
115
+ opts.on("--consolidate [FILES]", Array, "Consolidate spritesheets (comma-separated files or use with --dir)") do |c|
116
+ options[:consolidate_mode] = true
117
+ options[:consolidate] = c if c && !c.empty?
78
118
  end
79
119
 
80
120
  opts.on("--verify FILE", "Verify spritesheet metadata") do |v|
81
121
  options[:verify] = v
82
122
  end
83
123
 
124
+ opts.on("--split R:C", "Split image into frames (rows:columns, e.g., 4:4)") do |s|
125
+ options[:split] = s
126
+ end
127
+
128
+ opts.on("--override-md", "Override embedded metadata when using --split") do
129
+ options[:override_md] = true
130
+ end
131
+
132
+ opts.on("--extract FRAMES", "Extract specific frames by number (comma-separated, e.g., 1,2,4,5,8)") do |e|
133
+ options[:extract] = e
134
+ end
135
+
136
+ opts.on("--columns NUM", Integer, "Number of columns for extracted spritesheet (default: 4)") do |c|
137
+ options[:columns] = c
138
+ end
139
+
140
+ opts.on("--add-meta R:C", "Add spritesheet metadata to image (rows:columns, e.g., 4:4)") do |m|
141
+ options[:add_meta] = m
142
+ end
143
+
144
+ opts.on("--overwrite-meta", "Overwrite existing metadata when using --add-meta") do
145
+ options[:overwrite_meta] = true
146
+ end
147
+
84
148
  opts.separator ""
85
149
  end
86
150
 
@@ -107,11 +171,15 @@ module RubySpriter
107
171
  options[:bg_color] = b
108
172
  end
109
173
 
174
+ opts.on("--save-frames", "Save individual frames to disk (for --video or --extract)") do
175
+ options[:save_frames] = true
176
+ end
177
+
110
178
  opts.separator ""
111
179
  end
112
180
 
113
181
  def add_gimp_options(opts, options)
114
- opts.separator "GIMP Processing Options:"
182
+ opts.separator "Processing Options:"
115
183
 
116
184
  opts.on("-s", "--scale PERCENT", Integer, "Scale image by percentage") do |s|
117
185
  options[:scale_percent] = s
@@ -138,7 +206,7 @@ module RubySpriter
138
206
  options[:sharpen_threshold] = t
139
207
  end
140
208
 
141
- opts.on("-r", "--remove-bg", "Remove background from spritesheet using GIMP") do
209
+ opts.on("-r", "--remove-bg", "Remove background from spritesheet") do
142
210
  options[:remove_bg] = true
143
211
  end
144
212
 
@@ -208,6 +276,14 @@ module RubySpriter
208
276
  def add_other_options(opts, options)
209
277
  opts.separator "Other Options:"
210
278
 
279
+ opts.on("--max-compress", "Apply maximum PNG compression to output") do
280
+ options[:max_compress] = true
281
+ end
282
+
283
+ opts.on("--overwrite", "Overwrite existing output files (default: create unique filenames)") do
284
+ options[:overwrite] = true
285
+ end
286
+
211
287
  opts.on("--keep-temp", "Keep temporary files for debugging") do
212
288
  options[:keep_temp] = true
213
289
  end
@@ -239,11 +315,298 @@ module RubySpriter
239
315
  opts.separator " ruby_spriter --video input.mp4"
240
316
  opts.separator " ruby_spriter --video input.mp4 --remove-bg --scale 50"
241
317
  opts.separator " ruby_spriter --video input.mp4 --scale 50 --interpolation nohalo --sharpen"
318
+ opts.separator " ruby_spriter --video input.mp4 --max-compress"
242
319
  opts.separator " ruby_spriter --image sprite.png --scale 50 --sharpen --sharpen-gain 1.5"
243
320
  opts.separator " ruby_spriter --image sprite.png --remove-bg --fuzzy"
321
+ opts.separator " ruby_spriter --batch --dir videos/"
322
+ opts.separator " ruby_spriter --batch --dir videos/ --outputdir output/"
323
+ opts.separator " ruby_spriter --batch --dir videos/ --batch-consolidate --max-compress"
244
324
  opts.separator " ruby_spriter --consolidate file1.png,file2.png,file3.png"
325
+ opts.separator " ruby_spriter --consolidate --dir spritesheets/"
326
+ opts.separator " ruby_spriter --consolidate --dir spritesheets/ --outputdir output/ --max-compress"
245
327
  opts.separator " ruby_spriter --verify spritesheet.png"
246
328
  opts.separator ""
247
329
  end
330
+
331
+ def handle_context_sensitive_help(args)
332
+ if args.include?('--video') || args.include?('-v')
333
+ show_video_mode_help
334
+ elsif args.include?('--image') || args.include?('-i')
335
+ show_image_mode_help
336
+ elsif args.include?('--consolidate')
337
+ show_consolidate_mode_help
338
+ elsif args.include?('--batch')
339
+ show_batch_mode_help
340
+ elsif args.include?('--split')
341
+ show_split_mode_help
342
+ else
343
+ # Default help - let OptionParser handle it
344
+ return
345
+ end
346
+ end
347
+
348
+ def show_video_mode_help
349
+ puts ""
350
+ puts "Video Mode"
351
+ puts "=" * 60
352
+ puts ""
353
+ puts "Convert MP4 videos to spritesheets with advanced image processing."
354
+ puts ""
355
+ puts "Basic Usage:"
356
+ puts " ruby_spriter --video FILE [options]"
357
+ puts ""
358
+ puts "Required:"
359
+ puts " -v, --video FILE Input video file (MP4)"
360
+ puts ""
361
+ puts "Spritesheet Options:"
362
+ puts " -o, --output FILE Output file path"
363
+ puts " -f, --frames COUNT Number of frames to extract (default: 16)"
364
+ puts " -c, --columns COUNT Grid columns (default: 4)"
365
+ puts " -w, --width PIXELS Max frame width (default: 320)"
366
+ puts " -b, --background COLOR Tile background: black, white (default: black)"
367
+ puts " --save-frames Save individual frames to disk"
368
+ puts ""
369
+ puts "Image Processing:"
370
+ puts " -s, --scale PERCENT Scale image by percentage"
371
+ puts " --interpolation METHOD └─ Interpolation: none, linear, cubic, nohalo, lohalo (default: nohalo)"
372
+ puts ""
373
+ puts " --sharpen Apply unsharp mask for edge enhancement"
374
+ puts " --sharpen-radius VALUE └─ Sharpen radius in pixels (default: 2.0)"
375
+ puts " --sharpen-gain VALUE └─ Sharpen gain/strength (default: 0.5)"
376
+ puts " --sharpen-threshold VALUE └─ Sharpen threshold (default: 0.03)"
377
+ puts ""
378
+ puts " -r, --remove-bg Remove background"
379
+ puts " --fuzzy └─ Use fuzzy select (contiguous regions) - DEFAULT"
380
+ puts " --no-fuzzy └─ Use global color select (all matching pixels)"
381
+ puts " -t, --threshold VALUE └─ Feather radius (default: 0.0)"
382
+ puts " -g, --grow PIXELS └─ Grow selection pixels (default: 1)"
383
+ puts ""
384
+ puts " --order ORDER Operation order when using BOTH --scale AND --remove-bg:"
385
+ puts " scale_first or bg_first (default: scale_first)"
386
+ puts ""
387
+ puts "Output Options:"
388
+ puts " --max-compress Apply maximum PNG compression"
389
+ puts " --overwrite Overwrite existing files"
390
+ puts " --keep-temp Keep temporary files"
391
+ puts " --debug Enable debug mode"
392
+ puts ""
393
+ puts "Examples:"
394
+ puts " ruby_spriter --video input.mp4"
395
+ puts " ruby_spriter --video input.mp4 --scale 50 --interpolation nohalo"
396
+ puts " ruby_spriter --video input.mp4 --remove-bg --fuzzy --threshold 0.5"
397
+ puts " ruby_spriter --video input.mp4 --scale 50 --sharpen --max-compress"
398
+ puts ""
399
+ exit
400
+ end
401
+
402
+ def show_image_mode_help
403
+ puts ""
404
+ puts "Image Mode"
405
+ puts "=" * 60
406
+ puts ""
407
+ puts "Process PNG spritesheets with advanced image operations."
408
+ puts ""
409
+ puts "Basic Usage:"
410
+ puts " ruby_spriter --image FILE [options]"
411
+ puts ""
412
+ puts "Required:"
413
+ puts " -i, --image FILE Input image file (PNG)"
414
+ puts ""
415
+ puts "Image Processing:"
416
+ puts " -s, --scale PERCENT Scale image by percentage"
417
+ puts " --interpolation METHOD └─ Interpolation: none, linear, cubic, nohalo, lohalo (default: nohalo)"
418
+ puts ""
419
+ puts " --sharpen Apply unsharp mask for edge enhancement"
420
+ puts " --sharpen-radius VALUE └─ Sharpen radius in pixels (default: 2.0)"
421
+ puts " --sharpen-gain VALUE └─ Sharpen gain/strength (default: 0.5)"
422
+ puts " --sharpen-threshold VALUE └─ Sharpen threshold (default: 0.03)"
423
+ puts ""
424
+ puts " -r, --remove-bg Remove background"
425
+ puts " --fuzzy └─ Use fuzzy select (contiguous regions) - DEFAULT"
426
+ puts " --no-fuzzy └─ Use global color select (all matching pixels)"
427
+ puts " -t, --threshold VALUE └─ Feather radius (default: 0.0)"
428
+ puts " -g, --grow PIXELS └─ Grow selection pixels (default: 1)"
429
+ puts ""
430
+ puts " --order ORDER Operation order when using BOTH --scale AND --remove-bg:"
431
+ puts " scale_first or bg_first (default: scale_first)"
432
+ puts ""
433
+ puts "Frame Extraction & Reassembly:"
434
+ puts " --split R:C Split spritesheet into all individual frames (rows:columns)"
435
+ puts " --override-md └─ Override embedded metadata"
436
+ puts ""
437
+ puts " --extract FRAMES Extract specific frames and create new spritesheet (e.g., 1,2,4,5,8)"
438
+ puts " --columns NUM └─ Output grid columns (default: 4)"
439
+ puts " --save-frames └─ Keep individual extracted frames on disk"
440
+ puts ""
441
+ puts "Metadata Management:"
442
+ puts " --add-meta R:C Add spritesheet metadata (rows:columns, e.g., 4:4)"
443
+ puts " --overwrite-meta └─ Replace existing metadata"
444
+ puts " -f, --frames COUNT └─ Custom frame count for partial grids"
445
+ puts ""
446
+ puts "Output Options:"
447
+ puts " -o, --output FILE Output file path"
448
+ puts " --max-compress Apply maximum PNG compression"
449
+ puts " --overwrite Overwrite existing files"
450
+ puts " --keep-temp Keep temporary files"
451
+ puts " --debug Enable debug mode"
452
+ puts ""
453
+ puts "Examples:"
454
+ puts " ruby_spriter --image sprite.png --scale 50 --interpolation nohalo"
455
+ puts " ruby_spriter --image sprite.png --remove-bg --fuzzy --threshold 1.0"
456
+ puts " ruby_spriter --image sprite.png --scale 50 --sharpen --sharpen-gain 1.5"
457
+ puts " ruby_spriter --image sprite.png --split 4:4 --override-md"
458
+ puts " ruby_spriter --image sprite.png --extract 1,2,4,5,8 --columns 3"
459
+ puts " ruby_spriter --image sprite.png --extract 1,1,2,2,3,3 --save-frames"
460
+ puts " ruby_spriter --image sprite.png --add-meta 4:4"
461
+ puts " ruby_spriter --image sprite.png --add-meta 4:4 --frames 14 --output sprite_meta.png"
462
+ puts ""
463
+ exit
464
+ end
465
+
466
+ def show_consolidate_mode_help
467
+ puts ""
468
+ puts "Consolidate Mode"
469
+ puts "=" * 60
470
+ puts ""
471
+ puts "Combine multiple spritesheets into a single consolidated spritesheet."
472
+ puts ""
473
+ puts "Basic Usage:"
474
+ puts " ruby_spriter --consolidate FILE1,FILE2,FILE3 [options]"
475
+ puts " ruby_spriter --consolidate --dir DIRECTORY [options]"
476
+ puts ""
477
+ puts "Input Methods:"
478
+ puts " --consolidate [FILES] Comma-separated list of PNG files"
479
+ puts " --consolidate --dir DIRECTORY Process all spritesheets in directory"
480
+ puts " --[no-]validate-columns └─ Abort if column counts don't match (default: true)"
481
+ puts ""
482
+ puts "Output Options:"
483
+ puts " -o, --output FILE Output file path"
484
+ puts " --outputdir DIRECTORY Output directory (when using --dir)"
485
+ puts " --max-compress Apply maximum PNG compression"
486
+ puts " --overwrite Overwrite existing files"
487
+ puts " --keep-temp Keep temporary files"
488
+ puts " --debug Enable debug mode"
489
+ puts ""
490
+ puts "Requirements:"
491
+ puts " - All input files must be PNG spritesheets with embedded metadata"
492
+ puts " - Column counts must match across all spritesheets (unless --no-validate-columns)"
493
+ puts " - Minimum 2 spritesheets required"
494
+ puts ""
495
+ puts "Examples:"
496
+ puts " ruby_spriter --consolidate file1.png,file2.png,file3.png"
497
+ puts " ruby_spriter --consolidate --dir spritesheets/"
498
+ puts " ruby_spriter --consolidate --dir spritesheets/ --outputdir output/"
499
+ puts " ruby_spriter --consolidate --dir spritesheets/ --no-validate-columns"
500
+ puts ""
501
+ exit
502
+ end
503
+
504
+ def show_batch_mode_help
505
+ puts ""
506
+ puts "Batch Mode"
507
+ puts "=" * 60
508
+ puts ""
509
+ puts "Process multiple MP4 videos in a directory with consistent options."
510
+ puts ""
511
+ puts "Basic Usage:"
512
+ puts " ruby_spriter --batch --dir DIRECTORY [options]"
513
+ puts ""
514
+ puts "Required:"
515
+ puts " --batch Enable batch processing mode"
516
+ puts " --dir DIRECTORY Directory containing MP4 files"
517
+ puts ""
518
+ puts "Batch Options:"
519
+ puts " --outputdir DIRECTORY Output directory (default: same as input dir)"
520
+ puts " --batch-consolidate Consolidate all results into single spritesheet"
521
+ puts ""
522
+ puts "Spritesheet Options (applied to all videos):"
523
+ puts " -f, --frames COUNT Number of frames to extract (default: 16)"
524
+ puts " -c, --columns COUNT Grid columns (default: 4)"
525
+ puts " -w, --width PIXELS Max frame width (default: 320)"
526
+ puts " -b, --background COLOR Tile background: black, white (default: black)"
527
+ puts " --save-frames Save individual frames to disk"
528
+ puts ""
529
+ puts "Image Processing (applied to all videos):"
530
+ puts " -s, --scale PERCENT Scale images by percentage"
531
+ puts " --interpolation METHOD └─ Interpolation: none, linear, cubic, nohalo, lohalo (default: nohalo)"
532
+ puts ""
533
+ puts " --sharpen Apply unsharp mask for edge enhancement"
534
+ puts " --sharpen-radius VALUE └─ Sharpen radius (default: 2.0)"
535
+ puts " --sharpen-gain VALUE └─ Sharpen gain (default: 0.5)"
536
+ puts " --sharpen-threshold VALUE └─ Sharpen threshold (default: 0.03)"
537
+ puts ""
538
+ puts " -r, --remove-bg Remove background"
539
+ puts " --fuzzy └─ Use fuzzy select (DEFAULT)"
540
+ puts " --no-fuzzy └─ Use global color select"
541
+ puts " -t, --threshold VALUE └─ Feather radius (default: 0.0)"
542
+ puts " -g, --grow PIXELS └─ Grow selection (default: 1)"
543
+ puts ""
544
+ puts " --order ORDER Operation order when using BOTH --scale AND --remove-bg:"
545
+ puts " scale_first or bg_first (default: scale_first)"
546
+ puts ""
547
+ puts "Output Options:"
548
+ puts " --max-compress Apply maximum PNG compression"
549
+ puts " --overwrite Overwrite existing files"
550
+ puts " --keep-temp Keep temporary files"
551
+ puts " --debug Enable debug mode"
552
+ puts ""
553
+ puts "Behavior:"
554
+ puts " - Processes all MP4 files in the specified directory"
555
+ puts " - Enforces unique filenames unless --overwrite is specified"
556
+ puts " - Continues processing remaining videos if one fails"
557
+ puts " - Provides summary of successes and failures"
558
+ puts ""
559
+ puts "Examples:"
560
+ puts " ruby_spriter --batch --dir videos/"
561
+ puts " ruby_spriter --batch --dir videos/ --outputdir output/"
562
+ puts " ruby_spriter --batch --dir videos/ --scale 50 --sharpen"
563
+ puts " ruby_spriter --batch --dir videos/ --batch-consolidate --max-compress"
564
+ puts ""
565
+ exit
566
+ end
567
+
568
+ def show_split_mode_help
569
+ puts ""
570
+ puts "Split Mode"
571
+ puts "=" * 60
572
+ puts ""
573
+ puts "Extract individual frames from a spritesheet (requires --image)."
574
+ puts ""
575
+ puts "Basic Usage:"
576
+ puts " ruby_spriter --image FILE --split R:C [options]"
577
+ puts ""
578
+ puts "Required:"
579
+ puts " -i, --image FILE Input spritesheet file (PNG)"
580
+ puts " --split R:C Split format: rows:columns (e.g., 4:4)"
581
+ puts " --override-md └─ Override embedded metadata"
582
+ puts ""
583
+ puts "Format Requirements:"
584
+ puts " - Rows and columns must be 1-99"
585
+ puts " - Total frames (R × C) must be < 1000"
586
+ puts " - Image dimensions must divide evenly by rows and columns"
587
+ puts ""
588
+ puts "Metadata Behavior:"
589
+ puts " - If spritesheet has embedded metadata, it will be used automatically"
590
+ puts " - Use --override-md to ignore embedded metadata and use --split value"
591
+ puts " - If no metadata exists, --split value is required"
592
+ puts ""
593
+ puts "Output Options:"
594
+ puts " -o, --output FILE Output directory (default: filename_frames/)"
595
+ puts " --overwrite Overwrite existing files"
596
+ puts " --keep-temp Keep temporary files"
597
+ puts " --debug Enable debug mode"
598
+ puts ""
599
+ puts "Output:"
600
+ puts " - Frames are saved as: FR001_filename.png, FR002_filename.png, etc."
601
+ puts " - Frame naming uses 3-digit zero-padded format (FR001-FR999)"
602
+ puts " - Output directory: filename_frames/ (unless --output specified)"
603
+ puts ""
604
+ puts "Examples:"
605
+ puts " ruby_spriter --image sprite.png --split 4:4"
606
+ puts " ruby_spriter --image sprite.png --split 8:8 --override-md"
607
+ puts " ruby_spriter --image sprite.png --split 2:5 --output frames/"
608
+ puts ""
609
+ exit
610
+ end
248
611
  end
249
612
  end