296 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			296 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			HTML
		
	
	
	
| <!--
 | |
| --------------------------------------
 | |
| HTML QPA Image Viewer
 | |
| --------------------------------------
 | |
| 
 | |
| Copyright (c) 2020 The Khronos Group Inc.
 | |
| Copyright (c) 2020 Valve Corporation.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
| http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| -->
 | |
| <html>
 | |
|     <head>
 | |
|         <meta charset="utf-8"/>
 | |
|         <title>Load PNGs from QPA output</title>
 | |
|         <style>
 | |
|             body {
 | |
|                 background: white;
 | |
|                 text-align: left;
 | |
|                 font-family: sans-serif;
 | |
|             }
 | |
|             h1 {
 | |
|                 margin-top: 2ex;
 | |
|             }
 | |
|             h2 {
 | |
|                 font-size: large;
 | |
|             }
 | |
|             figure {
 | |
|                 display: flex;
 | |
|                 flex-direction: column;
 | |
|             }
 | |
|             img {
 | |
|                 margin-right: 1ex;
 | |
|                 margin-bottom: 1ex;
 | |
|                 /* Attempt to zoom images using the nearest-neighbor scaling
 | |
|                 algorithm. */
 | |
|                 image-rendering: pixelated;
 | |
|                 image-rendering: crisp-edges;
 | |
|                 /* Use a black background color for images in case some pixels
 | |
|                 are transparent to some degree. In the worst case, the image
 | |
|                 could appear to be missing. */
 | |
|                 background: black;
 | |
|             }
 | |
|             button {
 | |
|                 margin: 1ex;
 | |
|                 border: none;
 | |
|                 border-radius: .5ex;
 | |
|                 padding: 1ex;
 | |
|                 background-color: steelblue;
 | |
|                 color: white;
 | |
|                 font-size: large;
 | |
|             }
 | |
|             button:hover {
 | |
|                 opacity: .8;
 | |
|             }
 | |
|             #clearimagesbutton,#cleartextbutton {
 | |
|                 background-color: seagreen;
 | |
|             }
 | |
|             select {
 | |
|                 font-size: large;
 | |
|                 padding: 1ex;
 | |
|                 border-radius: .5ex;
 | |
|                 border: 1px solid darkgrey;
 | |
|             }
 | |
|             select:hover {
 | |
|                 opacity: .8;
 | |
|             }
 | |
|             .loadoption {
 | |
|                 text-align: center;
 | |
|                 margin: 1ex;
 | |
|                 padding: 2ex;
 | |
|                 border: 1px solid darkgrey;
 | |
|                 border-radius: 1ex;
 | |
|             }
 | |
|             #options {
 | |
|                 display: flex;
 | |
|                 flex-wrap: wrap;
 | |
|             }
 | |
|             #qpatext {
 | |
|                 display: block;
 | |
|                 min-width: 80ex;
 | |
|                 max-width: 132ex;
 | |
|                 min-height: 25ex;
 | |
|                 max-height: 25ex;
 | |
|                 margin: 1ex auto;
 | |
|             }
 | |
|             #fileselector {
 | |
|                 display: none;
 | |
|             }
 | |
|             #zoomandclear {
 | |
|                 margin: 2ex;
 | |
|             }
 | |
|             #images {
 | |
|                 margin: 2ex;
 | |
|                 display: flex;
 | |
|                 flex-direction: column;
 | |
|             }
 | |
|             .imagesblock {
 | |
|                 display: flex;
 | |
|                 flex-wrap: wrap;
 | |
|             }
 | |
|         </style>
 | |
|     </head>
 | |
|     <body>
 | |
|         <h1>Load PNGs from QPA output</h1>
 | |
| 
 | |
|         <div id="options">
 | |
|             <div class="loadoption">
 | |
|                 <h2>Option 1: Load local QPA files</h2>
 | |
|                 <!-- The file selector text cannot be changed, so we use a hidden selector trick. -->
 | |
|                 <button id="fileselectorbutton">📂 Load files</button>
 | |
|                 <input id="fileselector" type="file" multiple>
 | |
|             </div>
 | |
