depix 2.0.0 → 3.0.0

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.
@@ -1,5 +1,9 @@
1
+ require "stringio"
2
+
1
3
  # Generates a description of the structure in RDoc format
2
- class Depix::Binary::StructureExplainer
4
+ class Depix::Binary::RdocGenerator
5
+ include Depix::Binary::Fields
6
+
3
7
  attr_accessor :io, :attr_template, :struct_template
4
8
 
5
9
  TPL = <<eof
@@ -63,8 +63,14 @@ class Depix::Binary::Structure
63
63
  attr_accessor name
64
64
  fields << U16Field.new( {:name => name }.merge(opts) )
65
65
  end
66
-
67
-
66
+
67
+ # Define a blanking field (it's return value is always nil)
68
+ def self.blanking(name, *extras)
69
+ length, opts = count_and_opts_from(extras)
70
+ attr_accessor name
71
+ fields << BlankingField.new( {:name => name, :length => length}.merge(opts) )
72
+ end
73
+
68
74
  # Define a small unsigned integer
69
75
  def self.u8(name, *extras)
70
76
  count, opts = count_and_opts_from(extras)
@@ -182,7 +188,7 @@ class Depix::Binary::Structure
182
188
  def self.pack(instance, buffer = nil)
183
189
 
184
190
  # Preallocate a buffer just as big as me since we want everything to remain at fixed offsets
185
- buffer ||= ("\000" * length)
191
+ buffer ||= (0xFF.chr * length)
186
192
 
187
193
  # We need to enforce ASCII-8bit encoding which in Ruby parlance is actually "bytestream"
188
194
  byteify_string(buffer) unless RUBY_VERSION < '1.9.0'
@@ -204,7 +210,6 @@ class Depix::Binary::Structure
204
210
 
205
211
  # Validate the passed value using the format the field supports
206
212
  f.validate!(val)
207
-
208
213
  packed = f.pack(val)
209
214
 
210
215
  # Signal offset violation
@@ -0,0 +1,53 @@
1
+ # Returns terminal-ready colorized descriptions of DPX headers per file
2
+ class Depix::Describe
3
+ include Term::ANSIColor
4
+
5
+ # Returns a printable report on all the headers present in the file at the path passed
6
+ def describe(path, compact = false)
7
+ struct = Depix.from_file(path, compact)
8
+ describe_struct(struct) + describe_synthetics_of_struct(struct)
9
+ end
10
+
11
+ # Returns descriptions of the shorthand synthetic properties
12
+ def describe_synthetics(path, compact)
13
+ struct = Depix.from_file(path, compact)
14
+ describe_synthetics_of_struct(struct)
15
+ end
16
+
17
+ private
18
+
19
+ # Describe a filled DPX structure
20
+ def describe_struct(result, pad_offset = 0)
21
+ result.class.fields.inject([]) do | info, field |
22
+ value = result.send(field.name)
23
+ parts = []
24
+ if value
25
+ parts << " " * pad_offset
26
+ parts << red { field.name.to_s }
27
+ parts << "(#{field.desc})" if field.desc
28
+ parts << if field.is_a?(Depix::Binary::Fields::InnerField)
29
+ describe_struct(value, pad_offset + 1)
30
+ elsif field.is_a?(Depix::Binary::Fields::ArrayField)
31
+ value.map { | v | v.is_a?(Depix::Binary::Structure) ? describe_struct(v, pad_offset + 1) : v }
32
+ else
33
+ blue { value.to_s }
34
+ end
35
+ end
36
+ if parts.any?
37
+ info << parts.join(' ')
38
+ end
39
+ info
40
+ end.map{|e| (' ' * pad_offset) + e }.join("\n")
41
+ end
42
+
43
+
44
+ def describe_synthetics_of_struct(struct)
45
+ fields = Depix::Synthetics.instance_methods.reject{|m| m.to_s.include?('=')}.map do | m |
46
+ [red{ m.to_s }, blue { struct.send(m).to_s }].join(' : ')
47
+ end
48
+ fields.unshift("============")
49
+ fields.unshift(bold { "\nSynthetic properties" })
50
+ fields.join("\n")
51
+ end
52
+
53
+ end
@@ -1,19 +1,7 @@
1
+ require 'term/ansicolor'
2
+
1
3
  module Depix
