thumbnailing: Switch to libvips, from PIL/pillow.

This is done in as much of a drop-in fashion as possible.  Note that
libvips does not support animated PNGs[^1], and as such this
conversion removes support for them as emoji; however, libvips
includes support for webp images, which future commits will take
advantage of.

This removes the MAX_EMOJI_GIF_SIZE limit, since that existed to work
around bugs in Pillow.  MAX_EMOJI_GIF_FILE_SIZE_BYTES is fixed to
actually be 128KiB (not 128MiB, as it actually was), and is counted
_after_ resizing, since the point is to limit the amount of data
transfer to clients.

[^1]: https://github.com/libvips/libvips/discussions/2000
This commit is contained in:
Alex Vandiver
2024-06-12 18:19:15 +00:00
committed by Tim Abbott
parent 9fb03cb2c7
commit b14a33c659
8 changed files with 157 additions and 152 deletions

View File

@@ -13,7 +13,13 @@ from zerver.lib.avatar_hash import user_avatar_path
from zerver.lib.exceptions import ErrorCode, JsonableError
from zerver.lib.mime_types import guess_type
from zerver.lib.outgoing_http import OutgoingSession
from zerver.lib.thumbnail import MEDIUM_AVATAR_SIZE, resize_avatar, resize_emoji
from zerver.lib.thumbnail import (
MAX_EMOJI_GIF_FILE_SIZE_BYTES,
MEDIUM_AVATAR_SIZE,
BadImageError,
resize_avatar,
resize_emoji,
)
from zerver.lib.upload.base import ZulipUploadBackend
from zerver.models import Attachment, Message, Realm, RealmEmoji, ScheduledMessage, UserProfile
@@ -252,7 +258,11 @@ def upload_emoji_image(
backend.upload_single_emoji_image(
f"{emoji_path}.original", content_type, user_profile, image_data
)
resized_image_data, is_animated, still_image_data = resize_emoji(image_data)
resized_image_data, is_animated, still_image_data = resize_emoji(image_data, emoji_file_name)
if is_animated and len(still_image_data) > MAX_EMOJI_GIF_FILE_SIZE_BYTES: # nocoverage
raise BadImageError(_("Image size exceeds limit"))
if not is_animated and len(image_data) > MAX_EMOJI_GIF_FILE_SIZE_BYTES: # nocoverage
raise BadImageError(_("Image size exceeds limit"))
backend.upload_single_emoji_image(emoji_path, content_type, user_profile, resized_image_data)
if is_animated:
assert still_image_data is not None