mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-25 07:45:22 +00:00
Improve media filtering by using OffscreenCanvas, if supported
Also improve browser support by testing for each feature instead of checking the user agent. Also improve error handling by using promises. Fixes #2939 Fixes #4194
This commit is contained in:
parent
249468d154
commit
7d80ac3c93
1 changed files with 66 additions and 32 deletions
|
@ -1766,57 +1766,91 @@ export default {
|
||||||
|
|
||||||
applyFilterToMedia() {
|
applyFilterToMedia() {
|
||||||
// this is where the magic happens
|
// this is where the magic happens
|
||||||
var ua = navigator.userAgent.toLowerCase();
|
|
||||||
if(ua.indexOf('firefox') == -1 && ua.indexOf('chrome') == -1) {
|
|
||||||
this.isPosting = false;
|
|
||||||
swal('Oops!', 'Your browser does not support the filter feature.', 'error');
|
|
||||||
this.page = 3;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let count = this.media.filter(m => m.filter_class).length;
|
let count = this.media.filter(m => m.filter_class).length;
|
||||||
if(count) {
|
if(count) {
|
||||||
this.page = 'filteringMedia';
|
this.page = 'filteringMedia';
|
||||||
this.filteringRemainingCount = count;
|
this.filteringRemainingCount = count;
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.isFilteringMedia = true;
|
this.isFilteringMedia = true;
|
||||||
this.media.forEach((media, idx) => this.applyFilterToMediaSave(media, idx));
|
Promise.all(this.media.map(media => {
|
||||||
|
return this.applyFilterToMediaSave(media);
|
||||||
|
})).catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
swal('Oops!', 'An error occurred while applying filters to your media. Please refresh the page and try again. If the problem persist, please try a different web browser.', 'error');
|
||||||
|
});
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.page = 3;
|
this.page = 3;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
applyFilterToMediaSave(media, idx) {
|
async applyFilterToMediaSave(media) {
|
||||||
if(!media.filter_class) {
|
if(!media.filter_class) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let self = this;
|
// Load image
|
||||||
let data = null;
|
const image = document.createElement('img');
|
||||||
const canvas = document.createElement('canvas');
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
let image = document.createElement('img');
|
|
||||||
image.src = media.url;
|
image.src = media.url;
|
||||||
image.addEventListener('load', e => {
|
await new Promise((resolve, reject) => {
|
||||||
|
image.addEventListener('load', () => resolve());
|
||||||
|
image.addEventListener('error', () => {
|
||||||
|
reject(new Error('Failed to load image'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create canvas
|
||||||
|
let canvas;
|
||||||
|
let usingOffscreenCanvas = false;
|
||||||
|
if('OffscreenCanvas' in window) {
|
||||||
|
canvas = new OffscreenCanvas(image.width, image.height);
|
||||||
|
usingOffscreenCanvas = true;
|
||||||
|
} else {
|
||||||
|
canvas = document.createElement('canvas');
|
||||||
canvas.width = image.width;
|
canvas.width = image.width;
|
||||||
canvas.height = image.height;
|
canvas.height = image.height;
|
||||||
ctx.filter = App.util.filterCss[media.filter_class];
|
}
|
||||||
ctx.drawImage(image, 0, 0, image.width, image.height);
|
|
||||||
ctx.save();
|
// Draw image with filter to canvas
|
||||||
canvas.toBlob(function(blob) {
|
const ctx = canvas.getContext('2d');
|
||||||
data = new FormData();
|
if (!ctx) {
|
||||||
data.append('file', blob);
|
throw new Error('Failed to get canvas context');
|
||||||
data.append('id', media.id);
|
}
|
||||||
axios.post('/api/compose/v0/media/update', data)
|
if (!('filter' in ctx)) {
|
||||||
.then(res => {
|
throw new Error('Canvas filter not supported');
|
||||||
self.media[idx].is_filtered = true;
|
}
|
||||||
self.updateFilteringMedia();
|
ctx.filter = App.util.filterCss[media.filter_class];
|
||||||
}).catch(err => {
|
ctx.drawImage(image, 0, 0, image.width, image.height);
|
||||||
});
|
ctx.save();
|
||||||
}, media.mime, 0.9);
|
|
||||||
});
|
// Convert canvas to blob
|
||||||
ctx.clearRect(0, 0, image.width, image.height);
|
let blob;
|
||||||
|
if(usingOffscreenCanvas) {
|
||||||
|
blob = await canvas.convertToBlob({
|
||||||
|
type: media.mime,
|
||||||
|
quality: 1,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
blob = await new Promise(resolve => {
|
||||||
|
canvas.toBlob(blob => {
|
||||||
|
if(blob) {
|
||||||
|
resolve(blob);
|
||||||
|
} else {
|
||||||
|
reject(
|
||||||
|
new Error('Failed to convert canvas to blob'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, media.mime, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload blob / Update media
|
||||||
|
const data = new FormData();
|
||||||
|
data.append('file', blob);
|
||||||
|
data.append('id', media.id);
|
||||||
|
await axios.post('/api/compose/v0/media/update', data);
|
||||||
|
media.is_filtered = true;
|
||||||
|
this.updateFilteringMedia();
|
||||||
},
|
},
|
||||||
|
|
||||||
updateFilteringMedia() {
|
updateFilteringMedia() {
|
||||||
|
|
Loading…
Reference in a new issue