Skip to content

Commit 3901d98

Browse files
committed
works
1 parent c3ef9c2 commit 3901d98

File tree

4 files changed

+152
-17
lines changed

4 files changed

+152
-17
lines changed

app/scripts/classes/GDepthEncoder.js

+68-13
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,24 @@ Copyright (c) 2014 Rafał Lindemann. http://panrafal.github.com/depthy
3434
3535
*/
3636

37+
function makeUint16Buffer(arr, littleEndian) {
38+
var ab = new ArrayBuffer(arr.length * 2),
39+
dv = new DataView(ab);
40+
for (var i = 0; i < arr.length; ++i) {
41+
dv.setUint16(i * 2, arr[i], littleEndian);
42+
}
43+
return new Uint8Array(ab);
44+
}
45+
46+
function makeUint32Buffer(arr, littleEndian) {
47+
var ab = new ArrayBuffer(arr.length * 4),
48+
dv = new DataView(ab);
49+
for (var i = 0; i < arr.length; ++i) {
50+
dv.setUint32(i * 4, arr[i], littleEndian);
51+
}
52+
return new Uint8Array(ab);
53+
}
54+
3755
window.GDepthEncoder = {
3856

3957
xmlns: {
@@ -42,6 +60,8 @@ Copyright (c) 2014 Rafał Lindemann. http://panrafal.github.com/depthy
4260
'GDepth': 'http://ns.google.com/photos/1.0/depthmap/',
4361
'xmpNote': 'http://ns.adobe.com/xmp/note/',
4462
},
63+
xmpHeader: 'http://ns.adobe.com/xap/1.0/',
64+
xmpExtensionHeader: 'http://ns.adobe.com/xmp/extension/',
4565

4666
// This is NOT a general purpose XMP builder!
4767
buildXMP: function(props, xmlns) {
@@ -62,8 +82,13 @@ Copyright (c) 2014 Rafał Lindemann. http://panrafal.github.com/depthy
6282
// xmpNote:HasExtendedXMP="420161059863C43993D79FBDFA80C997"
6383
},
6484

65-
dataURIsplit: function(uri) {
66-
return uri.match(/^data:(.+?);(.+?),(.+)$/);
85+
dataURIinfo: function(uri) {
86+
var match = uri.match(/^data:(.+?);(.+?),(.+)$/);
87+
return match ? {
88+
mime: match[1],
89+
encoding: match[2],
90+
data: match[3]
91+
} : null;
6792
},
6893

6994
/**
@@ -73,16 +98,18 @@ Copyright (c) 2014 Rafał Lindemann. http://panrafal.github.com/depthy
7398
*/
7499
encodeDepthmap: function(buffer, depthmap, original, metadata) {
75100
var props = {}, extProps = {}, standardXMP, extendedXMP;
76-
depthmap = this.dataURIsplit(depthmap || '');
77-
original = this.dataURIsplit(original || '');
101+
depthmap = this.dataURIinfo(depthmap || '');
102+
original = this.dataURIinfo(original || '');
78103
if (depthmap) {
79104
props['GDepth:Format'] = 'RangeInverse';
80-
props['GDepth:Mime'] = depthmap[1];
81-
extProps['GDepth:Data'] = depthmap[3];
105+
props['GDepth:Mime'] = depthmap.mime;
106+
extProps['GDepth:Data'] = depthmap.data;
107+
console.log('Depthmap size', depthmap.data.length);
82108
}
83109
if (original) {
84-
props['GImage:Mime'] = original[1];
85-
extProps['GImage:Data'] = depthmap[3];
110+
props['GImage:Mime'] = original.mime;
111+
extProps['GImage:Data'] = original.data;
112+
console.log('Original size', original.data.length);
86113
}
87114
for (var k in metadata || {}) {
88115
props[k] = metadata[k];
@@ -119,7 +146,7 @@ Copyright (c) 2014 Rafał Lindemann. http://panrafal.github.com/depthy
119146
segType = data.getUint8(offset++);
120147
if (segType === 0xFF) {
121148
console.log('Padding 0xFF found');
122-
parts.push([0xFF]);
149+
parts.push(new Uint8Array([0xFF]));
123150
} else break;
124151
} while (true);
125152
if (segType === 0xC0 || segType === 0xC2 || segType === 0xDA) {
@@ -146,7 +173,7 @@ Copyright (c) 2014 Rafał Lindemann. http://panrafal.github.com/depthy
146173
}
147174
console.log('Found APP1 ' + app1Header);
148175
// ignore any existing XMP
149-
if (app1Header === 'http://ns.adobe.com/xap/1.0/') {
176+
if (app1Header === this.xmpHeader || app1Header === this.xmpExtensionHeader) {
150177
console.log('Found old XMP, skipping');
151178
offset += segSize - (offset - segStart - 2);
152179
continue;
@@ -155,17 +182,45 @@ Copyright (c) 2014 Rafał Lindemann. http://panrafal.github.com/depthy
155182
// copying segment
156183
console.log('Copying segment ' + segType + ', size: ' + segSize + ', left:' + (segSize - (offset - segStart - 2)));
157184
offset += segSize - (offset - segStart - 2);
158-
parts.push(new Uint8Array(buffer, segStart, 1 + segSize));
185+
parts.push(new Uint8Array(buffer, segStart, 2 + segSize));
159186
if (segType === 0xE1) {
160187
writeXMP(); // right after EXIF
161188
}
162189
}
190+
// console.log('Parts', parts);
163191
return new Blob(parts, {type: 'image/jpeg'});
164192
},
165193

166194
buildXMPsegments: function(standardXMP, extendedXMP) {
167-
// console.log('StandardXMP: ', standardXMP);
168-
// console.log('ExtendedXMP: ', extendedXMP);
195+
var extendedUid, parts = [];
196+
if (extendedXMP) {
197+
extendedUid = CryptoJS.MD5(extendedXMP).toString().toUpperCase();
198+
console.log(extendedUid);
199+
standardXMP = standardXMP.replace(/(<rdf:Description) /, '$1 xmpNote:HasExtendedXMP="420161059863C43993D79FBDFA80C997" ');
200+
}
201+
console.log('StandardXMP: ', standardXMP.length);
202+
console.log('ExtendedXMP: ', extendedXMP.length);
203+
204+
parts.push(new Uint8Array([0xFF, 0xE1]));
205+
parts.push(makeUint16Buffer([2 + this.xmpHeader.length + 1 + standardXMP.length]));
206+
parts.push(this.xmpHeader, new Uint8Array([0x00]));
207+
parts.push(standardXMP);
208+
console.log('Written standardXMP');
209+
if (extendedXMP) {
210+
var offset = 0;
211+
while (offset < extendedXMP.length) {
212+
var chunkSize = Math.min(65300, extendedXMP.length - offset);
213+
parts.push(new Uint8Array([0xFF, 0xE1]));
214+
parts.push(makeUint16Buffer([2 + this.xmpExtensionHeader.length + 1 + 32 + 4 + 4 + chunkSize]));
215+
parts.push(this.xmpExtensionHeader, new Uint8Array([0x00]));
216+
parts.push(extendedUid, makeUint32Buffer([extendedXMP.length, offset]));
217+
parts.push(extendedXMP.substr(offset, chunkSize));
218+
219+
console.log('Written extendedXMP chunk %d %db of %d', offset, chunkSize, extendedXMP.length);
220+
offset += chunkSize;
221+
}
222+
}
223+
return parts;
169224
},
170225

171226
};

app/scripts/controllers/exportjpgmodal.js

+25-3
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,31 @@ angular.module('depthyApp')
55
$scope.loading = true;
66
// wait for animation
77
$timeout(function() {
8-
depthy.getViewer().exportToPNG(null).then(
8+
var imageUrl, depthUrl, originalUrl;
9+
depthy.getViewer().exportSourceImage(depthy.opened.imageSource, {}).then(
910
function(url) {
10-
// shorten this!
11-
url = URL.createObjectURL(window.dataURItoBlob(url));
11+
imageUrl = url;
12+
return depthy.getViewer().exportDepthmap();
13+
}
14+
).then(
15+
function(url) {
16+
depthUrl = url;
17+
if (depthy.opened.originalSource) {
18+
return depthy.getViewer().exportSourceImage(depthy.opened.imageSource, {});
19+
} else {
20+
return false;
21+
}
22+
}
23+
).then(
24+
function(url) {
25+
originalUrl = url;
26+
27+
// ready! let's do this!
28+
return GDepthEncoder.encodeDepthmap(window.dataURItoArrayBuffer(imageUrl).buffer, depthUrl, originalUrl);
29+
}
30+
).then(
31+
function(blob) {
32+
var url = URL.createObjectURL(blob);
1233
var img = angular.element('img[image-source="export-jpg-modal"]')[0];
1334
img.onload = function() {
1435
$scope.loading = false;
@@ -18,5 +39,6 @@ angular.module('depthyApp')
1839
angular.element('a[image-source="export-jpg-modal"]').attr('href', url);
1940
}
2041
);
42+
2143
}, depthy.modalWait);
2244
});

karma.conf.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module.exports = function(config) {
1818
'app/scripts/vendor/md5.js',
1919
'app/scripts/classes/dataURITools.js',
2020
'app/scripts/classes/GDepthEncoder.js',
21+
'app/scripts/vendor/LensBlurDepthExtractor.js',
2122
// 'app/scripts/*.js',
2223
// 'app/scripts/**/*.js',
2324
// 'test/mock/**/*.js',

test/spec/classes/GDepthEncoder.js

+58-1
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,75 @@ describe('GDepthEncoder', function () {
55
var xmpDataURI = 'data:image/jpeg;base64,/9j/4QAqRXhpZgAASUkqAAgAAAABAJiCAgAFAAAAGgAAAAAAAABUZXN0AAAAAP/sABFEdWNreQABAAQAAABLAAD/4QSkaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcFJpZ2h0cz0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3JpZ2h0cy8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtcFJpZ2h0czpNYXJrZWQ9IlRydWUiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo2ZWJlYzg1YS1kODBmLWMxNGYtYWY3ZC1hZGMzN2M0ZTQwYTciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODc3NjlBMzNGNThFMTFFM0EwNzhCNTg4QTI3MThFNzkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODc3NjlBMzJGNThFMTFFM0EwNzhCNTg4QTI3MThFNzkiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjZlYmVjODVhLWQ4MGYtYzE0Zi1hZjdkLWFkYzM3YzRlNDBhNyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo2ZWJlYzg1YS1kODBmLWMxNGYtYWY3ZC1hZGMzN2M0ZTQwYTciLz4gPGRjOnJpZ2h0cz4gPHJkZjpBbHQ+IDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCI+VGVzdDwvcmRmOmxpPiA8L3JkZjpBbHQ+IDwvZGM6cmlnaHRzPiA8ZGM6dGl0bGU+IDxyZGY6QWx0PiA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPlRlc3Q8L3JkZjpsaT4gPC9yZGY6QWx0PiA8L2RjOnRpdGxlPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/tAFBQaG90b3Nob3AgMy4wADhCSU0EBAAAAAAAGBwBWgADGyVHHAIAAAIAAhwCdAAEVGVzdDhCSU0EJQAAAAAAEN5W4ZqfXiPJ8CAePrIPGv//7gAOQWRvYmUAZMAAAAAB/9sAhAADAgICAgIDAgIDBQMDAwUFBAMDBAUGBQUFBQUGCAYHBwcHBggICQoKCgkIDAwMDAwMDg4ODg4QEBAQEBAQEBAQAQMEBAYGBgwICAwSDgwOEhQQEBAQFBEQEBAQEBEREBAQEBAQERAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAgACADAREAAhEBAxEB/8QASwABAQAAAAAAAAAAAAAAAAAAAAkBAQAAAAAAAAAAAAAAAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAARAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKpgAAAAAAAAAAAAAAA//9k=';
66
var simpleDataURI = 'data:image/jpeg;base64,/9j/4QAqRXhpZgAASUkqAAgAAAABAJiCAgAFAAAAGgAAAAAAAABUZXN0AAAAAP/sABFEdWNreQABAAQAAABLAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNBCUAAAAAABAAAAAAAAAAAAAAAAAAAAAA/+4ADkFkb2JlAGTAAAAAAf/bAIQAAwICAgICAwICAwUDAwMFBQQDAwQFBgUFBQUFBggGBwcHBwYICAkKCgoJCAwMDAwMDA4ODg4OEBAQEBAQEBAQEAEDBAQGBgYMCAgMEg4MDhIUEBAQEBQREBAQEBARERAQEBAQEBEQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ/8AAEQgAIAAgAwERAAIRAQMRAf/EAEsAAQEAAAAAAAAAAAAAAAAAAAAJAQEAAAAAAAAAAAAAAAAAAAAAEAEAAAAAAAAAAAAAAAAAAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCVQAAAAAAAAAAAAAAAP//Z';
77

8+
var longDataURI = 'data:image/jpeg;base64,';
9+
for (var i = 0; i < 1600; ++i) {
10+
longDataURI += 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
11+
}
812

913
it('should build XMP', function () {
1014
var xmp = GDepthEncoder.buildXMP({test: 'TEST!'});
1115
expect(xmp).toMatch(/^<x:xmpmeta .+ xmlns:GFocus="http.+test="TEST!".+<\/x:xmpmeta>$/);
1216
});
1317

18+
it('should understand dataURI', function() {
19+
var info = GDepthEncoder.dataURIinfo(xmpDataURI);
20+
expect(info.mime).toBe('image/jpeg');
21+
expect(info.data).not.toBe(null);
22+
expect(info.data.length).toBeGreaterThan(500);
23+
});
24+
1425
it('should accept only ArrayBuffer', function () {
1526
expect(function() { GDepthEncoder.encodeDepthmap([1,2,3]); }).toThrow();
1627
});
1728

1829
it('should encode image', function () {
19-
var result = GDepthEncoder.encodeDepthmap(dataURItoArrayBuffer(xmpDataURI).buffer, simpleDataURI);
30+
var result = GDepthEncoder.encodeDepthmap(dataURItoArrayBuffer(xmpDataURI).buffer, longDataURI, simpleDataURI);
31+
32+
expect(result.type).toBe('image/jpeg');
33+
expect(result.size).toBeGreaterThan(1500);
34+
35+
var dr = null, parsed = false, loaded = false;
36+
runs(function() {
37+
var fr = new FileReader();
38+
fr.onload = function() {
39+
dr = new DepthReader();
40+
dr.parseFile(fr.result, function() {
41+
// console.log(dr);
42+
parsed = true;
43+
});
44+
};
45+
fr.readAsArrayBuffer(result);
46+
});
47+
48+
waitsFor(function() {
49+
return parsed;
50+
}, 'Result should be parsed', 100);
51+
52+
runs(function() {
53+
expect(dr.depth.mime).toBe('image/jpeg');
54+
// expect(dr.depth.data).toBe(GDepthEncoder.dataURIinfo(longDataURI).data);
55+
expect(dr.depth.data.length).toEqual(longDataURI.length - 23);
56+
expect(dr.image.mime).toBe('image/jpeg');
57+
expect(dr.image.data).toBe(GDepthEncoder.dataURIinfo(simpleDataURI).data);
58+
});
59+
60+
runs(function() {
61+
var img = document.createElement('img');
62+
63+
img.onload = function() {
64+
loaded = {w:img.width, h:img.height};
65+
};
66+
img.src = URL.createObjectURL(result);
67+
});
68+
69+
waitsFor(function() {
70+
return loaded;
71+
}, 'Result should be loadable', 100);
72+
73+
runs(function() {
74+
expect(loaded).toEqual({w:32, h:32});
75+
});
76+
2077
console.log('Encoded: %d / %s', result.size, result.type, result);
2178
// expect(xmp).toMatch(/^<x:xmpmeta .+ xmlns:GFocus="http.+test="TEST!".+<\/x:xmpmeta>$/);
2279
});

0 commit comments

Comments
 (0)