Browse Source

Caching webp converted

master
Igor Zhukov 10 years ago
parent
commit
27c6cb6eeb
  1. 16
      app/js/directives.js
  2. 27
      app/js/lib/mtproto_wrapper.js
  3. 115
      app/js/lib/ng_utils.js
  4. 100
      app/js/lib/utils.js
  5. 19
      app/js/services.js

16
app/js/directives.js

@ -1876,13 +1876,7 @@ angular.module('myApp.directives', ['myApp.filters'])
.addClass(attrs.imgClass); .addClass(attrs.imgClass);
var setSrc = function (blob) { var setSrc = function (blob) {
if (WebpManager.isWebpSupported()) { imgElement.attr('src', FileManager.getUrl(blob));
imgElement.attr('src', FileManager.getUrl(blob, 'image/webp'));
return;
}
FileManager.getByteArray(blob).then(function (bytes) {
imgElement.attr('src', WebpManager.getPngUrlFromData(bytes));
});
}; };
imgElement.css({ imgElement.css({
@ -1894,12 +1888,16 @@ angular.module('myApp.directives', ['myApp.filters'])
height: $scope.document.thumb.height height: $scope.document.thumb.height
}); });
var smallLocation = $scope.document.thumb.location; var smallLocation = angular.copy($scope.document.thumb.location);
smallLocation.sticker = true;
var fullLocation = { var fullLocation = {
_: 'inputDocumentFileLocation', _: 'inputDocumentFileLocation',
id: $scope.document.id, id: $scope.document.id,
access_hash: $scope.document.access_hash, access_hash: $scope.document.access_hash,
dc_id: $scope.document.dc_id dc_id: $scope.document.dc_id,
file_name: $scope.document.file_name,
sticker: true
}; };

27
app/js/lib/mtproto_wrapper.js

@ -255,7 +255,7 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
} }
}) })
.factory('MtpApiFileManager', function (MtpApiManager, $q, FileManager, IdbFileStorage, TmpfsFileStorage, MemoryFileStorage) { .factory('MtpApiFileManager', function (MtpApiManager, $q, FileManager, IdbFileStorage, TmpfsFileStorage, MemoryFileStorage, WebpManager) {
var cachedFs = false; var cachedFs = false;
var cachedFsPromise = false; var cachedFsPromise = false;
@ -318,17 +318,25 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
return 'video' + location.id + '.mp4'; return 'video' + location.id + '.mp4';
case 'inputDocumentFileLocation': case 'inputDocumentFileLocation':
var fileName = (location.file_name || '').split('.', 2);
var ext = fileName[1] || '';
if (location.sticker && !WebpManager.isWebpSupported()) {
ext += '.png';
}
if (fileName.length) {
return fileName[0] + '_' + location.id + '.' + ext;
}
return 'doc' + location.id; return 'doc' + location.id;
case 'inputAudioFileLocation': case 'inputAudioFileLocation':
return 'audio' + location.id; return 'audio' + location.id;
}
default:
if (!location.volume_id) { if (!location.volume_id) {
console.trace('Empty location', location); console.trace('Empty location', location);
} }
return location.volume_id + '_' + location.local_id + '_' + location.secret + '.jpg'; return location.volume_id + '_' + location.local_id + '_' + location.secret + '.jpg';
}
}; };
function getTempFileName(file) { function getTempFileName(file) {
@ -374,7 +382,7 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
} }
// console.log('dload small', location); // console.log('dload small', location);
var fileName = getFileName(location), var fileName = getFileName(location),
mimeType = 'image/jpeg', mimeType = location.sticker ? 'image/webp' : 'image/jpeg',
cachedPromise = cachedSavePromises[fileName] || cachedDownloadPromises[fileName]; cachedPromise = cachedSavePromises[fileName] || cachedDownloadPromises[fileName];
if (cachedPromise) { if (cachedPromise) {
@ -403,11 +411,20 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
}); });
}); });
var processDownloaded = function (blob) {
if (!location.sticker || WebpManager.isWebpSupported()) {
return qSync.when(blob);
}
return WebpManager.getPngBlobFromWebp(blob);
};
return fileStorage.getFileWriter(fileName, mimeType).then(function (fileWriter) { return fileStorage.getFileWriter(fileName, mimeType).then(function (fileWriter) {
return downloadPromise.then(function (result) { return downloadPromise.then(function (result) {
return FileManager.write(fileWriter, result.bytes).then(function () { return processDownloaded.then(function (proccessedResult) {
return FileManager.write(fileWriter, proccessedResult.bytes).then(function () {
return cachedDownloads[fileName] = fileWriter.finalize(); return cachedDownloads[fileName] = fileWriter.finalize();
}); });
})
}); });
}); });
}); });

