Browse Source

New stickers preview (photoPathSize)

Caching thumb after sticker render (with toneIndex too)
master
Eduard Kuzmenko 4 years ago
parent
commit
0f3c91ee2a
  1. 2
      src/components/chat/replyContainer.ts
  2. 40
      src/components/wrappers.ts
  3. 3
      src/layer.d.ts
  4. 62
      src/lib/appManagers/appDocsManager.ts
  5. 27
      src/lib/appManagers/appPhotosManager.ts
  6. 2
      src/lib/crypto/cryptoworker.ts
  7. 3
      src/scripts/in/schema_additional_params.json
  8. 3
      src/scss/partials/popups/_stickers.scss
  9. 12
      src/scss/style.scss

2
src/components/chat/replyContainer.ts

@ -42,7 +42,7 @@ export function wrapReplyDivAndCaption(options: {
div: mediaEl, div: mediaEl,
lazyLoadQueue: appImManager.lazyLoadQueue, lazyLoadQueue: appImManager.lazyLoadQueue,
group: CHAT_ANIMATION_GROUP, group: CHAT_ANIMATION_GROUP,
onlyThumb: media.document.sticker == 2, //onlyThumb: media.document.sticker == 2,
width: 32, width: 32,
height: 32 height: 32
}); });

40
src/components/wrappers.ts

