rework GUI, scripts, and fiddle main for multiple simultaneous configs

You can now select Raster, Ganesh, or both (PDF coming soon), and see
all output simultaneously.

BUG=skia:
R=jcgregorio@google.com
TBR=jcgregorio

Review URL: https://codereview.chromium.org/688433002
This commit is contained in:
Greg Humphreys 2014-10-29 10:31:33 -04:00
parent 6eea9e7871
commit af58b5fcba
10 changed files with 228 additions and 60 deletions

View File

@ -19,11 +19,13 @@
__SK_FORCE_IMAGE_DECODER_LINKING;
DEFINE_string(out, "", "Filename of the PNG to write to.");
DEFINE_string(out, "", "Output basename; fiddle will append the config used and the appropriate extension");
DEFINE_string(source, "", "Filename of the source image.");
DEFINE_int32(width, 256, "Width of output image.");
DEFINE_int32(height, 256, "Height of output image.");
DEFINE_bool(gpu, false, "Use GPU (Mesa) rendering.");
DEFINE_bool(raster, true, "Use Raster rendering.");
DEFINE_bool(pdf, false, "Use PDF rendering.");
// Defined in template.cpp.
extern SkBitmap source;
@ -98,6 +100,44 @@ static void setLimits() {
extern void draw(SkCanvas* canvas);
static void drawAndDump(SkSurface* surface, SkWStream* stream) {
SkCanvas *canvas = surface->getCanvas();
draw(canvas);
// Write out the image as a PNG.
SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
SkAutoTUnref<SkData> data(image->encode(SkImageEncoder::kPNG_Type, 100));
if (NULL == data.get()) {
printf("Failed to encode\n");
exit(1);
}
stream->write(data->data(), data->size());
}
static void drawRaster(SkWStream* stream, SkImageInfo info) {
SkAutoTUnref<SkSurface> surface;
surface.reset(SkSurface::NewRaster(info));
drawAndDump(surface, stream);
}
static void drawGPU(SkWStream* stream, SkImageInfo info) {
SkAutoTUnref<SkSurface> surface;
GrContextFactory* grFactory = NULL;
GrContext::Options grContextOpts;
grFactory = new GrContextFactory(grContextOpts);
GrContext* gr = grFactory->get(GrContextFactory::kMESA_GLContextType);
surface.reset(SkSurface::NewRenderTarget(gr,info));
drawAndDump(surface, stream);
delete grFactory;
}
static void drawPDF(SkWStream* stream, SkImageInfo info) {
printf( "Not implemented yet...\n");
}
int main(int argc, char** argv) {
SkCommandLineFlags::Parse(argc, argv);
SkAutoGraphics init;
@ -119,41 +159,42 @@ int main(int argc, char** argv) {
}
}
SkFILEWStream stream(FLAGS_out[0]);
// make sure to open any needed output files before we set up the security
// jail
SkWStream* streams[3];
if (FLAGS_raster) {
SkString outPath;
outPath.printf("%s_raster.png", FLAGS_out[0]);
streams[0] = SkNEW_ARGS(SkFILEWStream,(outPath.c_str()));
}
if (FLAGS_gpu) {
SkString outPath;
outPath.printf("%s_gpu.png", FLAGS_out[0]);
streams[1] = SkNEW_ARGS(SkFILEWStream,(outPath.c_str()));
}
if (FLAGS_pdf) {
SkString outPath;
outPath.printf("%s.pdf", FLAGS_out[0]);
streams[2] = SkNEW_ARGS(SkFILEWStream,(outPath.c_str()));
}
SkImageInfo info = SkImageInfo::MakeN32(FLAGS_width, FLAGS_height, kPremul_SkAlphaType);
SkCanvas* canvas;
SkAutoTUnref<SkSurface> surface;
GrContextFactory* grFactory = NULL;
if (FLAGS_gpu) {
GrContext::Options grContextOpts;
grFactory = new GrContextFactory(grContextOpts);
GrContext* gr = grFactory->get(GrContextFactory::kMESA_GLContextType);
surface.reset(SkSurface::NewRenderTarget(gr,info));
} else {
surface.reset(SkSurface::NewRaster(info));
}
canvas = surface->getCanvas();
setLimits();
if (!install_syscall_filter()) {
return 1;
}
draw(canvas);
// Write out the image as a PNG.
SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
SkAutoTUnref<SkData> data(image->encode(SkImageEncoder::kPNG_Type, 100));
if (NULL == data.get()) {
printf("Failed to encode\n");
exit(1);
if (FLAGS_raster) {
drawRaster(streams[0], info);
}
if (FLAGS_gpu) {
drawGPU(streams[1], info);
}
if (FLAGS_pdf) {
drawPDF(streams[2], info);
}
stream.write(data->data(), data->size());
delete grFactory;
}

View File

@ -26,7 +26,12 @@
var output = document.getElementById('output');
var outputWrapper = document.getElementById('output-wrapper');
var gpu = document.getElementById('use-gpu');
var img = document.getElementById('img');
var raster = document.getElementById('use-raster');
var pdf = document.getElementById('use-pdf');
var rasterOutput = document.getElementById('raster-output');
var rasterImg = document.getElementById('raster-img');
var gpuOutput = document.getElementById('gpu-output');
var gpuImg = document.getElementById('gpu-img');
var imageWidth = document.getElementById('image-width');
var imageHeight = document.getElementById('image-height');
var tryHistory = document.getElementById('tryHistory');
@ -148,6 +153,20 @@
enableSource.addEventListener('click', sourceClick, true);
selectedSource.addEventListener('click', sourceClick, true);
function configChange(e) {
if (!(gpu.checked || raster.checked || pdf.checked)) {
run.disabled = true;
run.innerHTML = "Choose a configuration"
} else {
run.disabled = false;
run.innerHTML = "Run"
}
}
gpu.addEventListener('change', configChange);
raster.addEventListener('change', configChange);
pdf.addEventListener('change', configChange);
var editor = CodeMirror.fromTextArea(code, {
theme: "default",
@ -217,7 +236,8 @@
body = JSON.parse(e.target.response);
code.value = body.code;
editor.setValue(body.code);
img.src = '/i/'+body.hash+'.png';
rasterImg.src = '/i/'+body.hash+'_raster.png';
gpuImg.src = '/i/'+body.hash+'_gpu.png';
imageWidth.value = body.width;
imageHeight.value = body.height;
gpu.checked = body.gpu;
@ -304,14 +324,23 @@
outputWrapper.style.display = 'block';
}
}
if (body.hasOwnProperty('img')) {
img.src = 'data:image/png;base64,' + body.img;
if (body.hasOwnProperty('rasterImg') && body.rasterImg != "") {
rasterImg.src = 'data:image/png;base64,' + body.rasterImg;
rasterOutput.style.display = "inline-block";
} else {
img.src = '';
rasterOutput.style.display = "none";
rasterImg.src = '';
}
if (body.hasOwnProperty('gpuImg') && body.gpuImg != "") {
gpuImg.src = 'data:image/png;base64,' + body.gpuImg;
gpuOutput.style.display = "inline-block";
} else {
gpuImg.src = '';
gpuOutput.style.display = "none";
}
// Add the image to the history if we are on a workspace page.
if (tryHistory) {
addToHistory(body.hash, 'data:image/png;base64,' + body.img);
addToHistory(body.hash, 'data:image/png;base64,' + body.rasterImg);
} else {
window.history.pushState(null, null, '/c/' + body.hash);
}
@ -327,7 +356,6 @@
}
}
function onSubmitCode() {
beginWait();
clearOutput();
@ -343,7 +371,9 @@
'height': parseInt(imageHeight.value),
'name': workspaceName,
'source': sourceId,
'gpu': gpu.checked
'gpu': gpu.checked,
'raster': raster.checked,
'pdf': pdf.checked
}));
}
run.addEventListener('click', onSubmitCode);
@ -375,3 +405,26 @@
}
})();
// TODO (humper) -- move the following functions out of the global
// namespace as part of a web-components based fiddle frontend rewrite.
function collectionHas(a, b) { //helper function (see below)
for(var i = 0, len = a.length; i < len; i ++) {
if(a[i] == b) return true;
}
return false;
}
function findParentBySelector(elm, selector) {
var all = document.querySelectorAll(selector);
var cur = elm.parentNode;
while(cur && !collectionHas(all, cur)) { //keep going up until you find a match
cur = cur.parentNode; //go up
}
return cur; //will return null if not found
}
function onLoadImage(img) {
var wrapper = findParentBySelector(img, ".image-wrapper");
wrapper.style.display = "inline-block";
}

