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 @@
+
+
+
+
+
+ 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.
+
+
+
+
+
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.
+
+
+
+
+
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.
+
+
+
+
+
+
+
+
+
+
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.
+
+
+
+
+
+
+
+
+
+
+
+
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
}