blob: 3167db7a1238bc5a8fadcdc32c061b6189be5aed [file] [log] [blame]
<!-- The <fiddle-sk> custom element declaration.
Handles displaying and running a fiddle. Note that the code
should be supplied as a child element textarea.
Any child <p> paragraphs are passed along to be displayed
between the code and the resulting images.
Attributes:
width - The width of the fiddle image.
height - The height of the fiddle image.
textonly - True if this fiddle emits only text.
srgb - True if SGRB.
f16 - True if f16.
animated - True if animated.
duration - Length of animation in seconds.
source - The index of the source image to use as input.
source_mipmap - If true then the source image is mipmap.
bug_link - If true then display a link to report a bug.
embedded - If true then show less controls because were embedded.
offscreen - True if an offscreen render target is to be made available.
offscreen_width - Width of offscreen render target.
offscreen_height - Height of offscreen render target.
offscreen_sample_count - Render target sample count.
offscreen_texturable - True if offscreen render target is texturable.
offscreen_mipmap - True if texturable offscreen render target is mipmapped.
Methods:
None.
Events:
fiddle-success - generates an event when a succesful run is complete. The event contains
the hash of the new fiddle, to be used to retrieve the resultant images.
-->
<link rel="import" href="/res/imp/bower_components/iron-icon/iron-icon.html">
<link rel="import" href="/res/imp/bower_components/iron-icons/iron-icons.html">
<link rel="import" href="/res/imp/bower_components/iron-selector/iron-selector.html">
<link rel="import" href="/res/imp/bower_components/paper-checkbox/paper-checkbox.html">
<link rel="import" href="/res/imp/bower_components/paper-input/paper-input.html">
<link rel="import" href="/res/imp/bower_components/paper-spinner/paper-spinner.html">
<link rel="import" href="/res/imp/bower_components/iron-icons/av-icons.html">
<link rel="stylesheet" href="/res/common/css/md.css" type="text/css" media="all">
<link rel="import" href="/res/common/imp/details-summary.html">
<link rel="import" href="/res/common/imp/toggle.html">
<link rel="import" href="/res/imp/text-src.html">
<dom-module id="fiddle-sk">
<style include="iron-flex iron-flex-alignment">
#namer,
#embedder {
display: none;
margin-left: 2em;
}
#namer.display,
#embedder.display {
display: block;
}
video,
img {
box-shadow: 2px 2px 5px gray;
display: inline-block;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAAXNSR0IArs4c6QAAAAJiS0dEAP+Hj8y/AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAB3RJTUUH3gUBEi4DGRAQYgAAAB1JREFUGNNjfMoAAVJQmokBDdBHgPE/lPFsYN0BABdaAwN6tehMAAAAAElFTkSuQmCC');
}
#submit {
margin-top: 1em;
}
button.action {
margin-left: 0;
}
paper-spinner {
display: inline-block;
top: 10px;
}
#results {
margin-top: 1em;
}
#results > div {
display: inline-block;
margin-right: 1em;
}
.compile-error {
margin: 0 0 0 2em;
font-weight: bold;
cursor: pointer;
}
.compile-error:hover {
background: #eee;
}
h2 {
color: #E31A1C;
font-size: 18px;
}
details-sk {
display: inline-block;
padding-bottom: 1em;
}
.options {
margin-left: 3em;
}
#bug {
display: inline-block;
margin-left: 1em;
}
iron-selector img {
margin: 0.5em;
}
img.iron-selected {
border: solid #1F78B4 3px;
}
.source-select {
color: darkgreen;
padding: 1em 1em;
background: #eee;
margin-left: 1em;
}
paper-input {
display: inline-block;
}
#srgb,
#f16,
#textonly
{
display: block;
margin-bottom: 4px;
}
#animated {
display: inline-block;
margin-right: 2em;
}
.textoutput {
box-shadow: 2px 2px 5px gray;
padding: 0.8em;
font-family: monospace;
border: solid lightgray 1px;
color: darkgreen;
margin-bottom: 1em;
width: 56em;
font-family: monospace;
font-size: 13px;
margin-right: 2em;
margin-top: 0.6em;
}
.hint {
color: #666;
}
h4 {
color: #444;
margin-left: 1em;
}
paper-checkbox {
--paper-checkbox-checked-ink-color: #1f78b4;
--paper-checkbox-checked-color: #1f78b4;
}
.imgsrc {
cursor: pointer;
}
#glinfo {
margin: 0.6em 2em;
display: inline-block;
}
a {
color: #1f78b4;
padding: 1em;
}
.offset {
padding: 1em;
border: solid 1px lightgray;
margin: 0.5em;
}
.indent {
padding: 0.6em;
}
</style>
<template>
<template is="dom-if" if="{{display_options}}">
<details-sk open="{{_options_open}}">
<summary-sk>Options</summary-sk>
<div class=options>
<paper-checkbox id=textonly title="A text-only fiddle."
checked="{{textonly}}" hidden$="[[_basic_mode]]">
Text Only <span class=hint>[Use SkDebugf()]</span>
</paper-checkbox>
<paper-checkbox id=srgb title="sRGB"
checked="{{srgb}}" disabled="[[f16]]" hidden$="[[_basic_mode]]">
sRGB
</paper-checkbox>
<paper-checkbox id=f16 title="Half floats"
checked="{{f16}}" disabled="[[_not(srgb)]]" hidden$="[[_basic_mode]]">
F16
</paper-checkbox>
<div>
<paper-input label="Width" size=5 auto-validate allowed-pattern="[0-9]+" value="{{width}}"></paper-input>
<paper-input label="Height" size=5 auto-validate allowed-pattern="[0-9]+" value="{{height}}"></paper-input>
</div>
<paper-checkbox id=animated title="Produce an animation." checked="{{animated}}">Animation</paper-checkbox>
<div hidden="[[_not(animated)]]" class=offset>
<paper-input label="duration (seconds)" size=10 required auto-validate error-message="Non-zero value required!" allowed-pattern="[0-9]+" value="{{duration}}" disabled="[[_not(animated)]]"></paper-input>
<div hidden="[[_not(animated)]]">
<h4>These globals are now defined:</h4>
<pre class=source-select>double duration; // The requested duration of the animation.
double frame; // A value in [0, 1] of where we are in the animation.</pre>
</div>
</div>
<!-- Add offscreen opts here-->
<div>
<paper-checkbox id=offscreen title="Create an offscreen render target on the GPU."
checked="{{offscreen}}" on-tap=_toggle_offscreen hidden$="[[_basic_mode]]">
Offscreen Render Target
</paper-checkbox>
<div hidden="[[_not(offscreen)]]" class=offset>
<div>
<paper-input label="Width" size=5 auto-validate allowed-pattern="[0-9]+" value="{{offscreen_width}}"></paper-input>
<paper-input label="Height" size=5 auto-validate allowed-pattern="[0-9]+" value="{{offscreen_height}}"></paper-input>
<paper-input label="Sample Count" size=5 auto-validate allowed-pattern="[0-9]+" value="{{offscreen_sample_count}}"></paper-input>
</div>
<paper-checkbox id=texturable title="The offscreen render target can be used as a texture." checked="{{offscreen_texturable}}" on-tap=_toggle_offscreen_texturable>Texturable</paper-checkbox>
<div class=indent>
<paper-checkbox id=offscreen_mipmap title="The offscreen render target can be used as a texture that is mipmapped." checked="{{offscreen_mipmap}}" disabled="[[_not(offscreen_texturable)]]">MipMap</paper-checkbox>
</div>
<h4>This global is now defined:</h4>
<pre class=source-select hidden="[[offscreen_texturable]]">GrBackendRenderTarget backEndRenderTarget;</pre>
<pre class=source-select hidden="[[_not(offscreen_texturable)]]">GrBackendTexture backEndTextureRenderTarget;</pre>
</div>
</div>
<h3>Optional source image</h3>
<iron-selector selected="{{source}}" attr-for-selected="name" class="layout horizontal wrap">
<template is="dom-repeat" items="{{sources}}">
<img width=64 height=64 name="{{item}}" src="[[domain]]/s/{{item}}" class=imgsrc>
</template>
</iron-selector>
<div hidden="[[_not(source)]]" class=offset>
<paper-checkbox title="The backEndTexture is mipmapped." checked="{{source_mipmap}}">MipMap</paper-checkbox>
<h4>These globals are now defined:</h4>
<pre class=source-select>SkBitmap source;
sk_sp&lt;SkImage> image;
GrBackendTexture backEndTexture; // GPU Only.</pre>
</div>
</div>
</details-sk>
</template>
<content></content>
<div id=description>
<content select="p"></content>
</div>
<div id=submit>
<template is="dom-if" if="[[_not(embedded)]]">
<button class=action on-tap="_run">Run</button>
<paper-spinner></paper-spinner>
<template is="dom-if" if="{{bug_link}}">
<a id=bug target=_blank href$="{{_bugLink(fiddlehash)}}">File Bug</a>
</template>
<toggle-display-sk hidden$="[[_basic_mode]]">Embed</toggle-display-sk>
<div id=embedder>
<h3>Embed as an image with a backlink:</h3>
<input type="text" readonly size=150 value="&lt;a href='https://fiddle.skia.org/c/{{fiddlehash}}'>&lt;img src='https://fiddle.skia.org/i/{{fiddlehash}}_raster.png'>&lt;/a>">
<h3>Embed as custom element (skia.org only):</h3>
<input type="text" readonly size=150 value="&lt;fiddle-embed name='{{fiddlehash}}'>&lt;/fiddle-embed> ">
</div>
</template>
</div>
<div on-tap="_errSelect">
<template is="dom-if" if="{{_hasCompileWarningsOrErrors(_compile_errors)}}">
<h2>Compilation Warnings/Errors</h2>
<template is="dom-repeat" items="{{_compile_errors}}">
<pre class=compile-error data-line$="{{item.line}}" data-col$="{{item.col}}">{{item.text}}</pre>
</template>
</template>
</div>
<template is="dom-if" if="{{_runtime_error}}">
<h2>Runtime Errors</h2>
<div>{{_runtime_error}}</div>
</template>
<!-- Remove textonly -->
<template is="dom-if" if="{{_hasImages(fiddlehash, _compile_errors, _runtime_error, textonly)}}">
<div id=results class="horizontal layout self-start">
<template is="dom-if" if="{{_showCpu(cpu_embedded,gpu_embedded,embedded)}}">
<div class="vertical layout center-justified">
<template is="dom-if" if="{{_not(animated)}}">
<img title=CPU src="[[domain]]/i/{{fiddlehash}}_raster.png" width="{{width}}" height="{{height}}">
</template>
<template is="dom-if" if="{{animated}}">
<video title=CPU on-ended="_playEnded" autoplay loop="[[loop]]" src="[[domain]]/i/{{fiddlehash}}_cpu.webm" width="{{width}}" height="{{height}}"></video>
</template>
<template is="dom-if" if="{{_not(embedded)}}">
<p>
CPU
</p>
</template>
</div>
</template>
<template is="dom-if" if="[[_showGpu(gpu_embedded,embedded,_basic_mode)]]">
<div class="vertical layout center-justified">
<template is="dom-if" if="[[_not(animated)]]">
<img title=GPU src="[[domain]]/i/[[fiddlehash]]_gpu.png" width="[[width]]" height="[[height]]">
</template>
<template is="dom-if" if="{{animated}}">
<video title=GPU loop="[[loop]]" autoplay src="[[domain]]/i/{{fiddlehash}}_gpu.webm" width="{{width}}" height="{{height}}"></video>
</template>
<template is="dom-if" if="{{_not(embedded)}}">
<p>
GPU
</p>
</template>
</div>
</template>
<template is="dom-if" if="[[_showLinks(animated,embedded,_basic_mode)]]">
<div class="vertical layout center">
<a href="[[domain]]/i/[[fiddlehash]].pdf">PDF</a>
</div>
<div class="vertical layout center">
<a href="[[domain]]/i/[[fiddlehash]].skp">SKP</a>
</div>
<div class="vertical layout center">
<a href="https://debugger.skia.org/loadfrom?url=https://fiddle.skia.org/i/{{fiddlehash}}.skp">Debug</a>
</div>
</template>
<template is="dom-if" if="{{embedded}}">
<button class=action on-tap="_run">Run</button>
<paper-spinner></paper-spinner>
<a href="https://fiddle.skia.org/c/{{fiddlehash}}" target="_blank">Pop-out</a>
</template>
<template is="dom-if" if="{{animated}}">
<div id=controls class="horizontal layout">
<button on-tap="_playToggle" title="Play the animation."><iron-icon id=play icon="av:pause"></iron-icon> </button>
<paper-checkbox id=loop checked="{{loop}}" title="Run animations in a loop">Loop</paper-checkbox>
<select id=speed on-change="_speed" size="1">
<option value="0.25">0.25</option>
<option value="0.5">0.5</option>
<option value="0.75">0.75</option>
<option value="1" selected>Normal speed</option>
<option value="1.25">1.25</option>
<option value="1.5">1.5</option>
<option value="2">2</option>
</select>
</div>
</template>
</div>
</template>
<template is="dom-if" if="{{textonly}}">
<template is="dom-if" if="{{_not(embedded)}}">
<h2>Output</h2>
</template>
<div class="layout horizontal">
<div class=textoutput>
<text-src src="[[_textURL(domain, fiddlehash)]]"></text-src>
</div>
<template is="dom-if" if="{{embedded}}">
<button class=action on-tap="_run">Run</button>
<paper-spinner></paper-spinner>
<a href="https://fiddle.skia.org/c/{{fiddlehash}}" target=_blank">Pop-out</a>
</template>
</div>
</template>
<template is="dom-if" if="{{_not(embedded)}}">
<template is="dom-if" if="{{_hasImages(fiddlehash, _compile_errors, _runtime_error, textonly)}}">
<details-sk>
<summary-sk>Run Details</summary-sk>
<text-src id=glinfo src="[[_glinfoURL(domain, fiddlehash)]]"></text-src>
</details-sk>
</template>
</template>
</template>
</dom-module>
<script>
Polymer({
is: "fiddle-sk",
properties: {
width: {
type: Number,
value: 256,
reflectToAttribute: true,
},
height: {
type: Number,
value: 256,
reflectToAttribute: true,
},
textonly: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
srgb: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
f16: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
animated: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
duration: {
type: Number,
value: 2,
reflectToAttribute: true,
},
source: {
type: Number,
value: 0,
reflectToAttribute: true,
},
source_mipmap: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
sources: {
type: Array,
value: function() { return []; },
reflectToAttribute: true,
},
fiddlehash: {
type: String,
value: "",
reflectToAttribute: true,
},
domain: {
type: String,
value: "",
reflectToAttribute: true,
},
bug_link: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
embed_button: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
embedded: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
offscreen: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
offscreen_width: {
type: Number,
value: 256,
reflectToAttribute: true,
},
offscreen_height: {
type: Number,
value: 256,
reflectToAttribute: true,
},
offscreen_sample_count: {
type: Number,
value: 1,
reflectToAttribute: true,
},
offscreen_texturable: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
offscreen_mipmap: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
display_options: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
loop: {
type: Boolean,
value: true,
reflectToAttribute: true,
},
gpu_embedded: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
cpu_embedded: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
_compile_errors: {
type: Array,
value: function() { return []; },
reflectToAttribute: false,
},
_runtime_error: {
type: String,
value: "",
reflectToAttribute: false,
},
_name: {
type: String,
value: "",
reflectToAttribute: false,
},
_basic_mode: {
type: Boolean,
value: false,
},
_options_open: {
type: Boolean,
value: false,
}
},
ready: function() {
this._errorLinesRe = /draw.cpp:([0-9]+):([0-9]+):/;
this._editor = $$$('textarea-numbers-sk', this);
this._basic_mode = window.location.search.indexOf("mode=basic") !== -1;
if (this._basic_mode) {
this._options_open = true;
}
},
_run: function() {
this._run_impl({});
},
_submit_name: function() {
this._run_impl({
name: this._name,
overwrite: $$$('#overwrite', this).checked,
});
},
_run_impl: function(extra) {
this.fiddlehash = "";
var speed = $$$('#speed', this);
if (speed) {
speed.value = "1";
}
$$("paper-spinner", this).forEach(function(s) {
s.active = true;
}, this);
var body = {
code: $$$('textarea', this).value,
options: {
width: +this.width,
height: +this.height,
source: +this.source,
source_mipmap: this.source_mipmap,
srgb: this.srgb,
f16: this.f16,
textOnly: this.textonly,
animated: this.animated,
duration: +this.duration,
offscreen: this.offscreen,
offscreen_width: +this.offscreen_width,
offscreen_height: +this.offscreen_height,
offscreen_sample_count: +this.offscreen_sample_count,
offscreen_texturable: this.offscreen_texturable,
offscreen_mipmap: this.offscreen_mipmap,
}
};
for (var key in extra) {
body[key] = extra[key];
}
sk.post(this.domain + "/_/run", JSON.stringify(body)).then(JSON.parse).then(function(json) {
this.fiddlehash = json.fiddleHash;
this._compile_errors = json.compile_errors || [];
this._runtime_error = json.runtime_error || "";
$$("paper-spinner", this).forEach(function(s) {
s.active = false;
}, this);
this.fire("fiddle-success", json.fiddleHash);
this._compile_errors.forEach(function(err) {
this._editor.setErrorLine(+err.line);
}.bind(this));
var overwrite = $$$('#overwrite', this);
if (overwrite) {
overwrite.checked = false;
}
}.bind(this)).catch(function(err) {
$$("paper-spinner", this).forEach(function(s) {
s.active = false;
}, this);
sk.errorMessage(err);
}.bind(this));
},
_playToggle: function() {
var play= $$$('#play', this);
var videos = $$('video', this);
if (play.icon == "av:pause") {
videos.forEach(function(e) {
e.pause();
});
play.icon = "av:play-arrow";
} else {
videos.forEach(function(e) {
e.play();
});
play.icon = "av:pause";
}
},
_playEnded: function () {
$$$('#play', this).icon = "av:play-arrow";
},
_speed: function() {
var speed = $$$('#speed', this).value;
$$('video', this).forEach(function(e) {
e.playbackRate = speed;
});
},
_toggle_offscreen: function() {
if (!this.offscreen) {
return
}
if (this.offscreen_width === 0) {
this.offscreen_width = 64;
}
if (this.offscreen_height === 0) {
this.offscreen_height = 64;
}
},
_toggle_offscreen_texturable: function() {
if (!this.offscreen_texturable) {
this.offscreen_mipmap = false;
}
},
_errSelect: function(e) {
if (e.target.nodeName == "PRE") {
this._editor.setCursor(+e.target.dataset.line, +e.target.dataset.col);
this._editor.focus();
}
},
_bugLink: function(fiddleHash) {
var comment = "Visit this link to see the issue on fiddle:\n\n https://fiddle.skia.org/c/" + fiddleHash;
return "https://bugs.chromium.org/p/skia/issues/entry?" + sk.query.fromObject({
comment: comment,
});
},
_hasImages: function(fiddlehash, compile_errors, runtime_error, textonly) {
return !textonly && fiddlehash != "" && runtime_error == "" && !this._hasCompileErrors(compile_errors);
},
_hasCompileErrors: function(compile_errors) {
return compile_errors.some((e) => e.text.includes("error:"));
},
_hasCompileWarningsOrErrors: function(compile_errors) {
return compile_errors.length > 0;
},
_isEmpty: function(name) {
return name == "";
},
_not: function(b) {
return !b;
},
_textURL: function(domain, fiddlehash) {
if (fiddlehash == "") {
return "";
}
return domain + "/i/" + fiddlehash + ".txt";
},
_glinfoURL: function(domain, fiddlehash) {
if (fiddlehash == "") {
return "";
}
return domain + "/i/" + fiddlehash + "_glinfo.txt";
},
_showCpu: function(cpu_embedded, gpu_embedded, embedded) {
return cpu_embedded || (!embedded || !gpu_embedded);
},
_showGpu: function(gpu_embedded, embedded, basic_mode) {
return (!embedded || gpu_embedded) && !basic_mode;
},
_showLinks: function(animated, embedded, basic_mode) {
return !animated && !embedded && !basic_mode;
},
});
</script>