From 3e50dc1f8669f04ea61b34d23cf6de4d35b8df32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20Wersd=C3=B6rfer?= Date: Sat, 18 Nov 2023 23:53:58 +0100 Subject: [PATCH] generate thumbnails in different sizes and compare manually to have a way to debug raw html directly --- .gitignore | 2 + example/templates/manual.html | 246 +++++++++++++++++++++++++ example/urls.py | 2 + notebooks/create_resized_images.ipynb | 255 ++++++++++++++++++++++++++ notebooks/image_example.ipynb | 6 +- 5 files changed, 508 insertions(+), 3 deletions(-) create mode 100644 example/templates/manual.html create mode 100644 notebooks/create_resized_images.ipynb diff --git a/.gitignore b/.gitignore index 0916422..631ecd9 100644 --- a/.gitignore +++ b/.gitignore @@ -271,6 +271,8 @@ tags apps/media/ tests/media +example/media/manual/ + .pytest_cache/ diff --git a/example/templates/manual.html b/example/templates/manual.html new file mode 100644 index 0000000..9194b7d --- /dev/null +++ b/example/templates/manual.html @@ -0,0 +1,246 @@ + + + + + Manual Images + + + + + +
+ +

Comparing Images Manually

+

+ We compare plain image to image with srcset and sizes attribute. + Also picture tag with source and img tag. +

+
+

Just a normal image tag without and with srcset attribute set but no sizes

+

+ The thumbnail has a width of 53px and a height of 80px. + + The srcset attribute has three additional images with a width of 106px, 159px and 265px. + Since the sizes attribute is not set, the browser should load the image with a width of 265px. +

+ img original thumbnail + img thumbnail with srcset +
+

First Image

+

+        Selected image source: 
+      
+
+

Second Image

+

+        Selected image source: 
+      
+ +
+
+

Just a normal image tag without and with srcset attribute set and sizes

+

+ The thumbnail has a width of 53px and a height of 80px. + + The srcset attribute has three additional images with a width of 106px, 159px and 265px. + With the sizes attribute set to 53px, the browser should load the image with a width of 106px. +

+ img original thumbnail + img thumbnail with srcset +
+

First Image

+

+        Selected image source: 
+      
+
+

Second Image

+

+        Selected image source: 
+      
+ +
+
+

Using the picture element

+

+ The thumbnail has a width of 53px and a height of 80px. + + The picture element has three additional images with a width of 106px, 159px and 265px. + The sizes attribute is set to 53px, so the browser should load the image with a width of 106px. +

+ + img original thumbnail + + + + img in picture with srcset + +
+

First Image

+

+        Selected image source: 
+      
+
+

Second Image

+

+        Selected image source: 
+      
+ +
+
+

Using the picture element with different formats

+

+ The thumbnail has a width of 53px and a height of 80px. + + The picture element has three additional images with a width of 106px, 159px and 265px. + The sizes attribute is set to 53px, so the browser should load the image with a width of 106px. +

+ + img original thumbnail + + + + + + img in picture with srcset + +
+

First Image

+

+        Selected image source: 
+      
+
+

Second Image

+

+        Selected image source: 
+      
+ +
+
+ + diff --git a/example/urls.py b/example/urls.py index 1d7a883..d9ab26c 100644 --- a/example/urls.py +++ b/example/urls.py @@ -1,9 +1,11 @@ from django.conf import settings from django.conf.urls.static import static from django.urls import path +from django.views.generic import TemplateView from .views import MainView urlpatterns = [ path("", MainView.as_view(), name="main"), + path("manual/", TemplateView.as_view(template_name="manual.html"), name="manual"), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/notebooks/create_resized_images.ipynb b/notebooks/create_resized_images.ipynb new file mode 100644 index 0000000..fbe5c53 --- /dev/null +++ b/notebooks/create_resized_images.ipynb @@ -0,0 +1,255 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "63373ee4-16bd-497f-b771-fd2add7805ce", + "metadata": {}, + "source": [ + "# Tests with plain HTML\n", + "\n", + "Just to have a baseline understanding how this whole responsive image stuff works." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f5f5dd52-5775-453b-b94c-9da8df76d21f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "from django.conf import settings\n", + "\n", + "from willow.image import Image" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "58696454-1862-46e3-a9dd-23fc8554ba13", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "media_root = Path(settings.MEDIA_ROOT)\n", + "original = media_root / \"original_images\" / \"apes.jpeg\"\n", + "\n", + "f = open(original, \"rb\")\n", + "img = Image.open(f)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5e17bd6a-446a-4b78-8734-7b3237e49aa8", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "manual_resized_dir = media_root / \"manual\"\n", + "manual_resized_dir.mkdir(exist_ok=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6df9e9ae-e2ad-4363-8d08-2b399ff01709", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2828, 3771)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "img.get_size()" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "04b0a744-2b11-4bff-b7ab-0698ebc88509", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def create_resized_image(img, width, height, base_dir=manual_resized_dir, images_count=5):\n", + " \"\"\"\n", + " Generate the same image images_count times to prevent Chrome\n", + " from caching image with same name for a different image tag :/.\n", + " \"\"\"\n", + " resized = img.resize((width, height))\n", + " for num in range(1, images_count + 1):\n", + " resized_jpeg_path = manual_resized_dir / f\"{num}_{width}x{height}.jpeg\"\n", + " with resized_jpeg_path.open(\"wb\") as out:\n", + " resized.save_as_jpeg(out, 70)\n", + " resized_webp_path = manual_resized_dir / f\"{num}_{width}x{height}.webp\"\n", + " with resized_webp_path.open(\"wb\") as out:\n", + " resized.save_as_webp(out, 70)\n", + " resized_avif_path = manual_resized_dir / f\"{num}_{width}x{height}.avif\"\n", + " with resized_avif_path.open(\"wb\") as out:\n", + " resized.save_as_avif(out, 65)\n", + "\n", + "\n", + "def create_resized_image_from_sizes(img, sizes, base_dir=manual_resized_dir):\n", + " for width, height in sizes:\n", + " create_resized_image(img, width, height, base_dir=base_dir)" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "834a8cb5-96b6-44ea-b755-e140f44905a7", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "sizes = [\n", + " (53, 80),\n", + " (106, 160),\n", + " (159, 240),\n", + " (265, 400),\n", + " (424, 640),\n", + "]\n", + "\n", + "create_resized_image_from_sizes(img, sizes)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "538f726a-12ae-470a-83e7-2501badb4038", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "424" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "53 * 8" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "48d118be-3cde-4f80-ad31-b7dc6e248684", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "640" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "80 * 8" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "14bbec48-ce77-4bf6-886f-6e67af82c3eb", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[(1, 0), (2, 1), (3, 2), (4, 3), (5, 4)]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(enumerate(range(5), 1))" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "198d9135-66b2-4fe4-8807-ec3679022702", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4]" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(range(1, 5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "74f233ff-259f-4387-a5bc-3c2d5846a356", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Django Kernel", + "language": "python", + "name": "django_extensions" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/image_example.ipynb b/notebooks/image_example.ipynb index cf1b1e1..0d449f8 100644 --- a/notebooks/image_example.ipynb +++ b/notebooks/image_example.ipynb @@ -68,7 +68,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Django Shell-Plus", + "display_name": "Django Kernel", "language": "python", "name": "django_extensions" }, @@ -82,9 +82,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.1" + "version": "3.12.0" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 }