xref: /aosp_15_r20/external/deqp/scripts/qpa_image_viewer.html (revision 35238bce31c2a825756842865a792f8cf7f89930)
1<!--
2--------------------------------------
3HTML QPA Image Viewer
4--------------------------------------
5
6Copyright (c) 2020 The Khronos Group Inc.
7Copyright (c) 2020 Valve Corporation.
8
9Licensed under the Apache License, Version 2.0 (the "License");
10you may not use this file except in compliance with the License.
11You may obtain a copy of the License at
12
13http://www.apache.org/licenses/LICENSE-2.0
14
15Unless required by applicable law or agreed to in writing, software
16distributed under the License is distributed on an "AS IS" BASIS,
17WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18See the License for the specific language governing permissions and
19limitations under the License.
20-->
21<html>
22    <head>
23        <meta charset="utf-8"/>
24        <title>Load PNGs from QPA output</title>
25        <style>
26            body {
27                background: white;
28                text-align: left;
29                font-family: sans-serif;
30            }
31            h1 {
32                margin-top: 2ex;
33            }
34            h2 {
35                font-size: large;
36            }
37            figure {
38                display: flex;
39                flex-direction: column;
40            }
41            img {
42                margin-right: 1ex;
43                margin-bottom: 1ex;
44                /* Attempt to zoom images using the nearest-neighbor scaling
45                algorithm. */
46                image-rendering: pixelated;
47                image-rendering: crisp-edges;
48                /* Use a black background color for images in case some pixels
49                are transparent to some degree. In the worst case, the image
50                could appear to be missing. */
51                background: black;
52            }
53            button {
54                margin: 1ex;
55                border: none;
56                border-radius: .5ex;
57                padding: 1ex;
58                background-color: steelblue;
59                color: white;
60                font-size: large;
61            }
62            button:hover {
63                opacity: .8;
64            }
65            #clearimagesbutton,#cleartextbutton {
66                background-color: seagreen;
67            }
68            select {
69                font-size: large;
70                padding: 1ex;
71                border-radius: .5ex;
72                border: 1px solid darkgrey;
73            }
74            select:hover {
75                opacity: .8;
76            }
77            .loadoption {
78                text-align: center;
79                margin: 1ex;
80                padding: 2ex;
81                border: 1px solid darkgrey;
82                border-radius: 1ex;
83            }
84            #options {
85                display: flex;
86                flex-wrap: wrap;
87            }
88            #qpatext {
89                display: block;
90                min-width: 80ex;
91                max-width: 132ex;
92                min-height: 25ex;
93                max-height: 25ex;
94                margin: 1ex auto;
95            }
96            #fileselector {
97                display: none;
98            }
99            #zoomandclear {
100                margin: 2ex;
101            }
102            #images {
103                margin: 2ex;
104                display: flex;
105                flex-direction: column;
106            }
107            .imagesblock {
108                display: flex;
109                flex-wrap: wrap;
110            }
111        </style>
112    </head>
113    <body>
114        <h1>Load PNGs from QPA output</h1>
115
116        <div id="options">
117            <div class="loadoption">
118                <h2>Option 1: Load local QPA files</h2>
119                <!-- The file selector text cannot be changed, so we use a hidden selector trick. -->
120                <button id="fileselectorbutton">&#x1F4C2; Load files</button>
121                <input id="fileselector" type="file" multiple>
122            </div>
123
124            <div class="loadoption">
125                <h2>Option 2: Paste QPA text or text extract containing &lt;Image&gt; elements below and click "Load images"</h2>
126                <textarea id="qpatext"></textarea>
127                <button id="loadimagesbutton">&#x1F4C3; Load images</button>
128                <button id="cleartextbutton">&#x267B; Clear text</button>
129            </div>
130        </div>
131
132        <div id="zoomandclear">
133            &#x1F50E; Image zoom
134            <select id="zoomselect">
135                <option value="1" selected>1x</option>
136                <option value="2">2x</option>
137                <option value="4">4x</option>
138                <option value="8">8x</option>
139                <option value="16">16x</option>
140                <option value="32">32x</option>
141            </select>
142            <button id="clearimagesbutton">&#x267B; Clear images</button>
143        </div>
144
145        <div id="images"></div>
146
147        <script>
148            // Returns zoom factor as a number.
149            var getSelectedZoom = function () {
150                return new Number(document.getElementById("zoomselect").value);
151            }
152
153            // Scales a given image with the selected zoom factor.
154            var scaleSingleImage = function (img) {
155                var factor = getSelectedZoom();
156                img.style.width = (img.naturalWidth * factor) + "px";
157                img.style.height = (img.naturalHeight * factor) + "px";
158            }
159
160            // Rescales all <img> elements in the page. Used after changing the selected zoom.
161            var rescaleImages = function () {
162                var imageList = document.getElementsByTagName("img");
163                for (var i = 0; i < imageList.length; i++) {
164                    scaleSingleImage(imageList[i])
165                }
166            }
167
168            // Removes everything contained in the images <div>.
169            var clearImages = function () {
170                var imagesNode = document.getElementById("images");
171                while (imagesNode.hasChildNodes()) {
172                    imagesNode.removeChild(imagesNode.lastChild);
173                }
174            }
175
176            // Clears textarea text.
177            var clearText = function() {
178                document.getElementById("qpatext").value = "";
179            }
180
181            // Returns a properly sized image with the given base64-encoded PNG data.
182            var createImage = function (pngData, imageName) {
183                var imageContainer = document.createElement("figure");
184                if (imageName.length > 0) {
185                    var newFileNameHeader = document.createElement("figcaption");
186                    newFileNameHeader.textContent = escape(imageName);
187                    imageContainer.appendChild(newFileNameHeader);
188                }
189                var newImage = document.createElement("img");
190                newImage.src = "data:image/png;base64," + pngData;
191                newImage.onload = (function () {
192                    // Grab the image for the callback. We need to wait until
193                    // the image has been properly loaded to access its
194                    // naturalWidth and naturalHeight properties, needed for
195                    // scaling.
196                    var cbImage = newImage;
197                    return function () {
198                        scaleSingleImage(cbImage);
199                    };
200                })();
201                imageContainer.appendChild(newImage);
202                return imageContainer;
203            }
204
205            // Returns a new h3 header with the given file name.
206            var createFileNameHeader = function (fileName) {
207                var newHeader = document.createElement("h3");
208                newHeader.textContent = fileName;
209                return newHeader;
210            }
211
212            // Returns a new image block to contain images from a file.
213            var createImagesBlock = function () {
214                var imagesBlock = document.createElement("div");
215                imagesBlock.className = "imagesblock";
216                return imagesBlock;
217            }
218
219            // Processes a chunk of QPA text from the given file name. Creates
220            // the file name header and a list of images in the images <div>, as
221            // found in the text.
222            var processText = function(textString, fileName) {
223                var imagesNode = document.getElementById("images");
224                var newHeader = createFileNameHeader(fileName);
225                imagesNode.appendChild(newHeader);
226                var imagesBlock = createImagesBlock();
227                // [\s\S] is a match-anything regexp like the dot, except it
228                // also matches newlines. Ideally, browsers would need to widely
229                // support the "dotall" regexp modifier, but that's not the case
230                // yet and this does the trick.
231                // Group 1 are the image element properties, if any.
232                // Group 2 is the base64 PNG data.
233                var imageRegexp = /<Image\b(.*?)>([\s\S]*?)<\/Image>/g;
234                var imageNameRegexp = /\bName="(.*?)"/;
235                var result;
236                var innerResult;
237                var imageName;
238                while ((result = imageRegexp.exec(textString)) !== null) {
239                    innerResult = result[1].match(imageNameRegexp);
240                    imageName = ((innerResult !== null) ? innerResult[1] : "");
241                    // Blanks need to be removed from the base64 string.
242                    var pngData = result[2].replace(/\s+/g, "");
243                    imagesBlock.appendChild(createImage(pngData, imageName));
244                }
245                imagesNode.appendChild(imagesBlock);
246            }
247
248            // Loads images from the text in the text area.
249            var loadImages = function () {
250                processText(document.getElementById("qpatext").value, "<Pasted Text>");
251            }
252
253            // Loads images from the files in the file selector.
254            var handleFileSelect = function (evt) {
255                var files = evt.target.files;
256                for (var i = 0; i < files.length; i++) {
257                    // Creates a reader per file.
258                    var reader = new FileReader();
259                    // Grab the needed objects to use them after the file has
260                    // been read, in order to process its contents and add
261                    // images, if found, in the images <div>.
262                    reader.onload = (function () {
263                        var cbFileName = files[i].name;
264                        var cbReader = reader;
265                        return function () {
266                            processText(cbReader.result, cbFileName);
267                        };
268                    })();
269                    // Reads file contents. This will trigger the event above.
270                    reader.readAsText(files[i]);
271                }
272            }
273
274            // File selector trick: click on the selector when clicking on the
275            // custom button.
276            var clickFileSelector = function () {
277                document.getElementById("fileselector").click();
278            }
279
280            // Clears selected files to be able to select them again if needed.
281            var clearSelectedFiles = function() {
282                document.getElementById("fileselector").value = "";
283            }
284
285            // Set event handlers for interactive elements in the page.
286            document.getElementById("fileselector").onclick = clearSelectedFiles;
287            document.getElementById("fileselector").addEventListener("change", handleFileSelect, false);
288            document.getElementById("fileselectorbutton").onclick = clickFileSelector;
289            document.getElementById("loadimagesbutton").onclick = loadImages;
290            document.getElementById("cleartextbutton").onclick = clearText;
291            document.getElementById("zoomselect").onchange = rescaleImages;
292            document.getElementById("clearimagesbutton").onclick = clearImages;
293        </script>
294    </body>
295</html>
296