Skip to content

Commit 6a22c4b

Browse files
committed
Use a sprinte instead of individual images for books
The book listing loads close to 100 images at this point, this is a huge number of roundtrips and... Slow. Instead, combine them into one sprite and use CSS. To do this add a generic support for generating sprites. Right now it's only used for books, but it can be used for others as well in the future. To add a new book now, add it to the YAML, add the image as usual, and then run "manage.py combine_image_sprites", and make sure to commit both the original image and the combined one. This brings the image downloads from about 2MB across 84 requests to less than 350KB across 1 request.
1 parent d97a2d8 commit 6a22c4b

7 files changed

Lines changed: 72 additions & 1 deletion

File tree

media/css/main.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,13 @@ table.sponsor-table tbody tr td:nth-child(3) {
18071807
width: 30%;
18081808
}
18091809

1810+
/* Docs/books */
1811+
img.bookcover {
1812+
width: 130px;
1813+
height: 160px;
1814+
background-image: url('/media/img/docs/books/combined.webp');
1815+
}
1816+
18101817
/**
18111818
* This sets the rule for the rendering in the sponsors table. The name is chosen
18121819
* to ensure compatibility with various browser plugins that may otherwise hide

media/img/docs/books/combined.webp

343 KB
Loading

media/img/empty.png

88 Bytes
Loading

pgweb/docs/views.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ class BooksData(YamlDataLoader):
401401

402402

403403
@xkey('data_books')
404+
@content_sources('style', "'unsafe-inline'")
404405
def books(request):
405406
return render_pgweb(request, 'docs', 'docs/books.html', {
406407
'books': booksdata.get()['books'],
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from django.core.management.base import BaseCommand
2+
from django.db import transaction
3+
from django.conf import settings
4+
5+
from pgweb.util.sprites import sprites
6+
7+
8+
class Command(BaseCommand):
9+
help = 'Combine images to sprites'
10+
11+
def add_arguments(self, parser):
12+
parser.add_argument('image', type=str, nargs='?', default='all', choices=('books', 'all', ))
13+
14+
def handle(self, *args, **options):
15+
if options['image'] == 'all':
16+
for k, s in sprites.items():
17+
print("Creating sprite for {}".format(k))
18+
s.build_sprite_image()
19+
else:
20+
sprites[options['image']].build_sprite_image()

pgweb/util/sprites.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import os
2+
from PIL import Image
3+
4+
from pgweb.docs.views import booksdata
5+
6+
7+
class HorizontalSpriteBuilder:
8+
def build_sprite_image(self):
9+
inputlist = list(self.items())
10+
11+
fullwidth = len(inputlist) * (self.width + 1)
12+
13+
fullimg = Image.new("RGBA", (fullwidth, self.height), (255, 0, 0, 0))
14+
for i, fn in enumerate(inputlist):
15+
im = Image.open(fn)
16+
ratio = min(self.width / im.width, self.height / im.height)
17+
new_width = round(im.width * ratio)
18+
new_height = round(im.height * ratio)
19+
20+
im = im.resize((new_width, new_height), Image.LANCZOS)
21+
22+
# Center if smaller
23+
xofs = (self.width - new_width) // 2
24+
yofs = (self.height - new_height) // 2
25+
26+
fullimg.paste(im, (i * (self.width + 1) + xofs, yofs))
27+
28+
fullimg.save(self.output, optimize=True, compress_level=9)
29+
30+
31+
class BooksSpriteBuilder(HorizontalSpriteBuilder):
32+
width = 130
33+
height = 160
34+
# We use webp here because it's jpeg size (even smaller actually) and can be made transparent
35+
output = "media/img/docs/books/combined.webp"
36+
37+
def items(self):
38+
return (os.path.join('media/img/docs/books/', b['image']) for b in booksdata.get()['books'])
39+
40+
41+
sprites = {
42+
'books': BooksSpriteBuilder(),
43+
}

templates/docs/books.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ <h1>Books <i class="fas fa-book"></i></h1>
1515
<tr>
1616
<td>
1717
{% if book.url %}<a href="https://leanpub.com/deep-dive-into-a-sql-query">{% endif %}
18-
<img src="/media/img/docs/books/{{ book.image }}" alt="{{ book.title }}">
18+
<img class="bookcover" src="/media/img/empty.png" alt="{{ book.title }}" style="background-position-x: {% widthratio forloop.counter0 1 -131 %}px">
1919
{% if book.url %}</a>{% endif %}
2020
<td>
2121
<strong>Title</strong>: {{ book.title }}<br />

0 commit comments

Comments
 (0)