From 6bd49ac82a9606ec394c52b1a8f583915c9bd8b5 Mon Sep 17 00:00:00 2001 From: "Javier S. Pedro" Date: Thu, 30 Jun 2011 22:48:35 +0200 Subject: initial import --- index.php | 390 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ qsvg.cc | 59 +++++++++ style.css | 78 ++++++++++++ template.svg | 12 ++ 4 files changed, 539 insertions(+) create mode 100644 index.php create mode 100644 qsvg.cc create mode 100644 style.css create mode 100644 template.svg diff --git a/index.php b/index.php new file mode 100644 index 0000000..79a4428 --- /dev/null +++ b/index.php @@ -0,0 +1,390 @@ + + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +define('SVGNS', 'http://www.w3.org/2000/svg'); + +define('COLOR_EXTRACT_RENDER_SIZE', 128); +define('HARMATTAN_ICON_SIZE', 80); +define('HARMATTAN_FOCAL_SIZE', 54); + +function svg2png($svg, $w, $h, $png) +{ + $output = array(); + $exitcode = -1; + exec(sprintf("~/bin/qsvg %s %d %d %s", escapeshellarg($svg), $w, $h, escapeshellarg($png)),$output,$exitcode); + return $exitcode == 0; +} + +function multiarray($d, $k, $n = 0) +{ + return array_fill(0, $k, $d == 1 ? $n : multiarray($d - 1, $k, $n)); +} + +function warn($s) +{ + global $warnings; + $warnings[] = $s; +} + +function colfilter_none($c) +{ + return false; +} + +function colfilter_pure($c) +{ + return ($c['red'] == 0 && $c['green'] == 0 && $c['blue'] == 0) || + ($c['red'] == 255 && $c['green'] == 255 && $c['blue'] == 255); +} + +function colfilter_grey($c) +{ + return ($c['red'] == $c['green']) && ($c['green'] == $c['blue']); +} + +function colfilter_grey5($c) +{ + $r = $c['red'] >> 3; + $g = $c['green'] >> 3; + $b = $c['blue'] >> 3; + return ($r == $g) && ($g == $b); +} + +function domcolor_mean($img, $flt) +{ + $w = imagesx($img); + $h = imagesy($img); + $t = $w * $h; + $r = 0; $g = 0; $b = 0; + for ($y = 0; $y < $h; $y++) { + for ($x = 0; $x < $w; $x++) { + $c = imagecolorsforindex($img, imagecolorat($img, $x, $y)); + if (call_user_func($flt, $c)) continue; + $r += $c['red']; + $g += $c['green']; + $b += $c['blue']; + } + } + $r /= $t; $g /= $t; $b /= $t; + return array($r, $g, $b); +} + +function domcolor_mode($img, $flt) +{ + $w = imagesx($img); + $h = imagesy($img); + $t = $w * $h; + $f = multiarray(3, 256, 0); + $max_c = false; + $max_f = 0; + for ($y = 0; $y < $h; $y++) { + for ($x = 0; $x < $w; $x++) { + $c = imagecolorsforindex($img, imagecolorat($img, $x, $y)); + if (call_user_func($flt, $c)) continue; + $this_f = $f[$c['red']][$c['green']][$c['blue']] += 1; + if ($this_f > $max_f) { + $max_c = $c; + $max_f = $this_f; + } + } + } + if ($max_c) + return array($max_c['red'], $max_c['green'], $max_c['blue']); + else + return false; +} + +$domalgos = array('mean', 'mode'); +$colfilters = array('none', 'pure', 'grey', 'grey5'); + +function extract_dominant_color($svg, $algo = 'average', $filter = 'grey') +{ + global $domalgos, $colfilters; + $png = '/tmp/hicg-icon.png'; + if (!in_array($algo, $domalgos)) { + return false; + } + if (!in_array($filter, $colfilters)) { + return false; + } + if (!svg2png($svg, COLOR_EXTRACT_RENDER_SIZE, COLOR_EXTRACT_RENDER_SIZE, $png)) { + return false; + } + $img = imagecreatefrompng($png); + if (!$img) { + return false; + } + $color = call_user_func('domcolor_'. $algo, $img, 'colfilter_'.$filter); + imagedestroy($img); + return $color; +} + +function clip_color_value($c) +{ + if ($c < 0) return 0; + else if ($c > 255) return 255; + return $c; +} + +function clip_color($c) +{ + return array_map('clip_color_value', $c); +} + +function greyscale_color($c) +{ + return .299*$c[0] + .587*$c[1] + .114*$c[2]; +} + +function color2hex($c) +{ + return sprintf("%02x%02x%02x", $c[0], $c[1], $c[2]); +} + +function get_disabled_color($c) +{ + /* Clear and desature a bit. */ + $nc = array_map(create_function('$x', 'return $x + 90;'), $c); + $grey = greyscale_color($nc); + $nc = array_map(create_function('$x', 'return $x * 0.8 + ('.$grey.') * 0.2;'), $nc); + return clip_color($nc); +} + +function get_pressed_color($c) +{ + /* Desaturate and darken. */ + $grey = greyscale_color($c); + $nc = array_map(create_function('$x', 'return $x * 0.5 + ('.$grey.') * 0.5;'), $c); + $nc = array_map(create_function('$x', 'return $x - 40;'), $nc); + + return clip_color($nc); +} + +function get_icon_light_color($c) +{ + $nc = array_map(create_function('$x', 'return $x + 60;'), $c); + return clip_color($nc); +} + +function get_icon_dark_color($c) +{ + $nc = array_map(create_function('$x', 'return $x - 50;'), $c); + return clip_color($nc); +} + +function get_svg_attribute($node, $attrib) +{ + $v = $node->getAttributeNS(SVGNS, 'svg:' . $attrib); + if (empty($v)) { + $v = $node->getAttribute('svg:' . $attrib); + if (empty($v)) { + $v = $node->getAttribute($attrib); + } + } + + return $v; +} + +function calc_scale_ratios($w, $h, &$s, &$tx, &$ty) +{ + if ($w > $h) { + $s = HARMATTAN_FOCAL_SIZE / $w; + $sw = HARMATTAN_FOCAL_SIZE; + $sh = $s * $h; + } else { + $s = HARMATTAN_FOCAL_SIZE / $h; + $sw = $s * $w; + $sh = HARMATTAN_FOCAL_SIZE; + } + $tx = HARMATTAN_ICON_SIZE / 2 - $sw / 2; + $ty = HARMATTAN_ICON_SIZE / 2 - $sh / 2; +} + +function make_icon($src, $template, $dst, $color) +{ + $srcDoc = new DOMDocument(); + $srcDoc->load($src); + $doc = new DOMDocument(); + $doc->load($template); + + /* Get scaling ratios from source SVG. */ + $srcNode = $srcDoc->documentElement; + $w = intval(get_svg_attribute($srcNode, 'width')); + $h = intval(get_svg_attribute($srcNode, 'height')); + calc_scale_ratios($w, $h, $s, $tx, $ty); + + /* Fix gradient colors. */ + $grd = $doc->getElementsByTagName('stop'); + $grd->item(0)->setAttribute('style', 'stop-color:#' . color2hex(get_icon_light_color($color)) . ';stop-opacity:1'); + $grd->item(1)->setAttribute('style', 'stop-color:#' . color2hex(get_icon_dark_color($color)) . ';stop-opacity:1'); + + /* Create the scaling & centering transform. */ + $g = $doc->createElement('g'); + $g->setAttribute('transform', "translate($tx, $ty) scale($s)"); + + foreach ($srcNode->childNodes as $node) { + $n = $doc->importNode($node, TRUE); + $g->appendChild($n); + } + + $doc->documentElement->appendChild($g); + + $doc->save($dst); +} + +function make_png($src, $dst) +{ + if (!svg2png($src, HARMATTAN_ICON_SIZE, HARMATTAN_ICON_SIZE, $dst)) { + warn("I failed to generate a 80x80 PNG."); + } +} + +$warnings = array(); + +if (@isset($_POST['send'])) { + $prefix = './files/' . uniqid() . '_'; + $srcfile = $prefix . 'src.svg'; + if (!move_uploaded_file($_FILES['iconfile']['tmp_name'], $srcfile)) { + die('Upload failed.'); + } + $domalgo = $_POST['domalgo']; + $colfilter = $_POST['colfilter']; + $iconfile = $prefix . 'icon.svg'; + $pngfile = $prefix . 'icon.png'; + + $domcolor = extract_dominant_color($srcfile, $domalgo, $colfilter); + if (!$domcolor) { + warn("I failed to get a dominant color. Your icon might be too greyish."); + $domcolor = array(0, 0, 0); + $discolor = array(0, 0, 0); + $prscolor = array(0, 0, 0); + } else { + $discolor = get_disabled_color($domcolor); + $prscolor = get_pressed_color($domcolor); + } + + make_icon($srcfile, 'template.svg', $iconfile, $domcolor); + make_png($iconfile, $pngfile); + + $srcurl = $srcfile; + $iconurl = $iconfile; + $pngurl = $pngfile; +} else { + /* Load some sample data. */ + $srcfile = 'samplesrc.svg'; + $domalgo = 'mode'; + $colfilter = 'grey5'; + $iconfile = 'sampleicon.svg'; + $pngfile = 'sampleicon.png'; + + $domcolor = extract_dominant_color($srcfile, $domalgo, $colfilter); + $discolor = get_disabled_color($domcolor); + $prscolor = get_pressed_color($domcolor); + + /* This should not get into production. ;) */ + make_icon($srcfile, 'template.svg', $iconfile, $domcolor); + make_png($iconfile, $pngfile); + + $srcurl = $srcfile; + $iconurl = $iconfile; + $pngurl = $pngfile; +} + +echo ''; +?> + + + +Harmattan Icon Generator + + + + +

