ruby_spriter 0.6.5 → 0.6.6
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -0
- data/README.md +40 -2
- data/lib/ruby_spriter/cli.rb +16 -0
- data/lib/ruby_spriter/gimp_processor.rb +6 -3
- data/lib/ruby_spriter/processor.rb +253 -23
- data/lib/ruby_spriter/utils/file_helper.rb +25 -0
- data/lib/ruby_spriter/utils/spritesheet_splitter.rb +86 -0
- data/lib/ruby_spriter/version.rb +1 -1
- data/lib/ruby_spriter/video_processor.rb +7 -7
- data/lib/ruby_spriter.rb +1 -0
- data/spec/ruby_spriter/cli_spec.rb +363 -0
- data/spec/ruby_spriter/processor_spec.rb +385 -0
- data/spec/ruby_spriter/utils/file_helper_spec.rb +80 -1
- data/spec/ruby_spriter/utils/spritesheet_splitter_spec.rb +104 -0
- data/spec/ruby_spriter/video_processor_spec.rb +29 -0
- metadata +4 -2
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'open3'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
|
|
6
|
+
module RubySpriter
|
|
7
|
+
module Utils
|
|
8
|
+
# Splits a spritesheet into individual frame images
|
|
9
|
+
class SpritesheetSplitter
|
|
10
|
+
# Split spritesheet into individual frames
|
|
11
|
+
# @param spritesheet_file [String] Path to spritesheet PNG
|
|
12
|
+
# @param output_dir [String] Directory to save individual frames
|
|
13
|
+
# @param columns [Integer] Number of columns in grid
|
|
14
|
+
# @param rows [Integer] Number of rows in grid
|
|
15
|
+
# @param frames [Integer] Total number of frames to extract
|
|
16
|
+
def split_into_frames(spritesheet_file, output_dir, columns, rows, frames)
|
|
17
|
+
FileUtils.mkdir_p(output_dir)
|
|
18
|
+
|
|
19
|
+
OutputFormatter.header("Extracting Frames")
|
|
20
|
+
OutputFormatter.indent("Splitting spritesheet into #{frames} frames to disk...")
|
|
21
|
+
OutputFormatter.indent("Output directory: #{output_dir}")
|
|
22
|
+
|
|
23
|
+
# Get spritesheet dimensions
|
|
24
|
+
dimensions = get_image_dimensions(spritesheet_file)
|
|
25
|
+
tile_width = dimensions[:width] / columns
|
|
26
|
+
tile_height = dimensions[:height] / rows
|
|
27
|
+
|
|
28
|
+
# Extract each frame
|
|
29
|
+
spritesheet_basename = File.basename(spritesheet_file, '.*')
|
|
30
|
+
|
|
31
|
+
frames.times do |i|
|
|
32
|
+
frame_number = i + 1
|
|
33
|
+
row = i / columns
|
|
34
|
+
col = i % columns
|
|
35
|
+
|
|
36
|
+
x_offset = col * tile_width
|
|
37
|
+
y_offset = row * tile_height
|
|
38
|
+
|
|
39
|
+
frame_filename = "FR#{format('%03d', frame_number)}_#{spritesheet_basename}.png"
|
|
40
|
+
frame_path = File.join(output_dir, frame_filename)
|
|
41
|
+
|
|
42
|
+
extract_tile(spritesheet_file, frame_path, tile_width, tile_height, x_offset, y_offset)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
OutputFormatter.indent("✅ Frames extracted successfully\n")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def get_image_dimensions(image_file)
|
|
51
|
+
cmd = [
|
|
52
|
+
'magick',
|
|
53
|
+
'identify',
|
|
54
|
+
'-format', '%wx%h',
|
|
55
|
+
PathHelper.quote_path(image_file)
|
|
56
|
+
].join(' ')
|
|
57
|
+
|
|
58
|
+
stdout, stderr, status = Open3.capture3(cmd)
|
|
59
|
+
|
|
60
|
+
unless status.success?
|
|
61
|
+
raise ProcessingError, "Could not get image dimensions: #{stderr}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
width, height = stdout.strip.split('x').map(&:to_i)
|
|
65
|
+
{ width: width, height: height }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def extract_tile(source_file, output_file, width, height, x_offset, y_offset)
|
|
69
|
+
cmd = [
|
|
70
|
+
'magick',
|
|
71
|
+
'convert',
|
|
72
|
+
PathHelper.quote_path(source_file),
|
|
73
|
+
'-crop', "#{width}x#{height}+#{x_offset}+#{y_offset}",
|
|
74
|
+
'+repage',
|
|
75
|
+
PathHelper.quote_path(output_file)
|
|
76
|
+
].join(' ')
|
|
77
|
+
|
|
78
|
+
stdout, stderr, status = Open3.capture3(cmd)
|
|
79
|
+
|
|
80
|
+
unless status.success?
|
|
81
|
+
raise ProcessingError, "Could not extract frame: #{stderr}"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
data/lib/ruby_spriter/version.rb
CHANGED
|
@@ -27,26 +27,26 @@ module RubySpriter
|
|
|
27
27
|
rows = (frame_count.to_f / columns).ceil
|
|
28
28
|
|
|
29
29
|
Utils::OutputFormatter.header("Creating Spritesheet")
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
temp_file = output_file.sub('.png', '_temp.png')
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
create_with_ffmpeg(video_file, temp_file, duration, columns, rows, frame_count)
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
# Embed metadata
|
|
36
36
|
MetadataManager.embed(
|
|
37
|
-
temp_file,
|
|
37
|
+
temp_file,
|
|
38
38
|
output_file,
|
|
39
39
|
columns: columns,
|
|
40
40
|
rows: rows,
|
|
41
41
|
frames: frame_count,
|
|
42
42
|
debug: options[:debug]
|
|
43
43
|
)
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
# Clean up temp file
|
|
46
46
|
File.delete(temp_file) if File.exist?(temp_file)
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
file_size = File.size(output_file)
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
# Display results with Godot instructions
|
|
51
51
|
display_spritesheet_results(output_file, file_size, columns, rows, frame_count)
|
|
52
52
|
|
data/lib/ruby_spriter.rb
CHANGED
|
@@ -15,6 +15,7 @@ require_relative 'ruby_spriter/version'
|
|
|
15
15
|
require_relative 'ruby_spriter/utils/path_helper'
|
|
16
16
|
require_relative 'ruby_spriter/utils/file_helper'
|
|
17
17
|
require_relative 'ruby_spriter/utils/output_formatter'
|
|
18
|
+
require_relative 'ruby_spriter/utils/spritesheet_splitter'
|
|
18
19
|
|
|
19
20
|
# Load core components
|
|
20
21
|
require_relative 'ruby_spriter/platform'
|
|
@@ -155,6 +155,32 @@ RSpec.describe RubySpriter::CLI do
|
|
|
155
155
|
end.to raise_error(SystemExit)
|
|
156
156
|
end
|
|
157
157
|
end
|
|
158
|
+
|
|
159
|
+
describe '--overwrite flag' do
|
|
160
|
+
it 'sets overwrite option to true' do
|
|
161
|
+
processor_double = instance_double(RubySpriter::Processor)
|
|
162
|
+
allow(processor_double).to receive(:run)
|
|
163
|
+
|
|
164
|
+
allow(RubySpriter::Processor).to receive(:new) do |options|
|
|
165
|
+
expect(options[:overwrite]).to eq(true)
|
|
166
|
+
processor_double
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
described_class.start(['--video', 'test.mp4', '--overwrite'])
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it 'defaults to false when not specified' do
|
|
173
|
+
processor_double = instance_double(RubySpriter::Processor)
|
|
174
|
+
allow(processor_double).to receive(:run)
|
|
175
|
+
|
|
176
|
+
allow(RubySpriter::Processor).to receive(:new) do |options|
|
|
177
|
+
expect(options[:overwrite]).to be_nil
|
|
178
|
+
processor_double
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
described_class.start(['--video', 'test.mp4'])
|
|
182
|
+
end
|
|
183
|
+
end
|
|
158
184
|
end
|
|
159
185
|
|
|
160
186
|
describe '--image flag' do
|
|
@@ -425,6 +451,225 @@ RSpec.describe RubySpriter::CLI do
|
|
|
425
451
|
|
|
426
452
|
described_class.start(['--image', fixture_with_meta, '--output', 'custom_output.png'])
|
|
427
453
|
end
|
|
454
|
+
|
|
455
|
+
it 'works with --overwrite option' do
|
|
456
|
+
processor_double = instance_double(RubySpriter::Processor)
|
|
457
|
+
allow(processor_double).to receive(:run)
|
|
458
|
+
|
|
459
|
+
allow(RubySpriter::Processor).to receive(:new) do |options|
|
|
460
|
+
expect(options[:image]).to eq(fixture_with_meta)
|
|
461
|
+
expect(options[:remove_bg]).to eq(true)
|
|
462
|
+
expect(options[:overwrite]).to eq(true)
|
|
463
|
+
processor_double
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
described_class.start(['--image', fixture_with_meta, '--remove-bg', '--overwrite'])
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
it 'works with --overwrite and --output options combined' do
|
|
470
|
+
processor_double = instance_double(RubySpriter::Processor)
|
|
471
|
+
allow(processor_double).to receive(:run)
|
|
472
|
+
|
|
473
|
+
allow(RubySpriter::Processor).to receive(:new) do |options|
|
|
474
|
+
expect(options[:image]).to eq(fixture_with_meta)
|
|
475
|
+
expect(options[:scale_percent]).to eq(50)
|
|
476
|
+
expect(options[:output]).to eq('custom.png')
|
|
477
|
+
expect(options[:overwrite]).to eq(true)
|
|
478
|
+
processor_double
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
described_class.start(['--image', fixture_with_meta, '--scale', '50', '--output', 'custom.png', '--overwrite'])
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
describe 'output filename behavior with processing' do
|
|
486
|
+
it 'generates unique filename by default when processing without --output' do
|
|
487
|
+
# Mock all dependencies
|
|
488
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_exists!)
|
|
489
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_readable!)
|
|
490
|
+
|
|
491
|
+
# Mock GimpProcessor to return a processed file
|
|
492
|
+
gimp_double = instance_double(RubySpriter::GimpProcessor)
|
|
493
|
+
allow(RubySpriter::GimpProcessor).to receive(:new).and_return(gimp_double)
|
|
494
|
+
allow(gimp_double).to receive(:process).and_return('input-nobg-fuzzy_20251023_123456_789.png')
|
|
495
|
+
|
|
496
|
+
processor = RubySpriter::Processor.new(
|
|
497
|
+
image: fixture_with_meta,
|
|
498
|
+
remove_bg: true,
|
|
499
|
+
overwrite: false
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
allow(processor).to receive(:check_dependencies!)
|
|
503
|
+
allow(processor).to receive(:setup_temp_directory)
|
|
504
|
+
allow(processor).to receive(:cleanup)
|
|
505
|
+
allow(processor).to receive(:gimp_path).and_return('/usr/bin/gimp')
|
|
506
|
+
|
|
507
|
+
result = nil
|
|
508
|
+
expect { result = processor.run }.to output(/SUCCESS/).to_stdout
|
|
509
|
+
|
|
510
|
+
# Should return the uniquely-named file from GIMP processing
|
|
511
|
+
expect(result[:output_file]).to match(/-nobg-fuzzy.*\.png$/)
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
it 'overwrites output file when --overwrite is specified' do
|
|
515
|
+
# Mock all dependencies
|
|
516
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_exists!)
|
|
517
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_readable!)
|
|
518
|
+
|
|
519
|
+
# Mock GimpProcessor - with overwrite:true, it should return same filename
|
|
520
|
+
gimp_double = instance_double(RubySpriter::GimpProcessor)
|
|
521
|
+
allow(RubySpriter::GimpProcessor).to receive(:new).and_return(gimp_double)
|
|
522
|
+
allow(gimp_double).to receive(:process).and_return('input-scaled-50pct.png')
|
|
523
|
+
|
|
524
|
+
processor = RubySpriter::Processor.new(
|
|
525
|
+
image: fixture_with_meta,
|
|
526
|
+
scale_percent: 50,
|
|
527
|
+
overwrite: true
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
allow(processor).to receive(:check_dependencies!)
|
|
531
|
+
allow(processor).to receive(:setup_temp_directory)
|
|
532
|
+
allow(processor).to receive(:cleanup)
|
|
533
|
+
allow(processor).to receive(:gimp_path).and_return('/usr/bin/gimp')
|
|
534
|
+
|
|
535
|
+
result = nil
|
|
536
|
+
expect { result = processor.run }.to output(/SUCCESS/).to_stdout
|
|
537
|
+
|
|
538
|
+
# Should return the base filename (no timestamp)
|
|
539
|
+
expect(result[:output_file]).to eq('input-scaled-50pct.png')
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
it 'generates unique output filename when --output is used without --overwrite' do
|
|
543
|
+
# Mock all dependencies
|
|
544
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_exists!)
|
|
545
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_readable!)
|
|
546
|
+
|
|
547
|
+
# Mock ensure_unique_output to verify it's called correctly
|
|
548
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:ensure_unique_output) do |path, overwrite:|
|
|
549
|
+
expect(path).to eq('custom_output.png')
|
|
550
|
+
expect(overwrite).to eq(false)
|
|
551
|
+
'custom_output_20251023_123456_789.png'
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
# Mock GimpProcessor
|
|
555
|
+
gimp_double = instance_double(RubySpriter::GimpProcessor)
|
|
556
|
+
allow(RubySpriter::GimpProcessor).to receive(:new).and_return(gimp_double)
|
|
557
|
+
allow(gimp_double).to receive(:process).and_return('temp-processed.png')
|
|
558
|
+
|
|
559
|
+
# Mock file operations
|
|
560
|
+
allow(FileUtils).to receive(:cp)
|
|
561
|
+
|
|
562
|
+
processor = RubySpriter::Processor.new(
|
|
563
|
+
image: fixture_with_meta,
|
|
564
|
+
remove_bg: true,
|
|
565
|
+
output: 'custom_output.png',
|
|
566
|
+
overwrite: false
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
allow(processor).to receive(:check_dependencies!)
|
|
570
|
+
allow(processor).to receive(:setup_temp_directory)
|
|
571
|
+
allow(processor).to receive(:cleanup)
|
|
572
|
+
allow(processor).to receive(:gimp_path).and_return('/usr/bin/gimp')
|
|
573
|
+
|
|
574
|
+
result = nil
|
|
575
|
+
expect { result = processor.run }.to output(/SUCCESS/).to_stdout
|
|
576
|
+
|
|
577
|
+
# Should return unique filename
|
|
578
|
+
expect(result[:output_file]).to match(/custom_output_\d{8}_\d{6}_\d{3}\.png$/)
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
it 'uses exact output filename when --output and --overwrite are both specified' do
|
|
582
|
+
# Mock all dependencies
|
|
583
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_exists!)
|
|
584
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_readable!)
|
|
585
|
+
|
|
586
|
+
# Mock ensure_unique_output to verify it's called with overwrite:true
|
|
587
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:ensure_unique_output) do |path, overwrite:|
|
|
588
|
+
expect(path).to eq('exact_output.png')
|
|
589
|
+
expect(overwrite).to eq(true)
|
|
590
|
+
'exact_output.png'
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
# Mock GimpProcessor
|
|
594
|
+
gimp_double = instance_double(RubySpriter::GimpProcessor)
|
|
595
|
+
allow(RubySpriter::GimpProcessor).to receive(:new).and_return(gimp_double)
|
|
596
|
+
allow(gimp_double).to receive(:process).and_return('temp-processed.png')
|
|
597
|
+
|
|
598
|
+
# Mock file operations
|
|
599
|
+
allow(FileUtils).to receive(:cp)
|
|
600
|
+
|
|
601
|
+
processor = RubySpriter::Processor.new(
|
|
602
|
+
image: fixture_with_meta,
|
|
603
|
+
scale_percent: 50,
|
|
604
|
+
output: 'exact_output.png',
|
|
605
|
+
overwrite: true
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
allow(processor).to receive(:check_dependencies!)
|
|
609
|
+
allow(processor).to receive(:setup_temp_directory)
|
|
610
|
+
allow(processor).to receive(:cleanup)
|
|
611
|
+
allow(processor).to receive(:gimp_path).and_return('/usr/bin/gimp')
|
|
612
|
+
|
|
613
|
+
result = nil
|
|
614
|
+
expect { result = processor.run }.to output(/SUCCESS/).to_stdout
|
|
615
|
+
|
|
616
|
+
# Should return exact filename (no timestamp)
|
|
617
|
+
expect(result[:output_file]).to eq('exact_output.png')
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
it 'generates unique filename when using --sharpen alone without --output' do
|
|
621
|
+
# Mock all dependencies
|
|
622
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_exists!)
|
|
623
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_readable!)
|
|
624
|
+
|
|
625
|
+
# Mock GimpProcessor to return a sharpened file
|
|
626
|
+
gimp_double = instance_double(RubySpriter::GimpProcessor)
|
|
627
|
+
allow(RubySpriter::GimpProcessor).to receive(:new).and_return(gimp_double)
|
|
628
|
+
allow(gimp_double).to receive(:process).and_return('input-sharpened_20251023_123456_789.png')
|
|
629
|
+
|
|
630
|
+
processor = RubySpriter::Processor.new(
|
|
631
|
+
image: fixture_with_meta,
|
|
632
|
+
sharpen: true,
|
|
633
|
+
overwrite: false
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
allow(processor).to receive(:check_dependencies!)
|
|
637
|
+
allow(processor).to receive(:setup_temp_directory)
|
|
638
|
+
allow(processor).to receive(:cleanup)
|
|
639
|
+
|
|
640
|
+
result = nil
|
|
641
|
+
expect { result = processor.run }.to output(/SUCCESS/).to_stdout
|
|
642
|
+
|
|
643
|
+
# Should return the uniquely-named sharpened file
|
|
644
|
+
expect(result[:output_file]).to match(/-sharpened.*\.png$/)
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
it 'overwrites sharpened file when --sharpen with --overwrite' do
|
|
648
|
+
# Mock all dependencies
|
|
649
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_exists!)
|
|
650
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_readable!)
|
|
651
|
+
|
|
652
|
+
# Mock GimpProcessor - with overwrite:true, should return base filename
|
|
653
|
+
gimp_double = instance_double(RubySpriter::GimpProcessor)
|
|
654
|
+
allow(RubySpriter::GimpProcessor).to receive(:new).and_return(gimp_double)
|
|
655
|
+
allow(gimp_double).to receive(:process).and_return('input-sharpened.png')
|
|
656
|
+
|
|
657
|
+
processor = RubySpriter::Processor.new(
|
|
658
|
+
image: fixture_with_meta,
|
|
659
|
+
sharpen: true,
|
|
660
|
+
overwrite: true
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
allow(processor).to receive(:check_dependencies!)
|
|
664
|
+
allow(processor).to receive(:setup_temp_directory)
|
|
665
|
+
allow(processor).to receive(:cleanup)
|
|
666
|
+
|
|
667
|
+
result = nil
|
|
668
|
+
expect { result = processor.run }.to output(/SUCCESS/).to_stdout
|
|
669
|
+
|
|
670
|
+
# Should return the base filename (no timestamp)
|
|
671
|
+
expect(result[:output_file]).to eq('input-sharpened.png')
|
|
672
|
+
end
|
|
428
673
|
end
|
|
429
674
|
end
|
|
430
675
|
|
|
@@ -763,6 +1008,19 @@ RSpec.describe RubySpriter::CLI do
|
|
|
763
1008
|
|
|
764
1009
|
described_class.start(['--video', fixture_video, '--output', 'custom_spritesheet.png'])
|
|
765
1010
|
end
|
|
1011
|
+
|
|
1012
|
+
it 'works with --save-frames option' do
|
|
1013
|
+
processor_double = instance_double(RubySpriter::Processor)
|
|
1014
|
+
allow(processor_double).to receive(:run)
|
|
1015
|
+
|
|
1016
|
+
allow(RubySpriter::Processor).to receive(:new) do |options|
|
|
1017
|
+
expect(options[:video]).to eq(fixture_video)
|
|
1018
|
+
expect(options[:save_frames]).to eq(true)
|
|
1019
|
+
processor_double
|
|
1020
|
+
end
|
|
1021
|
+
|
|
1022
|
+
described_class.start(['--video', fixture_video, '--save-frames'])
|
|
1023
|
+
end
|
|
766
1024
|
end
|
|
767
1025
|
|
|
768
1026
|
describe 'preset configurations' do
|
|
@@ -1117,6 +1375,83 @@ RSpec.describe RubySpriter::CLI do
|
|
|
1117
1375
|
'--debug'
|
|
1118
1376
|
])
|
|
1119
1377
|
end
|
|
1378
|
+
|
|
1379
|
+
it 'works with --overwrite option' do
|
|
1380
|
+
processor_double = instance_double(RubySpriter::Processor)
|
|
1381
|
+
allow(processor_double).to receive(:run)
|
|
1382
|
+
|
|
1383
|
+
allow(RubySpriter::Processor).to receive(:new) do |options|
|
|
1384
|
+
expect(options[:consolidate]).to eq([spritesheet_4x2, spritesheet_6x2])
|
|
1385
|
+
expect(options[:overwrite]).to eq(true)
|
|
1386
|
+
processor_double
|
|
1387
|
+
end
|
|
1388
|
+
|
|
1389
|
+
described_class.start(['--consolidate', "#{spritesheet_4x2},#{spritesheet_6x2}", '--overwrite'])
|
|
1390
|
+
end
|
|
1391
|
+
end
|
|
1392
|
+
|
|
1393
|
+
describe 'default output filename behavior' do
|
|
1394
|
+
it 'generates consolidated_spritesheet.png when no --output specified' do
|
|
1395
|
+
# Mock all the dependencies
|
|
1396
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_exists!)
|
|
1397
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:ensure_unique_output) do |path, overwrite:|
|
|
1398
|
+
expect(path).to eq('consolidated_spritesheet.png')
|
|
1399
|
+
expect(overwrite).to eq(false)
|
|
1400
|
+
'consolidated_spritesheet.png'
|
|
1401
|
+
end
|
|
1402
|
+
|
|
1403
|
+
consolidator_double = instance_double(RubySpriter::Consolidator)
|
|
1404
|
+
allow(RubySpriter::Consolidator).to receive(:new).and_return(consolidator_double)
|
|
1405
|
+
allow(consolidator_double).to receive(:consolidate).and_return({
|
|
1406
|
+
output_file: 'consolidated_spritesheet.png',
|
|
1407
|
+
columns: 2,
|
|
1408
|
+
rows: 4,
|
|
1409
|
+
frames: 8
|
|
1410
|
+
})
|
|
1411
|
+
|
|
1412
|
+
processor = RubySpriter::Processor.new(
|
|
1413
|
+
consolidate: [spritesheet_4x2, spritesheet_6x2],
|
|
1414
|
+
overwrite: false
|
|
1415
|
+
)
|
|
1416
|
+
|
|
1417
|
+
allow(processor).to receive(:check_dependencies!)
|
|
1418
|
+
allow(processor).to receive(:setup_temp_directory)
|
|
1419
|
+
allow(processor).to receive(:cleanup)
|
|
1420
|
+
|
|
1421
|
+
# Capture output to suppress console messages
|
|
1422
|
+
expect { processor.run }.to output(/SUCCESS/).to_stdout
|
|
1423
|
+
end
|
|
1424
|
+
|
|
1425
|
+
it 'respects --overwrite flag with default filename' do
|
|
1426
|
+
# Mock all the dependencies
|
|
1427
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:validate_exists!)
|
|
1428
|
+
allow(RubySpriter::Utils::FileHelper).to receive(:ensure_unique_output) do |path, overwrite:|
|
|
1429
|
+
expect(path).to eq('consolidated_spritesheet.png')
|
|
1430
|
+
expect(overwrite).to eq(true)
|
|
1431
|
+
'consolidated_spritesheet.png'
|
|
1432
|
+
end
|
|
1433
|
+
|
|
1434
|
+
consolidator_double = instance_double(RubySpriter::Consolidator)
|
|
1435
|
+
allow(RubySpriter::Consolidator).to receive(:new).and_return(consolidator_double)
|
|
1436
|
+
allow(consolidator_double).to receive(:consolidate).and_return({
|
|
1437
|
+
output_file: 'consolidated_spritesheet.png',
|
|
1438
|
+
columns: 2,
|
|
1439
|
+
rows: 4,
|
|
1440
|
+
frames: 8
|
|
1441
|
+
})
|
|
1442
|
+
|
|
1443
|
+
processor = RubySpriter::Processor.new(
|
|
1444
|
+
consolidate: [spritesheet_4x2, spritesheet_6x2],
|
|
1445
|
+
overwrite: true
|
|
1446
|
+
)
|
|
1447
|
+
|
|
1448
|
+
allow(processor).to receive(:check_dependencies!)
|
|
1449
|
+
allow(processor).to receive(:setup_temp_directory)
|
|
1450
|
+
allow(processor).to receive(:cleanup)
|
|
1451
|
+
|
|
1452
|
+
# Capture output to suppress console messages
|
|
1453
|
+
expect { processor.run }.to output(/SUCCESS/).to_stdout
|
|
1454
|
+
end
|
|
1120
1455
|
end
|
|
1121
1456
|
end
|
|
1122
1457
|
|
|
@@ -1138,5 +1473,33 @@ RSpec.describe RubySpriter::CLI do
|
|
|
1138
1473
|
end.to raise_error(SystemExit)
|
|
1139
1474
|
end
|
|
1140
1475
|
end
|
|
1476
|
+
|
|
1477
|
+
describe '--split option' do
|
|
1478
|
+
it 'parses split option with R:C format' do
|
|
1479
|
+
processor_double = instance_double(RubySpriter::Processor)
|
|
1480
|
+
allow(processor_double).to receive(:run)
|
|
1481
|
+
|
|
1482
|
+
allow(RubySpriter::Processor).to receive(:new) do |options|
|
|
1483
|
+
expect(options[:split]).to eq('4:4')
|
|
1484
|
+
processor_double
|
|
1485
|
+
end
|
|
1486
|
+
|
|
1487
|
+
described_class.start(['--image', 'test.png', '--split', '4:4'])
|
|
1488
|
+
end
|
|
1489
|
+
end
|
|
1490
|
+
|
|
1491
|
+
describe '--override-md option' do
|
|
1492
|
+
it 'sets override_md option to true' do
|
|
1493
|
+
processor_double = instance_double(RubySpriter::Processor)
|
|
1494
|
+
allow(processor_double).to receive(:run)
|
|
1495
|
+
|
|
1496
|
+
allow(RubySpriter::Processor).to receive(:new) do |options|
|
|
1497
|
+
expect(options[:override_md]).to eq(true)
|
|
1498
|
+
processor_double
|
|
1499
|
+
end
|
|
1500
|
+
|
|
1501
|
+
described_class.start(['--image', 'test.png', '--split', '4:4', '--override-md'])
|
|
1502
|
+
end
|
|
1503
|
+
end
|
|
1141
1504
|
end
|
|
1142
1505
|
end
|