codec: some random additional image and audio features
[sheet.git] / codec-image.inc.pl
1 use utf8;
2 +{
3 intro => 'Comparison of image encoding formats, based on <a href="https://cloudinary.com/blog/one_pixel_is_worth_three_thousand_words">Cloudinary</a> research.',
4 codec => {
5         jpeg => {
6                 name => '<abbr title="Joint Photographic Experts Group">JPEG</abbr>',
7                 available => 1992,
8                 generation => 0,
9         },
10         gif => {
11                 name => '<abbr title="Graphics Interchange Format">GIF</abbr>',
12                 available => 1987,
13                 generation => 0,
14         },
15         png => {
16                 name => '<abbr title="Portable Network Graphics">PNG</abbr>',
17                 available => 1996,
18                 generation => 1,
19         },
20         jp2k => {
21                 name => 'JPEG 2000',
22                 available => 2000,
23                 generation => 1,
24         },
25         webp => {
26                 name => 'WebP',
27                 available => 2010,
28                 generation => 1,
29         },
30         heic => {
31                 name => '<abbr title="High Efficiency Image Container (HEVC in HEIF)">HEIC</abbr>',
32                 available => 2015,
33                 generation => 2,
34         },
35         avif => {
36                 name => '<abbr title="AV1 Image File Format">AVIF</abbr>',
37                 available => 2019,
38                 generation => 2,
39         },
40         jxl => {
41                 name => 'JPEG XL',
42                 available => 2021,
43                 generation => 2,
44         },
45         pnm => {
46                 name => '<abbr title="Portable aNyMap">PNM</abbr>',
47                 available => 1988,
48                 generation => 0,
49         },
50 },
51 feature => {
52         default => {
53                 children => [qw( quality_photo quality_art speed limits features royalties overhead support web )],
54         },
55         quality_photo => {
56                 name => 'compression (photo)',
57                 score => {
58                         jpeg => 3,
59                         pnm  => 'n',
60                         gif  => 1,
61                         png  => 1,
62                         jp2k => 4,
63                         webp => 3,
64                         heic => 5,
65                         avif => 5,
66                         jxl  => 5,
67                 },
68                 children => [qw( quality_photo_ll quality_photo_3 quality_photo_2 quality_photo_1 quality_thumbs )],
69         },
70         quality_thumbs => {
71                 parent => 'quality_photo',
72                 name => 'thumbnails',
73                 score => {
74                         jpeg => 1,
75                         pnm  => 1,
76                         gif  => 2,
77                         png  => 3,
78                         jp2k => 1,
79                         webp => 2,
80                         heic => 4,
81                         avif => 4,
82                         jxl  => 3,
83                 },
84         },
85         quality_photo_1 => {
86                 parent => 'quality_photo',
87                 name => 'low fidelity',
88                 score => {
89                         jpeg => 2,
90                         pnm  => 1,
91                         gif  => 1,
92                         png  => 1,
93                         jp2k => 3,
94                         webp => 4,
95                         heic => 5,
96                         avif => 5,
97                         jxl  => 3,
98                 },
99         },
100         quality_photo_2 => {
101                 parent => 'quality_photo',
102                 name => 'medium fidelity',
103                 score => {
104                         jpeg => 3,
105                         pnm  => 1,
106                         gif  => 1,
107                         png  => 1,
108                         jp2k => 4,
109                         webp => 3,
110                         heic => 4,
111                         avif => 5,
112                         jxl  => 5,
113                 },
114         },
115         quality_photo_3 => {
116                 parent => 'quality_photo',
117                 name => 'high fidelity',
118                 score => {
119                         jpeg => 3,
120                         pnm  => 1,
121                         gif  => 1,
122                         png  => 2,
123                         jp2k => 4,
124                         webp => 2,
125                         heic => 3,
126                         avif => 4,
127                         jxl  => 5,
128                 },
129         },
130         quality_photo_ll => {
131                 parent => 'quality_photo',
132                 name => 'lossless',
133                 score => {
134                         jpeg => 1,
135                         pnm  => 1,
136                         gif  => 1,
137                         png  => 2,
138                         jp2k => 4,
139                         webp => 3,
140                         heic => 3,
141                         avif => 2,
142                         jxl  => 5,
143                 },
144         },
145         quality_art => {
146                 name => 'compression (other images)',
147                 score => {
148                         jpeg => 2,
149                         pnm  => 'n',
150                         gif  => 1,
151                         png  => 3,
152                         jp2k => 2,
153                         webp => 4,
154                         heic => 3,
155                         avif => 4.5,
156                         jxl  => 5,
157                 },
158                 children => [qw( quality_art_2 quality_art_ll quality_art_mixed )],
159         },
160         quality_art_2 => {
161                 name => 'lossy non-photographic',
162                 score => {
163                         jpeg => 2,
164                         pnm  => 1,
165                         gif  => 2,
166                         png  => 3,
167                         jp2k => 2,
168                         webp => 4,
169                         heic => 3,
170                         avif => 5,
171                         jxl  => 5,
172                 },
173         },
174         quality_art_ll => {
175                 name => 'lossless non-photographic',
176                 score => {
177                         jpeg => 1,
178                         pnm  => 1,
179                         gif  => 1,
180                         png  => 4,
181                         jp2k => 2,
182                         webp => 5,
183                         heic => 2,
184                         avif => 3,
185                         jxl  => 5,
186                 },
187         },
188         quality_art_mixed => {
189                 name => 'mixed photo/nonphoto',
190                 score => {
191                         jpeg => 2,
192                         pnm  => 1,
193                         gif  => 1,
194                         png  => 2,
195                         jp2k => 2,
196                         webp => 3,
197                         heic => 3,
198                         avif => 5,
199                         jxl  => 5,
200                 },
201         },
202         speed => {
203                 score => {
204                         jpeg => 5,
205                         pnm  => 5,
206                         gif  => 4,
207                         png  => 4,
208                         jp2k => 3,
209                         webp => 4,
210                         heic => 3,
211                         avif => 3,
212                         jxl  => 5,
213                 },
214                 children => [qw( speed_encode speed_decode speed_parallel )],
215         },
216         speed_encode => {
217                 parent => 'speed',
218                 name => 'single-core encode',
219                 score => {
220                         jpeg => 5,
221                         pnm  => 0,
222                         gif  => 3, # palette conversion
223                         png  => 3,
224                         jp2k => 4,
225                         webp => 4,
226                         heic => 3,
227                         avif => 2,
228                         jxl  => 5,
229                 },
230         },
231         speed_decode => {
232                 parent => 'speed',
233                 name => 'single-core decode',
234                 score => {
235                         jpeg => 5,
236                         pnm  => 0,
237                         gif  => 5,
238                         png  => 5,
239                         jp2k => 4,
240                         webp => 5,
241                         heic => 3,
242                         avif => 3,
243                         jxl  => 5,
244                 },
245         },
246         speed_parallel => {
247                 parent => 'speed',
248                 name => 'pararellizable',
249                 score => {
250                         jpeg => 2,
251                         pnm  => 0,
252                         gif  => 2,
253                         png  => 2,
254                         jp2k => 4,
255                         webp => 2,
256                         heic => 4,
257                         avif => 4,
258                         jxl  => 5,
259                 },
260         },
261         limits => {
262                 score => {
263                         jpeg => 3,
264                         pnm  => 3,
265                         gif  => 2,
266                         png  => 4,
267                         jp2k => 5,
268                         webp => 2,
269                         heic => 4,
270                         avif => 4.5,
271                         jxl  => 5,
272                 },
273                 children => [qw( max_dimensions max_bitdepth color_444 hdr max_channels  )],
274         },
275         max_dimensions => {
276                 parent => 'limits',
277                 name => 'maximum image dimensions',
278                 score => {
279                         jpeg => [3, '65k²'],   # 2**16
280                         pnm  => [5,    '∞'],
281                         gif  => [3, '65k²'],   # 2**16
282                         png  => [4,  '2G²'],   # 2**31
283                         jp2k => [4,  '4G²'],   # 2**32
284                         webp => [1, '16k²'],   # 2**14
285                         heic => [2,'8k×4k+', 'tilable, only 512×512 on Apple'], # 8193x4320
286                         avif => [3, '65k²+', 'tilable, 7680×4320 with Advanced profile'], # 2**16
287                         jxl  => [4,  '1G²'],   # 2**30
288                 },
289         },
290         max_bitdepth => {
291                 parent => 'limits',
292                 name => 'precision (max. bit depth)',
293                 score => {
294                         jpeg => [2,  8],
295                         pnm  => [2,  8, 'unofficial PFM extension for 32-bit'],
296                         gif  => [1,  8, '256 colour palette per frame'],
297                         png  => [4, 16],
298                         jp2k => [5, 38],
299                         webp => [2,  8],
300                         heic => [3, 10], #TODO 16?
301                         avif => [3, 12, '8, 10, 12 bit'],
302                         jxl  => [5, 32, '24-bit integer or 32-bit float'],
303                 },
304         },
305         color_444 => {
306                 parent => 'limits',
307                 name => 'chroma subsampling',
308                 score => {
309                         jpeg => ['y', undef, '4:2:0, 4:2:2, 4:4:4'],
310                         pnm  => [4, '✘'],
311                         gif  => [4, '✘'],
312                         png  => [4, '✘'],
313                         jp2k => 'y',
314                         webp => [1, '4:2:0'],
315                         heic => [1, '4:2:0'],
316                         avif => ['y', undef, '4:2:0, 4:2:2, 4:4:4'],
317                         jxl  => ['y', undef, 'for JPEG compatibility'],
318                 },
319         },
320         hdr => {
321                 parent => 'limits',
322                 name => 'wide gamut/HDR',
323                 score => {
324                         jpeg => 'n',
325                         pnm  => 'n',
326                         gif  => 'n',
327                         png  => 'y',
328                         jp2k => 'y',
329                         webp => 'n',
330                         heic => 'y',
331                         avif => 'y',
332                         jxl  => 'y',
333                 },
334         },
335         max_channels => {
336                 parent => 'limits',
337                 name => 'maximum number of channels',
338                 score => {
339                         jpeg => [3, 4, 'RGB or CMYK'],
340                         pnm  => [3, 3, 'RGB'],
341                         gif  => [3, 3, 'RGB palette'],
342                         png  => [3, 4, 'RGBA'],
343                         jp2k => [5, 2**15],
344                         webp => [3, 4, 'RGBA'],
345                         heic => [3, 3, 'RGB, separate alpha and depth'],
346                         avif => [3, 3, 'RGB, separate alpha and depth'],
347                         jxl  => [4, 4099, 'native XYB'],
348                 },
349         },
350         features => {
351                 score => {
352                         jpeg => 2,
353                         pnm  => [2, undef, 'great for simplicity and ASCII storage'],
354                         gif  => 2,
355                         png  => 3,
356                         jp2k => 4,
357                         webp => 2,
358                         heic => 4,
359                         avif => 4,
360                         jxl  => 5,
361                 },
362                 children => [qw( animation progressive alpha depthmap overlays vector authoring reencode compat_jpeg )],
363         },
364         animation => {
365                 parent => 'features',
366                 name => 'supports animation',
367                 score => {
368                         jpeg => [2, 'MJPEG'],
369                         pnm  => 'n',
370                         gif  => 'y',
371                         png  => [4, 'APNG', 'later backwards-compatible extension'],
372                         jp2k => [2, 'MJP2'],
373                         webp => 'y',
374                         heic => 'y',
375                         avif => 'y',
376                         jxl  => 'y',
377                 },
378         },
379         progressive => {
380                 parent => 'features',
381                 name => 'progressive decoding',
382                 score => {
383                         jpeg => 4,
384                         pnm  => 'n',
385                         gif  => 2,
386                         png  => 2,
387                         jp2k => 5,
388                         webp => 'n',
389                         heic => 'n',
390                         avif => 'n',
391                         jxl  => 5,
392                 },
393         },
394         vector => {
395                 parent => 'features',
396                 name => 'vector drawing',
397                 score => {
398                         jpeg => 'n',
399                         pnm => 'n',
400                         gif => 'n',
401                         png => 'n',
402                         jp2k => 'n',
403                         webp => 'n',
404                         heic => 'n',
405                         avif => 'n',
406                         jxl => [2, undef, 'splines'],
407                 },
408         },
409         alpha => {
410                 parent => 'features',
411                 name => 'alpha transparency',
412                 score => {
413                         jpeg => 'n',
414                         pnm  => ['n', undef, 'PAM extension'],
415                         gif  => [3, '1 bit'],
416                         png  => 'y',
417                         jp2k => 'y',
418                         webp => 'y',
419                         heic => 'y',
420                         avif => 'y',
421                         jxl  => 'y',
422                 },
423         },
424         depthmap => {
425                 parent => 'features',
426                 name => 'depth map',
427                 score => {
428                         jpeg => 'n',
429                         pnm  => 'n',
430                         gif  => 'n',
431                         png  => 'n',
432                         jp2k => 'n',
433                         webp => 'n',
434                         heic => 'y',
435                         avif => 'y',
436                         jxl  => 'y',
437                 },
438         },
439         overlays => {
440                 parent => 'features',
441                 name => 'overlays (layers)',
442                 score => {
443                         jpeg => 'n',
444                         pnm  => 'n',
445                         gif  => 'y',
446                         png  => 'n',
447                         jp2k => 'n',
448                         webp => 'n',
449                         heic => 'y',
450                         avif => 'y',
451                         jxl  => 'y',
452                 },
453         },
454         authoring => {
455                 parent => 'features',
456                 name => 'authoring workflow suitability',
457                 score => {
458                         jpeg => 2,
459                         pnm  => 2,
460                         gif  => 2,
461                         png  => 3,
462                         jp2k => 3,
463                         webp => 2,
464                         heic => 2,
465                         avif => 2,
466                         jxl  => 5,
467                 },
468         },
469         reencode => {
470                 parent => 'features',
471                 name => 'generation loss resilience',
472                 score => {
473                         jpeg => 4,
474                         pnm  => 0,
475                         png  => 0,
476                         gif  => 0,
477                         jp2k => 3,
478                         webp => 2,
479                         heic => 3,
480                         avif => 3,
481                         jxl  => 4,
482                 },
483         },
484         compat_jpeg => {
485                 parent => 'features',
486                 name => 'lossless JPEG recompression',
487                 score => {
488                         jpeg => 0,
489                         pnm  => 'n',
490                         gif  => 'n',
491                         png  => 'n',
492                         jp2k => 'n',
493                         webp => 'n',
494                         heic => 'n',
495                         avif => 'n',
496                         jxl  => 'y',
497                 },
498         },
499         royalties => {
500                 name => 'royalty-free',
501                 score => {
502                         jpeg => 5,
503                         pnm  => 5,
504                         gif  => [5, undef, 'patented before 2003'],
505                         png  => 5,
506                         jp2k => [3, undef, 'ISO specification not freely available'],
507                         webp => [4, undef, 'free format, low remaining risk of patent trolls'],
508                         heic => ['n', undef, 'heavily patented'],
509                         avif => [4, undef, 'free format, risk of patent trolls'],
510                         jxl  => [4, undef, 'free format, risk of patent trolls'],
511                 },
512                 children => [],
513         },
514         web => {
515                 name => 'browser support',
516                 score => {
517                         jpeg => [5, undef, 'ubiquitous since first inline images (1993)'],
518                         pnm  => [1, undef, 'rarely by (unix) systems'],
519                         xbm  => [2, undef, 'common before 200X'],
520                         gif  => [5, undef, 'predates the web, but unrestricted since 2004'],
521                         png  => [5, undef, 'problematic prior to IE7, currently ubiquitous'],
522                         jp2k => [2, undef, 'just Safari'],
523                         webp => [4, undef, 'widespread since 2020'],
524                         heic => [1, undef, 'no browser support'],
525                         avif => [3, undef, 'significant since 2021 (Safari 2023, no Edge yet)'],
526                         jxl  => [2, undef, 'experimental, blocked by chrome'],
527                 },
528         },
529         support => {
530                 name => 'adoption',
531                 score => {
532                         jpeg => [5, undef, 'standard photography'],
533                         pnm  => [2, undef, 'uncomplicated bitmap interchange'],
534                         gif  => [4, undef, 'declining due to limitations'],
535                         png  => [5, undef, 'standard illustrations'],
536                         jp2k => [3, undef, 'limited beyond Apple'],
537                         webp => [4, undef, 'mostly online'],
538                         heic => [2, undef, 'stored by latest cameras, interchange unlikely'],
539                         avif => [3, undef, 'upcoming'],
540                         jxl  => [2, undef, 'ongoing'],
541                 },
542         },
543         overhead => {
544                 name => 'container overhead (file size)',
545                 score => {
546                         png  => [3,  67, 'upto 70 bytes for specific RGBA'],
547                         jpeg => [2, 160, '159 bytes minimum for gray, 288 for specific colours'],
548                         gif  => [4,  35, '43 bytes for transparent'],
549                         webp => [4,  34, 'black or transparent lossless; 44-92 bytes lossy'],
550                         bpg  => [4,  31, 'lossy 29-62 bytes, lossless 37-160'],
551                         flif => [5,  14, 'black or transparent; 20 bytes for specific RGBA'],
552                         pnm  => [5,   8, 'monochrome text PBM; 12 bytes PPM; 69 bytes PAM'],
553                         jxl  => [5,  12, '512×256 black pixels'],
554                         avif => [1, 282, 'container overhead; 457 bytes with alpha'],
555                         jp2k => [2, 123, 'experimental results, likely not optimal'],
556                         heic => [1, 386],
557                 },
558         },
559 },
560 }