| 
 | |
|             <div class="loadoption">
 | |
|                 <h2>Option 2: Paste QPA text or text extract containing <Image> elements below and click "Load images"</h2>
 | |
|                 <textarea id="qpatext"></textarea>
 | |
|                 <button id="loadimagesbutton">📃 Load images</button>
 | |
|                 <button id="cleartextbutton">♻ Clear text</button>
 | |
|             </div>
 | |
|         </div>
 | |
| 
 | |
|         <div id="zoomandclear">
 | |
|             🔎 Image zoom
 | |
|             <select id="zoomselect">
 | |
|                 <option value="1" selected>1x</option>
 | |
|                 <option value="2">2x</option>
 | |
|                 <option value="4">4x</option>
 | |
|                 <option value="8">8x</option>
 | |
|                 <option value="16">16x</option>
 | |
|                 <option value="32">32x</option>
 | |
|             </select>
 | |
|             <button id="clearimagesbutton">♻ Clear images</button>
 | |
|         </div>
 | |
| 
 | |
|         <div id="images"></div>
 | |
| 
 | |
|         <script>
 | |
|             // Returns zoom factor as a number.
 | |
|             var getSelectedZoom = function () {
 | |
|                 return new Number(document.getElementById("zoomselect").value);
 | |
|             }
 | |
| 
 | |
|             // Scales a given image with the selected zoom factor.
 | |
|             var scaleSingleImage = function (img) {
 | |
|                 var factor = getSelectedZoom();
 | |
|                 img.style.width = (img.naturalWidth * factor) + "px";
 | |
|                 img.style.height = (img.naturalHeight * factor) + "px";
 | |
|             }
 | |
| 
 | |
|             // Rescales all <img> elements in the page. Used after changing the selected zoom.
 | |