2
4
  class Reader
3
-
4
- # Returns a printable report on all the headers present in the file at the path passed
5
- def describe_file(path, compact = false)
6
- header = File.open(path, 'r') { |f| f.read(DPX.length) }
7
- struct = parse(header, false)
8
- describe_struct(struct) + describe_synthetics_of_struct(struct)
9
- end
10
-
11
- def describe_synthetics_of_struct(struct)
12
- Synthetics.instance_methods.reject{|m| m.to_s.include?('=')}.map do | m |
13
- [m, struct.send(m)].join(' : ')
14
- end.unshift("============").unshift("\nSynthetic properties").join("\n")
15
- end
16
-
17
5
  # Parse DPX headers at the start of file
18
6
  def from_file(path, compact)
19
7
  header = File.open(path, 'r') { |f| f.read(DPX.length) }
@@ -24,7 +12,19 @@ module Depix
24
12
  end
25
13
  end
26
14
 
27
- # Parse a DPX header (blob of bytes starting at the magic word)
15
+ # Parse a DPX header (blob of bytes starting at the magic word). The "compact"
16
+ # flag specifies whether a full-blown parser has to be used. This has substantial
17
+ # speed implications. For example:
18
+ # Reading DPX header 1000 times, all data
19
+ # 1.220000 0.080000 1.300000 ( 1.898979)
20
+ # Reading DPX header 1000 times, compact data
21
+ # 0.480000 0.050000 0.530000 ( 0.766509)
22
+ # This is 2.5 times faster when using compact header form. The compact header form
23
+ # is usually sufficient for reliable sequence data (it only takes fields which change)
24
+ # from one frame to another.
25
+ #
26
+ # When using the compact form a CompactDPX structure will be returned instead of the
27
+ # full-blown DPX structure.
28
28
  def parse(data, compact)
29
29
  magic = data[0..3]
30
30
  raise InvalidHeader, "No magic bytes found at start" unless %w( SDPX XPDS).include?(magic)
@@ -42,30 +42,5 @@ module Depix
42
42
  struct = compact ? CompactDPX : DPX
43
43
  is_le ? struct.apply_le!(data) : struct.apply!(data)
44
44
  end
45
-
46
- # Describe a filled DPX structure
47
- def describe_struct(result, pad_offset = 0)
48
- result.class.fields.inject([]) do | info, field |
49
- value = result.send(field.name)
50
- parts = []
51
- if value
52
- parts << field.desc if field.desc
53
- parts << if field.is_a?(Depix::Binary::Fields::InnerField)
54
- describe_struct(value, pad_offset + 1)
55
- elsif field.is_a?(ArrayField)
56
- # Exception for image elements
57
- value = result.image_elements[0...result.number_elements] if field.name == :image_elements
58
- value.map { | v | v.is_a?(Depix::Binary::Structure) ? describe_struct(v, pad_offset + 2) : v }
59
- else
60
- value
61
- end
62
- end
63
- if parts.any?
64
- info << parts.join(' ')
65
- end
66
- info
67
- end.map{|e| (' ' * pad_offset) + e }.join("\n")
68
- end
69
-
70
45
  end
71
46
  end
@@ -18,7 +18,7 @@ module Depix
18
18
  char :copyright, 200, :desc => 'Copyright'
19
19
 
20
20
  u32 :encrypt_key, :desc => 'Encryption key'
21
- char :reserve, 104
21
+ blanking :reserve, 104
22
22
  end
23
23
 
24
24
  class FilmInfo < Binary::Structure
@@ -38,7 +38,7 @@ module Depix
38
38
 
39
39
  char :frame_id, 32, :desc => 'Frame identification (keyframe)'
40
40
  char :slate, 100, :desc => 'Slate information'
41
- char :reserve, 56
41
+ blanking :reserve, 56
42
42
  end
43
43
 
44
44
  class ImageElement < Binary::Structure
@@ -82,7 +82,7 @@ module Depix
82
82
  array :border, :u16, 4, :desc => 'Border validity: XL, XR, YT, YB'
83
83
  array :aspect_ratio , :u32, 2, :desc => "Aspect (H:V)"
84
84
 
85
- char :reserve, 28
85
+ blanking :reserve, 28
86
86
  end
