Merge vk-gl-cts/vulkan-cts-1.3.4 into vk-gl-cts/main
[platform/upstream/VK-GL-CTS.git] / scripts / qpa_image_viewer.html
1 <!--
2 --------------------------------------
3 HTML QPA Image Viewer
4 --------------------------------------
5
6 Copyright (c) 2020 The Khronos Group Inc.
7 Copyright (c) 2020 Valve Corporation.
8
9 Licensed under the Apache License, Version 2.0 (the "License");
10 you may not use this file except in compliance with the License.
11 You may obtain a copy of the License at
12
13 http://www.apache.org/licenses/LICENSE-2.0
14
15 Unless required by applicable law or agreed to in writing, software
16 distributed under the License is distributed on an "AS IS" BASIS,
17 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 See the License for the specific language governing permissions and
19 limitations 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>