115
app/js/lib/ng_utils.js

@ -656,6 +656,121 @@ angular.module('izhukov.utils', [])
}; };
}) })
.service('WebpManager', function (qSync, $q) {
var nativeWebpSupport = false;
var image = new Image();
image.onload = function () {
nativeWebpSupport = this.width === 2 && this.height === 1;
};
image.onerror = function () {
nativeWebpSupport = false;
};
image.src = 'data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACyAgCdASoCAAEALmk0mk0iIiIiIgBoSygABc6zbAAA/v56QAAAAA==';
var canvas, context;
function getCanvasFromWebp(data) {
var start = tsNow();
var decoder = new WebPDecoder();
var config = decoder.WebPDecoderConfig;
var buffer = config.j || config.output;
var bitstream = config.input;
if (!decoder.WebPInitDecoderConfig(config)) {
console.error('[webpjs] Library version mismatch!');
return false;
}
// console.log('[webpjs] status code', decoder.VP8StatusCode);
var StatusCode = decoder.VP8StatusCode;
status = decoder.WebPGetFeatures(data, data.length, bitstream);
if (status != (StatusCode.VP8_STATUS_OK || 0)) {
console.error('[webpjs] status error', status, StatusCode);
}
var mode = decoder.WEBP_CSP_MODE;
buffer.colorspace = mode.MODE_RGBA;
buffer.J = 4;
try {
status = decoder.WebPDecode(data, data.length, config);
} catch (e) {
status = e;
}
ok = (status == 0);
if (!ok) {
console.error('[webpjs] decoding failed', status);
return false;
}
// console.log('[webpjs] decoded: ', buffer.width, buffer.height, bitstream.has_alpha, 'Now saving...');
var bitmap = buffer.c.RGBA.ma;
// console.log('[webpjs] done in ', tsNow() - start);
if (!bitmap) {
return false;
}
var biHeight = buffer.height;
var biWidth = buffer.width;
if (!canvas || !context) {
canvas = document.createElement('canvas');
context = canvas.getContext('2d');
} else {
context.clearRect(0, 0, canvas.width, canvas.height);
}
canvas.height = biHeight;
canvas.width = biWidth;
var output = context.createImageData(canvas.width, canvas.height);
var outputData = output.data;
for (var h = 0; h < biHeight; h++) {
for (var w = 0; w < biWidth; w++) {
outputData[0+w*4+(biWidth*4)*h] = bitmap[1+w*4+(biWidth*4)*h];
outputData[1+w*4+(biWidth*4)*h] = bitmap[2+w*4+(biWidth*4)*h];
outputData[2+w*4+(biWidth*4)*h] = bitmap[3+w*4+(biWidth*4)*h];
outputData[3+w*4+(biWidth*4)*h] = bitmap[0+w*4+(biWidth*4)*h];
};
}
context.putImageData(output, 0, 0);
return true;
}
function getPngBlobFromWebp (data) {
if (!getCanvasFromWebp(data)) {
return qSync.reject({type: 'WEBP_PROCESS_FAILEd'});
}
if (canvas.toBlob === undefined) {
return dataUrlToBlob(canvas.toDataURL('image/png'));
}
var deferred = $q.defer();
canvas.toBlob(function (blob) {
deferred.resolve(blob);
}, 'image/png');
return deferred.promise;
}
return {
isWebpSupported: function () {
return nativeWebpSupport;
},
getPngBlobFromWebp: getPngBlobFromWebp
}
})
.service('CryptoWorker', function ($timeout, $q) { .service('CryptoWorker', function ($timeout, $q) {
var webWorker = false, var webWorker = false,

100
app/js/lib/utils.js

@ -451,103 +451,3 @@ function versionCompare (ver1, ver2) {
}; };
})(window); })(window);
(function (global) {
var nativeWebpSupport = false;
var image = new Image();
image.onload = function () {
nativeWebpSupport = this.width === 2 && this.height === 1;
};
image.onerror = function () {
nativeWebpSupport = false;
};
image.src = 'data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACyAgCdASoCAAEALmk0mk0iIiIiIgBoSygABc6zbAAA/v56QAAAAA==';
var canvas, context;
function getPngUrlFromData(data) {
var start = tsNow();
var decoder = new WebPDecoder();
var config = decoder.WebPDecoderConfig;
var buffer = config.j || config.output;
var bitstream = config.input;
if (!decoder.WebPInitDecoderConfig(config)) {
console.error('[webpjs] Library version mismatch!');
return false;
}
// console.log('[webpjs] status code', decoder.VP8StatusCode);
var StatusCode = decoder.VP8StatusCode;
status = decoder.WebPGetFeatures(data, data.length, bitstream);
if (status != (StatusCode.VP8_STATUS_OK || 0)) {
console.error('[webpjs] status error', status, StatusCode);
}
var mode = decoder.WEBP_CSP_MODE;
buffer.colorspace = mode.MODE_RGBA;
buffer.J = 4;
try {
status = decoder.WebPDecode(data, data.length, config);
} catch (e) {
status = e;
}
ok = (status == 0);
if (!ok) {
console.error('[webpjs] decoding failed', status);
return false;
}
// console.log('[webpjs] decoded: ', buffer.width, buffer.height, bitstream.has_alpha, 'Now saving...');
var bitmap = buffer.c.RGBA.ma;
// console.log('[webpjs] done in ', tsNow() - start);
if (!bitmap) {
return false;
}
var biHeight = buffer.height;
var biWidth = buffer.width;
if (!canvas || !context) {
canvas = document.createElement('canvas');
context = canvas.getContext('2d');
} else {
context.clearRect(0, 0, canvas.width, canvas.height);
}
canvas.height = biHeight;
canvas.width = biWidth;
var output = context.createImageData(canvas.width, canvas.height);
var outputData = output.data;
for (var h = 0; h < biHeight; h++) {
for (var w = 0; w < biWidth; w++) {
outputData[0+w*4+(biWidth*4)*h] = bitmap[1+w*4+(biWidth*4)*h];
outputData[1+w*4+(biWidth*4)*h] = bitmap[2+w*4+(biWidth*4)*h];
outputData[2+w*4+(biWidth*4)*h] = bitmap[3+w*4+(biWidth*4)*h];
outputData[3+w*4+(biWidth*4)*h] = bitmap[0+w*4+(biWidth*4)*h];
};
}
context.putImageData(output, 0, 0);
return canvas.toDataURL('image/png');
}
global.WebpManager = {
isWebpSupported: function () {
return nativeWebpSupport;
},
getPngUrlFromData: getPngUrlFromData
}
})(window);

