Merge vk-gl-cts/vulkan-cts-1.2.5 into vk-gl-cts/vulkan-cts-1.2.6
[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. Unfortunately, not supported under Firefox at the
46                 time this text is being written. */
47                 image-rendering: pixelated;
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 {
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             </div>
129         </div>
130
131         <div id="zoomandclear">
132             &#x1F50E; Image zoom
133             <select id="zoomselect">
134                 <option value="1" selected>1x</option>
135                 <option value="2">2x</option>
136                 <option value="4">4x</option>
137                 <option value="8">8x</option>
138                 <option value="16">16x</option>
139                 <option value="32">32x</option>
140             </select>
141             <button id="clearimagesbutton">&#x267B; Clear images</button>
142         </div>
143
144         <div id="images"></div>
145
146         <script>
147             // Returns zoom factor as a number.
148             var getSelectedZoom = function () {
149                 return new Number(document.getElementById("zoomselect").value);
150             }
151
152             // Scales a given image with the selected zoom factor.
153             var scaleSingleImage = function (img) {
154                 var factor = getSelectedZoom();
155                 img.style.width = (img.naturalWidth * factor) + "px";
156                 img.style.height = (img.naturalHeight * factor) + "px";
157             }
158
159             // Rescales all <img> elements in the page. Used after changing the selected zoom.
160             var rescaleImages = function () {
161                 var imageList = document.getElementsByTagName("img");
162                 for (var i = 0; i < imageList.length; i++) {
163                     scaleSingleImage(imageList[i])
164                 }
165             }
166
167             // Removes everything contained in the images <div>.
168             var clearImages = function () {
169                 var imagesNode = document.getElementById("images");
170                 while (imagesNode.hasChildNodes()) {
171                     imagesNode.removeChild(imagesNode.lastChild);
172                 }
173             }
174
175             // Returns a properly sized image with the given base64-encoded PNG data.
176             var createImage = function (pngData, imageName) {
177                 var imageContainer = document.createElement("figure");
178                 if (imageName.length > 0) {
179                     var newFileNameHeader = document.createElement("figcaption");
180                     newFileNameHeader.textContent = escape(imageName);
181                     imageContainer.appendChild(newFileNameHeader);
182                 }
183                 var newImage = document.createElement("img");
184                 newImage.src = "data:image/png;base64," + pngData;
185                 newImage.onload = (function () {
186                     // Grab the image for the callback. We need to wait until
187                     // the image has been properly loaded to access its
188                     // naturalWidth and naturalHeight properties, needed for
189                     // scaling.
190                     var cbImage = newImage;
191                     return function () {
192                         scaleSingleImage(cbImage);
193                     };
194                 })();
195                 imageContainer.appendChild(newImage);
196                 return imageContainer;
197             }
198
199             // Returns a new h3 header with the given file name.
200             var createFileNameHeader = function (fileName) {
201                 var newHeader = document.createElement("h3");
202                 newHeader.textContent = fileName;
203                 return newHeader;
204             }
205
206             // Returns a new image block to contain images from a file.
207             var createImagesBlock = function () {
208                 var imagesBlock = document.createElement("div");
209                 imagesBlock.className = "imagesblock";
210                 return imagesBlock;
211             }
212
213             // Processes a chunk of QPA text from the given file name. Creates
214             // the file name header and a list of images in the images <div>, as
215             // found in the text.
216             var processText = function(textString, fileName) {
217                 var imagesNode = document.getElementById("images");
218                 var newHeader = createFileNameHeader(fileName);
219                 imagesNode.appendChild(newHeader);
220                 var imagesBlock = createImagesBlock();
221                 // [\s\S] is a match-anything regexp like the dot, except it
222                 // also matches newlines. Ideally, browsers would need to widely
223                 // support the "dotall" regexp modifier, but that's not the case
224                 // yet and this does the trick.
225                 // Group 1 are the image element properties, if any.
226                 // Group 2 is the base64 PNG data.
227                 var imageRegexp = /<Image\b(.*?)>([\s\S]*?)<\/Image>/g;
228                 var imageNameRegexp = /\bName="(.*?)"/;
229                 var result;
230                 var innerResult;
231                 var imageName;
232                 while ((result = imageRegexp.exec(textString)) !== null) {
233                     innerResult = result[1].match(imageNameRegexp);
234                     imageName = ((innerResult !== null) ? innerResult[1] : "");
235                     // Blanks need to be removed from the base64 string.
236                     var pngData = result[2].replace(/\s+/g, "");
237                     imagesBlock.appendChild(createImage(pngData, imageName));
238                 }
239                 imagesNode.appendChild(imagesBlock);
240             }
241
242             // Loads images from the text in the text area.
243             var loadImages = function () {
244                 processText(document.getElementById("qpatext").value, "<Pasted Text>");
245             }
246
247             // Loads images from the files in the file selector.
248             var handleFileSelect = function (evt) {
249                 var files = evt.target.files;
250                 for (var i = 0; i < files.length; i++) {
251                     // Creates a reader per file.
252                     var reader = new FileReader();
253                     // Grab the needed objects to use them after the file has
254                     // been read, in order to process its contents and add
255                     // images, if found, in the images <div>.
256                     reader.onload = (function () {
257                         var cbFileName = files[i].name;
258                         var cbReader = reader;
259                         return function () {
260                             processText(cbReader.result, cbFileName);
261                         };
262                     })();
263                     // Reads file contents. This will trigger the event above.
264                     reader.readAsText(files[i]);
265                 }
266             }
267
268             // File selector trick: click on the selector when clicking on the
269             // custom button.
270             var clickFileSelector = function () {
271                 document.getElementById("fileselector").click();
272             }
273
274             // Clears selected files to be able to select them again if needed.
275             var clearSelectedFiles = function() {
276                 document.getElementById("fileselector").value = "";
277             }
278
279             // Set event handlers for interactive elements in the page.
280             document.getElementById("fileselector").onclick = clearSelectedFiles;
281             document.getElementById("fileselector").addEventListener("change", handleFileSelect, false);
282             document.getElementById("fileselectorbutton").onclick = clickFileSelector;
283             document.getElementById("loadimagesbutton").onclick = loadImages;
284             document.getElementById("zoomselect").onchange = rescaleImages;
285             document.getElementById("clearimagesbutton").onclick = clearImages;
286         </script>
287     </body>
288 </html>