depix 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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