add support for skfiddle width/height options

BUG=skia:
R=jcgregorio@google.com

Review URL: https://codereview.chromium.org/656463002
This commit is contained in:
Greg Humphreys 2014-10-13 13:58:09 -04:00
parent 946700134f
commit 6c07907eaa
13 changed files with 199 additions and 71 deletions

View File

@ -144,6 +144,8 @@ Initial setup of the database, the user, and the tables:
code TEXT DEFAULT '' NOT NULL,
create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
hash CHAR(64) DEFAULT '' NOT NULL,
width INTEGER DEFAULT 256 NOT NULL,
height INTEGER DEFAULT 256 NOT NULL,
source_image_id INTEGER DEFAULT 0 NOT NULL,
PRIMARY KEY(hash),
@ -160,6 +162,8 @@ Initial setup of the database, the user, and the tables:
name CHAR(64) DEFAULT '' NOT NULL,
create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
hash CHAR(64) DEFAULT '' NOT NULL,
width INTEGER DEFAULT 256 NOT NULL,
height INTEGER DEFAULT 256 NOT NULL,
source_image_id INTEGER DEFAULT 0 NOT NULL,
hidden INTEGER DEFAULT 0 NOT NULL,

View File

@ -9,6 +9,7 @@
#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
#include "SkImageInfo.h"
#include "SkOSFile.h"
#include "SkStream.h"
#include "SkSurface.h"
@ -18,6 +19,8 @@ __SK_FORCE_IMAGE_DECODER_LINKING;
DEFINE_string(out, "", "Filename of the PNG to write to.");
DEFINE_string(source, "", "Filename of the source image.");
DEFINE_int32(width, 256, "Width of output image.");
DEFINE_int32(height, 256, "Height of output image.");
// Defined in template.cpp.
extern SkBitmap source;
@ -102,14 +105,20 @@ int main(int argc, char** argv) {
}
if (FLAGS_source.count() == 1) {
if (!SkImageDecoder::DecodeFile(FLAGS_source[0], &source)) {
perror("Unable to read the source image.");
}
const char *sourceDir = getenv("WEBTRY_INOUT");
if (NULL == sourceDir) {
sourceDir = "/skia_build/inout";
}
SkString sourcePath = SkOSPath::Join(sourceDir, FLAGS_source[0]);
if (!SkImageDecoder::DecodeFile(sourcePath.c_str(), &source)) {
perror("Unable to read the source image.");
}
}
SkFILEWStream stream(FLAGS_out[0]);
SkImageInfo info = SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType);
SkImageInfo info = SkImageInfo::MakeN32(FLAGS_width, FLAGS_height, kPremul_SkAlphaType);
SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
SkCanvas* canvas = surface->getCanvas();

View File