File diff suppressed because one or more lines are too long

View File

@ -118,6 +118,10 @@ code {
display: block !important;
}
.image-wrapper {
display: none;
}
#chooseSource {
display: none;
}

View File

@ -8,4 +8,4 @@
[ -z $SKIA_ROOT ] && SKIA_ROOT="/skia_build/skia"
[ -z $WEBTRY_INOUT ] && WEBTRY_INOUT="/skia_build/inout"
$SKIA_ROOT/out/Release/$1 --out $WEBTRY_INOUT/$1.png "${@:2}"
$SKIA_ROOT/out/Release/$1 --out $WEBTRY_INOUT/$1 "${@:2}"

View File

@ -51,9 +51,28 @@
<input type="text" value="" id="embed" readonly style="display:none;">
</div>
<p>
<img touch-action='none' class='zoom' id='img' src='{{if .Hash}}/i/{{.Hash}}.png{{end}}'/>
</p>
<div id="raster-output" class="image-wrapper panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">
Raster Output
</h3>
</div>
<div class="panel-body">
<img touch-action='none' class='zoom' id='raster-img' onload="onLoadImage(this)" src='{{if .Hash}}/i/{{.Hash}}_raster.png{{end}}'/>
</div>
</div>
<div id="gpu-output" class="image-wrapper panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">
Ganesh Output
</h3>
</div>
<div class="panel-body">
<img touch-action='none' class='zoom' id='gpu-img' onload="onLoadImage(this)" src='{{if .Hash}}/i/{{.Hash}}_gpu.png{{end}}'/>
</div>
</div>
<p id='zoomHex'></p>
<div id="output-wrapper">

