1
+ /*
2
+ The MIT License
3
+
4
+ Copyright (c) 2014 Rafał Lindemann. http://panrafal.github.com/depthy
5
+ */
6
+ ( function ( ) {
7
+ 'use strict' ;
8
+
9
+ /*
10
+
11
+ <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.0-jc003"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
12
+ <rdf:Description rdf:about=""
13
+ xmlns:GFocus="http://ns.google.com/photos/1.0/focus/"
14
+ xmlns:GImage="http://ns.google.com/photos/1.0/image/"
15
+ xmlns:GDepth="http://ns.google.com/photos/1.0/depthmap/"
16
+ xmlns:xmpNote="http://ns.adobe.com/xmp/note/"
17
+ GFocus:BlurAtInfinity="0.02976035"
18
+ GFocus:FocalDistance="9.569768"
19
+ GFocus:FocalPointX="0.512963" GFocus:FocalPointY="0.49999997"
20
+ GImage:Mime="image/jpeg"
21
+ GDepth:Format="RangeInverse"
22
+ GDepth:Near="6.2360944747924805"
23
+ GDepth:Far="19.068166732788086"
24
+ GDepth:Mime="image/png"
25
+ xmpNote:HasExtendedXMP="420161059863C43993D79FBDFA80C997"/> </rdf:RDF> </x:xmpmeta>
26
+
27
+ <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.0-jc003">
28
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
29
+ <rdf:Description rdf:about="" xmlns:GImage="http://ns.google.com/photos/1.0/image/" xmlns:GDepth="http://ns.google.com/photos/1.0/depthmap/"
30
+ GImage:Data="/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAA...=="
31
+ GDepth:Data="iVBORw0KGgoAAAANSUhEUg...C"/>
32
+ </rdf:RDF> </x:xmpmeta>
33
+
34
+
35
+ */
36
+
37
+ window . GDepthEncoder = {
38
+
39
+ xmlns : {
40
+ 'GFocus' : 'http://ns.google.com/photos/1.0/focus/' ,
41
+ 'GImage' : 'http://ns.google.com/photos/1.0/image/' ,
42
+ 'GDepth' : 'http://ns.google.com/photos/1.0/depthmap/' ,
43
+ 'xmpNote' : 'http://ns.adobe.com/xmp/note/' ,
44
+ } ,
45
+
46
+ // This is NOT a general purpose XMP builder!
47
+ buildXMP : function ( props , xmlns ) {
48
+ var xmp = [ ] , k ;
49
+ xmlns = xmlns || this . xmlns ;
50
+ xmp . push ( '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.0-jc003">' ) ;
51
+ xmp . push ( '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">' ) ;
52
+ xmp . push ( '<rdf:Description rdf:about=""' ) ;
53
+ for ( k in xmlns ) {
54
+ xmp . push ( ' xmlns:' , k , '="' , xmlns [ k ] , '"' ) ;
55
+ }
56
+ for ( k in props ) {
57
+ // TODO html entities escaping
58
+ xmp . push ( ' ' , k , '="' + props [ k ] + '"' ) ;
59
+ }
60
+ xmp . push ( '/></rdf:RDF></x:xmpmeta>' ) ;
61
+ return xmp . join ( '' ) ;
62
+ // xmpNote:HasExtendedXMP="420161059863C43993D79FBDFA80C997"
63
+ } ,
64
+
65
+ dataURIsplit : function ( uri ) {
66
+ return uri . match ( / ^ d a t a : ( .+ ?) ; ( .+ ?) , ( .+ ) $ / ) ;
67
+ } ,
68
+
69
+ /**
70
+ @param ArrayBuffer buffer image JPG as an ArrayBuffer
71
+ @param dataURI depthmap
72
+ @param dataURI original
73
+ */
74
+ encodeDepthmap : function ( buffer , depthmap , original , metadata ) {
75
+ var props = { } , extProps = { } , standardXMP , extendedXMP ;
76
+ depthmap = this . dataURIsplit ( depthmap || '' ) ;
77
+ original = this . dataURIsplit ( original || '' ) ;
78
+ if ( depthmap ) {
79
+ props [ 'GDepth:Format' ] = 'RangeInverse' ;
80
+ props [ 'GDepth:Mime' ] = depthmap [ 1 ] ;
81
+ extProps [ 'GDepth:Data' ] = depthmap [ 3 ] ;
82
+ }
83
+ if ( original ) {
84
+ props [ 'GImage:Mime' ] = original [ 1 ] ;
85
+ extProps [ 'GImage:Data' ] = depthmap [ 3 ] ;
86
+ }
87
+ for ( var k in metadata || { } ) {
88
+ props [ k ] = metadata [ k ] ;
89
+ }
90
+ standardXMP = this . buildXMP ( props ) ;
91
+ extendedXMP = this . buildXMP ( extProps ) ;
92
+
93
+ return this . encodeXMP ( buffer , standardXMP , extendedXMP ) ;
94
+ } ,
95
+
96
+ encodeXMP : function ( buffer , standardXMP , extendedXMP ) {
97
+ var data = new DataView ( buffer ) ,
98
+ offset = 0 ,
99
+ parts = [ ] ,
100
+ xmpWritten = false ,
101
+ self = this ;
102
+
103
+ function writeXMP ( ) {
104
+ if ( ! xmpWritten ) {
105
+ parts . push . apply ( parts , self . buildXMPsegments ( standardXMP , extendedXMP ) ) ;
106
+ console . log ( 'XMP written!' ) ;
107
+ xmpWritten = true ;
108
+ }
109
+ }
110
+
111
+ while ( offset < data . byteLength ) {
112
+ var segType , segSize , app1Header , segStart , b ;
113
+ segStart = offset ;
114
+ console . log ( 'Offset ' + offset ) ;
115
+ if ( ( b = data . getUint8 ( offset ++ ) ) !== 0xFF ) {
116
+ throw 'Bad JPG Format, 0xFF expected, got ' + b ;
117
+ }
118
+ do {
119
+ segType = data . getUint8 ( offset ++ ) ;
120
+ if ( segType === 0xFF ) {
121
+ console . log ( 'Padding 0xFF found' ) ;
122
+ parts . push ( [ 0xFF ] ) ;
123
+ } else break ;
124
+ } while ( true ) ;
125
+ if ( segType === 0xC0 || segType === 0xC2 || segType === 0xDA ) {
126
+ writeXMP ( ) ; // right before SOF / SOS
127
+ }
128
+ if ( segType === 0xDA ) {
129
+ // copy the rest on SOS... no XMP should exist beyound that point
130
+ console . log ( 'SOS found, copy remaining bytes ' + ( buffer . byteLength - segStart ) ) ;
131
+ parts . push ( new Uint8Array ( buffer , segStart , buffer . byteLength - segStart ) ) ;
132
+ break ;
133
+ }
134
+ if ( segType === 0x00 || ( segType >= 0xD0 && segType <= 0xD9 ) ) {
135
+ parts . push ( new Uint8Array ( buffer , segStart , 2 ) ) ;
136
+ console . log ( 'Found ctrl segment ' + segType ) ;
137
+ continue ;
138
+ }
139
+ segSize = data . getUint16 ( offset ) ;
140
+ offset += 2 ;
141
+ if ( segType === 0xE1 ) {
142
+ // read header
143
+ app1Header = '' ;
144
+ while ( ( b = data . getUint8 ( offset ++ ) ) !== 0x0 ) {
145
+ app1Header += String . fromCharCode ( b ) ;
146
+ }
147
+ console . log ( 'Found APP1 ' + app1Header ) ;
148
+ // ignore any existing XMP
149
+ if ( app1Header === 'http://ns.adobe.com/xap/1.0/' ) {
150
+ console . log ( 'Found old XMP, skipping' ) ;
151
+ offset += segSize - ( offset - segStart - 2 ) ;
152
+ continue ;
153
+ }
154
+ }
155
+ // copying segment
156
+ console . log ( 'Copying segment ' + segType + ', size: ' + segSize + ', left:' + ( segSize - ( offset - segStart - 2 ) ) ) ;
157
+ offset += segSize - ( offset - segStart - 2 ) ;
158
+ parts . push ( new Uint8Array ( buffer , segStart , 1 + segSize ) ) ;
159
+ if ( segType === 0xE1 ) {
160
+ writeXMP ( ) ; // right after EXIF
161
+ }
162
+ }
163
+ return new Blob ( parts , { type : 'image/jpeg' } ) ;
164
+ } ,
165
+
166
+ buildXMPsegments : function ( standardXMP , extendedXMP ) {
167
+ // console.log('StandardXMP: ', standardXMP);
168
+ // console.log('ExtendedXMP: ', extendedXMP);
169
+ } ,
170
+
171
+ } ;
172
+
173
+ } ) ( ) ;
0 commit comments