Warnings

+ +

Warning:

+ + +

Upload your icon

+
+
+ Your application's current icon file + +
+
+ Algorithm for dominant color extraction + + +
+
+ Colors to ignore + + + + +
+
+ Drop shadow + + +
+
+ +
+
+

Results

+
+

You uploaded:

+ +
+
+
+

Your icon's dominant color is #

+
+
+
+

A suggested disabled color is #

+
+
+
+

A suggested pressed color is #

+
+
+
+

Harmattan style icon:

+
+
+ +

YourApp

+
+
+
+

Download SVG file. Or a 80x80 PNG file.

+
+

Suggested reading

+ + + diff --git a/qsvg.cc b/qsvg.cc new file mode 100644 index 0000000..2eb737e --- /dev/null +++ b/qsvg.cc @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include + +/* + hicg -- Harmattan Icon/Color Generator + Copyright (C) 2011 Javier S. Pedro + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +using std::cerr; +using std::endl; + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + QStringList args = a.arguments(); + if (args.size() != 5) { + cerr << "usage: qsvg " << endl; + return 1; + } + + QSvgRenderer r(args[1]); + + if (!r.isValid()) { + cerr << "Could not open source file." << endl; + return 1; + } + + int w, h; + w = args[2].toInt(); + h = args[3].toInt(); + + QImage i(w, h, QImage::Format_ARGB32); + i.fill(QColor(Qt::transparent).rgba()); + + QPainter p(&i); + r.render(&p); + + if (!i.save(args[4], "png")) { + cerr << "Could not save to destination file." << endl; + } + + return 0; +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..fddeb46 --- /dev/null +++ b/style.css @@ -0,0 +1,78 @@ +body { + background-color: #fff; + width: 80%; + margin: 0 auto; + font-family: sans-serif; + font-size: 0.9em; +} + +h1 { + display: block; + border-bottom: 1px solid black; + font-size: 1.4em; +} + +fieldset { + width: 90%; + margin: 1em auto; + border: 1px solid #bbb; +} + +legend { + color: #222; +} + +#uploaded { + text-align: center; + float: left; + border: 1px solid #ccc; + width: 28%; +} + +#results { + margin-left: 32%; +} + +div.color { + height: 3em; + border: 1px solid white; + -moz-border-radius: 1em; +} + +span.color { + font-weight: bold; +} + +#youricon { + min-height: 200px; +} + +#youricon div { + text-align: center; +} + +.icon { + display: block; + width: 100px; + min-height: 120px; + background-color: black; + border: 1px solid #ccc; + -moz-border-radius: 1em; + text-align: center; + font-family: sans-serif; + padding: 10px; + margin: 0 auto; + color: white; +} + +.icon object { + width: 80px; + height: 80px; + overflow: hidden; +} + +.icon p { + margin: 10px auto; + font-size: 1.1em; +} + diff --git a/template.svg b/template.svg new file mode 100644 index 0000000..a9f3ebc --- /dev/null +++ b/template.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + -- cgit v1.2.3