87
87
 
88
88
  class TelevisionInfo < Binary::Structure
@@ -104,7 +104,7 @@ module Depix
104
104
  r32 :break_point, :desc => 'Break point (?)'
105
105
  r32 :white_level, :desc => 'White level'
106
106
  r32 :integration_times, :desc => 'Integration times (S)'
107
- r32 :reserve
107
+ blanking :reserve, 4 # As long as a real
108
108
  end
109
109
 
110
110
  class UserInfo < Binary::Structure
@@ -123,7 +123,7 @@ module Depix
123
123
  elements[0].req = true
124
124
  end
125
125
 
126
- char :reserve, 52
126
+ blanking :reserve, 52
127
127
 
128
128
  # Only expose the elements present
129
129
  def image_elements #:nodoc:
@@ -52,13 +52,23 @@ module Depix::Synthetics
52
52
  Depix::COMPONENT_TYPE.invert[image.image_elements[0].descriptor]
53
53
  end
54
54
 
55
+ # Returns the pixel aspect
56
+ def pixel_aspect
57
+ (orientation.aspect_ratio[0].to_f / orientation.aspect_ratio[1].to_f)
58
+ end
59
+
55
60
  # Aspect in it's traditional representation (1.77 for 16x9 and so on)
56
61
  def aspect
57
- "%.2f" % (orientation.aspect_ratio[0].to_f / orientation.aspect_ratio[1].to_f)
62
+ "%.2f" % (image.pixels_per_line / image.lines_per_element.to_f * pixel_aspect)
58
63
  end
59
64
 
60
65
  # Is this DPX file little-endian?
61
- def le?
66
+ def little_endian?
62
67
  file.magic == 'XPDS'
63
68
  end
69
+
70
+ def le?
71
+ # $stderr.puts "Depix::Synthetics.le? is deprecated, use little_endian? instead"
72
+ little_endian?
73
+ end
64
74
  end
Binary file
@@ -1,4 +1,10 @@
1
+ # coding: ASCII-8BIT
2
+
3
+ # Pay attention. The test verifies packing into bit blobs. These are much nahdier
4
+ # to compare and check as US-ASCII. Even though I am completely into Unicode this is exactly
5
+ # the moment to NOT use it.
1
6
  require 'test/unit'
7
+ require File.dirname(__FILE__) + '/../lib/depix' unless defined?(Depix)
2
8
 
3
9
  include Depix::Binary
4
10
  include Depix::Binary::Fields
@@ -227,13 +233,13 @@ class TestArrayField < Test::Unit::TestCase
227
233
 
228
234
  def test_pack_pads_properly
229
235
  f = ArrayField.new :members => [U32Field.new, R32Field.new, R32Field.new]
230
- assert_equal "\000\000\000\001@\000\000\000\377\377\377\377", f.pack([1.0, 2.0])
236
+ assert_equal "\x00\x00\x00\x1A?\x80\x00\x00@\x00\x00\x00", f.pack([26,1.0, 2.0])
231
237
  assert_equal f.length, f.pack([1.0, 2.0]).length
232
238
  end
233
239
 
234
240
  def test_does_not_try_to_pack_nil_values
235
241
  f = ArrayField.new(:members => [AlwaysInvalidField.new(:length => 2)])
236
- assert_equal "\377\377", f.pack([])
242
+ assert_equal 0xFF.chr * 2, f.pack([nil])
237
243
  end
238
244
 
239
245
  end
@@ -329,7 +335,7 @@ class TestWideIntField < Test::Unit::TestCase
329
335
  def test_pack
330
336
  w = U32Field.new
331
337
  assert_equal "\000\000\000\036", w.pack(30)
332
- assert_equal "\377\377\377\377", w.pack(nil)
338
+ assert_equal "\xFF\xFF\xFF\xFF", w.pack(nil)
333
339
  end
334
340
  end
335
341
 
@@ -345,7 +351,7 @@ class TestCharField < Test::Unit::TestCase
345
351
  def test_char_field_pads
346
352
  f = CharField.new :name => :foo, :length => 15
347
353
 
348
- assert_equal "A15", f.pattern
354
+ assert_equal "Z15", f.pattern
349
355
  assert_equal 15, f.length
350
356
  assert_equal String, f.rtype
351
357
  end
