julik-depix 1.0.2
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.
- data/DPX_HEADER_STRUCTURE.txt +97 -0
- data/History.txt +18 -0
- data/Manifest.txt +17 -0
- data/README.txt +59 -0
- data/Rakefile +19 -0
- data/bin/depix-describe +23 -0
- data/lib/depix/compact_structs.rb +42 -0
- data/lib/depix/dict.rb +357 -0
- data/lib/depix/enums.rb +43 -0
- data/lib/depix/struct_explainer.rb +59 -0
- data/lib/depix/structs.rb +137 -0
- data/lib/depix.rb +142 -0
- data/test/samples/E012_P001_L000002_lin.0001.dpx +0 -0
- data/test/samples/E012_P001_L000002_lin.0002.dpx +0 -0
- data/test/samples/E012_P001_L000002_log.0001.dpx +0 -0
- data/test/samples/E012_P001_L000002_log.0002.dpx +0 -0
- data/test/test_depix.rb +98 -0
- data/test/test_dict.rb +686 -0
- metadata +84 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
= DPX header structure description
|
2
|
+
|
3
|
+
DPX metadata gets returned as a Depix::DPX object with nested properties.
|
4
|
+
|
5
|
+
meta.file.magic # => "SDPX"
|
6
|
+
|
7
|
+
== Metadata structure
|
8
|
+
|
9
|
+
* <tt>file</tt> (Depix::FileInfo) File information:
|
10
|
+
* <tt>magic</tt> (String) Endianness (SDPX is big endian) - required
|
11
|
+
* <tt>image_offset</tt> Offset to image data in bytes - required
|
12
|
+
* <tt>version</tt> (String) Version of header format - required
|
13
|
+
* <tt>file_size</tt> Total image size in bytes - required
|
14
|
+
* <tt>ditto_key</tt> Whether the basic headers stay the same through the sequence (1 means they do)
|
15
|
+
* <tt>generic_size</tt> Generic header length
|
16
|
+
* <tt>industry_size</tt> Industry header length
|
17
|
+
* <tt>user_size</tt> User header length
|
18
|
+
* <tt>filename</tt> (String) Original filename
|
19
|
+
* <tt>timestamp</tt> (String) Creation 15
|
20
|
+
* <tt>creator</tt> (String) Creator application
|
21
|
+
* <tt>roject</tt> (String) Project name
|
22
|
+
* <tt>copyright</tt> (String) Copyright
|
23
|
+
* <tt>encrypt_key</tt> Encryption key
|
24
|
+
* <tt>reserve</tt> (String)
|
25
|
+
* <tt>image</tt> (Depix::ImageInfo) Image information:
|
26
|
+
* <tt>orientation</tt> (Integer) Orientation descriptor - required
|
27
|
+
* <tt>number_elements</tt> (Integer) How many elements to scan - required
|
28
|
+
* <tt>pixels_per_line</tt> Pixels per horizontal line - required
|
29
|
+
* <tt>lines_per_element</tt> Line count - required
|
30
|
+
* <tt>image_elements</tt> (Array of 8 Depix::ImageElement fields) Image elements:
|
31
|
+
* <tt>data_sign</tt> Data sign (0=unsigned, 1=signed). Core is unsigned - required
|
32
|
+
* <tt>low_data</tt> Reference low data code value
|
33
|
+
* <tt>low_quantity</tt> (Float) Reference low quantity represented
|
34
|
+
* <tt>high_data</tt> Reference high data code value (1023 for 10bit per channel)
|
35
|
+
* <tt>high_quantity</tt> (Float) Reference high quantity represented
|
36
|
+
* <tt>descriptor</tt> (Integer) Descriptor for this image element (ie Video or Film), by enum - required
|
37
|
+
* <tt>transfer</tt> (Integer) Transfer function (ie Linear), by enum - required
|
38
|
+
* <tt>colorimetric</tt> (Integer) Colorimetric (ie YcbCr), by enum - required
|
39
|
+
* <tt>bit_size</tt> (Integer) Bit size for element (ie 10) - required
|
40
|
+
* <tt>packing</tt> (Integer) Packing (0=Packed into 32-bit words, 1=Filled to 32-bit words)) - required
|
41
|
+
* <tt>encoding</tt> (Integer) Encoding (0=None, 1=RLE) - required
|
42
|
+
* <tt>data_offset</tt> Offset to data for this image element - required
|
43
|
+
* <tt>end_of_line_padding</tt> End-of-line padding for this image element
|
44
|
+
* <tt>end_of_image_padding</tt> End-of-line padding for this image element
|
45
|
+
* <tt>description</tt> (String)
|
46
|
+
* <tt>reserve</tt> (String)
|
47
|
+
* <tt>orientation</tt> (Depix::OrientationInfo) Orientation:
|
48
|
+
* <tt>x_offset</tt>
|
49
|
+
* <tt>y_offset</tt>
|
50
|
+
* <tt>x_center</tt> (Float)
|
51
|
+
* <tt>y_center</tt> (Float)
|
52
|
+
* <tt>x_size</tt> Original X size
|
53
|
+
* <tt>y_size</tt> Original Y size
|
54
|
+
* <tt>filename</tt> (String) Source image filename
|
55
|
+
* <tt>timestamp</tt> (String) Source image/tape timestamp
|
56
|
+
* <tt>device</tt> (String) Input device or tape
|
57
|
+
* <tt>serial</tt> (String) Input device serial number
|
58
|
+
* <tt>border</tt> (Array of 4 Integer fields) Border validity: XL, XR, YT, YB:
|
59
|
+
* <tt>aspect_ratio</tt> (Array of 2 fields) Aspect (H:V):
|
60
|
+
* <tt>reserve</tt> (String)
|
61
|
+
* <tt>film</tt> (Depix::FilmInfo) Film industry info:
|
62
|
+
* <tt>id</tt> (String) Film mfg. ID code (2 digits from film edge code)
|
63
|
+
* <tt>type</tt> (String) Film type (2 digits from film edge code)
|
64
|
+
* <tt>offset</tt> (String) Offset in perfs (2 digits from film edge code)
|
65
|
+
* <tt>prefix</tt> (String) Prefix (6 digits from film edge code
|
66
|
+
* <tt>count</tt> (String) Count (4 digits from film edge code)
|
67
|
+
* <tt>format</tt> (String) Format (e.g. Academy)
|
68
|
+
* <tt>frame_position</tt> Frame position in sequence
|
69
|
+
* <tt>sequence_extent</tt> Sequence length
|
70
|
+
* <tt>held_count</tt> For how many frames the frame is held
|
71
|
+
* <tt>frame_rate</tt> (Float) Frame rate
|
72
|
+
* <tt>shutter_angle</tt> (Float) Shutter angle
|
73
|
+
* <tt>frame_id</tt> (String) Frame identification (keyframe)
|
74
|
+
* <tt>slate</tt> (String) Slate information
|
75
|
+
* <tt>reserve</tt> (String)
|
76
|
+
* <tt>television</tt> (Depix::TelevisionInfo) TV industry info:
|
77
|
+
* <tt>time_code</tt> Timecode, formatted as HH:MM:SS:FF in the 4 higher bits of each 8bit group
|
78
|
+
* <tt>user_bits</tt> Timecode UBITs
|
79
|
+
* <tt>interlace</tt> (Integer) Interlace (0 = noninterlaced; 1 = 2:1 interlace
|
80
|
+
* <tt>field_number</tt> (Integer) Field number
|
81
|
+
* <tt>video_signal</tt> (Integer) Video signal (by enum)
|
82
|
+
* <tt>padding</tt> (Integer) Zero (for byte alignment)
|
83
|
+
* <tt>horizontal_sample_rate</tt> (Float) Horizontal sampling Hz
|
84
|
+
* <tt>vertical_sample_rate</tt> (Float) Vertical sampling Hz
|
85
|
+
* <tt>frame_rate</tt> (Float) Frame rate
|
86
|
+
* <tt>time_offset</tt> (Float) From sync pulse to first pixel
|
87
|
+
* <tt>gamma</tt> (Float) Gamma
|
88
|
+
* <tt>black_level</tt> (Float) Black pedestal code value
|
89
|
+
* <tt>black_gain</tt> (Float) Black gain code value
|
90
|
+
* <tt>break_point</tt> (Float) Break point (?)
|
91
|
+
* <tt>white_level</tt> (Float) White level
|
92
|
+
* <tt>integration_times</tt> (Float) Integration times (S)
|
93
|
+
* <tt>reserve</tt> (Float)
|
94
|
+
* <tt>user</tt> (Depix::UserInfo) User info:
|
95
|
+
* <tt>id</tt> (String) Name of the user data tag
|
96
|
+
* <tt>user_data_ptr</tt>
|
97
|
+
|
data/History.txt
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
=== 1.0.2 / 2008-12-23
|
2
|
+
|
3
|
+
* Do not cleanup null bytes and terminators in the middle of char[] attributes (something useful might be in there)
|
4
|
+
|
5
|
+
=== 1.0.2 / 2008-12-19
|
6
|
+
|
7
|
+
* refactor struct parsing completely
|
8
|
+
|
9
|
+
=== 1.0.1 / 2008-12-18
|
10
|
+
|
11
|
+
* small doc and usability improvements
|
12
|
+
|
13
|
+
=== 1.0.0 / 2008-12-18
|
14
|
+
|
15
|
+
* 1 major enhancement
|
16
|
+
|
17
|
+
* Birthday!
|
18
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
DPX_HEADER_STRUCTURE.txt
|
5
|
+
Rakefile
|
6
|
+
bin/depix-describe
|
7
|
+
lib/depix.rb
|
8
|
+
lib/depix/struct_explainer.rb
|
9
|
+
lib/depix/structs.rb
|
10
|
+
lib/depix/compact_structs.rb
|
11
|
+
lib/depix/enums.rb
|
12
|
+
lib/depix/dict.rb
|
13
|
+
test/test_depix.rb
|
14
|
+
test/samples/E012_P001_L000002_lin.0001.dpx
|
15
|
+
test/samples/E012_P001_L000002_lin.0002.dpx
|
16
|
+
test/samples/E012_P001_L000002_log.0001.dpx
|
17
|
+
test/samples/E012_P001_L000002_log.0002.dpx
|
data/README.txt
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
= depix
|
2
|
+
|
3
|
+
* http://wiretap.rubyforge.org/depix
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Read DPX file metadata
|
8
|
+
|
9
|
+
== SYNOPSIS:
|
10
|
+
|
11
|
+
meta = Depix.from_file(dpx_file_path)
|
12
|
+
puts meta.time_code #=> 10:00:00:02
|
13
|
+
|
14
|
+
The data returned is described in the DPX_HEADER_STRUCTURE[link:files/DPX_HEADER_STRUCTURE_txt.html]. It's
|
15
|
+
a vanilla Ruby object with no extra methods except for the readers that have the same name as the specified
|
16
|
+
fields
|
17
|
+
|
18
|
+
The gem also contains an executable called depix-desribe which can be used from the command line
|
19
|
+
|
20
|
+
$book depix-describe 001_PTAPE_001.001.dpx
|
21
|
+
|
22
|
+
== NOTES:
|
23
|
+
|
24
|
+
In the future there will be a possibility to modify and commit the headers, but it's not a priority at this time.
|
25
|
+
|
26
|
+
Autodesk IFFS systems write the reel name for the file to the orientation.device field
|
27
|
+
|
28
|
+
== REQUIREMENTS:
|
29
|
+
|
30
|
+
* timecode gem (sudo gem install timecode)
|
31
|
+
|
32
|
+
== INSTALL:
|
33
|
+
|
34
|
+
* sudo gem install depix
|
35
|
+
|
36
|
+
== LICENSE:
|
37
|
+
|
38
|
+
(The MIT License)
|
39
|
+
|
40
|
+
Copyright (c) 2008 Julik Tarkhanov
|
41
|
+
|
42
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
43
|
+
a copy of this software and associated documentation files (the
|
44
|
+
'Software'), to deal in the Software without restriction, including
|
45
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
46
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
47
|
+
permit persons to whom the Software is furnished to do so, subject to
|
48
|
+
the following conditions:
|
49
|
+
|
50
|
+
The above copyright notice and this permission notice shall be
|
51
|
+
included in all copies or substantial portions of the Software.
|
52
|
+
|
53
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
54
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
55
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
56
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
57
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
58
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
59
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hoe'
|
3
|
+
require './lib/depix'
|
4
|
+
|
5
|
+
Class.new(Hoe) do
|
6
|
+
def extra_deps
|
7
|
+
super.reject {|e| e[0] == 'hoe' }
|
8
|
+
end
|
9
|
+
end.new('depix', Depix::VERSION) do |p|
|
10
|
+
p.developer('Julik Tarkhanov', 'me@julik.nl')
|
11
|
+
p.rubyforge_name = 'wiretap'
|
12
|
+
p.extra_deps << 'timecode'
|
13
|
+
p.remote_rdoc_dir = 'depix'
|
14
|
+
end
|
15
|
+
|
16
|
+
task :describe_structs do
|
17
|
+
require File.dirname(__FILE__) + '/lib/depix/struct_explainer'
|
18
|
+
File.open('DPX_HEADER_STRUCTURE.txt', 'w') {|f| f << RdocExplainer.new.get_rdoc_for(Depix::DPX) }
|
19
|
+
end
|
data/bin/depix-describe
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../lib/depix'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
OptionParser.new do |opts|
|
8
|
+
opts.banner = "Usage: depix-describe somefile.dpx anotherfile.dpx [options]"
|
9
|
+
|
10
|
+
opts.on("-c", "--compact", "Compact output (only fields that change per frame)") do |v|
|
11
|
+
options[:compact] = true
|
12
|
+
end
|
13
|
+
end.parse!
|
14
|
+
|
15
|
+
ARGV.each do | file |
|
16
|
+
puts "Describing DPX #{file}. Empty elements are omitted.\n\n"
|
17
|
+
puts "===================================================\n\n"
|
18
|
+
begin
|
19
|
+
puts Depix.describe_file(file, options[:compact])
|
20
|
+
rescue Depix::InvalidHeader
|
21
|
+
puts " - Invalid header data"
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/structs'
|
2
|
+
|
3
|
+
module Depix
|
4
|
+
CompactInfo = FileInfo.only(
|
5
|
+
:magic,
|
6
|
+
:ditto_key,
|
7
|
+
:filename,
|
8
|
+
:timestamp
|
9
|
+
)
|
10
|
+
|
11
|
+
|
12
|
+
CompactFilmInfo = FilmInfo.only(
|
13
|
+
:offset,
|
14
|
+
:count,
|
15
|
+
:frame_position,
|
16
|
+
:frame_id,
|
17
|
+
:slate
|
18
|
+
)
|
19
|
+
|
20
|
+
CompactOrientation = OrientationInfo.only(
|
21
|
+
:filename,
|
22
|
+
:timestamp
|
23
|
+
)
|
24
|
+
|
25
|
+
CompactTelevision = TelevisionInfo.only(
|
26
|
+
:time_code,
|
27
|
+
:user_bits,
|
28
|
+
:field_number
|
29
|
+
)
|
30
|
+
|
31
|
+
|
32
|
+
# A version of the DPX structure that only accounts for the values that change per frame if the ditto_key is set to 1
|
33
|
+
class CompactDPX < Dict
|
34
|
+
inner :file, CompactInfo, :desc => "File information, only frame-transient values"
|
35
|
+
|
36
|
+
inner :image, ImageInfo.filler
|
37
|
+
|
38
|
+
inner :orientation, OrientationInfo, :desc => "Orientation, only frame-transient values"
|
39
|
+
inner :film, CompactFilmInfo, :desc => "Film industry info, only frame-dependent values"
|
40
|
+
inner :television, CompactTelevision, :desc => "TV industry info, only frame-dependent values"
|
41
|
+
end
|
42
|
+
end
|
data/lib/depix/dict.rb
ADDED
@@ -0,0 +1,357 @@
|
|
1
|
+
module Depix
|
2
|
+
|
3
|
+
#:stopdoc:
|
4
|
+
class Field
|
5
|
+
attr_accessor :name, :length, :pattern, :req, :desc, :rtype
|
6
|
+
alias_method :req?, :req
|
7
|
+
|
8
|
+
# Hash init
|
9
|
+
def initialize(opts = {})
|
10
|
+
opts.each_pair {|k, v| send(k.to_s + '=', v) }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Emit an unsigned int field
|
14
|
+
def self.emit_u32(o = {})
|
15
|
+
U32Field.new(o)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Emit a short int field
|
19
|
+
def self.emit_u8(o = {})
|
20
|
+
U8Field.new(o)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Emit a double int field
|
24
|
+
def self.emit_u16(o = {})
|
25
|
+
U16Field.new(o)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Emit a char field
|
29
|
+
def self.emit_char(o = {})
|
30
|
+
opts = {:length => 1}.merge(o)
|
31
|
+
CharField.new(opts)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Emit a float field
|
35
|
+
def self.emit_r32(o = {})
|
36
|
+
R32Field.new(o)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return a cleaned value
|
40
|
+
def clean(v)
|
41
|
+
v
|
42
|
+
end
|
43
|
+
|
44
|
+
def explain
|
45
|
+
[rtype ? ("(%s)" % rtype) : nil, desc, (req? ? "- required" : nil)].compact.join(' ')
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return the actual values from the stack. The stack will begin on the element we need,
|
49
|
+
# so the default consumption is shift
|
50
|
+
def consume!(stack)
|
51
|
+
clean(stack.shift)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class U32Field < Field
|
56
|
+
BLANK = 0xFFFFFFFF
|
57
|
+
undef :length=, :pattern=
|
58
|
+
|
59
|
+
def pattern
|
60
|
+
"N"
|
61
|
+
end
|
62
|
+
|
63
|
+
def length
|
64
|
+
4
|
65
|
+
end
|
66
|
+
|
67
|
+
def clean(value)
|
68
|
+
value == BLANK ? nil : value
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class U8Field < Field
|
73
|
+
undef :length=, :pattern=
|
74
|
+
|
75
|
+
BLANK = 0xFF
|
76
|
+
|
77
|
+
def pattern
|
78
|
+
"c"
|
79
|
+
end
|
80
|
+
|
81
|
+
def length
|
82
|
+
1
|
83
|
+
end
|
84
|
+
|
85
|
+
def rtype
|
86
|
+
Integer
|
87
|
+
end
|
88
|
+
|
89
|
+
def clean(v)
|
90
|
+
v == BLANK ? nil : v
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class Filler < Field
|
95
|
+
undef :pattern=
|
96
|
+
def pattern
|
97
|
+
"x#{length ? length.to_i : 1}"
|
98
|
+
end
|
99
|
+
|
100
|
+
# Leave the stack alone since we skipped
|
101
|
+
def consume(stack)
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class U16Field < Field
|
107
|
+
BLANK = 0xFFFF
|
108
|
+
undef :length=, :pattern=
|
109
|
+
|
110
|
+
def pattern
|
111
|
+
"n"
|
112
|
+
end
|
113
|
+
|
114
|
+
def length
|
115
|
+
2
|
116
|
+
end
|
117
|
+
|
118
|
+
def rtype
|
119
|
+
Integer
|
120
|
+
end
|
121
|
+
|
122
|
+
def clean(v)
|
123
|
+
v == BLANK ? nil : v
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class R32Field < Field
|
128
|
+
undef :length=, :pattern=
|
129
|
+
|
130
|
+
def pattern
|
131
|
+
"g"
|
132
|
+
end
|
133
|
+
|
134
|
+
def clean(v)
|
135
|
+
v.nan? ? nil : v
|
136
|
+
end
|
137
|
+
|
138
|
+
def length
|
139
|
+
4
|
140
|
+
end
|
141
|
+
|
142
|
+
def rtype
|
143
|
+
Float
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class CharField < Field
|
148
|
+
BLANK = "\0"
|
149
|
+
undef :pattern=
|
150
|
+
|
151
|
+
BLANKING_VALUES = [0x00.chr, 0xFF.chr]
|
152
|
+
BLANKING_PATTERNS = BLANKING_VALUES.inject([]) do | p, char |
|
153
|
+
p << /^(#{char}+)/ << /(#{char}+)$/
|
154
|
+
end
|
155
|
+
|
156
|
+
def pattern
|
157
|
+
"A#{(length || 1).to_i}"
|
158
|
+
end
|
159
|
+
|
160
|
+
def clean(v)
|
161
|
+
if v == BLANK
|
162
|
+
nil
|
163
|
+
else
|
164
|
+
2.times { BLANKING_PATTERNS.each{|p| v.gsub!(p, '')} }
|
165
|
+
v.empty? ? nil : v
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def rtype
|
170
|
+
String
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Wrapper for an array structure
|
175
|
+
class ArrayField < Field
|
176
|
+
attr_accessor :members
|
177
|
+
undef :length=, :pattern=
|
178
|
+
|
179
|
+
def length
|
180
|
+
members.inject(0){|_, s| _ + s.length }
|
181
|
+
end
|
182
|
+
|
183
|
+
def pattern
|
184
|
+
members.inject(''){|_, s| _ + s.pattern }
|
185
|
+
end
|
186
|
+
|
187
|
+
def consume!(stack)
|
188
|
+
members.map{|m| m.consume!(stack)}
|
189
|
+
end
|
190
|
+
|
191
|
+
def rtype
|
192
|
+
Array
|
193
|
+
end
|
194
|
+
|
195
|
+
def explain
|
196
|
+
return 'Empty array' if (!members || members.empty?)
|
197
|
+
tpl = "(Array of %d %s fields)" % [ members.length, members[0].rtype]
|
198
|
+
r = (req? ? "- required" : nil)
|
199
|
+
[tpl, desc, r].compact.join(' ')
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Wrapper for a contained structure
|
204
|
+
class InnerField < Field
|
205
|
+
attr_accessor :cast
|
206
|
+
undef :length=, :pattern=
|
207
|
+
|
208
|
+
def length
|
209
|
+
cast.length
|
210
|
+
end
|
211
|
+
|
212
|
+
def pattern
|
213
|
+
cast.pattern
|
214
|
+
end
|
215
|
+
|
216
|
+
def consume!(stack)
|
217
|
+
cast.consume!(stack)
|
218
|
+
end
|
219
|
+
|
220
|
+
def rtype
|
221
|
+
cast
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Base class for a struct. Could also be implemented as a module actually
|
226
|
+
class Dict
|
227
|
+
DEF_OPTS = { :req => false, :desc => nil }
|
228
|
+
|
229
|
+
class << self
|
230
|
+
|
231
|
+
# Get the array of fields defined in this struct
|
232
|
+
def fields
|
233
|
+
@fields ||= []
|
234
|
+
end
|
235
|
+
|
236
|
+
# Define a 4-byte unsigned integer
|
237
|
+
def u32(name, *extras)
|
238
|
+
count, opts = count_and_opts_from(extras)
|
239
|
+
attr_accessor name
|
240
|
+
fields << Field.emit_u32( {:name => name }.merge(opts) )
|
241
|
+
end
|
242
|
+
|
243
|
+
# Define a double-width unsigned integer
|
244
|
+
def u16(name, *extras)
|
245
|
+
count, opts = count_and_opts_from(extras)
|
246
|
+
attr_accessor name
|
247
|
+
fields << Field.emit_u16( {:name => name }.merge(opts) )
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
# Define a small unsigned integer
|
252
|
+
def u8(name, *extras)
|
253
|
+
count, opts = count_and_opts_from(extras)
|
254
|
+
attr_accessor name
|
255
|
+
fields << Field.emit_u8( {:name => name }.merge(opts) )
|
256
|
+
end
|
257
|
+
|
258
|
+
# Define a real number
|
259
|
+
def r32(name, *extras)
|
260
|
+
count, opts = count_and_opts_from(extras)
|
261
|
+
attr_accessor name
|
262
|
+
fields << Field.emit_r32( {:name => name}.merge(opts) )
|
263
|
+
end
|
264
|
+
|
265
|
+
# Define an array of values
|
266
|
+
def array(name, mapped_to, *extras)
|
267
|
+
count, opts = count_and_opts_from(extras)
|
268
|
+
attr_accessor name
|
269
|
+
|
270
|
+
a = ArrayField.new({:name => name}.merge(opts))
|
271
|
+
a.members = if mapped_to.is_a?(Class) # Array of structs
|
272
|
+
[InnerField.new(:cast => mapped_to)] * count
|
273
|
+
else
|
274
|
+
[Field.send("emit_#{mapped_to}")] * count
|
275
|
+
end
|
276
|
+
fields << a
|
277
|
+
end
|
278
|
+
|
279
|
+
# Define a nested struct
|
280
|
+
def inner(name, mapped_to, *extras)
|
281
|
+
count, opts = count_and_opts_from(extras)
|
282
|
+
attr_accessor name
|
283
|
+
fields << InnerField.new({:name => name, :cast => mapped_to}.merge(opts))
|
284
|
+
end
|
285
|
+
|
286
|
+
# Define a char field
|
287
|
+
def char(name, *extras)
|
288
|
+
count, opts = count_and_opts_from(extras)
|
289
|
+
attr_accessor name
|
290
|
+
fields << Field.emit_char( {:name => name, :length => count}.merge(opts) )
|
291
|
+
end
|
292
|
+
|
293
|
+
# Get the pattern that will be used to unpack this structure and all of it's descendants
|
294
|
+
def pattern
|
295
|
+
fields.map{|f| f.pattern }.join
|
296
|
+
end
|
297
|
+
|
298
|
+
# How many bytes are needed to complete this structure
|
299
|
+
def length
|
300
|
+
fields.inject(0){|_, s| _ + s.length }
|
301
|
+
end
|
302
|
+
|
303
|
+
# Consume a stack of unpacked values, letting each field decide how many to consume
|
304
|
+
def consume!(stack_of_unpacked_values)
|
305
|
+
new_item = new
|
306
|
+
@fields.each do | field |
|
307
|
+
new_item.send("#{field.name}=", field.consume!(stack_of_unpacked_values)) unless field.name.nil?
|
308
|
+
end
|
309
|
+
new_item
|
310
|
+
end
|
311
|
+
|
312
|
+
# Apply this structure to data in the string, returning an instance of this structure with fields completed
|
313
|
+
def apply!(string)
|
314
|
+
consume!(string.unpack(pattern))
|
315
|
+
end
|
316
|
+
|
317
|
+
# Get a class that would parse just the same, preserving only the fields passed in the array. This speeds
|
318
|
+
# up parsing because we only extract and conform the fields that we need
|
319
|
+
def only(*field_names)
|
320
|
+
distillate = fields.inject([]) do | m, f |
|
321
|
+
if field_names.include?(f.name) # preserve
|
322
|
+
m.push(f)
|
323
|
+
else # create filler
|
324
|
+
unless m[-1].is_a?(Filler)
|
325
|
+
m.push(Filler.new(:length => f.length))
|
326
|
+
else
|
327
|
+
m[-1].length += f.length
|
328
|
+
end
|
329
|
+
m
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
anon = Class.new(self)
|
334
|
+
anon.fields.replace(distillate)
|
335
|
+
only_items = distillate.map{|n| n.name }
|
336
|
+
|
337
|
+
anon
|
338
|
+
end
|
339
|
+
|
340
|
+
# Get an opaque struct based on this one, that will consume exactly as many bytes as this
|
341
|
+
# structure would occupy, but discard them instead
|
342
|
+
def filler
|
343
|
+
only([])
|
344
|
+
end
|
345
|
+
|
346
|
+
private
|
347
|
+
|
348
|
+
# extract_options! on a diet
|
349
|
+
def count_and_opts_from(args)
|
350
|
+
options, count = (args[-1].is_a?(Hash) ? DEF_OPTS.merge(args.pop) : DEF_OPTS), (args.shift || 1)
|
351
|
+
[count, options]
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
#:startdoc:
|
357
|
+
end
|
data/lib/depix/enums.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Depix
|
2
|
+
COLORIMETRIC = {
|
3
|
+
:UserDefined => 0,
|
4
|
+
:PrintingDensity => 1,
|
5
|
+
:Linear => 2,
|
6
|
+
:Logarithmic => 3,
|
7
|
+
:UnspecifiedVideo => 4,
|
8
|
+
:SMTPE_274M => 5,
|
9
|
+
:ITU_R709 => 6,
|
10
|
+
:ITU_R601_625L => 7,
|
11
|
+
:ITU_R601_525L => 8,
|
12
|
+
:NTSCCompositeVideo => 9,
|
13
|
+
:PALCompositeVideo => 10,
|
14
|
+
:ZDepthLinear => 11,
|
15
|
+
:DepthHomogeneous => 12
|
16
|
+
}
|
17
|
+
|
18
|
+
COMPONENT_TYPE = {
|
19
|
+
:Undefined => 0,
|
20
|
+
:Red => 1,
|
21
|
+
:Green => 2,
|
22
|
+
:Blue => 3,
|
23
|
+
:Alpha => 4,
|
24
|
+
:Luma => 6,
|
25
|
+
:ColorDifferenceCbCr => 7,
|
26
|
+
:Depth => 8,
|
27
|
+
:CompositeVideo => 9,
|
28
|
+
:RGB => 50,
|
29
|
+
:RGBA => 51,
|
30
|
+
:ABGR => 52,
|
31
|
+
:CbYCrY422 => 100,
|
32
|
+
:CbYACrYA4224 => 101,
|
33
|
+
:CbYCr444 => 102,
|
34
|
+
:CbYCrA4444 => 103,
|
35
|
+
:UserDef2Element => 150,
|
36
|
+
:UserDef3Element => 151,
|
37
|
+
:UserDef4Element => 152,
|
38
|
+
:UserDef5Element => 153,
|
39
|
+
:UserDef6Element => 154,
|
40
|
+
:UserDef7Element => 155,
|
41
|
+
:UserDef8Element => 156,
|
42
|
+
}
|
43
|
+
end
|