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>
 |