UNPKG

webglimpse

Version:

Webglimpse is a data visualization library for the web.

442 lines 84.7 kB
/* * Copyright (c) 2014, Metron, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import { Cache } from './util/cache'; import { Texture2D } from './texture'; import { black, parseRgba } from './color'; import { TwoKeyCache, ThreeKeyCache, SixKeyCache } from './util/multikey_cache'; import { GL, concatLines, hasval } from './util/util'; import { Program, Uniform2f, Uniform1f, UniformSampler2D, Attribute } from './shader'; import { newStaticBuffer } from './buffer'; import { nearestPixel } from './misc'; const textDim = (function () { // Use div to figure out how big our texture needs to be const div = document.createElement('div'); div.style.setProperty('position', 'absolute'); div.style.setProperty('padding', '0'); div.style.setProperty('margin', '0'); div.style.setProperty('width', 'auto'); div.style.setProperty('height', 'auto'); div.style.setProperty('visibility', 'hidden'); return function (s, font) { div.style.setProperty('font', font); div.textContent = s; document.body.appendChild(div); const width = div.clientWidth; const height = div.clientHeight; document.body.removeChild(div); return { w: width, h: height }; }; })(); function newFontMetricsCache() { return new Cache({ create: function (font) { const dim = textDim('fMgyj', font); const w = dim.w; const h = dim.h; const canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; const g = canvas.getContext('2d', { willReadFrequently: true }); g.font = font; g.textAlign = 'left'; g.textBaseline = 'top'; g.fillStyle = 'black'; g.clearRect(0, 0, w, h); g.fillText('fM', 0, 0); let rgbaData = g.getImageData(0, 0, w, h).data; let jTop = -1; for (let j = 0; j < h && jTop < 0; j++) { for (let i = 0; i < w && jTop < 0; i++) { const alpha = rgbaData[(j * w + i) * 4 + 3]; if (alpha !== 0) { jTop = j; } } } let jBaseline = -1; for (let j = h - 1; j >= 0 && jBaseline < 0; j--) { for (let i = 0; i < w && jBaseline < 0; i++) { const alpha = rgbaData[(j * w + i) * 4 + 3]; if (alpha !== 0) { jBaseline = j; } } } g.clearRect(0, 0, w, h); g.fillText('gyj', 0, 0); rgbaData = g.getImageData(0, 0, w, h).data; let jBottom = -1; for (let j = h - 1; j >= 0 && jBottom < 0; j--) { for (let i = 0; i < w && jBottom < 0; i++) { const alpha = rgbaData[(j * w + i) * 4 + 3]; if (alpha !== 0) { jBottom = j; } } } return { jTop: jTop, jBaseline: jBaseline, jBottom: jBottom }; }, dispose: function () { } }); } const getRawFontMetrics = (function () { const cache = newFontMetricsCache(); return function (font) { return cache.value(font); }; })(); export const getTextWidth = (function () { const canvas = document.createElement('canvas'); canvas.width = 1; canvas.height = 1; const g = canvas.getContext('2d'); g.textAlign = 'left'; g.textBaseline = 'top'; return function (font, text) { g.font = font; return g.measureText(text).width; }; })(); export class TextTexture2D extends Texture2D { constructor(w, h, jBaseline, minFilter, magFilter, draw) { super(w, h, minFilter, magFilter, draw); this._jBaseline = jBaseline; } get jBaseline() { return this._jBaseline; } yAnchor(textFrac) { // Things tend to look the way you expect if textFrac is interpreted as // a fraction of the way from the bottom of the baseline pixel up to the // top of the top pixel // const bottom = this.jBaseline + 1; const h = this.h; return 1 - ((1 - textFrac) * bottom / h); } } export function newTextTextureCache(font, color) { const createTextTexture = createTextTextureFactory(font); return new Cache({ create: function (text) { return createTextTexture(color, text); }, dispose: function (texture) { texture.dispose(); } }); } export function newTextTextureCache2(font) { const createTextTexture = createTextTextureFactory(font); return new TwoKeyCache({ create: function (rgbaString, text) { const color = parseRgba(rgbaString); return createTextTexture(color, text); }, dispose: function (texture) { texture.dispose(); } }); } export function newTextTextureCache3() { return new ThreeKeyCache({ create: function (font, rgbaString, text) { const createTextTexture = createTextTextureFactory(font); const color = parseRgba(rgbaString); return createTextTexture(color, text); }, dispose: function (texture) { texture.dispose(); } }); } export function newTextTextureCache6() { return new SixKeyCache({ create: function (font, rgbaString, text, rgbaBgString, bgPadding, bgBorderRadius) { const createTextTexture = createTextTextureFactory(font); const color = parseRgba(rgbaString); const bgColor = parseRgba(rgbaBgString); const bgBorderRadiusNumber = parseInt(bgBorderRadius, 10); const paddingNumber = parseInt(bgPadding, 10); return createTextTexture(color, text, bgColor, paddingNumber, bgBorderRadiusNumber); }, dispose: function (texture) { texture.dispose(); } }); } export function createTextTextureFactory(font) { const rawFontMetrics = getRawFontMetrics(font); let jBaseline = rawFontMetrics.jBaseline - rawFontMetrics.jTop; let h = rawFontMetrics.jBottom - rawFontMetrics.jTop + 1; return function (color, text, bgColor, bgPadding = 0, bgBorderRadius = 0) { let w = Math.ceil(getTextWidth(font, text)); if (bgColor && bgColor.a !== 0) { const addedPadding = bgPadding * 2; w = w + addedPadding + 4; h = h + addedPadding; jBaseline = jBaseline + addedPadding; } return new TextTexture2D(w, h, jBaseline, GL.NEAREST, GL.NEAREST, function (g) { // Some browsers use hinting for canvas fillText! This behaves poorly on a transparent // background -- so we draw white text onto a black background, then infer alpha from // the pixel color (black = transparent, white = opaque). // // We compute alpha as (R+G+B)/3. This grayscales the image the browser drew, effectively // de-hinting it. // // If background color not transparent show background color, else follow comment above. if (bgColor && bgColor.a !== 0) { g.fillStyle = bgColor.cssString; // Round corners of text background if (bgBorderRadius) { bgBorderRadius = Math.min(bgBorderRadius, h / 2, w / 2); g.beginPath(); g.moveTo(bgBorderRadius, 0); g.lineTo(w - bgBorderRadius, 0); g.quadraticCurveTo(w, 0, w, bgBorderRadius); g.lineTo(w, h - bgBorderRadius); g.quadraticCurveTo(w, h, w - bgBorderRadius, h); g.lineTo(bgBorderRadius, h); g.quadraticCurveTo(0, h, 0, h - bgBorderRadius); g.lineTo(0, bgBorderRadius); g.quadraticCurveTo(0, 0, bgBorderRadius, 0); g.closePath(); g.fill(); } else { g.fillRect(0, 0, w, h); } g.font = font; g.textAlign = 'center'; g.textBaseline = 'middle'; g.fillStyle = color.cssString; g.fillText(text, w / 2, h / 2); } else { g.fillStyle = 'black'; g.fillRect(0, 0, w, h); g.font = font; g.textAlign = 'left'; g.textBaseline = 'top'; g.fillStyle = 'white'; g.save(); g.translate(0, -rawFontMetrics.jTop); g.fillText(text, 0, 0); g.restore(); const r255 = 255 * color.r; const g255 = 255 * color.g; const b255 = 255 * color.b; const aFactor = color.a / 3; const pixels = g.getImageData(0, 0, w, h); for (let j = 0; j < pixels.height; j++) { for (let i = 0; i < pixels.width; i++) { const pixelOffset = (j * pixels.width + i) * 4; const a255 = aFactor * (pixels.data[pixelOffset + 0] + pixels.data[pixelOffset + 1] + pixels.data[pixelOffset + 2]); pixels.data[pixelOffset + 0] = r255; pixels.data[pixelOffset + 1] = g255; pixels.data[pixelOffset + 2] = b255; pixels.data[pixelOffset + 3] = a255; } } g.putImageData(pixels, 0, 0); } }); }; } export function newTextHintsCache(font) { const rawFontMetrics = getRawFontMetrics(font); const jBaseline = rawFontMetrics.jBaseline - rawFontMetrics.jTop; const h = rawFontMetrics.jBottom - rawFontMetrics.jTop + 1; return new Cache({ create: function (text) { const w = getTextWidth(font, text); // XXX: For now, assuming subpixels are horizontal-RGB // Draw text triple-sized, to get an alpha for each r,g,b subpixel const canvas3 = document.createElement('canvas'); canvas3.width = 3 * w; canvas3.height = h; const g3 = canvas3.getContext('2d'); g3.fillStyle = 'black'; g3.fillRect(0, 0, canvas3.width, canvas3.height); g3.save(); g3.translate(0, -rawFontMetrics.jTop); g3.scale(3, 1); g3.font = font; g3.textAlign = 'left'; g3.textBaseline = 'top'; g3.fillStyle = 'white'; g3.fillText(text, 0, 0); g3.restore(); const srcRgba = g3.getImageData(0, 0, canvas3.width, canvas3.height).data; return new Texture2D(w, h, GL.NEAREST, GL.NEAREST, function (g) { const destImage = g.createImageData(w, h); const destRgba = destImage.data; const weightLeft = 1; const weightCenter = 2; const weightRight = 1; const weightNorm = 1 / (weightLeft + weightCenter + weightRight); for (let j = 0; j < h; j++) { for (let i = 0; i < w; i++) { // Get alpha values for relevant src-pixels: one from just left of the dest-pixel, all // three from inside the dest-pixel, and one from just right of the dest-pixel. // // Some browsers use hinting for canvas fillText! This behaves poorly on a transparent // background -- so we draw white text onto a black background, then infer alpha from // the pixel color (black = transparent, white = opaque). // // We compute alpha as (R+G+B)/3. This grayscales the image the browser drew, effectively // de-hinting it so that we can re-hint it ourselves later (during blending, when the // background color is known). // const srcPixelIndex = (j * 3 * w + 3 * i) * 4; const srcAlphaL = (i > 0 ? (srcRgba[srcPixelIndex - 4] + srcRgba[srcPixelIndex - 3] + srcRgba[srcPixelIndex - 2]) / (3 * 255) : 0); const srcAlpha0 = (srcRgba[srcPixelIndex + 0] + srcRgba[srcPixelIndex + 1] + srcRgba[srcPixelIndex + 2]) / (3 * 255); const srcAlpha1 = (srcRgba[srcPixelIndex + 4] + srcRgba[srcPixelIndex + 5] + srcRgba[srcPixelIndex + 6]) / (3 * 255); const srcAlpha2 = (srcRgba[srcPixelIndex + 8] + srcRgba[srcPixelIndex + 9] + srcRgba[srcPixelIndex + 10]) / (3 * 255); const srcAlphaR = (i < w - 1 ? (srcRgba[srcPixelIndex + 12] + srcRgba[srcPixelIndex + 13] + srcRgba[srcPixelIndex + 14]) / (3 * 255) : 0); // Weighted averages to find subpixel alphas const alphaLeft = weightNorm * (weightLeft * srcAlphaL + weightCenter * srcAlpha0 + weightRight * srcAlpha1); const alphaCenter = weightNorm * (weightLeft * srcAlpha0 + weightCenter * srcAlpha1 + weightRight * srcAlpha2); const alphaRight = weightNorm * (weightLeft * srcAlpha1 + weightCenter * srcAlpha2 + weightRight * srcAlphaR); // Store subpixel alphas in dest-pixel const destPixelIndex = (j * w + i) * 4; destRgba[destPixelIndex + 0] = Math.round(255 * alphaLeft); destRgba[destPixelIndex + 1] = Math.round(255 * alphaCenter); destRgba[destPixelIndex + 2] = Math.round(255 * alphaRight); // Alpha will be used in computing final alpha of blended result -- use the average of // the subpixel alphas // // If alpha is 1, Firefox will interpret it as 100%. Causes some pixels that should be // very dim to come out very bright. As a workaround, nudge them down to zero. // const alphaAvg = Math.round(255 * (alphaLeft + alphaCenter + alphaRight) / 3); destRgba[destPixelIndex + 3] = (alphaAvg === 1 ? 0 : alphaAvg); } } g.putImageData(destImage, 0, 0); }); }, dispose: function (texture) { texture.dispose(); } }); } export class HintedTextRenderer { constructor() { this.textRenderer_VERTSHADER = concatLines(' ', ' uniform vec2 u_XyFrac; ', ' uniform vec2 u_Anchor; ', ' uniform vec2 u_ImageSize; ', ' uniform vec2 u_ViewportSize; ', ' ', ' attribute vec2 a_ImageFrac; ', ' ', ' varying vec2 v_StCoord; ', ' ', ' void main( ) { ', ' vec2 xy = -1.0 + 2.0*( u_XyFrac + u_ImageSize*( a_ImageFrac - u_Anchor )/u_ViewportSize ); ', ' gl_Position = vec4( xy, 0.0, 1.0 ); ', ' ', ' v_StCoord = vec2( a_ImageFrac.x, 1.0 - a_ImageFrac.y ); ', ' } ', ' '); this.textRenderer_FRAGSHADER = concatLines(' ', ' precision mediump float; ', ' ', ' uniform sampler2D u_Hints; ', ' uniform float u_Alpha; ', ' ', ' varying vec2 v_StCoord; ', ' ', ' void main( ) { ', ' gl_FragColor = u_Alpha * texture2D( u_Hints, v_StCoord ); ', ' } ', ' '); this.program = new Program(this.textRenderer_VERTSHADER, this.textRenderer_FRAGSHADER); this.u_XyFrac = new Uniform2f(this.program, 'u_XyFrac'); this.u_Anchor = new Uniform2f(this.program, 'u_Anchor'); this.u_ImageSize = new Uniform2f(this.program, 'u_ImageSize'); this.u_ViewportSize = new Uniform2f(this.program, 'u_ViewportSize'); this.u_Alpha = new Uniform1f(this.program, 'u_Alpha'); this.u_Hints = new UniformSampler2D(this.program, 'u_Hints'); this.a_ImageFrac = new Attribute(this.program, 'a_ImageFrac'); this.imageFracData = newStaticBuffer(new Float32Array([0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0])); this.wViewport = 0; this.hViewport = 0; } begin(gl, viewport) { this.program.use(gl); this.u_ViewportSize.setData(gl, viewport.w, viewport.h); this.a_ImageFrac.setDataAndEnable(gl, this.imageFracData, 2, GL.FLOAT); this.wViewport = viewport.w; this.hViewport = viewport.h; } draw(gl, hints, xFrac, yFrac, options) { const xAnchor = (hasval(options) && hasval(options.xAnchor) ? options.xAnchor : 0.5); const yAnchor = (hasval(options) && hasval(options.yAnchor) ? options.yAnchor : 0.5); const color = (hasval(options) && hasval(options.color) ? options.color : black); // The hints texture stores subpixel alphas in its R,G,B components. For example, a red hint-pixel indicates // that, at that pixel, the final R component should be color.r (R_hint=1, so red is opaque), but the // final G and B components should be the G and B from the background pixel (G_hint=0, B_hint=0, so green // and blue are transparent). // // GL does not allow arbitrary blending, but we can use the blending options it does provide to get the effect // we want. // // There are 4 things to keep an eye on: // // 1. The RGB part of color, which is put into glBlendColor // 2. The A part of color, which is sent to the frag-shader (NOT passed to glBlendColor) // 3. The subpixel alphas, which are sent to the frag-shader in the hints-texture // 4. The glBlendFunc, which is set up in an unusual way // // // With this setup, we get: // // R_final = ( R_frag )*R_foreground + ( 1 - R_frag )*R_background // G_final = ( G_frag )*G_foreground + ( 1 - G_frag )*G_background // B_final = ( B_frag )*B_foreground + ( 1 - B_frag )*B_background // A_final = ( A_frag )*1 + ( 1 - A_frag )*A_background // // So R_frag, the output from the frag shader, is a weight that is used to take a weighted average between // foreground and background colors. // // The frag-shader is doing A_foreground*RGBA_hint. Conceptually, R_hint is really a subpixel alpha for the // red subpixel, so it is helpful to call it A_redhint (and analogously for G and B): // // R_frag = A_foreground * A_redhint // G_frag = A_foreground * A_greenhint // B_frag = A_foreground * A_bluehint // // So the blended RGB is a weighted average of RGB_foreground and RGB_background, with a weighting factor of // ( A_foreground * A_subpixel ), where A_subpixel is either A_redhint, A_greenhint, or A_bluehint. Basically, // we have a separate alpha for each subpixel! // // // For the final alpha component, we want: // // A_final = ( 1 - (1-A_background)*(1-A_frag) ) // // A little algebra shows that to be equivalent to: // // A_final = ( A_frag )*1 + ( 1 - A_frag )*A_background // // Which is exactly what we get, as long as we pass an alpha of 1 into glBlendColor. // // this.u_XyFrac.setData(gl, nearestPixel(xFrac, this.wViewport, xAnchor, hints.w), nearestPixel(yFrac, this.hViewport, yAnchor, hints.h)); this.u_Anchor.setData(gl, xAnchor, yAnchor); this.u_ImageSize.setData(gl, hints.w, hints.h); this.u_Alpha.setData(gl, color.a); this.u_Hints.setDataAndBind(gl, 0, hints); gl.enable(GL.BLEND); gl.blendColor(color.r, color.g, color.b, 1); gl.blendFunc(GL.CONSTANT_COLOR, GL.ONE_MINUS_SRC_COLOR); gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4); } end(gl) { this.a_ImageFrac.disable(gl); this.u_Hints.unbind(gl); this.program.endUse(gl); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGV4dC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy93ZWJnbGltcHNlL3RleHQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E0Qkc7QUFDSCxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ3JDLE9BQU8sRUFBRSxTQUFTLEVBQWUsTUFBTSxXQUFXLENBQUM7QUFDbkQsT0FBTyxFQUFTLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDbEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxhQUFhLEVBQUUsV0FBVyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDaEYsT0FBTyxFQUFFLEVBQUUsRUFBRSxXQUFXLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3RELE9BQU8sRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxnQkFBZ0IsRUFBRSxTQUFTLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDdEYsT0FBTyxFQUFVLGVBQWUsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUVuRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBUXRDLE1BQU0sT0FBTyxHQUFHLENBQUM7SUFDYix3REFBd0Q7SUFDeEQsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMxQyxHQUFHLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDOUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsU0FBUyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ3RDLEdBQUcsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLFFBQVEsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUNyQyxHQUFHLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxPQUFPLEVBQUUsTUFB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QUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN4SSxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzVDLElBQUksQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLEVBQUUsRUFBRSxLQUFLLENBQUMsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMvQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2xDLElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLEVBQUUsRUFBRSxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFMUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDcEIsRUFBRSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUM1QyxFQUFFLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxjQUFjLEVBQUUsRUFBRSxDQUFDLG1CQUFtQixDQUFDLENBQUM7UUFFeEQsRUFBRSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsY0FBYyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRUQsR0FBRyxDQUFDLEVBQXlCO1FBQ3pCLElBQUksQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzdCLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3hCLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzVCLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbIi8qXHJcbiAqIENvcHlyaWdodCAoYykgMjAxNCwgTWV0cm9uLCBJbmMuXHJcbiAqIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbiAqXHJcbiAqIFJlZGlzdHJpYnV0aW9uIGFuZCB1c2UgaW4gc291cmNlIGFuZCBiaW5hcnkgZm9ybXMsIHdpdGggb3Igd2l0aG91dCBtb2RpZmljYXRpb24sXHJcbiAqIGFyZSBwZXJtaXR0ZWQgcHJvdmlkZWQgdGhhdCB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnMgYXJlIG1ldDpcclxuICpcclxuICogMS4gUmVkaXN0cmlidXRpb25zIG9mIHNvdXJjZSBjb2RlIG11c3QgcmV0YWluIHRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlLCB0aGlzXHJcbiAqICAgIGxpc3Qgb2YgY29uZGl0aW9ucyBhbmQgdGhlIGZvbGxvd2luZyBkaXNjbGFpbWVyLlxyXG4gKlxyXG4gKiAyLiBSZWRpc3RyaWJ1dGlvbnMgaW4gYmluYXJ5IGZvcm0gbXVzdCByZXByb2R1Y2UgdGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UsXHJcbiAqICAgIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIgaW4gdGhlIGRvY3VtZW50YXRpb25cclxuICogICAgYW5kL29yIG90aGVyIG1hdGVyaWFscyBwcm92aWRlZCB3aXRoIHRoZSBkaXN0cmlidXRpb24uXHJcbiAqXHJcbiAqIDMuIE5laXRoZXIgdGhlIG5hbWUgb2YgdGhlIGNvcHlyaWdodCBob2xkZXIgbm9yIHRoZSBuYW1lcyBvZiBpdHMgY29udHJpYnV0b3JzXHJcbiAqICAgIG1heSBiZSB1c2VkIHRvIGVuZG9yc2Ugb3IgcHJvbW90ZSBwcm9kdWN0cyBkZXJpdmVkIGZyb20gdGhpcyBzb2Z0d2FyZSB3aXRob3V0XHJcbiAqICAgIHNwZWNpZmljIHByaW9yIHdyaXR0ZW4gcGVybWlzc2lvbi5cclxuICpcclxuICogVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUyBcIkFTIElTXCIgQU5EXHJcbiAqIEFOWSBFWFBSRVNTIE9SIElNUExJRUQgV0FSUkFOVElFUywgSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFRIRSBJTVBMSUVEXHJcbiAqIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZIEFORCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBUkVcclxuICogRElTQ0xBSU1FRC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIENPUFlSSUdIVCBIT0xERVIgT1IgQ09OVFJJQlVUT1JTIEJFIExJQUJMRSBGT1JcclxuICogQU5ZIERJUkVDVCwgSU5ESVJFQ1QsIElOQ0lERU5UQUwsIFNQRUNJQUwsIEVYRU1QTEFSWSwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTXHJcbiAqIChJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgUFJPQ1VSRU1FTlQgT0YgU1VCU1RJVFVURSBHT09EUyBPUiBTRVJWSUNFUztcclxuICogTE9TUyBPRiBVU0UsIERBVEEsIE9SIFBST0ZJVFM7IE9SIEJVU0lORVNTIElOVEVSUlVQVElPTikgSE9XRVZFUiBDQVVTRUQgQU5EIE9OXHJcbiAqIEFOWSBUSEVPUlkgT0YgTElBQklMSVRZLCBXSEVUSEVSIElO