- /* ==UserStyle==
- @name Crisp up-scaled images for displays with 150% DPI scale factor (Firefox 74+)
- @description Prevents blurry anti-aliased borders between picture sampling areas ("pixels").
- @namespace myfonj
- @version 1.0.1
- @license CC0 - Public Domain
- ==/UserStyle== */
-
- @-moz-document regexp(".*\\.(png|gif)(\\?.*)?(#.*)?$") {
- /*
- "DPI Zoom (scale) factor" can be obtained with JavaScript:
-
- window.devicePixelRatio
-
- for 150% DPI scale factor at page with 100% zoom level this should be 1.5
-
- min zoom is 30 %
- max zoom is 300 %
-
- [ctrl] + [mouse wheel] modifies DPI consistently by +- 10 %.
-
- [ctrl] + [+ / -] modifies DPI in prefefined values - powers of 10 % with two exceptions: 30, 50, *67*, 80, 90, 100, 110, 120, *133*, 150, 170, 200, 240, 300. Those exceptions and derived values [1] are not in scope of this stylesheet.
-
- [1] in theory combining wheel and keyboard zoom one could possibly get 30, *33*, *37*, 40, *43*, *47*, 50 (...) zoom factors.
-
- a.k.a. resolution value for media query
- this file expects "normal" scale factor to be *150%*
- what normally causes ugly blurry "scaled down" images
- this style undoes that scale so that image pixels are mapped to physical pixels and anti-aliasing is suppressed
- producing crisp scaled image in "100% zoom" (then it is mapped to 2×2 pixels) and controls resizing with `transform` to maintain crispness and predictable dimension in most zoom levels.
- */
-
-
- /*
- 100% page zoom
- 1 = __ monitor scale × __ transform scale to "undo"
- 1 = 1.25 × 0.8
- 1 = 1.5 × 0.6666666666666
- 1 = 2 × 0.5
- strangemesses:
- ?? __ page zoom at monitor set to __ scale is matched by `(resolution: __)`
- !! 90% 100% 0.895522445440292259dppx
- OK 50% 100% 0.5dppx
- OK 100% 125% 1.25dppx
- !! 100% 175% 1.76470595598220835dppx
-
- centering crisp image of odd dimesion would produce unwanted "notch" from rounding
- drawbacks:
- - zooming is funky: apparently "zoom" is done by ajdusting DPI factor, so sometimes some of our rules hits
-
- [width][height] = image fits viewport
- */
-
-
- @media
- (resolution: 0.75dppx),
- (resolution: 0.8955223880597015dppx),
- (resolution: 1.0526315789473684dppx),
- (resolution: 1.2dppx),
- (resolution: 1.3636363636363635dppx),
- (resolution: 1.50dppx),
- (resolution: 1.6666666666666667dppx),
- (resolution: 1.8181818181818181dppx),
- (resolution: 1.935483870967742dppx),
- (resolution: 2.0689655172413794dppx),
- (resolution: 2.00dppx),
- (resolution: 2.2222222222222223dppx),
- (resolution: 2.4dppx),
- (resolution: 2.50dppx),
- (resolution: 2.727272727272727dppx),
- (resolution: 2.857142857142857dppx),
- (resolution: 2.857142857142857dppx),
- (resolution: 3.00dppx),
- (resolution: 3.1578947368421053dppx),
- (resolution: 3.3333333333333335dppx),
- (resolution: 3.5294117647058822dppx),
- (resolution: 3.3333333333333335dppx),
- (resolution: 3.5294117647058822dppx), /* twice ?! */
- (resolution: 3.75dppx),
- (resolution: 4.00dppx),
- (resolution: 4.285714285714286dppx),
- (resolution: 4.615384615384615dppx)
- {
- html>body>img:only-child:not([width]):not([height]) {
- image-rendering: var(--image-rendering, -moz-crisp-edges);
- transform: scale(calc(var(--scale-to) / var(--dppx)));
- transform-origin: top left;
- margin: 0;
- top: auto;
- bottom: auto;
- left: auto;
- right: auto;
- }
- /*
- debug
- */
- html::after {
- content: var(--dppx-debug) ' dppx; ' var(--zoom-debug) ' zoom; scale: ' var(--scale-to-debug) '×';
- position: fixed;
- top: 0;
- right: 0;
- color: #ccc;
- background-color: rgba(0,0,0,0.5);
- font-size: calc(1 / var(--dppx) * 1.9rem);
- /*
- this effectively cancels page zoom
- so the physical text size remains the same
- like using viewport units, but without viewport
- accessibility warning
- never ever do this
- */
- }
- }
-
- @media (resolution: 0.75dppx) {
- :root {
- --zoom-debug: '50%';
- --dppx: 0.75;
- --dppx-debug: '0.75';
- --scale-to: 0.0625;
- --scale-to-debug: '0.0625';
- --image-rendering: auto;
- }
- }
-
- @media (resolution: 0.8955223880597015dppx) {
- :root {
- --zoom-debug: '60%';
- --dppx: 0.8955223880597015;
- --dppx-debug: '0.8955223880597015';
- --scale-to: 0.125;
- --scale-to-debug: '0.125';
- --image-rendering: auto;
- }
- }
-
- @media (resolution: 1.0526315789473684dppx) {
- :root {
- --zoom-debug: '70%';
- --dppx: 1.0526315789473684;
- --dppx-debug: '1.0526315789473684';
- --scale-to: 0.25;
- --scale-to-debug: '0.25';
- --image-rendering: auto;
- }
- }
-
- @media (resolution: 1.2dppx) {
- :root {
- --zoom-debug: '80%';
- --dppx: 1.2;
- --dppx-debug: '1.2';
- --scale-to: 0.5;
- --scale-to-debug: '0.5';
- --image-rendering: auto;
- }
- }
-
- @media (resolution: 1.2dppx) {
- :root {
- --zoom-debug: '80%';
- --dppx: 1.2;
- --dppx-debug: '1.2';
- --scale-to: 0.5;
- --scale-to-debug: '0.5';
- --image-rendering: auto;
- }
- }
-
- @media (resolution: 1.3636363636363635dppx) {
- :root {
- --zoom-debug: '90%';
- --dppx: 1.3636363636363635;
- --dppx-debug: '1.3636363636363635';
- --scale-to: 1;
- --scale-to-debug: '1';
- }
- }
-
- @media (resolution: 1.50dppx) {
- :root {
- --zoom-debug: '100%';
- --dppx: 1.50;
- --dppx-debug: '1.50';
- --scale-to: 2;
- --scale-to-debug: '2';
- }
- }
-
- @media (resolution: 1.6666666666666667dppx) {
- :root {
- --zoom-debug: '110%';
- --dppx: 1.6666666666666667;
- --dppx-debug: '1.6666666666666667';
- --scale-to: 3;
- --scale-to-debug: '3';
- }
- }
-
- @media (resolution: 1.8181818181818181dppx) {
- :root {
- --zoom-debug: '120%';
- --dppx: 1.8181818181818181;
- --dppx-debug: '1.8181818181818181';
- --scale-to: 4;
- --scale-to-debug: '4';
- }
- }
-
- @media (resolution: 1.935483870967742dppx) {
- :root {
- --zoom-debug: '130%';
- --dppx: 1.935483870967742;
- --dppx-debug: '1.935483870967742';
- --scale-to: 5;
- --scale-to-debug: '5';
- }
- }
-
- @media (resolution: 2.00dppx) {
- :root {
- --zoom-debug: '133%';
- --dppx: 2.00;
- --dppx-debug: '2.00';
- --scale-to: 6;
- --scale-to-debug: '6';
- }
- }
-
- @media (resolution: 2.0689655172413794dppx) {
- :root {
- --zoom-debug: '140%';
- --dppx: 2.0689655172413794;
- --dppx-debug: '2.0689655172413794';
- --scale-to: 7;
- --scale-to-debug: '7';
- }
- }
-
- @media (resolution: 2.2222222222222223dppx) {
- :root {
- --zoom-debug: '150%';
- --dppx: 2.2222222222222223;
- --dppx-debug: '2.2222222222222223';
- --scale-to: 8;
- --scale-to-debug: '8';
- }
- }
-
- @media (resolution: 2.4dppx) {
- :root {
- --zoom-debug: '160%';
- --dppx: 2.4;
- --dppx-debug: '2.4';
- --scale-to: 9;
- --scale-to-debug: '9';
- }
- }
-
- @media (resolution: 2.50dppx) {
- :root {
- --zoom-debug: '170%';
- --dppx: 2.50;
- --dppx-debug: '2.50';
- --scale-to: 10;
- --scale-to-debug: '10';
- }
- }
-
- @media (resolution: 2.727272727272727dppx) {
- :root {
- --zoom-debug: '180%';
- --dppx: 2.727272727272727;
- --dppx-debug: '2.727272727272727';
- --scale-to: 11;
- --scale-to-debug: '11';
- }
- }
-
- @media (resolution: 2.857142857142857dppx) {
- :root {
- --zoom-debug: '190%';
- --dppx: 2.857142857142857;
- --dppx-debug: '2.857142857142857';
- --scale-to: 12;
- --scale-to-debug: '12';
- }
- }
-
- @media (resolution: 3.00dppx) {
- :root {
- --zoom-debug: '200%';
- --dppx: 3.00;
- --dppx-debug: '3.00';
- --scale-to: 13;
- --scale-to-debug: '13';
- }
- }
-
- @media (resolution: 3.1578947368421053dppx) {
- :root {
- --zoom-debug: '210%';
- --dppx: 3.1578947368421053;
- --dppx-debug: '3.1578947368421053';
- --scale-to: 14;
- --scale-to-debug: '14';
- }
- }
-
- @media (resolution: 3.3333333333333335dppx) {
- :root {
- --zoom-debug: '220%';
- --dppx: 3.3333333333333335;
- --dppx-debug: '3.3333333333333335';
- --scale-to: 15;
- --scale-to-debug: '15';
- }
- }
-
- @media (resolution: 3.5294117647058822dppx) {
- :root {
- --zoom-debug: '230% or 240%';
- /*
- for some mysterious reason both zoom levels matches this resolution
- */
- --dppx: 3.5294117647058822;
- --dppx-debug: '3.5294117647058822';
- --scale-to: 16;
- --scale-to-debug: '16';
- }
- }
-
- @media (resolution: 3.75dppx) {
- :root {
- --zoom-debug: '250%';
- --dppx: 3.75;
- --dppx-debug: '3.75';
- --scale-to: 17;
- --scale-to-debug: '17';
- }
- }
-
- @media (resolution: 4.00dppx) {
- :root {
- --zoom-debug: '260% or 270%';
- /*
- for some mysterious reason both zoom levels matches this resolution
- */
- --dppx: 4.00;
- --dppx-debug: '4.00';
- --scale-to: 18;
- --scale-to-debug: '18';
- }
- }
-
- @media (resolution: 4.285714285714286dppx) {
- :root {
- --zoom-debug: '280% or 290%';
- /*
- for some mysterious reason both zoom levels matches this resolution
- */
- --dppx: 4.285714285714286;
- --dppx-debug: '4.285714285714286';
- --scale-to: 19;
- --scale-to-debug: '19';
- }
- }
-
- @media (resolution: 4.615384615384615dppx) {
- :root {
- --zoom-debug: '300%';
- --dppx: 4.615384615384615;
- --dppx-debug: '4.615384615384615';
- --scale-to: 20;
- --scale-to-debug: '20';
- }
- }
- }