Handle edge cases with image outline (semi-transparency, tiny pieces and errors)

This commit is contained in:
Mitchell McCaffrey 2021-06-18 13:11:29 +10:00
parent 5e69ffcef8
commit bfc9c4fba3

View File

@ -189,41 +189,64 @@ export async function createThumbnail(image, type, size = 300, quality = 0.5) {
* @returns {Outline} * @returns {Outline}
*/ */
export function getImageOutline(image, maxPoints = 100) { export function getImageOutline(image, maxPoints = 100) {
let baseOutline = imageOutline(image); // Basic rect outline for fail conditions
const defaultOutline = {
type: "rect",
x: 0,
y: 0,
width: image.width,
height: image.height,
};
try {
let outlinePoints = imageOutline(image, {
opacityThreshold: 1, // Allow everything except full transparency
});
if (baseOutline) { if (outlinePoints) {
if (baseOutline.length > maxPoints) { if (outlinePoints.length > maxPoints) {
baseOutline = Vector2.resample(baseOutline, maxPoints); outlinePoints = Vector2.resample(outlinePoints, maxPoints);
} }
const bounds = Vector2.getBoundingBox(baseOutline); const bounds = Vector2.getBoundingBox(outlinePoints);
if (Vector2.rectangular(baseOutline)) {
return { // Reject outline if it's area is less than 5% of the image
type: "rect", const imageArea = image.width * image.height;
x: Math.round(bounds.min.x), const area = bounds.width * bounds.height;
y: Math.round(bounds.min.y), if (area < imageArea * 0.05) {
width: Math.round(bounds.width), return defaultOutline;
height: Math.round(bounds.height), }
};
} else if ( // Detect if the outline is a rectangle or circle
Vector2.circular( if (Vector2.rectangular(outlinePoints)) {
baseOutline, return {
Math.max(bounds.width / 10, bounds.height / 10) type: "rect",
) x: Math.round(bounds.min.x),
) { y: Math.round(bounds.min.y),
return { width: Math.round(bounds.width),
type: "circle", height: Math.round(bounds.height),
x: Math.round(bounds.center.x), };
y: Math.round(bounds.center.y), } else if (
radius: Math.round(Math.min(bounds.width, bounds.height) / 2), Vector2.circular(
}; outlinePoints,
Math.max(bounds.width / 10, bounds.height / 10)
)
) {
return {
type: "circle",
x: Math.round(bounds.center.x),
y: Math.round(bounds.center.y),
radius: Math.round(Math.min(bounds.width, bounds.height) / 2),
};
} else {
// Flatten and round outline to save on storage size
const points = outlinePoints
.map(({ x, y }) => [Math.round(x), Math.round(y)])
.flat();
return { type: "path", points };
}
} else { } else {
// Flatten and round outline to save on storage size return defaultOutline;
const points = baseOutline
.map(({ x, y }) => [Math.round(x), Math.round(y)])
.flat();
return { type: "path", points };
} }
} else { } catch {
return { type: "rect", x: 0, y: 0, width: 1, height: 1 }; return defaultOutline;
} }
} }