Skip to content

Commit e5a8c96

Browse files
committed
Avoid double WebP encoding for circle crops
1 parent d3114e3 commit e5a8c96

File tree

1 file changed

+23
-39
lines changed

1 file changed

+23
-39
lines changed

packages/web/titanium/smart-attachment-input/crop-and-save-image-dialog.ts

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { ifDefined } from 'lit/directives/if-defined.js';
1919

2020
import Bowser from 'bowser';
2121
const LoaderGif = new URL('./images/duck-loader.gif', import.meta.url).href;
22-
const DEFAULT_IMAGE_QUALITY = 0.80;
22+
const DEFAULT_IMAGE_QUALITY = 0.8;
2323

2424
export declare type CropperOptions = {
2525
shape?: 'square' | 'circle';
@@ -164,39 +164,29 @@ export class CropAndSaveImageDialog extends LoadWhile(LitElement) {
164164
});
165165
}
166166

167-
async #applyCircleMask(sourceUrl: string): Promise<Blob> {
168-
const canvas = document.createElement('canvas');
169-
const image = new Image();
170-
171-
const blobPromise = new Promise<Blob>((resolve, reject) => {
172-
image.onload = async () => {
173-
const size = Math.min(image.naturalWidth, image.naturalHeight);
174-
175-
canvas.width = size;
176-
canvas.height = size;
177-
178-
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
179-
ctx.drawImage(image, 0, 0);
180-
181-
ctx.globalCompositeOperation = 'destination-in';
182-
183-
ctx.fillStyle = '#000';
184-
ctx.beginPath();
185-
ctx.arc(size * 0.5, size * 0.5, size * 0.5, 0, 2 * Math.PI);
186-
ctx.fill();
167+
#applyCircleMask(canvas: HTMLCanvasElement) {
168+
const size = Math.min(canvas.width, canvas.height);
169+
if (canvas.width !== size || canvas.height !== size) {
170+
const sourceCanvas = document.createElement('canvas');
171+
sourceCanvas.width = size;
172+
sourceCanvas.height = size;
173+
const sourceCtx = sourceCanvas.getContext('2d') as CanvasRenderingContext2D;
174+
sourceCtx.drawImage(canvas, 0, 0);
175+
canvas.width = size;
176+
canvas.height = size;
177+
const resizedCtx = canvas.getContext('2d') as CanvasRenderingContext2D;
178+
resizedCtx.drawImage(sourceCanvas, 0, 0);
179+
}
187180

188-
ctx.globalCompositeOperation = 'source-over';
181+
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
182+
ctx.globalCompositeOperation = 'destination-in';
189183

190-
try {
191-
resolve(await this.#canvasToBlob(canvas, this.#mimeType, this.options?.outputQuality ?? DEFAULT_IMAGE_QUALITY));
192-
} catch (e) {
193-
reject(e);
194-
}
195-
};
196-
});
197-
image.src = sourceUrl;
184+
ctx.fillStyle = '#000';
185+
ctx.beginPath();
186+
ctx.arc(size * 0.5, size * 0.5, size * 0.5, 0, 2 * Math.PI);
187+
ctx.fill();
198188

199-
return blobPromise;
189+
ctx.globalCompositeOperation = 'source-over';
200190
}
201191

202192
// adapted from https://fengyuanchen.github.io/cropperjs/v2/api/cropper-selection.html#limit-boundaries
@@ -487,16 +477,10 @@ export class CropAndSaveImageDialog extends LoadWhile(LitElement) {
487477
return;
488478
}
489479
490-
const blob = await this.#canvasToBlob(canvas, this.#mimeType, this.options?.outputQuality ?? DEFAULT_IMAGE_QUALITY);
491-
492-
let previewBlob: Blob;
493480
if (this.options?.shape === 'circle') {
494-
const tempUrl = URL.createObjectURL(blob);
495-
previewBlob = await this.#applyCircleMask(tempUrl);
496-
URL.revokeObjectURL(tempUrl);
497-
} else {
498-
previewBlob = blob;
481+
this.#applyCircleMask(canvas);
499482
}
483+
const previewBlob = await this.#canvasToBlob(canvas, this.#mimeType, this.options?.outputQuality ?? DEFAULT_IMAGE_QUALITY);
500484
501485
const previewUrl = URL.createObjectURL(previewBlob);
502486
const file = this.blobToFile(previewBlob, this.#changeFileExtension(this.fileName, this.#extension));

0 commit comments

Comments
 (0)