View File

@ -1,9 +0,0 @@
<link rel='stylesheet' href='/res/webtry/css/webtry.css' type='text/css'>
<link rel='stylesheet' href='/res/css/cm/codemirror.css' type='text/css'>
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="/res/webtry/js/bootstrap.js"></script>
<script src='/res/js/polyfill.js' type='text/javascript'></script>
<script src='/res/js/cm/codemirror.js' type='text/javascript'></script>
<script src='/res/js/cm/clike.js' type='text/javascript'></script>
<script src='/res/js/webtry.js' type='text/javascript'></script>

View File

@ -1,2 +1,12 @@
<meta charset='utf-8' />
<meta name=viewport content='width=device-width, initial-scale=1'>
<link rel='stylesheet' href='/res/webtry/css/webtry.css' type='text/css'>
<link rel='stylesheet' href='/res/css/cm/codemirror.css' type='text/css'>
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="/res/webtry/js/bootstrap.js"></script>
<script src='/res/js/polyfill.js' type='text/javascript'></script>
<script src='/res/js/cm/codemirror.js' type='text/javascript'></script>
<script src='/res/js/cm/clike.js' type='text/javascript'></script>
<script src='/res/js/webtry.js' type='text/javascript'></script>

View File

@ -2,7 +2,7 @@
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">
Options
Fiddle Options
</h3>
</div>
<div class="panel-body">
@ -19,12 +19,35 @@
<input type="number" class="form-control" id="image-height" value="{{.Height}}">
</div>
</div>
</form>
</div>
</div>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">
Run Configuration
</h3>
</div>
<div class="panel-body">
<form id="run-configuration" class="form-horizontal" role="form">
<div class="checkbox">
<label>
<input id="use-raster" type="checkbox" value="" checked>
Raster
</label>
</div>
<div class="checkbox">
<label>
<input id="use-gpu" type="checkbox" value="">
Ganesh
</label>
</div>
<div class="checkbox">
<label>
<input id="use-pdf" type="checkbox" value="" disabled>
PDF (coming soon)
</label>
</div>
</form>
</div>
</div>