19
app/js/services.js

@ -3445,7 +3445,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
inputFileLocation = { inputFileLocation = {
_: 'inputDocumentFileLocation', _: 'inputDocumentFileLocation',
id: docID, id: docID,
access_hash: doc.access_hash access_hash: doc.access_hash,
file_name: doc.file_name
}; };
if (historyDoc.downloaded === undefined) { if (historyDoc.downloaded === undefined) {
@ -3463,7 +3464,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
inputFileLocation = { inputFileLocation = {
_: 'inputDocumentFileLocation', _: 'inputDocumentFileLocation',
id: docID, id: docID,
access_hash: doc.access_hash access_hash: doc.access_hash,
file_name: doc.file_name
}; };
if (historyDoc.downloaded && !toFileEntry) { if (historyDoc.downloaded && !toFileEntry) {
@ -3783,20 +3785,13 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
function downloadStickerThumb (docID) { function downloadStickerThumb (docID) {
var doc = AppDocsManager.getDoc(docID); var doc = AppDocsManager.getDoc(docID);
return MtpApiFileManager.downloadSmallFile(doc.thumb.location).then(function (blob) { var thumbLocation = angular.copy(doc.thumb.location);
if (WebpManager.isWebpSupported()) { thumbLocation.sticker = true;
return MtpApiFileManager.downloadSmallFile(thumbLocation).then(function (blob) {
return { return {
id: doc.id, id: doc.id,
src: FileManager.getUrl(blob, 'image/webp') src: FileManager.getUrl(blob, 'image/webp')
}; };
}
return FileManager.getByteArray(blob).then(function (bytes) {
return {
id: doc.id,
src: WebpManager.getPngUrlFromData(bytes)
};
});
}); });
} }

Loading…
Cancel
Save