@@ -379,8 +385,8 @@ class TestCharField < Test::Unit::TestCase
379
385
  def test_char_field_packs
380
386
  f = CharField.new(:length => 6)
381
387
  assert_equal "xx\000\000\000\000", f.pack("xx")
382
- assert_equal "\000\000\000\000\000\000", f.pack("")
383
- assert_equal "\000\000\000\000\000\000", f.pack(nil)
388
+ assert_equal "\xFF\xFF\xFF\xFF\xFF\xFF", f.pack("")
389
+ assert_equal "\xFF\xFF\xFF\xFF\xFF\xFF", f.pack(nil)
384
390
  end
385
391
  end
386
392
 
@@ -391,21 +397,17 @@ class TestFloatField < Test::Unit::TestCase
391
397
  f = R32Field.new :name => :foo
392
398
  conform_field!(f)
393
399
 
394
- the_nan = Class.new do
395
- def nan?; true; end
396
- end.new
397
-
398
400
  assert_equal "g", f.pattern
399
401
  assert_equal 4, f.length
400
402
  assert_equal :foo, f.name
401
403
  assert_equal Float, f.rtype
402
- assert_equal nil, f.clean(the_nan)
403
404
  end
404
405
 
405
406
  def test_pack
406
407
  w = R32Field.new
407
408
  assert_equal "@fff", w.pack(3.6)
408
- assert_equal "O\200\000\000", w.pack(nil)
409
+ blank = 0xFF.chr * 4
410
+ assert_equal blank, w.pack(nil)
409
411
  end
410
412
  end
411
413
 
@@ -538,13 +540,13 @@ class TestFieldEmit < Test::Unit::TestCase
538
540
  f = CharField.new
539
541
  conform_field!(f)
540
542
 
541
- assert_equal "A1", f.pattern
543
+ assert_equal "Z1", f.pattern
542
544
  assert_equal 1, f.length
543
545
 
544
546
  f = CharField.new :length => 3
545
547
  conform_field!(f)
546
548
 
547
- assert_equal "A3", f.pattern
549
+ assert_equal "Z3", f.pattern
548
550
  assert_equal 3, f.length
549
551
  end
550
552
 
@@ -601,7 +603,7 @@ class TestStructure < Test::Unit::TestCase
601
603
  c.fields << CharField.new
602
604
 
603
605
  assert_respond_to c, :pattern
604
- assert_equal 'A1A1', c.pattern
606
+ assert_equal 'Z1Z1', c.pattern
605
607
  assert_equal 2, c.length
606
608
  end
607
609
 
@@ -645,7 +647,22 @@ class TestStructureConsume < Test::Unit::TestCase
645
647
  end
646
648
 
647
649
  class TestStructureEmitDSL < Test::Unit::TestCase
648
-
650
+
651
+ def test_dict_emit_char
652
+ c = Class.new(Structure)
653
+ c.blanking :reserved, :length => 48, :desc => "Reserved"
654
+ assert c.instance_methods.map{|e| e.to_s }.include?("reserved"),
655
+ "Should create the tag accessor"
656
+
657
+ assert_equal 1, c.fields.length
658
+ field = c.fields[0]
659
+
660
+ assert_equal 48, field.length
661
+ assert_equal "Z48", field.pattern
662
+ assert_equal :reserved, field.name
663
+ end
664
+
665
+
649
666
  def test_dict_emit_char
650
667
  c = Class.new(Structure)
651
668
  c.char :tag, :desc => "Some name"
@@ -656,7 +673,7 @@ class TestStructureEmitDSL < Test::Unit::TestCase
656
673
  field = c.fields[0]
657
674
 
658
675
  assert_equal 1, field.length
659
- assert_equal "A1", field.pattern
676
+ assert_equal "Z1", field.pattern
660
677
  assert_equal :tag, field.name
661
678
  end
662
679
 
@@ -669,7 +686,7 @@ class TestStructureEmitDSL < Test::Unit::TestCase
669
686
  assert_equal 1, c.fields.length
670
687
  field = c.fields[0]
671
688
  assert_equal 3, field.length
672
- assert_equal "A3", field.pattern
689
+ assert_equal "Z3", field.pattern
673
690
  assert_equal :joe, field.name
674
691
  end
675
692
 