|             var rescaleImages = function () {
 | |
|                 var imageList = document.getElementsByTagName("img");
 | |
|                 for (var i = 0; i < imageList.length; i++) {
 | |
|                     scaleSingleImage(imageList[i])
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Removes everything contained in the images <div>.
 | |
|             var clearImages = function () {
 | |
|                 var imagesNode = document.getElementById("images");
 | |
|                 while (imagesNode.hasChildNodes()) {
 | |
|                     imagesNode.removeChild(imagesNode.lastChild);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Clears textarea text.
 | |
|             var clearText = function() {
 | |
|                 document.getElementById("qpatext").value = "";
 | |
|             }
 | |
| 
 | |
|             // Returns a properly sized image with the given base64-encoded PNG data.
 | |
|             var createImage = function (pngData, imageName) {
 | |
|                 var imageContainer = document.createElement("figure");
 | |
|                 if (imageName.length > 0) {
 | |
|                     var newFileNameHeader = document.createElement("figcaption");
 | |
|                     newFileNameHeader.textContent = escape(imageName);
 | |
|                     imageContainer.appendChild(newFileNameHeader);
 | |
|                 }
 | |
|                 var newImage = document.createElement("img");
 | |
|                 newImage.src = "data:image/png;base64," + pngData;
 | |
|                 newImage.onload = (function () {
 | |
|                     // Grab the image for the callback. We need to wait until
 | |
|                     // the image has been properly loaded to access its
 | |
|                     // naturalWidth and naturalHeight properties, needed for
 | |
|                     // scaling.
 | |
|                     var cbImage = newImage;
 | |
|                     return function () {
 | |
|                         scaleSingleImage(cbImage);
 | |
|                     };
 | |
|                 })();
 | |
|                 imageContainer.appendChild(newImage);
 | |
|                 return imageContainer;
 | |
|             }
 | |
| 
 | |
|             // Returns a new h3 header with the given file name.
 | |
|             var createFileNameHeader = function (fileName) {
 | |
|                 var newHeader = document.createElement("h3");
 | |
|                 newHeader.textContent = fileName;
 | |
|                 return newHeader;
 | |
|             }
 | |
| 
 | |
|             // Returns a new image block to contain images from a file.
 | |
|             var createImagesBlock = function () {
 | |
|                 var imagesBlock = document.createElement("div");
 | |
|                 imagesBlock.className = "imagesblock";
 | |
|                 return imagesBlock;
 | |
|             }
 | |
| 
 | |
|             // Processes a chunk of QPA text from the given file name. Creates
 | |
|             // the file name header and a list of images in the images <div>, as
 | |
|             // found in the text.
 | |
|             var processText = function(textString, fileName) {
 | |
|                 var imagesNode = document.getElementById("images");
 | |
|                 var newHeader = createFileNameHeader(fileName);
 | |
|                 imagesNode.appendChild(newHeader);
 | |
|                 var imagesBlock = createImagesBlock();
 | |
|                 // [\s\S] is a match-anything regexp like the dot, except it
 | |
|                 // also matches newlines. Ideally, browsers would need to widely
 | |
|                 // support the "dotall" regexp modifier, but that's not the case
 | |
|                 // yet and this does the trick.
 | |
|                 // Group 1 are the image element properties, if any.
 | |
|                 // Group 2 is the base64 PNG data.
 | |
|                 var imageRegexp = /<Image\b(.*?)>([\s\S]*?)<\/Image>/g;
 | |
|                 var imageNameRegexp = /\bName="(.*?)"/;
 | |
|                 var result;
 | |
|                 var innerResult;
 | |
|                 var imageName;
 | |
|                 while ((result = imageRegexp.exec(textString)) !== null) {
 | |
|                     innerResult = result[1].match(imageNameRegexp);
 | |
|                     imageName = ((innerResult !== null) ? innerResult[1] : "");
 | |
|                     // Blanks need to be removed from the base64 string.
 | |
|                     var pngData = result[2].replace(/\s+/g, "");
 | |
|                     imagesBlock.appendChild(createImage(pngData, imageName));
 | |
|                 }
 | |
|                 imagesNode.appendChild(imagesBlock);
 | |
|             }
 | |
| 
 | |
|             // Loads images from the text in the text area.
 | |
|             var loadImages = function () {
 | |
|                 processText(document.getElementById("qpatext").value, "<Pasted Text>");
 | |
|             }
 | |
| 
 | |
|             // Loads images from the files in the file selector.
 | |
|             var handleFileSelect = function (evt) {
 | |
|                 var files = evt.target.files;
 | |
|                 for (var i = 0; i < files.length; i++) {
 | |
|                     // Creates a reader per file.
 | |
|                     var reader = new FileReader();
 | |
|                     // Grab the needed objects to use them after the file has
 | |
|                     // been read, in order to process its contents and add
 | |
|                     // images, if found, in the images <div>.
 | |
|                     reader.onload = (function () {
 | |
|                         var cbFileName = files[i].name;
 | |
|                         var cbReader = reader;
 | |
|                         return function () {
 | |
|                             processText(cbReader.result, cbFileName);
 | |
|                         };
 | |
|                     })();
 | |
|                     // Reads file contents. This will trigger the event above.
 | |
|                     reader.readAsText(files[i]);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // File selector trick: click on the selector when clicking on the
 | |
|             // custom button.
 | |
|             var clickFileSelector = function () {
 | |
|                 document.getElementById("fileselector").click();
 | |
|             }
 | |
| 
 | |
|             // Clears selected files to be able to select them again if needed.
 | |
|             var clearSelectedFiles = function() {
 | |
|                 document.getElementById("fileselector").value = "";
 | |
|             }
 | |
| 
 | |
|             // Set event handlers for interactive elements in the page.
 | |
|             document.getElementById("fileselector").onclick = clearSelectedFiles;
 | |
|             document.getElementById("fileselector").addEventListener("change", handleFileSelect, false);
 | |
|             document.getElementById("fileselectorbutton").onclick = clickFileSelector;
 | |
|             document.getElementById("loadimagesbutton").onclick = loadImages;
 | |
|             document.getElementById("cleartextbutton").onclick = clearText;
 | |
|             document.getElementById("zoomselect").onchange = rescaleImages;
 | |
|             document.getElementById("clearimagesbutton").onclick = clearImages;
 | |
|         </script>
 | |
|     </body>
 | |
| </html>
 |