smart_ass 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rspec', '~> 2.11.0'
6
+ gem 'rr', '~> 1.0.4'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Stefano Pigozzi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # SmartAss
2
+
3
+ Utility for Y'CbCr ColorMatrix conversion of ASS Subtitles
4
+
5
+ If you are smart and know about ASS subtitles you are probably wondering
6
+ what Y'CbCr has to do with ASS. As a matter of fact ASS uses RGB hex for
7
+ color representation.
8
+
9
+ The problem is some subtitles renderers (VSFilter) on Windows decided it was
10
+ too stupid to render the RGB directly (don't even ask about it). So they
11
+ started doing a `R'G'B' ->(BT.601) Y'CbCr ->(BT.601) R'G'B'` chain of
12
+ conversions.
13
+
14
+ The problem is some fansubbers started to actually author content for
15
+ these broken renderers and the result is you get broken colors on
16
+ subtitles in the video players that are actually getting it right.
17
+
18
+ This tool is an attempt to fix this problem. Using mkvextract/mkvmerge
19
+ and some matrix multiplications it generates a new ass file where the
20
+ colors passed through the following conversions:
21
+
22
+ R'G'B' ->(BT.601) Y'CbCr ->(BT.709) R'G'B'
23
+
24
+ ## Installation
25
+
26
+ Make sure you have mkvtoolnix (mkvmerge and mkvextract) utilities
27
+ installed and in your path. For example, on OSX and homebrew it's as simple as:
28
+
29
+ brew install mkvtoolnix
30
+
31
+ Install the Ruby Gem:
32
+
33
+ [sudo] gem smart_ass
34
+
35
+ ## Usage
36
+
37
+ This is really minimal. No options for now.
38
+
39
+ smart_ass path/to/movie.mkv
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create new Pull Request
48
+
49
+ ## Contacts
50
+
51
+ Look for pigoz on irc.freenode.net.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new('spec')
6
+
7
+ task :default => :spec
data/bin/smart_ass ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: syntax=ruby
3
+ $:.unshift File.join(File.dirname(File.dirname(__FILE__)), 'lib')
4
+
5
+ require 'smart_ass'
6
+
7
+ if arg = ARGV[0] then
8
+ app = SmartAss::App.new(arg)
9
+ app.run
10
+ else
11
+ puts "Please provide an input file"
12
+ exit()
13
+ end
@@ -0,0 +1,67 @@
1
+ class SmartAss::App
2
+ def initialize(file)
3
+ @ass = SmartAss::Ass.new(file)
4
+ @file = Pathname.new(file)
5
+ end
6
+
7
+ def run
8
+ # function composition baby!
9
+ write_ass_script replace_colors extract track
10
+ end
11
+
12
+ private
13
+ def write_ass_script(ass_script)
14
+ script_file_basename = "#{@file.basename(@file.extname)}.ass"
15
+ File.open(@file.expand_path.dirname + script_file_basename, "w") {|f|
16
+ f.puts(ass_script)
17
+ }
18
+ end
19
+
20
+ def replace_colors(ass_script)
21
+ ass_script.gsub(/&H[\da-zA-Z]{6,8}/) {|c| convert_color(c)}
22
+ end
23
+
24
+ def convert_color(c)
25
+ @_colors_cache ||= Hash.new
26
+
27
+ unless @_colors_cache[c]
28
+ color = SmartAss::RGBAColor.from_ass(c)
29
+
30
+ bt601 = SmartAss::YCbCrConverter.new(:bt601)
31
+ bt709 = SmartAss::YCbCrConverter.new(:bt709)
32
+
33
+ ycbcr = bt601.to_ycbcr(*color.rgb_components)
34
+ rgb = bt709.to_rgb(*ycbcr)
35
+
36
+ puts "[converted color] ass-hex: #{c}, rgb: #{color.rgb_components}, " \
37
+ "ycbcr: #{ycbcr}, mangled-rgb: #{rgb}"
38
+
39
+ @_colors_cache[c] = SmartAss::RGBAColor.from_rgba(*rgb).to_ass
40
+ end
41
+
42
+ @_colors_cache[c]
43
+ end
44
+
45
+ def track
46
+ if @ass.tracks.size > 1
47
+ track = ask_for_track
48
+ elsif @ass.tracks.size == 1
49
+ track = @ass.tracks[0]
50
+ else
51
+ puts "No ASS tracks found. Aborting."
52
+ exit
53
+ end
54
+ end
55
+
56
+ def ask_for_track
57
+ puts "Please enter a track name - #{@ass.tracks.inspect}:"
58
+ readline.to_i
59
+ rescue
60
+ puts "Invalid track provided. Try again."
61
+ retry
62
+ end
63
+
64
+ def extract(track_id)
65
+ @ass.extract(track_id)
66
+ end
67
+ end
@@ -0,0 +1,40 @@
1
+ require 'pathname'
2
+
3
+ class SmartAss::Ass
4
+ attr_reader :file
5
+
6
+ def initialize(file)
7
+ @file = Pathname.new(file)
8
+ end
9
+
10
+ def tracks
11
+ identify.each_line \
12
+ .map {|l| (l =~ identify_ass_regexp) ? $1 : nil } \
13
+ .compact \
14
+ .map &:to_i
15
+ end
16
+
17
+ # TODO: test this!
18
+ def extract(track_id)
19
+ puts `mkvextract tracks "#{expanded_file}" #{track_id}:#{export_temp_file}`
20
+ IO.read(export_temp_file, :encoding => 'UTF-8') \
21
+ .tap {|_| File.delete(export_temp_file) }
22
+ end
23
+
24
+ private
25
+ def identify
26
+ `mkvmerge -i "#{expanded_file}"`
27
+ end
28
+
29
+ def expanded_file
30
+ file.expand_path
31
+ end
32
+
33
+ def identify_ass_regexp
34
+ /Track ID (\d): subtitles \(S_TEXT\/ASS\)/
35
+ end
36
+
37
+ def export_temp_file
38
+ "/tmp/smart_ass_export-#{Process.pid}"
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ class SmartAss::BT601ColorMatrix < SmartAss::YCbCrColorMatrix
2
+ def initialize
3
+ super(0.299, 0.587, 0.114)
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ class SmartAss::BT709ColorMatrix < SmartAss::YCbCrColorMatrix
2
+ def initialize
3
+ super(0.2126, 0.7152, 0.0722)
4
+ end
5
+
6
+ def offset_vector
7
+ Matrix.column_vector([22.0, 125.0, 125.0])
8
+ end
9
+ end
@@ -0,0 +1,49 @@
1
+ class SmartAss::RGBAColor
2
+ attr_reader :r, :g, :b, :a
3
+
4
+ def self.from_ass(string)
5
+ argb_hex = string.gsub(/^&H/, '')
6
+ components = argb_hex.scan(/.{2}/).map {|h| h.to_i(16)}
7
+ if components.size == 4
8
+ from_argb(*components)
9
+ else
10
+ from_rgba(*components)
11
+ end
12
+ end
13
+
14
+ def self.from_argb(*components)
15
+ from_rgba(*components.push(components.shift))
16
+ end
17
+
18
+ def self.from_rgba(*components)
19
+ new(*components)
20
+ end
21
+
22
+ def to_ass
23
+ hex = argb_components
24
+ .map {|c| c.to_s(16)}
25
+ .map {|c| c.rjust(2, "0")}
26
+ .map(&:upcase)
27
+ .join
28
+ "&H#{hex}"
29
+ end
30
+
31
+ def initialize(r, g, b, a=0x00)
32
+ @r, @g, @b, @a = [r, g, b, a].map {|n| n < 0 ? 0 : n}
33
+ end
34
+
35
+ def components
36
+ [@r, @g, @b, @a]
37
+ end
38
+
39
+ def argb_components
40
+ c = components
41
+ c.unshift(c.pop)
42
+ end
43
+
44
+ def rgb_components
45
+ c = components
46
+ c.pop
47
+ c
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ module SmartAss
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,96 @@
1
+ require 'matrix'
2
+
3
+ class SmartAss::YCbCrColorMatrix
4
+ attr_reader :wr, :wg, :wb
5
+ def initialize(wr, wg, wb)
6
+ @wr, @wg, @wb = wr, wg, wb
7
+ end
8
+
9
+ # basic luma relation: converts R'G'B' -> Y'(B'-Y')(R'-Y')
10
+ def to_ybmyrmy_matrix
11
+ Matrix[
12
+ [ wr, wg, wb ],
13
+ [-wr, -wg, wr+wg],
14
+ [wg+wb, -wg, -wb ],
15
+ ]
16
+ end
17
+
18
+ # RGB -> YPbPr []
19
+ def to_ypbpr_matrix_from_rgb
20
+ scale_rows to_ybmyrmy_matrix, 1.0, 0.5 / (wr+wg), 0.5 / (wg+wb)
21
+ end
22
+
23
+ # YPbPr -> RGB []
24
+ def to_rgb_matrix_from_ypbpr
25
+ zeros to_ypbpr_matrix_from_rgb.inverse
26
+ end
27
+
28
+ # RGB -> YCbCr []
29
+ def to_ycbcr_matrix_from_rgb
30
+ scale_rows to_ypbpr_matrix_from_rgb, 219.0, 224.0, 224.0
31
+ end
32
+
33
+ # YCbCr -> RGB []
34
+ def to_rgb_matrix_from_ycbcr
35
+ zeros to_ycbcr_matrix_from_rgb.inverse
36
+ end
37
+
38
+ # RGB(digital) -> YCbCr []
39
+ def to_ycbcr_matrix_from_rgbd
40
+ scale_rows to_ycbcr_matrix_from_rgb, *[256.0/255.0]*3
41
+ end
42
+
43
+ # YCbCr -> RGB(digital) []
44
+ def to_rgbd_matrix_from_ycbcr
45
+ scale_rows zeros(to_ycbcr_matrix_from_rgbd.inverse), *[256.0*256.0]*3
46
+ end
47
+
48
+ # RGB(digital) -> YCbCr Equation
49
+ def to_ycbcr_from_rgb(*rgb)
50
+ apply_equation(*rgb) do |*rgb|
51
+ offset_vector + (to_ycbcr_matrix_from_rgbd * 1.0 / 256.0) *
52
+ Matrix.column_vector(rgb)
53
+ end
54
+ end
55
+
56
+ # YCbCr -> RGB(digital) Equation
57
+ def to_rgb_from_ycbcr(*ycbcr)
58
+ r = apply_equation(*ycbcr) do |*ycbcr|
59
+ (to_rgbd_matrix_from_ycbcr * 1.0 / 256.0) *
60
+ (Matrix.column_vector(ycbcr) - offset_vector)
61
+ end.map(&:round)
62
+
63
+ clip_rgb(r)
64
+ end
65
+
66
+ private
67
+ def clip_rgb(input)
68
+ input.map {|n| n >= 0 ? n : 0}
69
+ .map {|n| n <= 255 ? n : 255}
70
+ end
71
+
72
+ def offset_vector
73
+ Matrix.column_vector([16.0, 128.0, 128.0])
74
+ end
75
+
76
+ def apply_equation(*inputs, &block)
77
+ block.call(*inputs).to_a.flatten
78
+ end
79
+
80
+ def scale_rows(matrix, *factors)
81
+ raise InvalidArgumentError unless matrix.row_size == factors.size
82
+ Matrix[*factors.each_with_index.map{|factor, index|
83
+ matrix.row(index) * factor
84
+ }]
85
+ end
86
+
87
+ def zeros(matrix)
88
+ matrix.map {|n|
89
+ if n < 1e-5 and n > -1e-5 then
90
+ 0.0
91
+ else
92
+ n
93
+ end
94
+ }
95
+ end
96
+ end
@@ -0,0 +1,15 @@
1
+ class SmartAss::YCbCrConverter
2
+ attr_reader :matrix
3
+
4
+ def initialize(matrix)
5
+ @matrix = SmartAss.const_get("#{matrix.upcase}ColorMatrix".to_sym).new
6
+ end
7
+
8
+ def to_ycbcr(*rgb)
9
+ matrix.to_ycbcr_from_rgb(*rgb)
10
+ end
11
+
12
+ def to_rgb(*ycbcr)
13
+ matrix.to_rgb_from_ycbcr(*ycbcr)
14
+ end
15
+ end
data/lib/smart_ass.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "smart_ass/version"
2
+
3
+ module SmartAss
4
+ autoload :App, 'smart_ass/app'
5
+ autoload :Ass, 'smart_ass/ass'
6
+ autoload :BT601ColorMatrix, 'smart_ass/bt601_colormatrix'
7
+ autoload :BT709ColorMatrix, 'smart_ass/bt709_colormatrix'
8
+ autoload :RGBAColor, 'smart_ass/rgba_color'
9
+ autoload :YCbCrColorMatrix, 'smart_ass/ycbcr_colormatrix'
10
+ autoload :YCbCrConverter, 'smart_ass/ycbcr_converter'
11
+ end
data/smart_ass.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/smart_ass/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Stefano Pigozzi"]
6
+ gem.email = ["stefano.pigozzi@gmail.com"]
7
+ gem.description = %q{Utility for YCbCr ColorMatrix conversion of ASS Subtitles.}
8
+ gem.summary = %q{Utility for YCbCr ColorMatrix conversion of ASS Subtitles.}
9
+ gem.homepage = "http://github.com/pigoz/smart_ass"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "smart_ass"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = SmartAss::VERSION
17
+ end
data/spec/ass_spec.rb ADDED
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ describe SmartAss::Ass do
4
+ subject {
5
+ @file = "path/to/file/movie.mkv"
6
+ SmartAss::Ass.new(@file)
7
+ }
8
+
9
+ it "holds onto a file" do
10
+ subject.file.should == Pathname.new(@file)
11
+ end
12
+
13
+ describe "#tracks" do
14
+ [
15
+ {:f => 'info_1_t_3', :e => [3], :t => 'with 1 element'},
16
+ {:f => 'info_3_t_3_5_6', :e => [3, 5, 6], :t => 'with many elements'},
17
+ {:f => 'info_0', :e => [], :t => 'with 0 elements'},
18
+ ].each do |test|
19
+ it "returns an array of ASS track IDs (#{test[:t]})" do
20
+ mock(subject).identify {
21
+ IO.read(
22
+ File.join(File.dirname(__FILE__), 'fixtures', test[:f]))
23
+ }
24
+ subject.tracks.should == test[:e]
25
+ end # it #tracks
26
+ end # do
27
+ end # describe
28
+
29
+ end
@@ -0,0 +1,15 @@
1
+ File '/Users/stefano/Movies/[UTWoots]_Sword_Art_Online_-_01v2_[720p][E22288BC].mkv': container: Matroska
2
+ Track ID 1: video (V_MPEG4/ISO/AVC)
3
+ Track ID 2: audio (A_AAC)
4
+ Attachment ID 1: type 'application/x-truetype-font', size 168852 bytes, file name 'ACaslonPro-Regular.otf'
5
+ Attachment ID 2: type 'application/x-truetype-font', size 129676 bytes, file name 'GOTHICB.TTF'
6
+ Attachment ID 3: type 'application/x-truetype-font', size 6455918 bytes, file name 'IwaOMinPro-Hv-En.ttf'
7
+ Attachment ID 4: type 'application/x-truetype-font', size 767840 bytes, file name 'LinLibertine_DR.ttf'
8
+ Attachment ID 5: type 'application/x-truetype-font', size 43052 bytes, file name 'Montara-Bold.otf'
9
+ Attachment ID 6: type 'application/x-truetype-font', size 42004 bytes, file name 'Montara-BoldItalic.otf'
10
+ Attachment ID 7: type 'application/x-truetype-font', size 45316 bytes, file name 'Montara-Gothic.otf'
11
+ Attachment ID 8: type 'application/x-truetype-font', size 42608 bytes, file name 'Montara-Italic.TTF'
12
+ Attachment ID 9: type 'application/x-truetype-font', size 53092 bytes, file name 'QuaySansMdITCTT.ttf'
13
+ Attachment ID 10: type 'application/x-truetype-font', size 94300 bytes, file name 'raavi.ttf'
14
+ Attachment ID 11: type 'application/x-truetype-font', size 30352 bytes, file name 'wscsnrg.TTF'
15
+ Chapters: 6 entries
@@ -0,0 +1,16 @@
1
+ File '/Users/stefano/Movies/[UTWoots]_Sword_Art_Online_-_01v2_[720p][E22288BC].mkv': container: Matroska
2
+ Track ID 1: video (V_MPEG4/ISO/AVC)
3
+ Track ID 2: audio (A_AAC)
4
+ Track ID 3: subtitles (S_TEXT/ASS)
5
+ Attachment ID 1: type 'application/x-truetype-font', size 168852 bytes, file name 'ACaslonPro-Regular.otf'
6
+ Attachment ID 2: type 'application/x-truetype-font', size 129676 bytes, file name 'GOTHICB.TTF'
7
+ Attachment ID 3: type 'application/x-truetype-font', size 6455918 bytes, file name 'IwaOMinPro-Hv-En.ttf'
8
+ Attachment ID 4: type 'application/x-truetype-font', size 767840 bytes, file name 'LinLibertine_DR.ttf'
9
+ Attachment ID 5: type 'application/x-truetype-font', size 43052 bytes, file name 'Montara-Bold.otf'
10
+ Attachment ID 6: type 'application/x-truetype-font', size 42004 bytes, file name 'Montara-BoldItalic.otf'
11
+ Attachment ID 7: type 'application/x-truetype-font', size 45316 bytes, file name 'Montara-Gothic.otf'
12
+ Attachment ID 8: type 'application/x-truetype-font', size 42608 bytes, file name 'Montara-Italic.TTF'
13
+ Attachment ID 9: type 'application/x-truetype-font', size 53092 bytes, file name 'QuaySansMdITCTT.ttf'
14
+ Attachment ID 10: type 'application/x-truetype-font', size 94300 bytes, file name 'raavi.ttf'
15
+ Attachment ID 11: type 'application/x-truetype-font', size 30352 bytes, file name 'wscsnrg.TTF'
16
+ Chapters: 6 entries
@@ -0,0 +1,19 @@
1
+ File '/Users/stefano/Movies/[UTWoots]_Sword_Art_Online_-_01v2_[720p][E22288BC].mkv': container: Matroska
2
+ Track ID 1: video (V_MPEG4/ISO/AVC)
3
+ Track ID 2: audio (A_AAC)
4
+ Track ID 3: subtitles (S_TEXT/ASS)
5
+ Track ID 4: audio (A_AAC)
6
+ Track ID 5: subtitles (S_TEXT/ASS)
7
+ Track ID 6: subtitles (S_TEXT/ASS)
8
+ Attachment ID 1: type 'application/x-truetype-font', size 168852 bytes, file name 'ACaslonPro-Regular.otf'
9
+ Attachment ID 2: type 'application/x-truetype-font', size 129676 bytes, file name 'GOTHICB.TTF'
10
+ Attachment ID 3: type 'application/x-truetype-font', size 6455918 bytes, file name 'IwaOMinPro-Hv-En.ttf'
11
+ Attachment ID 4: type 'application/x-truetype-font', size 767840 bytes, file name 'LinLibertine_DR.ttf'
12
+ Attachment ID 5: type 'application/x-truetype-font', size 43052 bytes, file name 'Montara-Bold.otf'
13
+ Attachment ID 6: type 'application/x-truetype-font', size 42004 bytes, file name 'Montara-BoldItalic.otf'
14
+ Attachment ID 7: type 'application/x-truetype-font', size 45316 bytes, file name 'Montara-Gothic.otf'
15
+ Attachment ID 8: type 'application/x-truetype-font', size 42608 bytes, file name 'Montara-Italic.TTF'
16
+ Attachment ID 9: type 'application/x-truetype-font', size 53092 bytes, file name 'QuaySansMdITCTT.ttf'
17
+ Attachment ID 10: type 'application/x-truetype-font', size 94300 bytes, file name 'raavi.ttf'
18
+ Attachment ID 11: type 'application/x-truetype-font', size 30352 bytes, file name 'wscsnrg.TTF'
19
+ Chapters: 6 entries
@@ -0,0 +1,43 @@
1
+ require "spec_helper.rb"
2
+
3
+ describe SmartAss::RGBAColor do
4
+
5
+ it "holds onto its components" do
6
+ c = SmartAss::RGBAColor.new(50, 150, 200, 255)
7
+ c.r.should == 50
8
+ c.g.should == 150
9
+ c.b.should == 200
10
+ c.a.should == 255
11
+ c.components.should == [50, 150, 200, 255]
12
+ end
13
+
14
+ it "can be created from a hex rgba" do
15
+ c = SmartAss::RGBAColor.from_rgba(0xff, 0xff, 0xff, 0x00)
16
+ c.components.should == [255, 255, 255, 0]
17
+ end
18
+
19
+ it "can be created from a hex argb" do
20
+ c = SmartAss::RGBAColor.from_argb(0x00, 0xff, 0xff, 0xff)
21
+ c.components.should == [255, 255, 255, 0]
22
+ end
23
+
24
+ it "can be created from an .ass hex (with alpha)" do
25
+ c = SmartAss::RGBAColor.from_ass("&H00FFFFFF")
26
+ c.components.should == [255, 255, 255, 0]
27
+ end
28
+
29
+ it "can be created from an .ass hex (without alpha)" do
30
+ c = SmartAss::RGBAColor.from_ass("&HFFFFFF")
31
+ c.components.should == [255, 255, 255, 0]
32
+ end
33
+
34
+ describe "#to_ass" do
35
+ it "returns ass repesentation" do
36
+ c = SmartAss::RGBAColor.from_argb(0x00, 0xff, 0xff, 0xff)
37
+ c.to_ass.should == "&H00FFFFFF"
38
+
39
+ c = SmartAss::RGBAColor.from_rgba(*[0xff, 0xff, 0xff] + [0x00])
40
+ c.to_ass.should == "&H00FFFFFF"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ Bundler.require(:default)
4
+
5
+ RSpec.configure do |config|
6
+ config.treat_symbols_as_metadata_keys_with_true_values = true
7
+ config.run_all_when_everything_filtered = true
8
+ config.filter_run :focus
9
+ config.order = 'random'
10
+ config.mock_with :rr
11
+ end
@@ -0,0 +1,15 @@
1
+ require "spec_helper"
2
+
3
+ describe SmartAss::YCbCrConverter do
4
+
5
+ it "can mangle RGB doing RGB ->(bt601) YCbCr ->(b709) RGB" do
6
+ bt601 = SmartAss::YCbCrConverter.new(:bt601)
7
+ bt709 = SmartAss::YCbCrConverter.new(:bt709)
8
+
9
+ yuv = bt601.to_ycbcr(16, 138, 187)
10
+ rgb = bt709.to_rgb(*yuv)
11
+
12
+ rgb.should =~ [5, 129, 191]
13
+ end
14
+
15
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: smart_ass
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Stefano Pigozzi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-17 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Utility for YCbCr ColorMatrix conversion of ASS Subtitles.
15
+ email:
16
+ - stefano.pigozzi@gmail.com
17
+ executables:
18
+ - smart_ass
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .gitignore
23
+ - .rspec
24
+ - Gemfile
25
+ - LICENSE
26
+ - README.md
27
+ - Rakefile
28
+ - bin/smart_ass
29
+ - lib/smart_ass.rb
30
+ - lib/smart_ass/app.rb
31
+ - lib/smart_ass/ass.rb
32
+ - lib/smart_ass/bt601_colormatrix.rb
33
+ - lib/smart_ass/bt709_colormatrix.rb
34
+ - lib/smart_ass/rgba_color.rb
35
+ - lib/smart_ass/version.rb
36
+ - lib/smart_ass/ycbcr_colormatrix.rb
37
+ - lib/smart_ass/ycbcr_converter.rb
38
+ - smart_ass.gemspec
39
+ - spec/ass_spec.rb
40
+ - spec/fixtures/info_0
41
+ - spec/fixtures/info_1_t_3
42
+ - spec/fixtures/info_3_t_3_5_6
43
+ - spec/rgba_color_spec.rb
44
+ - spec/spec_helper.rb
45
+ - spec/ycbcr_converter_spec.rb
46
+ homepage: http://github.com/pigoz/smart_ass
47
+ licenses: []
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 1.8.24
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Utility for YCbCr ColorMatrix conversion of ASS Subtitles.
70
+ test_files:
71
+ - spec/ass_spec.rb
72
+ - spec/fixtures/info_0
73
+ - spec/fixtures/info_1_t_3
74
+ - spec/fixtures/info_3_t_3_5_6
75
+ - spec/rgba_color_spec.rb
76
+ - spec/spec_helper.rb
77
+ - spec/ycbcr_converter_spec.rb