ruby_spriter 0.6.6 → 0.6.7.1

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,214 @@
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 and version from dependency checker
156
+ checker = DependencyChecker.new(verbose: false)
157
+ results = checker.check_all
158
+ gimp_path = checker.gimp_path
159
+ gimp_version = checker.gimp_version
160
+
161
+ unless gimp_path
162
+ raise DependencyError, "GIMP not found but required for processing"
163
+ end
164
+
165
+ gimp_options = options.merge(gimp_version: gimp_version)
166
+ gimp_processor = GimpProcessor.new(gimp_path, gimp_options)
167
+ output_file = gimp_processor.process(input_file)
168
+
169
+ # Clean up intermediate file if different
170
+ if output_file != input_file && File.exist?(input_file)
171
+ File.delete(input_file) unless options[:keep_temp]
172
+ end
173
+
174
+ output_file
175
+ end
176
+
177
+ def apply_compression(file)
178
+ Utils::OutputFormatter.indent("Applying maximum compression...")
179
+
180
+ temp_file = file.gsub('.png', '_temp.png')
181
+ CompressionManager.compress_with_metadata(file, temp_file)
182
+
183
+ # Show compression stats
184
+ if options[:debug]
185
+ stats = CompressionManager.compression_stats(file, temp_file)
186
+ Utils::OutputFormatter.indent("Saved #{Utils::FileHelper.format_size(stats[:saved_bytes])} (#{stats[:reduction_percent].round(1)}% reduction)")
187
+ end
188
+
189
+ # Replace original with compressed
190
+ FileUtils.mv(temp_file, file)
191
+
192
+ file
193
+ end
194
+
195
+ def display_summary(results)
196
+ Utils::OutputFormatter.header("Batch Processing Summary")
197
+
198
+ puts "Total videos: #{results[:processed_count] + results[:errors].length}"
199
+ Utils::OutputFormatter.success("Successfully processed: #{results[:processed_count]}")
200
+
201
+ if results[:errors].any?
202
+ Utils::OutputFormatter.error("Failed: #{results[:errors].length}")
203
+ results[:errors].each do |error|
204
+ Utils::OutputFormatter.indent("- #{error}")
205
+ end
206
+ end
207
+
208
+ if results[:consolidated]
209
+ puts "\nConsolidated spritesheet:"
210
+ Utils::OutputFormatter.indent(results[:consolidated][:output_file])
211
+ end
212
+ end
213
+ end
214
+ 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,8 +96,25 @@ 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|
@@ -89,6 +129,22 @@ module RubySpriter
89
129
  options[:override_md] = true
90
130
  end
91
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
+
92
148
  opts.separator ""
93
149
  end
94
150
 
@@ -115,7 +171,7 @@ module RubySpriter
115
171
  options[:bg_color] = b
116
172
  end
117
173
 
118
- opts.on("--save-frames", "Save individual frames to disk (video only)") do
174
+ opts.on("--save-frames", "Save individual frames to disk (for --video or --extract)") do
119
175
  options[:save_frames] = true
120
176
  end
121
177
 
@@ -123,7 +179,7 @@ module RubySpriter
123
179
  end
124
180
 
125
181
  def add_gimp_options(opts, options)
126
- opts.separator "GIMP Processing Options:"
182
+ opts.separator "Processing Options:"
127
183
 
128
184
  opts.on("-s", "--scale PERCENT", Integer, "Scale image by percentage") do |s|
129
185
  options[:scale_percent] = s
@@ -150,11 +206,11 @@ module RubySpriter
150
206
  options[:sharpen_threshold] = t
151
207
  end
152
208
 
153
- opts.on("-r", "--remove-bg", "Remove background from spritesheet using GIMP") do
209
+ opts.on("-r", "--remove-bg", "Remove background from spritesheet") do
154
210
  options[:remove_bg] = true
155
211
  end
156
212
 
157
- opts.on("-t", "--threshold VALUE", Float, "Feather radius (default: 0.0 = no feathering)") do |t|
213
+ opts.on("-t", "--threshold VALUE", Float, "Background color tolerance % (default: 15.0, range: 0-100)") do |t|
158
214
  options[:bg_threshold] = t
159
215
  end
160
216
 
@@ -220,6 +276,10 @@ module RubySpriter
220
276
  def add_other_options(opts, options)
221
277
  opts.separator "Other Options:"
222
278
 
279
+ opts.on("--max-compress", "Apply maximum PNG compression to output") do
280
+ options[:max_compress] = true
281
+ end
282
+
223
283
  opts.on("--overwrite", "Overwrite existing output files (default: create unique filenames)") do
224
284
  options[:overwrite] = true
225
285
  end
@@ -255,11 +315,298 @@ module RubySpriter
255
315
  opts.separator " ruby_spriter --video input.mp4"
256
316
  opts.separator " ruby_spriter --video input.mp4 --remove-bg --scale 50"
257
317
  opts.separator " ruby_spriter --video input.mp4 --scale 50 --interpolation nohalo --sharpen"
318
+ opts.separator " ruby_spriter --video input.mp4 --max-compress"
258
319
  opts.separator " ruby_spriter --image sprite.png --scale 50 --sharpen --sharpen-gain 1.5"
259
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"
260
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"
261
327
  opts.separator " ruby_spriter --verify spritesheet.png"
262
328
  opts.separator ""
263
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
264
611
  end
265
612
  end