@ -564,8 +564,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
const toneIndex = emoji ? getEmojiToneIndex(emoji) : -1; const toneIndex = emoji ? getEmojiToneIndex(emoji) : -1;
if(doc.thumbs?.length && !div.firstElementChild && (!doc.downloaded || stickerType == 2 || onlyThumb) && toneIndex <= 0/* && doc.thumbs[0]._ != 'photoSizeEmpty' */) { if((doc.thumbs?.length || doc.stickerCachedThumbs) && !div.firstElementChild && (!doc.downloaded || stickerType == 2 || onlyThumb)/* && doc.thumbs[0]._ != 'photoSizeEmpty' */) {
const thumb = doc.thumbs[0]; const thumb = doc.stickerCachedThumbs && doc.stickerCachedThumbs[toneIndex] || doc.thumbs[0];
//console.log('wrap sticker', thumb, div); //console.log('wrap sticker', thumb, div);
@ -580,8 +580,13 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
img = new Image(); img = new Image();
renderImageFromUrl(img, thumb.url, afterRender); renderImageFromUrl(img, thumb.url, afterRender);
} else if('bytes' in thumb) { } else if('bytes' in thumb) {
if(thumb._ == 'photoPathSize') {
//if(!doc.w) console.error('no w', doc);
div.innerHTML = `<svg class="rlottie-vector" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 ${doc.w || 512} ${doc.h || 512}" xml:space="preserve">
<path d="${appPhotosManager.getPathFromPhotoPathSize(thumb)}"/>
</svg>`;
} else if(toneIndex <= 0) {
img = new Image(); img = new Image();
if((webpWorkerController.isWebpSupported() || doc.pFlags.stickerThumbConverted || thumb.url)/* && false */) { if((webpWorkerController.isWebpSupported() || doc.pFlags.stickerThumbConverted || thumb.url)/* && false */) {
renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb, true), afterRender); renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb, true), afterRender);
} else { } else {
@ -596,7 +601,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
} }
}).catch(() => {}); }).catch(() => {});
} }
} else if(stickerType == 2 && (withThumb || onlyThumb)) { }
} else if(stickerType == 2 && (withThumb || onlyThumb) && toneIndex <= 0) {
img = new Image(); img = new Image();
const load = () => { const load = () => {
@ -637,6 +643,9 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
console.log('loaded sticker:', doc, div); console.log('loaded sticker:', doc, div);
} */ } */
//await new Promise((resolve) => setTimeout(resolve, 500));
//return;
//console.time('download sticker' + doc.id); //console.time('download sticker' + doc.id);
//appDocsManager.downloadDocNew(doc.id).promise.then(res => res.json()).then(async(json) => { //appDocsManager.downloadDocNew(doc.id).promise.then(res => res.json()).then(async(json) => {
@ -649,7 +658,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
//console.log('loaded sticker:', doc, div/* , blob */); //console.log('loaded sticker:', doc, div/* , blob */);
if(middleware && !middleware()) return; if(middleware && !middleware()) return;
let animation = await LottieLoader.loadAnimationWorker/* loadAnimation */({ let animation = await LottieLoader.loadAnimationWorker({
container: div, container: div,
loop: loop && !emoji, loop: loop && !emoji,
autoplay: play, autoplay: play,
@ -661,11 +670,28 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
//const deferred = deferredPromise<void>(); //const deferred = deferredPromise<void>();
animation.addListener('firstFrame', () => { animation.addListener('firstFrame', () => {
if(div.firstElementChild && div.firstElementChild.tagName == 'IMG') { const element = div.firstElementChild;
div.firstElementChild.remove(); const needFadeIn = !element || element.tagName === 'svg';
const cb = () => {
if(element && element != animation.canvas) {
element.remove();
}
};
if(!needFadeIn) {
cb();
} else { } else {
animation.canvas.classList.add('fade-in'); animation.canvas.classList.add('fade-in');
if(element) {
setTimeout(() => {
cb();
}, element.tagName === 'svg' ? 50 : 200);
} }
}
appDocsManager.saveLottiePreview(doc, animation.canvas, toneIndex);
//deferred.resolve(); //deferred.resolve();
}, true); }, true);

3
src/layer.d.ts vendored

@ -2927,7 +2927,7 @@ export namespace Document {
date: number, date: number,
mime_type: string, mime_type: string,
size: number, size: number,
thumbs?: Array<PhotoSize.photoSize | PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize>, thumbs?: Array<PhotoSize.photoSize | PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize | PhotoSize.photoPathSize>,
video_thumbs?: Array<VideoSize>, video_thumbs?: Array<VideoSize>,
dc_id: number, dc_id: number,
attributes: Array<DocumentAttribute>, attributes: Array<DocumentAttribute>,
@ -2948,6 +2948,7 @@ export namespace Document {
pFlags?: Partial<{ pFlags?: Partial<{
stickerThumbConverted?: true, stickerThumbConverted?: true,
}>, }>,
stickerCachedThumbs?: {[toneIndex: number]: {url: string, w: number, h: number}},
animated?: boolean, animated?: boolean,
supportsStreaming?: boolean supportsStreaming?: boolean
}; };

62
src/lib/appManagers/appDocsManager.ts

@ -1,6 +1,5 @@
import { FileURLType, getFileNameByLocation, getFileURL } from '../../helpers/fileName'; import { FileURLType, getFileNameByLocation, getFileURL } from '../../helpers/fileName';
import { safeReplaceArrayInObject, defineNotNumerableProperties, isObject } from '../../helpers/object'; import { safeReplaceArrayInObject, defineNotNumerableProperties, isObject } from '../../helpers/object';
import { isSafari } from '../../helpers/userAgent';
import { Document, InputFileLocation, PhotoSize } from '../../layer'; import { Document, InputFileLocation, PhotoSize } from '../../layer';
import { MOUNT_CLASS_TO } from '../mtproto/mtproto_config'; import { MOUNT_CLASS_TO } from '../mtproto/mtproto_config';
import referenceDatabase, { ReferenceContext } from '../mtproto/referenceDatabase'; import referenceDatabase, { ReferenceContext } from '../mtproto/referenceDatabase';
@ -16,6 +15,7 @@ export type MyDocument = Document.document;
class AppDocsManager { class AppDocsManager {
private docs: {[docID: string]: MyDocument} = {}; private docs: {[docID: string]: MyDocument} = {};
private savingLottiePreview: {[docID: string]: true} = {};
public saveDoc(doc: Document, context?: ReferenceContext): MyDocument { public saveDoc(doc: Document, context?: ReferenceContext): MyDocument {
if(doc._ == 'documentEmpty') { if(doc._ == 'documentEmpty') {
@ -326,6 +326,66 @@ class AppDocsManager {
return download; return download;
} }
public saveLottiePreview(doc: MyDocument, canvas: HTMLCanvasElement, toneIndex: number) {
const key = doc.id + '-' + toneIndex;
if(this.savingLottiePreview[key]/* || true */) return;
if(!doc.stickerCachedThumbs) {
defineNotNumerableProperties(doc, ['stickerCachedThumbs']);
doc.stickerCachedThumbs = {};
}
const thumb = doc.stickerCachedThumbs[toneIndex];
if(thumb && thumb.w >= canvas.width && thumb.h >= canvas.height) {
return;
}
/* if(doc.thumbs.find(t => t._ == 'photoStrippedSize')
|| (doc.stickerCachedThumb || (doc.stickerSavedThumbWidth >= canvas.width && doc.stickerSavedThumbHeight >= canvas.height))) {
return;
} */
this.savingLottiePreview[key] = true;
canvas.toBlob((blob) => {
//console.log('got lottie preview', doc, blob, URL.createObjectURL(blob));
const thumb = {
url: URL.createObjectURL(blob),
w: canvas.width,
h: canvas.height
};
doc.stickerCachedThumbs[toneIndex] = thumb;
delete this.savingLottiePreview[key];
/* const reader = new FileReader();
reader.onloadend = (e) => {
const uint8 = new Uint8Array(e.target.result as ArrayBuffer);
const thumb: PhotoSize.photoStrippedSize = {
_: 'photoStrippedSize',
bytes: uint8,
type: 'i'
};
doc.stickerSavedThumbWidth = canvas.width;
doc.stickerSavedThumbHeight = canvas.width;
defineNotNumerableProperties(thumb, ['url']);
thumb.url = URL.createObjectURL(blob);
doc.thumbs.findAndSplice(t => t._ == thumb._);
doc.thumbs.unshift(thumb);
if(!webpWorkerController.isWebpSupported()) {
doc.pFlags.stickerThumbConverted = true;
}
delete this.savingLottiePreview[doc.id];
};
reader.readAsArrayBuffer(blob); */
});
}
public saveDocFile(doc: MyDocument) { public saveDocFile(doc: MyDocument) {
const options = this.getFileDownloadOptions(doc); const options = this.getFileDownloadOptions(doc);
return appDownloadManager.downloadToDisc(options, doc.file_name); return appDownloadManager.downloadToDisc(options, doc.file_name);

27
src/lib/appManagers/appPhotosManager.ts

@ -158,6 +158,33 @@ export class AppPhotosManager {
return URL.createObjectURL(blob); return URL.createObjectURL(blob);
} }
/**
* https://core.telegram.org/api/files#vector-thumbnails
*/
public getPathFromPhotoPathSize(size: PhotoSize.photoPathSize) {
const bytes = size.bytes;
const lookup = "AACAAAAHAAALMAAAQASTAVAAAZaacaaaahaaalmaaaqastava.az0123456789-,";
let path = 'M';
for(let i = 0, length = bytes.length; i < length; ++i) {
const num = bytes[i];
if(num >= (128 + 64)) {
path += lookup[num - 128 - 64];
} else {
if(num >= 128) {
path += ',';
} else if(num >= 64) {
path += '-';
}
path += '' + (num & 63);
}
}
path += 'z';
return path;
}
public getPreviewURLFromThumb(thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize, isSticker = false) { public getPreviewURLFromThumb(thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize, isSticker = false) {
return thumb.url ?? (defineNotNumerableProperties(thumb, ['url']), thumb.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker)); return thumb.url ?? (defineNotNumerableProperties(thumb, ['url']), thumb.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker));
} }

2
src/lib/crypto/cryptoworker.ts

@ -1,3 +1,4 @@
import { MOUNT_CLASS_TO } from '../mtproto/mtproto_config';
import CryptoWorkerMethods from './crypto_methods'; import CryptoWorkerMethods from './crypto_methods';
type Task = { type Task = {
@ -124,5 +125,6 @@ class CryptoWorker extends CryptoWorkerMethods {
} }
const cryptoWorker = new CryptoWorker(); const cryptoWorker = new CryptoWorker();
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.CryptoWorker = cryptoWorker);
//(window as any).CryptoWorker = cryptoWorker; //(window as any).CryptoWorker = cryptoWorker;
export default cryptoWorker; export default cryptoWorker;

3
src/scripts/in/schema_additional_params.json

@ -1,7 +1,7 @@
[{ [{
"predicate": "document", "predicate": "document",
"params": [ "params": [
{"name": "thumbs", "type": "Array<PhotoSize.photoSize | PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize>"}, {"name": "thumbs", "type": "Array<PhotoSize.photoSize | PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize | PhotoSize.photoPathSize>"},
{"name": "type", "type": "'gif' | 'sticker' | 'audio' | 'voice' | 'video' | 'round' | 'photo'"}, {"name": "type", "type": "'gif' | 'sticker' | 'audio' | 'voice' | 'video' | 'round' | 'photo'"},
{"name": "h", "type": "number"}, {"name": "h", "type": "number"},
{"name": "w", "type": "number"}, {"name": "w", "type": "number"},
@ -17,6 +17,7 @@
{"name": "stickerEmojiRaw", "type": "string"}, {"name": "stickerEmojiRaw", "type": "string"},
{"name": "stickerSetInput", "type": "InputStickerSet.inputStickerSetID"}, {"name": "stickerSetInput", "type": "InputStickerSet.inputStickerSetID"},
{"name": "stickerThumbConverted", "type": "true"}, {"name": "stickerThumbConverted", "type": "true"},
{"name": "stickerCachedThumbs", "type": "{[toneIndex: number]: {url: string, w: number, h: number}}"},
{"name": "animated", "type": "boolean"}, {"name": "animated", "type": "boolean"},
{"name": "supportsStreaming", "type": "boolean"} {"name": "supportsStreaming", "type": "boolean"}
] ]

3
src/scss/partials/popups/_stickers.scss

@ -65,6 +65,7 @@
margin-bottom: 2px; margin-bottom: 2px;
justify-self: center; justify-self: center;
cursor: pointer; cursor: pointer;
position: relative;
@include respond-to(handhelds) { @include respond-to(handhelds) {
margin-bottom: 8px; margin-bottom: 8px;
@ -78,6 +79,8 @@
img { img {
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;
width: 100%;
height: 100%;
} }
} }
} }

12
src/scss/style.scss

@ -699,15 +699,21 @@ img.emoji {
pointer-events: none; pointer-events: none;
} }
.rlottie { .rlottie, .rlottie-vector {
position: absolute;
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 1;
}
&.fade-in { .rlottie.fade-in {
animation: fade-in-opacity .2s ease forwards; animation: fade-in-opacity .2s ease forwards;
} }
.rlottie-vector {
fill: rgba(0, 0, 0, .08);
} }
.fade-in-transition { .fade-in-transition {

Loading…
Cancel
Save