View File

@ -79,7 +79,7 @@ var (
iframeLink = regexp.MustCompile("^/iframe/([a-f0-9]+)$")
// imageLink is the regex that matches URLs paths that are direct links to PNGs.
imageLink = regexp.MustCompile("^/i/([a-z0-9-]+.png)$")
imageLink = regexp.MustCompile("^/i/([a-z0-9-_]+.png)$")
// tryInfoLink is the regex that matches URLs paths that are direct links to data about a single try.
tryInfoLink = regexp.MustCompile("^/json/([a-f0-9]+)$")
@ -407,7 +407,8 @@ func expandGyp(hash string) error {
type response struct {
Message string `json:"message"`
CompileErrors []compileError `json:"compileErrors"`
Img string `json:"img"`
RasterImg string `json:"rasterImg"`
GPUImg string `json:"gpuImg"`
Hash string `json:"hash"`
}
@ -724,6 +725,8 @@ type TryRequest struct {
Width int `json:"width"`
Height int `json:"height"`
GPU bool `json:"gpu"`
Raster bool `json:"raster"`
PDF bool `json:"pdf"`
Name string `json:"name"` // Optional name of the workspace the code is in.
Source int `json:"source"` // ID of the source image, 0 if none.
}
@ -858,6 +861,10 @@ func mainHandler(w http.ResponseWriter, r *http.Request) {
reportTryError(w, r, err, "Coulnd't decode JSON.", "")
return
}
if !(request.GPU || request.Raster || request.PDF) {
reportTryError(w, r, nil, "No run configuration supplied...", "")
return
}
if hasPreProcessor(request.Code) {
err := fmt.Errorf("Found preprocessor macro in code.")
reportTryError(w, r, err, "Preprocessor macros aren't allowed.", "")
@ -875,9 +882,15 @@ func mainHandler(w http.ResponseWriter, r *http.Request) {
return
}
cmd := fmt.Sprintf("scripts/fiddle_wrapper %s --width %d --height %d", hash, request.Width, request.Height)
if request.Raster {
cmd += " --raster"
}
if request.GPU {
cmd += " --gpu"
}
if request.PDF {
cmd += " --pdf"
}
if *useChroot {
cmd = "schroot -c webtry --directory=/ -- /skia_build/skia/experimental/webtry/" + cmd
}
@ -920,16 +933,30 @@ func mainHandler(w http.ResponseWriter, r *http.Request) {
return
}
png, err := ioutil.ReadFile("../../../inout/" + hash + ".png")
if err != nil {
reportTryError(w, r, err, "Failed to open the generated PNG.", hash)
return
}
m := response{
Img: base64.StdEncoding.EncodeToString([]byte(png)),
Hash: hash,
}
if request.Raster {
png, err := ioutil.ReadFile("../../../inout/" + hash + "_raster.png")
if err != nil {
reportTryError(w, r, err, "Failed to open the raster-generated PNG.", hash)
return
}
m.RasterImg = base64.StdEncoding.EncodeToString([]byte(png))
}
if request.GPU {
png, err := ioutil.ReadFile("../../../inout/" + hash + "_GPU.png")
if err != nil {
reportTryError(w, r, err, "Failed to open the GPU-generated PNG.", hash)
return
}
m.GPUImg = base64.StdEncoding.EncodeToString([]byte(png))
}
resp, err := json.Marshal(m)
if err != nil {
reportTryError(w, r, err, "Failed to serialize a response.", hash)