@ -26,6 +26,8 @@
var output = document.getElementById('output');
var stdout = document.getElementById('stdout');
var img = document.getElementById('img');
var imageWidth = document.getElementById('image-width');
var imageHeight = document.getElementById('image-height');
var tryHistory = document.getElementById('tryHistory');
var parser = new DOMParser();
var tryTemplate = document.getElementById('tryTemplate');
@ -211,6 +213,8 @@
code.value = body.code;
editor.setValue(body.code);
img.src = '/i/'+body.hash+'.png';
imageWidth.value = body.width;
imageHeight.value = body.height;
sourceSelectByID(body.source);
if (permalink) {
permalink.href = '/c/' + body.hash;
@ -245,7 +249,6 @@
// The img is optional and only appears if there is a valid
// image to display.
endWait();
console.log(e.target.response);
body = JSON.parse(e.target.response);
output.textContent = body.message;
if (body.message) {
@ -290,7 +293,7 @@
req.overrideMimeType('application/json');
req.open('POST', '/', true);
req.setRequestHeader('content-type', 'application/json');
req.send(JSON.stringify({'code': editor.getValue(), 'name': workspaceName, 'source': sourceId}));
req.send(JSON.stringify({'code': editor.getValue(), 'width': parseInt(imageWidth.value), 'height': parseInt(imageHeight.value), 'name': workspaceName, 'source': sourceId}));
}
run.addEventListener('click', onSubmitCode);

File diff suppressed because one or more lines are too long

View File

@ -333,6 +333,7 @@
// Basics of a navbar
// $navbar-height: 50px
// $navbar-margin-bottom: $line-height-computed
$navbar-margin-bottom: 0;
// $navbar-border-radius: $border-radius-base
// $navbar-padding-horizontal: floor(($grid-gutter-width / 2))
// $navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2)

View File

@ -2,6 +2,60 @@
@import "bootstrap-variables";
@import "bootstrap";
$sidebar-width: 200px;
$content-background: white;
$sidebar-background: #eee;
html, body {
min-height: 100%;
}
#wrapper {
padding-top: 50px;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
#lower-wrapper {
min-height: 100%;
position: relative;
}
#content {
height: 100%;
padding: 1em;
margin-left: $sidebar-width;
background: $content-background;
}
#sidebar {
position: absolute;
top: 0px;
left: 0px;
width: $sidebar-width;
background: $sidebar-background;
height: 100%;
padding: 5px;
.panel-heading {
padding: 3px 5px;
.panel-title {
font-size: 12px;
font-weight: 200;
}
}
}
.navbar {
margin-top: -50px;
}
img {
box-shadow: 2px 2px 5px gray;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAAXNSR0IArs4c6QAAAAJiS0dEAP+Hj8y/AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAB3RJTUUH3gUBEi4DGRAQYgAAAB1JREFUGNNjfMoAAVJQmokBDdBHgPE/lPFsYN0BABdaAwN6tehMAAAAAElFTkSuQmCC");
@ -40,10 +94,6 @@ code {
float: left;
}
#content {
padding: 1em;
}
#tryHistory {
position: absolute;
top: 3em;
@ -111,6 +161,10 @@ code {
/* Twitter Bootstrap customization */
.navbar {
border: none;
}
.navbar-brand {
padding: 0;

View File

@ -8,8 +8,4 @@
[ -z $SKIA_ROOT ] && SKIA_ROOT="/skia_build/skia"
[ -z $WEBTRY_INOUT ] && WEBTRY_INOUT="/skia_build/inout"
if [ "$#" -eq 2 ]; then
FIDDLE_SOURCE_ARGS="--source $WEBTRY_INOUT/$2"
fi
$SKIA_ROOT/out/Release/$1 --out $WEBTRY_INOUT/$1.png $FIDDLE_SOURCE_ARGS
$SKIA_ROOT/out/Release/$1 --out $WEBTRY_INOUT/$1.png "${@:2}"

View File

@ -1,7 +1,7 @@
#!/bin/bash
# fiddle_wrapper takes the hash of the fiddle as its first argument, and a source image to include as an
# optional second. Then it:
# fiddle_wrapper takes the hash of the fiddle as its first argument, and additional arguments to
# be passed to the build fiddle executable. Then it:
#
# 1) runs fiddle_gyp to create the gyp file
# 2) runs fiddle_ninja to build the executable
@ -16,4 +16,4 @@ cd $SKIA_ROOT/experimental/webtry/scripts
./fiddle_gyp $1
./fiddle_ninja $1
./fiddle_run $1 $2
./fiddle_run $@

View File

@ -1,8 +1,7 @@
<section id=content>
<section id="content">
<template id="sourcesTemplate">
<button id="" class=source><img width=64 height=64 src=''></button>
<button id="" class=source><img width=64 height=64 src=""></button>
</template>
<div id="inputBitmapEnable">

View File

@ -6,8 +6,13 @@
<link rel='import' type='text/html' href='/res/imp/zoom.html'>
</head>
<body>
{{template "titlebar.html" .}}
{{template "content.html" .}}
<div id="wrapper">
{{template "titlebar.html" .}}
<div id="lower-wrapper">
{{template "sidebar.html" .}}
{{template "content.html" .}}
</div>
</div>
<script type='text/javascript'>
// Not running in a workspace.

View File

@ -0,0 +1,25 @@
<section id="sidebar">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">
Options
</h3>
</div>
<div class="panel-body">
<form class="form-horizontal" role="form">
<div class="form-group">
<label for="image-width" class="col-sm-4 control-label">Width</label>
<div class="col-sm-8">
<input type="number" class="form-control" id="image-width" value="{{.Width}}">
</div>
</div>
<div class="form-group">
<label for="image-height" class="col-sm-4 control-label">Height</label>
<div class="col-sm-8">
<input type="number" class="form-control" id="image-height" value="{{.Height}}">
</div>
</div>
</form>
</div>
</div>
</section>

View File

@ -6,35 +6,37 @@
<link rel='import' type='text/html' href='/res/imp/zoom.html'>
</head>
<body>
<template id=tryTemplate>
<template id="tryTemplate">
<div class=tries data-try=''>
<img width=64 height=64 src=''>
<img width="64" src=''>
</div>
</template>
{{template "titlebar.html" .}}
{{if .Name}}
{{template "content.html" .}}
<div id="wrapper">
{{template "titlebar.html" .}}
{{if .Name}}
<div id="lower-wrapper">
{{template "sidebar.html" .}}
{{template "content.html" .}}
</div>
<section id="tryHistory">
</section>
<section id=tryHistory>
</section>
<script type='text/javascript'>
var history_ = {{.Tries}};
console.log(history_);
</script>
<script type='text/javascript'>
// Set the workspace name so run.js also updates the history.
var workspaceName = '{{.Name}}';
</script>
{{else}}
<section id=content>
<h1>Create</h1>
Create a new workspace:
<form action='.' method='POST'>
<p><input type='submit' value='Create'></p>
</form>
</section>
{{end}}
{{template "footercommon.html" .}}
<script type='text/javascript'>
var history_ = {{.Tries}};
</script>
<script type='text/javascript'>
// Set the workspace name so run.js also updates the history.
var workspaceName = '{{.Name}}';
</script>
{{else}}
<section id="content">
<h1>Create</h1>
<form action="" method="POST">
<input class='btn btn-primary btn-lg' type='submit' value='Create a new workspace'>
</form>
</section>
{{end}}
{{template "footercommon.html" .}}
</div>
</body>
</html>

View File

@ -157,6 +157,7 @@ func init() {
indexTemplate, err = htemplate.ParseFiles(
filepath.Join(cwd, "templates/index.html"),
filepath.Join(cwd, "templates/titlebar.html"),
filepath.Join(cwd, "templates/sidebar.html"),
filepath.Join(cwd, "templates/content.html"),
filepath.Join(cwd, "templates/headercommon.html"),
filepath.Join(cwd, "templates/footercommon.html"),
@ -176,6 +177,7 @@ func init() {
recentTemplate, err = htemplate.ParseFiles(
filepath.Join(cwd, "templates/recent.html"),
filepath.Join(cwd, "templates/titlebar.html"),
filepath.Join(cwd, "templates/sidebar.html"),
filepath.Join(cwd, "templates/headercommon.html"),
filepath.Join(cwd, "templates/footercommon.html"),
)
@ -185,6 +187,7 @@ func init() {
workspaceTemplate, err = htemplate.ParseFiles(
filepath.Join(cwd, "templates/workspace.html"),
filepath.Join(cwd, "templates/titlebar.html"),
filepath.Join(cwd, "templates/sidebar.html"),
filepath.Join(cwd, "templates/content.html"),
filepath.Join(cwd, "templates/headercommon.html"),
filepath.Join(cwd, "templates/footercommon.html"),
@ -252,6 +255,8 @@ func init() {
code TEXT DEFAULT '' NOT NULL,
create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
hash CHAR(64) DEFAULT '' NOT NULL,
width INTEGER DEFAULT 256 NOT NULL,
height INTEGER DEFAULT 256 NOT NULL,
source_image_id INTEGER DEFAULT 0 NOT NULL,
PRIMARY KEY(hash)
@ -275,6 +280,8 @@ func init() {
name CHAR(64) DEFAULT '' NOT NULL,
create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
hash CHAR(64) DEFAULT '' NOT NULL,
width INTEGER DEFAULT 256 NOT NULL,
height INTEGER DEFAULT 256 NOT NULL,
hidden INTEGER DEFAULT 0 NOT NULL,
source_image_id INTEGER DEFAULT 0 NOT NULL,
@ -345,6 +352,8 @@ type Titlebar struct {
type userCode struct {
Code string
Hash string
Width int
Height int
Source int
Titlebar Titlebar
}
@ -362,17 +371,22 @@ func writeTemplate(filename string, t *template.Template, context interface{}) e
// expandToFile expands the template and writes the result to the file.
func expandToFile(filename string, code string, t *template.Template) error {
return writeTemplate(filename, t, userCode{Code: code, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}})
return writeTemplate(filename, t, userCode{
Code: code,
Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo},
})
}
// expandCode expands the template into a file and calculates the MD5 hash.
func expandCode(code string, source int) (string, error) {
// We include the width and height here so that a single hash can capture
// both the code and the supplied width/height parameters.
func expandCode(code string, source int, width, height int) (string, error) {
// in order to support fonts in the chroot jail, we need to make sure
// we're using portable typefaces.
// TODO(humper): Make this more robust, supporting things like setTypeface
inputCodeLines := strings.Split(code, "\n")
outputCodeLines := []string{"DECLARE_bool(portableFonts);"}
outputCodeLines := []string{"DECLARE_bool(portableFonts);", fmt.Sprintf("// WxH: %d, %d", width, height)}
for _, line := range inputCodeLines {
outputCodeLines = append(outputCodeLines, line)
if strings.HasPrefix(strings.TrimSpace(line), "SkPaint p") {
@ -451,15 +465,15 @@ func reportTryError(w http.ResponseWriter, r *http.Request, err error, message,
w.Write(resp)
}
func writeToDatabase(hash string, code string, workspaceName string, source int) {
func writeToDatabase(hash string, code string, workspaceName string, source int, width, height int) {
if db == nil {
return
}
if _, err := db.Exec("INSERT INTO webtry (code, hash, source_image_id) VALUES(?, ?, ?)", code, hash, source); err != nil {
if _, err := db.Exec("INSERT INTO webtry (code, hash, width, height, source_image_id) VALUES(?, ?, ?, ?, ?)", code, hash, width, height, source); err != nil {
log.Printf("ERROR: Failed to insert code into database: %q\n", err)
}
if workspaceName != "" {
if _, err := db.Exec("INSERT INTO workspacetry (name, hash, source_image_id) VALUES(?, ?, ?)", workspaceName, hash, source); err != nil {
if _, err := db.Exec("INSERT INTO workspacetry (name, hash, width, height, source_image_id) VALUES(?, ?, ?, ?, ?)", workspaceName, hash, width, height, source); err != nil {
log.Printf("ERROR: Failed to insert into workspacetry table: %q\n", err)
}
}
@ -599,6 +613,8 @@ type Workspace struct {
Name string
Code string
Hash string
Width int
Height int
Source int
Tries []Try
Titlebar Titlebar
@ -621,14 +637,16 @@ func newWorkspace() (string, error) {
}
// getCode returns the code for a given hash, or the empty string if not found.
func getCode(hash string) (string, int, error) {
func getCode(hash string) (string, int, int, int, error) {
code := ""
width := 0
height := 0
source := 0
if err := db.QueryRow("SELECT code, source_image_id FROM webtry WHERE hash=?", hash).Scan(&code, &source); err != nil {
if err := db.QueryRow("SELECT code, width, height, source_image_id FROM webtry WHERE hash=?", hash).Scan(&code, &width, &height, &source); err != nil {
log.Printf("ERROR: Code for hash is missing: %q\n", err)
return code, source, err
return code, width, height, source, err
}
return code, source, nil
return code, width, height, source, nil
}
func workspaceHandler(w http.ResponseWriter, r *http.Request) {
@ -657,15 +675,19 @@ func workspaceHandler(w http.ResponseWriter, r *http.Request) {
}
var code string
var hash string
var width int
var height int
source := 0
if len(tries) == 0 {
code = DEFAULT_SAMPLE
width = 256
height = 256
} else {
hash = tries[len(tries)-1].Hash
code, source, _ = getCode(hash)
code, width, height, source, _ = getCode(hash)
}
w.Header().Set("Content-Type", "text/html")
if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, Code: code, Name: name, Hash: hash, Source: source, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil {
if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, Code: code, Name: name, Hash: hash, Width: width, Height: height, Source: source, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil {
log.Printf("ERROR: Failed to expand template: %q\n", err)
}
} else if r.Method == "POST" {
@ -691,6 +713,8 @@ func hasPreProcessor(code string) bool {
type TryRequest struct {
Code string `json:"code"`
Width int `json:"width"`
Height int `json:"height"`
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.
}
@ -713,14 +737,14 @@ func iframeHandler(w http.ResponseWriter, r *http.Request) {
return
}
var code string
code, source, err := getCode(hash)
code, width, height, source, err := getCode(hash)
if err != nil {
http.NotFound(w, r)
return
}
// Expand the template.
w.Header().Set("Content-Type", "text/html")
if err := iframeTemplate.Execute(w, userCode{Code: code, Hash: hash, Source: source}); err != nil {
if err := iframeTemplate.Execute(w, userCode{Code: code, Width: width, Height: height, Hash: hash, Source: source}); err != nil {
log.Printf("ERROR: Failed to expand template: %q\n", err)
}
}
@ -728,6 +752,8 @@ func iframeHandler(w http.ResponseWriter, r *http.Request) {
type TryInfo struct {
Hash string `json:"hash"`
Code string `json:"code"`
Width int `json:"width"`
Height int `json:"height"`
Source int `json:"source"`
}
@ -744,7 +770,7 @@ func tryInfoHandler(w http.ResponseWriter, r *http.Request) {
return
}
hash := match[1]
code, source, err := getCode(hash)
code, width, height, source, err := getCode(hash)
if err != nil {
http.NotFound(w, r)
return
@ -752,6 +778,8 @@ func tryInfoHandler(w http.ResponseWriter, r *http.Request) {
m := TryInfo{
Hash: hash,
Code: code,
Width: width,
Height: height,
Source: source,
}
resp, err := json.Marshal(m)
@ -776,6 +804,8 @@ func mainHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
code := DEFAULT_SAMPLE
source := 0
width := 256
height := 256
match := directLink.FindStringSubmatch(r.URL.Path)
var hash string
if len(match) == 2 && r.URL.Path != "/" {
@ -785,14 +815,14 @@ func mainHandler(w http.ResponseWriter, r *http.Request) {
return
}
// Update 'code' with the code found in the database.
if err := db.QueryRow("SELECT code, source_image_id FROM webtry WHERE hash=?", hash).Scan(&code, &source); err != nil {
if err := db.QueryRow("SELECT code, width, height, source_image_id FROM webtry WHERE hash=?", hash).Scan(&code, &width, &height, &source); err != nil {
http.NotFound(w, r)
return
}
}
// Expand the template.
w.Header().Set("Content-Type", "text/html")
if err := indexTemplate.Execute(w, userCode{Code: code, Hash: hash, Source: source, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil {
if err := indexTemplate.Execute(w, userCode{Code: code, Hash: hash, Source: source, Width: width, Height: height, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil {
log.Printf("ERROR: Failed to expand template: %q\n", err)
}
} else if r.Method == "POST" {
@ -818,23 +848,23 @@ func mainHandler(w http.ResponseWriter, r *http.Request) {
reportTryError(w, r, err, "Preprocessor macros aren't allowed.", "")
return
}
hash, err := expandCode(LineNumbers(request.Code), request.Source)
hash, err := expandCode(LineNumbers(request.Code), request.Source, request.Width, request.Height)
if err != nil {
reportTryError(w, r, err, "Failed to write the code to compile.", hash)
return
}
writeToDatabase(hash, request.Code, request.Name, request.Source)
writeToDatabase(hash, request.Code, request.Name, request.Source, request.Width, request.Height)
err = expandGyp(hash)
if err != nil {
reportTryError(w, r, err, "Failed to write the gyp file.", hash)
return
}
cmd := "scripts/fiddle_wrapper " + hash
cmd := fmt.Sprintf("scripts/fiddle_wrapper %s --width %d --height %d", hash, request.Width, request.Height)
if *useChroot {
cmd = "schroot -c webtry --directory=/ -- /skia_build/skia/experimental/webtry/" + cmd
}
if request.Source > 0 {
cmd += fmt.Sprintf(" image-%d.png", request.Source)
cmd += fmt.Sprintf(" --source image-%d.png", request.Source)
}
message, err := doCmd(cmd)