@@ -1,5 +1,5 @@
1
- require File.expand_path(File.dirname(__FILE__)) + '/../lib/depix'
2
1
  require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/depix' unless defined?(Depix)
3
3
  require "fileutils"
4
4
 
5
5
  SAMPLE_DPX = File.dirname(__FILE__) + '/samples/E012_P001_L000002_lin.0001.dpx'
@@ -24,7 +24,7 @@ class ReaderTest < Test::Unit::TestCase
24
24
 
25
25
  assert_equal 320, parsed.image.pixels_per_line
26
26
  assert_equal 240, parsed.image.lines_per_element
27
-
27
+
28
28
  assert_equal 1, parsed.image.number_elements
29
29
  assert_equal 1, parsed.image.image_elements.length
30
30
  ie = parsed.image.image_elements[0]
@@ -45,8 +45,7 @@ class ReaderTest < Test::Unit::TestCase
45
45
  assert_equal 0, ie.end_of_line_padding
46
46
  assert_equal 0, ie.end_of_image_padding
47
47
  assert_equal "IMAGE DESCRIPTION DATA ", ie.description
48
- assert_equal "E012",
49
- parsed.orientation.device #- this is where Flame writes the reel
48
+ assert_equal "E012", parsed.orientation.device #- this is where Flame writes the reel
50
49
 
51
50
  assert_equal 853, parsed.orientation.aspect_ratio[0]
52
51
  assert_equal 640, parsed.orientation.aspect_ratio[1]
@@ -66,7 +65,7 @@ class ReaderTest < Test::Unit::TestCase
66
65
 
67
66
  def test_syntethics
68
67
  assert_nothing_raised { Depix::Synthetics }
69
-
68
+
70
69
  file = SAMPLE_DPX
71
70
  parsed = Depix.from_file(file)
72
71
  assert_equal false, parsed.le?
@@ -75,7 +74,16 @@ class ReaderTest < Test::Unit::TestCase
75
74
  assert_equal :RGB, parsed.component_type
76
75
  assert_equal :Linear, parsed.colorimetric
77
76
  assert_equal "E012", parsed.flame_reel
78
- assert_equal "1.33", parsed.aspect
77
+ assert_equal "1.78", parsed.aspect
78
+
79
+ file = File.dirname(SAMPLE_DPX) + "/scratch.dpx"
80
+ parsed = Depix.from_file(file)
81
+ assert_equal 1, parsed.pixel_aspect, "Should parse square pixels"
82
+ assert_equal "1.78", parsed.aspect, "Should parse 16x9 aspect"
83
+
84
+ file = File.dirname(SAMPLE_DPX) + "/026_FROM_HERO_TAPE_5-3-1_MOV.0029.dpx"
85
+ parsed = Depix.from_file(file)
86
+ assert_equal "2.37", parsed.aspect, "Should parse 2.37 Viper aspect"
79
87
  end
80
88
 
81
89
  def test_parsed_properly_using_compact_structs
@@ -83,12 +91,6 @@ class ReaderTest < Test::Unit::TestCase
83
91
  assert_nothing_raised { Depix.from_file(file, compact = true) }
84
92
  end
85
93
 
86
- def test_describe
87
- desc = Depix.describe_file(SAMPLE_DPX)
88
- assert_match(/320/, desc)
89
- assert_match(/Offset to data for this image element/, desc)
90
- end
91
-
92
94
  def test_packing
93
95
  original_header = File.read(SAMPLE_DPX)[0...Depix::DPX.length]
94
96
 
@@ -135,6 +137,13 @@ class ReaderTest < Test::Unit::TestCase
135
137
  assert_in_delta 25, dpx.film.frame_rate, 0.01
136
138
  end
137
139
 
140
+ def test_fields_properly_niled
141
+ dpx = Depix.from_file(File.dirname(__FILE__) + "/samples/scratch.dpx")
142
+ assert_equal "Assimilate,SCRATCH", dpx.file.creator
143
+ [:project, :copyright, :encrypt_key, :reserve].each do | field |
144
+ assert_nil dpx.file.send(field), "The blanking field #{field.inspect} should have been decoded as nil"
145
+ end
146
+ end
138
147
  end
139
148
 
140
149
  class EditorTest < Test::Unit::TestCase