Skip to content

Instantly share code, notes, and snippets.

@ahebrank
Last active November 29, 2018 16:08
Show Gist options
  • Select an option

  • Save ahebrank/f5be768df6eeee2a3208cb829e16b0c6 to your computer and use it in GitHub Desktop.

Select an option

Save ahebrank/f5be768df6eeee2a3208cb829e16b0c6 to your computer and use it in GitHub Desktop.
diff --git a/config/install/lazyloader.configuration.yml b/config/install/lazyloader.configuration.yml
index 3f58d71..0aacf83 100644
--- a/config/install/lazyloader.configuration.yml
+++ b/config/install/lazyloader.configuration.yml
@@ -2,3 +2,4 @@ enabled: TRUE
placeholder: ""
debugging: FALSE
cdn: FALSE
+use_traced_placeholder: FALSE
diff --git a/config/schema/lazyloader.schema.yml b/config/schema/lazyloader.schema.yml
index 6d4d315..5e56ab3 100644
--- a/config/schema/lazyloader.schema.yml
+++ b/config/schema/lazyloader.schema.yml
@@ -10,6 +10,8 @@ lazyloader.configuration:
type: boolean
cdn:
type: boolean
+ use_traced_placeholder:
+ type: boolean
lazyloader.exclude:
type: config_object
diff --git a/lazyloader.module b/lazyloader.module
index 1e7f5f1..24998ed 100644
--- a/lazyloader.module
+++ b/lazyloader.module
@@ -52,21 +52,12 @@ function lazyloader_preprocess_image(&$vars) {
}
}
- $image_placeholder_src = NULL;
-
- // Set placeholder image.
- if (!isset($image_placeholder_src)) {
- // Path to dummy placeholder image, to be replaced by actual image.
- $image_placeholder = trim($config->get('placeholder'));
- $image_placeholder_src = $image_placeholder ? base_path() . $image_placeholder : 'data:image/gif;base64,R0lGODlhAQABAIAAAP7//wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==';
- }
-
if (isset($vars['attributes']['class']) && is_string($vars['attributes']['class'])) {
$vars['attributes']['class'] = explode(" ", $vars['attributes']['class']);
}
$vars['attributes']['class'][] = 'lazyload';
- $vars['attributes']['src'] = file_create_url($image_placeholder_src);
+ $vars['attributes']['src'] = \Drupal::service('lazyloader.placeholder')->getPlaceholderSrc($uri);
if (isset($responsive_image)) {
$vars['attributes']['data-srcset'] = $responsive_image->__toString();
@@ -99,15 +90,11 @@ function lazyloader_preprocess_responsive_image(&$vars) {
if (!$checker->isValidFilename($uri) || !$checker->isValidImageStyle($uri)) {
continue;
}
-
- $image_attributes->addClass('lazyload');
-
- unset($vars['sources'][$key]['srcset']);
$vars['sources'][$key]['data-srcset'] = $responsive_image->__toString();
+ unset($vars['sources'][$key]['srcset']);
}
}
-
/**
* Implements hook_libraries_info().
*/
diff --git a/lazyloader.services.yml b/lazyloader.services.yml
index b8d998d..2a346ba 100644
--- a/lazyloader.services.yml
+++ b/lazyloader.services.yml
@@ -6,3 +6,7 @@ services:
lazyloader.preprocess:
class: \Drupal\lazyloader\ThemePreprocess
arguments: ['@config.factory']
+
+ lazyloader.placeholder:
+ class: \Drupal\lazyloader\Placeholder
+ arguments: ['@config.factory']
\ No newline at end of file
diff --git a/lib/Potracio.php b/lib/Potracio.php
new file mode 100644
index 0000000..28014b8
--- /dev/null
+++ b/lib/Potracio.php
@@ -0,0 +1,1258 @@
+<?php
+namespace Potracio;
+/* Potracio - Port by Otamay (2017) (https://github.com/Otamay/potracio.git)
+ * A PHP Port of Potrace (http://potrace.sourceforge.net),
+ * ported from https://github.com/kilobtye/potrace. Info below:
+ *
+ * Copyright (C) 2001-2013 Peter Selinger.
+ *
+ * A javascript port of Potrace (http://potrace.sourceforge.net).
+ *
+ * Licensed under the GPL
+ *
+ * Usage
+ * loadImageFromFile(file) : load image from File
+ *
+ * setParameter({para1: value, ...}) : set parameters
+ * parameters:
+ * turnpolicy ("black" / "white" / "left" / "right" / "minority" / "majority")
+ * how to resolve ambiguities in path decomposition. (default: "minority")
+ * turdsize
+ * suppress speckles of up to this size (default: 2)
+ * optcurve (true / false)
+ * turn on/off curve optimization (default: true)
+ * alphamax
+ * corner threshold parameter (default: 1)
+ * opttolerance
+ * curve optimization tolerance (default: 0.2)
+ *
+ * getSVG(size, opt_type) : return a string of generated SVG image.
+ * result_image_size = original_image_size * size
+ * optional parameter opt_type can be "curve"
+ */
+
+class Point{
+ public $x;
+ public $y;
+
+ public function __construct($x=NULL, $y=NULL) {
+ if($x !== NULL)
+ $this->x = $x;
+ if($y !== NULL)
+ $this->y = $y;
+ }
+}
+
+class Opti{
+ public $pen = 0;
+ public $c;
+ public $t = 0;
+ public $s = 0;
+ public $alpha = 0;
+
+ public function __construct(){
+ $this->c = array(new Point(), new Point());
+ }
+}
+
+class Bitmap{
+ public $w;
+ public $h;
+ public $size;
+ public $data;
+
+ public function __construct($w, $h){
+ $this->w = $w;
+ $this->h = $h;
+ $this->size = $w * $h;
+ $this->data = array();
+ }
+
+ public function at($x, $y) {
+ return ($x >= 0 && $x < $this->w && $y >=0 && $y < $this->h) &&
+ $this->data[$this->w * $y + $x] === 1;
+ }
+
+ public function index($i) {
+ $point = new Point();
+ $point->y = floor($i / $this->w);
+ $point->x = $i - $point->y * $this->w;
+ return $point;
+ }
+
+ public function flip($x, $y) {
+ if ($this->at($x, $y)) {
+ $this->data[$this->w * $y + $x] = 0;
+ } else {
+ $this->data[$this->w * $y + $x] = 1;
+ }
+ }
+}
+
+class Path{
+ public $area = 0;
+ public $len = 0;
+ public $curve = array();
+ public $pt = array();
+ public $minX = 100000;
+ public $minY = 100000;
+ public $maxX= -1;
+ public $maxY = -1;
+ public $sum = array();
+ public $lon = array();
+}
+
+class Curve{
+ public $n;
+ public $tag;
+ public $c;
+ public $alphaCurve = 0;
+ public $vertex;
+ public $alpha;
+ public $alpha0;
+ public $beta;
+
+ public function __construct($n){
+ $this->n = $n;
+ $this->tag = array_fill(0, $n, NULL);
+ $this->c = array_fill(0, $n * 3, NULL);
+ $this->vertex = array_fill(0, $n, NULL);
+ $this->alpha = array_fill(0, $n, NULL);
+ $this->alpha0 = array_fill(0, $n, NULL);
+ $this->beta = array_fill(0, $n, NULL);
+ }
+}
+
+class Quad{
+ public $data = array(0,0,0,0,0,0,0,0,0);
+
+ public function at($x, $y) {
+ return $this->data[$x * 3 + $y];
+ }
+}
+
+class Sum{
+ public $x;
+ public $y;
+ public $xy;
+ public $x2;
+ public $y2;
+
+ public function __construct($x, $y, $xy, $x2, $y2) {
+ $this->x = $x;
+ $this->y = $y;
+ $this->xy = $xy;
+ $this->x2 = $x2;
+ $this->y2 = $y2;
+ }
+}
+
+class Potracio{
+ public $imgElement;
+ public $imgCanvas;
+ public $bm = NULL;
+ public $pathlist = array();
+ public $info = array('turnpolicy' => "minority",
+ 'turdsize' => 2,
+ 'optcurve' => TRUE,
+ 'alphamax' => 1,
+ 'opttolerance' => 0.2);
+
+ public function __construct($data=array()){
+ $this->setParameter($data);
+ }
+
+ public function setParameter($data){
+ $this->info = (object) array_merge((array) $this->info, $data);
+ }
+
+ public function loadImageFromFile($file){
+ list($w, $h) = getimagesize($file);
+ $image = imagecreatefromstring(file_get_contents($file));
+
+ $this->bm = new Bitmap($w, $h);
+
+ for($i=0; $i<$h; $i++){
+ for($j=0; $j<$w; $j++){
+ $rgb = imagecolorat($image, $j, $i);
+ $r = ($rgb >> 16) & 0xFF;
+ $g = ($rgb >> 8) & 0xFF;
+ $b = $rgb & 0xFF;
+ $color = (0.2126 * $r) + (0.7153 * $g) + (0.0721 * $b);
+ $this->bm->data[] = $color < 128 ? 1 : 0;
+ }
+ }
+ }
+
+ private function bmToPathlist(){
+ $info = $this->info;
+ $bm = &$this->bm;
+ $bm1 = clone $bm;
+ $currentPoint = new Point(0, 0);
+
+ $findNext = function($point) use ($bm1) {
+ $i = $bm1->w * $point->y + $point->x;
+ while ($i < $bm1->size && $bm1->data[$i] !== 1) {
+ $i++;
+ }
+ if($i < $bm1->size)
+ return $bm1->index($i);
+ return false;
+ };
+
+ $majority = function($x, $y) use ($bm1) {
+ for ($i = 2; $i < 5; $i++) {
+ $ct = 0;
+ for ($a = -$i + 1; $a <= $i - 1; $a++) {
+ $ct += $bm1->at($x + $a, $y + $i - 1) ? 1 : -1;
+ $ct += $bm1->at($x + $i - 1, $y + $a - 1) ? 1 : -1;
+ $ct += $bm1->at($x + $a - 1, $y - $i) ? 1 : -1;
+ $ct += $bm1->at($x - $i, $y + $a) ? 1 : -1;
+ }
+ if ($ct > 0) {
+ return 1;
+ } else if ($ct < 0) {
+ return 0;
+ }
+ }
+ return 0;
+ };
+
+ $findPath = function($point) use($bm, $bm1, $majority, $info) {
+ $path = new Path();
+ $x = $point->x;
+ $y = $point->y;
+ $dirx = 0; $diry = 1;
+
+ $path->sign = $bm->at($point->x, $point->y) ? "+" : "-";
+
+ while (1) {
+ $path->pt[] = new Point($x, $y);
+ if ($x > $path->maxX)
+ $path->maxX = $x;
+ if ($x < $path->minX)
+ $path->minX = $x;
+ if ($y > $path->maxY)
+ $path->maxY = $y;
+ if ($y < $path->minY)
+ $path->minY = $y;
+ $path->len++;
+
+ $x += $dirx;
+ $y += $diry;
+ $path->area -= $x * $diry;
+
+ if ($x === $point->x && $y === $point->y)
+ break;
+
+ $l = $bm1->at($x + ($dirx + $diry - 1 ) / 2, $y + ($diry - $dirx - 1) / 2);
+ $r = $bm1->at($x + ($dirx - $diry - 1) / 2, $y + ($diry + $dirx - 1) / 2);
+
+ if ($r && !$l) {
+ if ($info->turnpolicy === "right" ||
+ ($info->turnpolicy === "black" && $path->sign === '+') ||
+ ($info->turnpolicy === "white" && $path->sign === '-') ||
+ ($info->turnpolicy === "majority" && $majority($x, $y)) ||
+ ($info->turnpolicy === "minority" && !$majority($x, $y))) {
+ $tmp = $dirx;
+ $dirx = - $diry;
+ $diry = $tmp;
+ } else {
+ $tmp = $dirx;
+ $dirx = $diry;
+ $diry = - $tmp;
+ }
+ } else if ($r) {
+ $tmp = $dirx;
+ $dirx = - $diry;
+ $diry = $tmp;
+ } else if (!$l) {
+ $tmp = $dirx;
+ $dirx = $diry;
+ $diry = - $tmp;
+ }
+ }
+ return $path;
+ };
+
+ $xorPath = function ($path) use(&$bm1){
+ $y1 = $path->pt[0]->y;
+ $len = $path->len;
+
+ for ($i = 1; $i < $len; $i++) {
+ $x = $path->pt[$i]->x;
+ $y = $path->pt[$i]->y;
+
+ if ($y !== $y1) {
+ $minY = $y1 < $y ? $y1 : $y;
+ $maxX = $path->maxX;
+ for ($j = $x; $j < $maxX; $j++) {
+ $bm1->flip($j, $minY);
+ }
+ $y1 = $y;
+ }
+ }
+ };
+
+ while ($currentPoint = $findNext($currentPoint)) {
+ $path = $findPath($currentPoint);
+
+ $xorPath($path);
+
+ if ($path->area > $info->turdsize) {
+ $this->pathlist[] = $path;
+ }
+ }
+ }
+
+ private function processPath() {
+ $info = $this->info;
+
+ $mod = function ($a, $n) {
+ return $a >= $n ? $a % $n : ($a>=0 ? $a : $n-1-(-1-$a) % $n);
+ };
+
+ $xprod = function ($p1, $p2) {
+ return $p1->x * $p2->y - $p1->y * $p2->x;
+ };
+
+ $cyclic = function ($a, $b, $c) {
+ if ($a <= $c) {
+ return ($a <= $b && $b < $c);
+ } else {
+ return ($a <= $b || $b < $c);
+ }
+ };
+
+ $sign = function ($i) {
+ return $i > 0 ? 1 : ($i < 0 ? -1 : 0);
+ };
+
+ $quadform = function ($Q, $w) {
+ $v = array_fill(0, 3, NULL);
+
+ $v[0] = $w->x;
+ $v[1] = $w->y;
+ $v[2] = 1;
+ $sum = 0.0;
+
+ for ($i=0; $i<3; $i++) {
+ for ($j=0; $j<3; $j++) {
+ $sum += $v[$i] * $Q->at($i, $j) * $v[$j];
+ }
+ }
+ return $sum;
+ };
+
+ $interval = function ($lambda, $a, $b) {
+ $res = new Point();
+
+ $res->x = $a->x + $lambda * ($b->x - $a->x);
+ $res->y = $a->y + $lambda * ($b->y - $a->y);
+ return $res;
+ };
+
+ $dorth_infty = function ($p0, $p2) use($sign) {
+ $r = new Point();
+
+ $r->y = $sign($p2->x - $p0->x);
+ $r->x = - $sign($p2->y - $p0->y);
+
+ return $r;
+ };
+
+ $ddenom = function ($p0, $p2) use ($dorth_infty){
+ $r = $dorth_infty($p0, $p2);
+
+ return $r->y * ($p2->x - $p0->x) - $r->x * ($p2->y - $p0->y);
+ };
+
+ $dpara = function ($p0, $p1, $p2) {
+ $x1 = $p1->x - $p0->x;
+ $y1 = $p1->y - $p0->y;
+ $x2 = $p2->x - $p0->x;
+ $y2 = $p2->y - $p0->y;
+
+ return $x1 * $y2 - $x2 * $y1;
+ };
+
+ $cprod = function ($p0, $p1, $p2, $p3) {
+ $x1 = $p1->x - $p0->x;
+ $y1 = $p1->y - $p0->y;
+ $x2 = $p3->x - $p2->x;
+ $y2 = $p3->y - $p2->y;
+
+ return $x1 * $y2 - $x2 * $y1;
+ };
+
+ $iprod = function ($p0, $p1, $p2) {
+ $x1 = $p1->x - $p0->x;
+ $y1 = $p1->y - $p0->y;
+ $x2 = $p2->x - $p0->x;
+ $y2 = $p2->y - $p0->y;
+
+ return $x1 * $x2 + $y1 * $y2;
+ };
+
+ $iprod1 = function ($p0, $p1, $p2, $p3) {
+ $x1 = $p1->x - $p0->x;
+ $y1 = $p1->y - $p0->y;
+ $x2 = $p3->x - $p2->x;
+ $y2 = $p3->y - $p2->y;
+
+ return $x1 * $x2 + $y1 * $y2;
+ };
+
+ $ddist = function ($p, $q) {
+ return sqrt(($p->x - $q->x) * ($p->x - $q->x) + ($p->y - $q->y) * ($p->y - $q->y));
+ };
+
+ $bezier = function ($t, $p0, $p1, $p2, $p3) {
+ $s = 1 - $t; $res = new Point();
+
+ $res->x = $s * $s * $s * $p0->x
+ + 3*($s * $s * $t) * $p1->x
+ + 3*($t * $t * $s) * $p2->x
+ + $t * $t * $t * $p3->x;
+
+ $res->y = $s * $s * $s * $p0->y
+ + 3*($s * $s * $t) * $p1->y
+ + 3*($t * $t * $s) * $p2->y
+ + $t * $t * $t * $p3->y;
+
+ return $res;
+ };
+
+ $tangent = function ($p0, $p1, $p2, $p3, $q0, $q1) use($cprod){
+ $A = $cprod($p0, $p1, $q0, $q1);
+ $B = $cprod($p1, $p2, $q0, $q1);
+ $C = $cprod($p2, $p3, $q0, $q1);
+ $a = $A - 2 * $B + $C;
+ $b = -2 * $A + 2 * $B;
+ $c = $A;
+
+ $d = $b * $b - 4 * $a * $c;
+
+ if ($a==0 || $d<0) {
+ return -1.0;
+ }
+
+ $s = sqrt($d);
+
+ if($a == 0){
+ return -1.0;
+ }
+ $r1 = (-$b + $s) / (2 * $a);
+ $r2 = (-$b - $s) / (2 * $a);
+
+ if ($r1 >= 0 && $r1 <= 1) {
+ return $r1;
+ } else if ($r2 >= 0 && $r2 <= 1) {
+ return $r2;
+ } else {
+ return -1.0;
+ }
+ };
+
+ $calcSums = function (&$path) {
+ $path->x0 = $path->pt[0]->x;
+ $path->y0 = $path->pt[0]->y;
+
+ $path->sums = array();
+ $s = &$path->sums;
+ $s[] = new Sum(0, 0, 0, 0, 0);
+ for($i = 0; $i < $path->len; $i++){
+ $x = $path->pt[$i]->x - $path->x0;
+ $y = $path->pt[$i]->y - $path->y0;
+ $s[] = new Sum($s[$i]->x + $x, $s[$i]->y + $y, $s[$i]->xy + $x * $y,
+ $s[$i]->x2 + $x * $x, $s[$i]->y2 + $y * $y);
+ }
+ };
+
+ $calcLon = function (&$path) use($mod, $xprod, $sign, $cyclic){
+ $n = $path->len; $pt = &$path->pt;
+ $pivk = array_fill(0, $n, NULL);
+ $nc = array_fill(0, $n, NULL);
+ $ct = array_fill(0, 4, NULL);
+ $path->lon = array_fill(0, $n, NULL);
+
+ $constraint = array(new Point(), new Point());
+ $cur = new Point();
+ $off = new Point();
+ $dk = new Point();
+
+ $k = 0;
+ for($i = $n - 1; $i >= 0; $i--){
+ if ($pt[$i]->x != $pt[$k]->x && $pt[$i]->y != $pt[$k]->y) {
+ $k = $i + 1;
+ }
+ $nc[$i] = $k;
+ }
+
+ for ($i = $n - 1; $i >= 0; $i--) {
+ $ct[0] = $ct[1] = $ct[2] = $ct[3] = 0;
+ $dir = (3 + 3 * ($pt[$mod($i + 1, $n)]->x - $pt[$i]->x) +
+ ($pt[$mod($i + 1, $n)]->y - $pt[$i]->y)) / 2;
+ $ct[$dir]++;
+
+ $constraint[0]->x = 0;
+ $constraint[0]->y = 0;
+ $constraint[1]->x = 0;
+ $constraint[1]->y = 0;
+
+ $k = $nc[$i];
+ $k1 = $i;
+ while (1) {
+ $foundk = 0;
+ $dir = (3 + 3 * $sign($pt[$k]->x - $pt[$k1]->x) +
+ $sign($pt[$k]->y - $pt[$k1]->y)) / 2;
+ $ct[$dir]++;
+
+ if ($ct[0] && $ct[1] && $ct[2] && $ct[3]) {
+ $pivk[$i] = $k1;
+ $foundk = 1;
+ break;
+ }
+
+ $cur->x = $pt[$k]->x - $pt[$i]->x;
+ $cur->y = $pt[$k]->y - $pt[$i]->y;
+
+ if ($xprod($constraint[0], $cur) < 0 || $xprod($constraint[1], $cur) > 0) {
+ break;
+ }
+
+ if (abs($cur->x) <= 1 && abs($cur->y) <= 1) {
+
+ } else {
+ $off->x = $cur->x + (($cur->y >= 0 && ($cur->y > 0 || $cur->x < 0)) ? 1 : -1);
+ $off->y = $cur->y + (($cur->x <= 0 && ($cur->x < 0 || $cur->y < 0)) ? 1 : -1);
+ if ($xprod($constraint[0], $off) >= 0) {
+ $constraint[0]->x = $off->x;
+ $constraint[0]->y = $off->y;
+ }
+ $off->x = $cur->x + (($cur->y <= 0 && ($cur->y < 0 || $cur->x < 0)) ? 1 : -1);
+ $off->y = $cur->y + (($cur->x >= 0 && ($cur->x > 0 || $cur->y < 0)) ? 1 : -1);
+ if ($xprod($constraint[1], $off) <= 0) {
+ $constraint[1]->x = $off->x;
+ $constraint[1]->y = $off->y;
+ }
+ }
+ $k1 = $k;
+ $k = $nc[$k1];
+ if (!$cyclic($k, $i, $k1)) {
+ break;
+ }
+ }
+ if ($foundk == 0) {
+ $dk->x = $sign($pt[$k]->x - $pt[$k1]->x);
+ $dk->y = $sign($pt[$k]->y - $pt[$k1]->y);
+ $cur->x = $pt[$k1]->x - $pt[$i]->x;
+ $cur->y = $pt[$k1]->y - $pt[$i]->y;
+
+ $a = $xprod($constraint[0], $cur);
+ $b = $xprod($constraint[0], $dk);
+ $c = $xprod($constraint[1], $cur);
+ $d = $xprod($constraint[1], $dk);
+
+ $j = 10000000;
+ if ($b < 0) {
+ $j = floor($a / -$b);
+ }
+ if ($d > 0) {
+ $j = min($j, floor(-$c / $d));
+ }
+ $pivk[$i] = $mod($k1+$j,$n);
+ }
+ }
+
+ $j=$pivk[$n-1];
+ $path->lon[$n-1]=$j;
+ for ($i=$n-2; $i>=0; $i--) {
+ if ($cyclic($i+1,$pivk[$i],$j)) {
+ $j=$pivk[$i];
+ }
+ $path->lon[$i]=$j;
+ }
+
+ for ($i=$n-1; $cyclic($mod($i+1,$n),$j,$path->lon[$i]); $i--) {
+ $path->lon[$i] = $j;
+ }
+ };
+
+ $bestPolygon = function (&$path) use($mod){
+
+ $penalty3 = function ($path, $i, $j) {
+ $n = $path->len; $pt = $path->pt; $sums = $path->sums;
+ $r = 0;
+ if ($j>=$n) {
+ $j -= $n;
+ $r = 1;
+ }
+
+ if ($r == 0) {
+ $x = $sums[$j+1]->x - $sums[$i]->x;
+ $y = $sums[$j+1]->y - $sums[$i]->y;
+ $x2 = $sums[$j+1]->x2 - $sums[$i]->x2;
+ $xy = $sums[$j+1]->xy - $sums[$i]->xy;
+ $y2 = $sums[$j+1]->y2 - $sums[$i]->y2;
+ $k = $j+1 - $i;
+ } else {
+ $x = $sums[$j+1]->x - $sums[$i]->x + $sums[$n]->x;
+ $y = $sums[$j+1]->y - $sums[$i]->y + $sums[$n]->y;
+ $x2 = $sums[$j+1]->x2 - $sums[$i]->x2 + $sums[$n]->x2;
+ $xy = $sums[$j+1]->xy - $sums[$i]->xy + $sums[$n]->xy;
+ $y2 = $sums[$j+1]->y2 - $sums[$i]->y2 + $sums[$n]->y2;
+ $k = $j+1 - $i + $n;
+ }
+
+ $px = ($pt[$i]->x + $pt[$j]->x) / 2.0 - $pt[0]->x;
+ $py = ($pt[$i]->y + $pt[$j]->y) / 2.0 - $pt[0]->y;
+ $ey = ($pt[$j]->x - $pt[$i]->x);
+ $ex = -($pt[$j]->y - $pt[$i]->y);
+
+ $a = (($x2 - 2*$x*$px) / $k + $px*$px);
+ $b = (($xy - $x*$py - $y*$px) / $k + $px*$py);
+ $c = (($y2 - 2*$y*$py) / $k + $py*$py);
+
+ $s = $ex*$ex*$a + 2*$ex*$ey*$b + $ey*$ey*$c;
+
+ return sqrt($s);
+ };
+
+ $n = $path->len;
+ $pen = array_fill(0, $n + 1, NULL);
+ $prev = array_fill(0, $n + 1, NULL);
+ $clip0 = array_fill(0, $n, NULL);
+ $clip1 = array_fill(0, $n + 1, NULL);
+ $seg0 = array_fill(0, $n + 1, NULL);
+ $seg1 = array_fill(0, $n + 1, NULL);
+
+ for ($i=0; $i<$n; $i++) {
+ $c = $mod($path->lon[$mod($i-1,$n)]-1,$n);
+ if ($c == $i) {
+ $c = $mod($i+1,$n);
+ }
+ if ($c < $i) {
+ $clip0[$i] = $n;
+ } else {
+ $clip0[$i] = $c;
+ }
+ }
+
+ $j = 1;
+ for ($i=0; $i<$n; $i++) {
+ while ($j <= $clip0[$i]) {
+ $clip1[$j] = $i;
+ $j++;
+ }
+ }
+
+ $i = 0;
+ for ($j=0; $i<$n; $j++) {
+ $seg0[$j] = $i;
+ $i = $clip0[$i];
+ }
+ $seg0[$j] = $n;
+ $m = $j;
+
+ $i = $n;
+ for ($j=$m; $j>0; $j--) {
+ $seg1[$j] = $i;
+ $i = $clip1[$i];
+ }
+ $seg1[0] = 0;
+
+ $pen[0]=0;
+ for ($j=1; $j<=$m; $j++) {
+ for ($i=$seg1[$j]; $i<=$seg0[$j]; $i++) {
+ $best = -1;
+ for ($k=$seg0[$j-1]; $k>=$clip1[$i]; $k--) {
+ $thispen = $penalty3($path, $k, $i) + $pen[$k];
+ if ($best < 0 || $thispen < $best) {
+ $prev[$i] = $k;
+ $best = $thispen;
+ }
+ }
+ $pen[$i] = $best;
+ }
+ }
+ $path->m = $m;
+ $path->po = array_fill(0, $m, NULL);
+
+ for ($i=$n, $j=$m-1; $i>0; $j--) {
+ $i = $prev[$i];
+ $path->po[$j] = $i;
+ }
+ };
+
+ $adjustVertices = function (&$path) use($mod, $quadform){
+
+ $pointslope = function ($path, $i, $j, &$ctr, &$dir) {
+
+ $n = $path->len; $sums = $path->sums;
+ $r=0;
+
+ while ($j>=$n) {
+ $j-=$n;
+ $r+=1;
+ }
+ while ($i>=$n) {
+ $i-=$n;
+ $r-=1;
+ }
+ while ($j<0) {
+ $j+=$n;
+ $r-=1;
+ }
+ while ($i<0) {
+ $i+=$n;
+ $r+=1;
+ }
+
+ $x = $sums[$j+1]->x - $sums[$i]->x + $r * $sums[$n]->x;
+ $y = $sums[$j+1]->y - $sums[$i]->y + $r * $sums[$n]->y;
+ $x2 = $sums[$j+1]->x2 - $sums[$i]->x2 + $r * $sums[$n]->x2;
+ $xy = $sums[$j+1]->xy - $sums[$i]->xy + $r * $sums[$n]->xy;
+ $y2 = $sums[$j+1]->y2 - $sums[$i]->y2 + $r * $sums[$n]->y2;
+ $k = $j+1-$i+$r*$n;
+
+ $ctr->x = $x/$k;
+ $ctr->y = $y/$k;
+
+ $a = ($x2-$x*$x/$k)/$k;
+ $b = ($xy-$x*$y/$k)/$k;
+ $c = ($y2-$y*$y/$k)/$k;
+
+ $lambda2 = ($a + $c+ sqrt(($a - $c)*($a - $c) + 4 * $b * $b))/2;
+
+ $a -= $lambda2;
+ $c -= $lambda2;
+
+ if (abs($a) >= abs($c)) {
+ $l = sqrt($a*$a+$b*$b);
+ if ($l!=0) {
+ $dir->x = -$b/$l;
+ $dir->y = $a/$l;
+ }
+ } else {
+ $l = sqrt($c*$c+$b*$b);
+ if ($l!==0) {
+ $dir->x = -$c/$l;
+ $dir->y = $b/$l;
+ }
+ }
+ if ($l==0) {
+ $dir->x = $dir->y = 0;
+ }
+ };
+
+ $m = $path->m; $po = $path->po; $n = $path->len; $pt = $path->pt;
+ $x0 = $path->x0; $y0 = $path->y0;
+ $ctr = array_fill(0, $m, NULL); $dir = array_fill(0, $m, NULL);
+ $q = array_fill(0, $m, NULL);
+ $v = array_fill(0, 3, NULL);
+ $s = new Point();
+
+ $path->curve = new Curve($m);
+
+ for ($i=0; $i<$m; $i++) {
+ $j = $po[$mod($i+1,$m)];
+ $j = $mod($j-$po[$i],$n)+$po[$i];
+ $ctr[$i] = new Point();
+ $dir[$i] = new Point();
+ $pointslope($path, $po[$i], $j, $ctr[$i], $dir[$i]);
+ }
+
+ for ($i=0; $i<$m; $i++) {
+ $q[$i] = new Quad();
+ $d = $dir[$i]->x * $dir[$i]->x + $dir[$i]->y * $dir[$i]->y;
+ if ($d == 0.0) {
+ for ($j=0; $j<3; $j++) {
+ for ($k=0; $k<3; $k++) {
+ $q[$i]->data[$j * 3 + $k] = 0;
+ }
+ }
+ } else {
+ $v[0] = $dir[$i]->y;
+ $v[1] = -$dir[$i]->x;
+ $v[2] = - $v[1] * $ctr[$i]->y - $v[0] * $ctr[$i]->x;
+ for ($l=0; $l<3; $l++) {
+ for ($k=0; $k<3; $k++) {
+ if($d != 0){
+ $q[$i]->data[$l * 3 + $k] = $v[$l] * $v[$k] / $d;
+ }else{
+ $q[$i]->data[$l * 3 + $k] = INF; // TODO Hack para evitar división por 0
+ }
+ }
+ }
+ }
+ }
+
+ for ($i=0; $i<$m; $i++) {
+ $Q = new Quad();
+ $w = new Point();
+
+ $s->x = $pt[$po[$i]]->x - $x0;
+ $s->y = $pt[$po[$i]]->y - $y0;
+
+ $j = $mod($i-1,$m);
+
+ for ($l=0; $l<3; $l++) {
+ for ($k=0; $k<3; $k++) {
+ $Q->data[$l * 3 + $k] = $q[$j]->at($l, $k) + $q[$i]->at($l, $k);
+ }
+ }
+
+ while(1) {
+
+ $det = $Q->at(0, 0)*$Q->at(1, 1) - $Q->at(0, 1)*$Q->at(1, 0);
+ if ($det != 0) {
+ $w->x = (-$Q->at(0, 2)*$Q->at(1, 1) + $Q->at(1, 2)*$Q->at(0, 1)) / $det;
+ $w->y = ( $Q->at(0, 2)*$Q->at(1, 0) - $Q->at(1, 2)*$Q->at(0, 0)) / $det;
+ break;
+ }
+
+ if ($Q->at(0, 0)>$Q->at(1, 1)) {
+ $v[0] = -$Q->at(0, 1);
+ $v[1] = $Q->at(0, 0);
+ } else if ($Q->at(1, 1)) {
+ $v[0] = -$Q->at(1, 1);
+ $v[1] = $Q->at(1, 0);
+ } else {
+ $v[0] = 1;
+ $v[1] = 0;
+ }
+ $d = $v[0] * $v[0] + $v[1] * $v[1];
+ $v[2] = - $v[1] * $s->y - $v[0] * $s->x;
+ for ($l=0; $l<3; $l++) {
+ for ($k=0; $k<3; $k++) {
+ $Q->data[$l * 3 + $k] += $v[$l] * $v[$k] / $d;
+ }
+ }
+ }
+ $dx = abs($w->x-$s->x);
+ $dy = abs($w->y-$s->y);
+ if ($dx <= 0.5 && $dy <= 0.5) {
+ $path->curve->vertex[$i] = new Point($w->x+$x0, $w->y+$y0);
+ continue;
+ }
+
+ $min = $quadform($Q, $s);
+ $xmin = $s->x;
+ $ymin = $s->y;
+
+ if ($Q->at(0, 0) != 0.0) {
+ for ($z=0; $z<2; $z++) {
+ $w->y = $s->y-0.5+$z;
+ $w->x = - ($Q->at(0, 1) * $w->y + $Q->at(0, 2)) / $Q->at(0, 0);
+ $dx = abs($w->x-$s->x);
+ $cand = $quadform($Q, $w);
+ if ($dx <= 0.5 && $cand < $min) {
+ $min = $cand;
+ $xmin = $w->x;
+ $ymin = $w->y;
+ }
+ }
+ }
+
+ if ($Q->at(1, 1) != 0.0) {
+ for ($z=0; $z<2; $z++) {
+ $w->x = $s->x-0.5+$z;
+ $w->y = - ($Q->at(1, 0) * $w->x + $Q->at(1, 2)) / $Q->at(1, 1);
+ $dy = abs($w->y-$s->y);
+ $cand = $quadform($Q, $w);
+ if ($dy <= 0.5 && $cand < $min) {
+ $min = $cand;
+ $xmin = $w->x;
+ $ymin = $w->y;
+ }
+ }
+ }
+
+ for ($l=0; $l<2; $l++) {
+ for ($k=0; $k<2; $k++) {
+ $w->x = $s->x-0.5+$l;
+ $w->y = $s->y-0.5+$k;
+ $cand = $quadform($Q, $w);
+ if ($cand < $min) {
+ $min = $cand;
+ $xmin = $w->x;
+ $ymin = $w->y;
+ }
+ }
+ }
+
+ $path->curve->vertex[$i] = new Point($xmin + $x0, $ymin + $y0);
+ }
+ };
+
+ $reverse = function (&$path) {
+ $curve = &$path->curve; $m = &$curve->n; $v = &$curve->vertex;
+
+ for ($i=0, $j=$m-1; $i<$j; $i++, $j--) {
+ $tmp = $v[$i];
+ $v[$i] = $v[$j];
+ $v[$j] = $tmp;
+ }
+ };
+
+ $smooth = function (&$path) use($mod, $interval, $ddenom, $dpara, $info){
+ $m = $path->curve->n; $curve = &$path->curve;
+
+ for ($i=0; $i<$m; $i++) {
+ $j = $mod($i+1, $m);
+ $k = $mod($i+2, $m);
+ $p4 = $interval(1/2.0, $curve->vertex[$k], $curve->vertex[$j]);
+
+ $denom = $ddenom($curve->vertex[$i], $curve->vertex[$k]);
+ if ($denom != 0.0) {
+ $dd = $dpara($curve->vertex[$i], $curve->vertex[$j], $curve->vertex[$k]) / $denom;
+ $dd = abs($dd);
+ $alpha = $dd>1 ? (1 - 1.0/$dd) : 0;
+ $alpha = $alpha / 0.75;
+ } else {
+ $alpha = 4/3.0;
+ }
+ $curve->alpha0[$j] = $alpha;
+
+ if ($alpha >= $info->alphamax) {
+ $curve->tag[$j] = "CORNER";
+ $curve->c[3 * $j + 1] = $curve->vertex[$j];
+ $curve->c[3 * $j + 2] = $p4;
+ } else {
+ if ($alpha < 0.55) {
+ $alpha = 0.55;
+ } else if ($alpha > 1) {
+ $alpha = 1;
+ }
+ $p2 = $interval(0.5+0.5*$alpha, $curve->vertex[$i], $curve->vertex[$j]);
+ $p3 = $interval(0.5+0.5*$alpha, $curve->vertex[$k], $curve->vertex[$j]);
+ $curve->tag[$j] = "CURVE";
+ $curve->c[3 * $j + 0] = $p2;
+ $curve->c[3 * $j + 1] = $p3;
+ $curve->c[3 * $j + 2] = $p4;
+ }
+ $curve->alpha[$j] = $alpha;
+ $curve->beta[$j] = 0.5;
+ }
+ $curve->alphacurve = 1;
+ };
+
+ $optiCurve = function (&$path) use($mod, $ddist, $sign, $cprod, $dpara, $interval, $tangent, $bezier, $iprod, $iprod1, $info){
+ $opti_penalty = function ($path, $i, $j, $res, $opttolerance, $convc, $areac) use($mod, $ddist, $sign, $cprod, $dpara, $interval, $tangent, $bezier, $iprod, $iprod1){
+ $m = $path->curve->n; $curve = $path->curve; $vertex = $curve->vertex;
+ if ($i==$j) {
+ return 1;
+ }
+
+ $k = $i;
+ $i1 = $mod($i+1, $m);
+ $k1 = $mod($k+1, $m);
+ $conv = $convc[$k1];
+ if ($conv == 0) {
+ return 1;
+ }
+ $d = $ddist($vertex[$i], $vertex[$i1]);
+ for ($k=$k1; $k!=$j; $k=$k1) {
+ $k1 = $mod($k+1, $m);
+ $k2 = $mod($k+2, $m);
+ if ($convc[$k1] != $conv) {
+ return 1;
+ }
+ if ($sign($cprod($vertex[$i], $vertex[$i1], $vertex[$k1], $vertex[$k2])) !=
+ $conv) {
+ return 1;
+ }
+ if ($iprod1($vertex[$i], $vertex[$i1], $vertex[$k1], $vertex[$k2]) <
+ $d * $ddist($vertex[$k1], $vertex[$k2]) * -0.999847695156) {
+ return 1;
+ }
+ }
+
+ $p0 = clone $curve->c[$mod($i,$m) * 3 + 2];
+ $p1 = clone $vertex[$mod($i+1,$m)];
+ $p2 = clone $vertex[$mod($j,$m)];
+ $p3 = clone $curve->c[$mod($j,$m) * 3 + 2];
+
+ $area = $areac[$j] - $areac[$i];
+ $area -= $dpara($vertex[0], $curve->c[$i * 3 + 2], $curve->c[$j * 3 + 2])/2;
+ if ($i>=$j) {
+ $area += $areac[$m];
+ }
+
+ $A1 = $dpara($p0, $p1, $p2);
+ $A2 = $dpara($p0, $p1, $p3);
+ $A3 = $dpara($p0, $p2, $p3);
+
+ $A4 = $A1+$A3-$A2;
+
+ if ($A2 == $A1) {
+ return 1;
+ }
+
+ $t = $A3/($A3-$A4);
+ $s = $A2/($A2-$A1);
+ $A = $A2 * $t / 2.0;
+
+ if ($A == 0.0) {
+ return 1;
+ }
+
+ $R = $area / $A;
+ $alpha = 2 - sqrt(4 - $R / 0.3);
+
+ $res->c[0] = $interval($t * $alpha, $p0, $p1);
+ $res->c[1] = $interval($s * $alpha, $p3, $p2);
+ $res->alpha = $alpha;
+ $res->t = $t;
+ $res->s = $s;
+
+ $p1 = clone $res->c[0];
+ $p2 = clone $res->c[1];
+
+ $res->pen = 0;
+
+ for ($k=$mod($i+1,$m); $k!=$j; $k=$k1) {
+ $k1 = $mod($k+1,$m);
+ $t = $tangent($p0, $p1, $p2, $p3, $vertex[$k], $vertex[$k1]);
+ if ($t<-0.5) {
+ return 1;
+ }
+ $pt = $bezier($t, $p0, $p1, $p2, $p3);
+ $d = $ddist($vertex[$k], $vertex[$k1]);
+ if ($d == 0.0) {
+ return 1;
+ }
+ $d1 = $dpara($vertex[$k], $vertex[$k1], $pt) / $d;
+ if (abs($d1) > $opttolerance) {
+ return 1;
+ }
+ if ($iprod($vertex[$k], $vertex[$k1], $pt) < 0 ||
+ $iprod($vertex[$k1], $vertex[$k], $pt) < 0) {
+ return 1;
+ }
+ $res->pen += $d1 * $d1;
+ }
+
+ for ($k=$i; $k!=$j; $k=$k1) {
+ $k1 = $mod($k+1,$m);
+ $t = $tangent($p0, $p1, $p2, $p3, $curve->c[$k * 3 + 2], $curve->c[$k1 * 3 + 2]);
+ if ($t<-0.5) {
+ return 1;
+ }
+ $pt = $bezier($t, $p0, $p1, $p2, $p3);
+ $d = $ddist($curve->c[$k * 3 + 2], $curve->c[$k1 * 3 + 2]);
+ if ($d == 0.0) {
+ return 1;
+ }
+ $d1 = $dpara($curve->c[$k * 3 + 2], $curve->c[$k1 * 3 + 2], $pt) / $d;
+ $d2 = $dpara($curve->c[$k * 3 + 2], $curve->c[$k1 * 3 + 2], $vertex[$k1]) / $d;
+ $d2 *= 0.75 * $curve->alpha[$k1];
+ if ($d2 < 0) {
+ $d1 = -$d1;
+ $d2 = -$d2;
+ }
+ if ($d1 < $d2 - $opttolerance) {
+ return 1;
+ }
+ if ($d1 < $d2) {
+ $res->pen += ($d1 - $d2) * ($d1 - $d2);
+ }
+ }
+
+ return 0;
+ };
+
+ $curve = $path->curve; $m = $curve->n; $vert = $curve->vertex;
+ $pt = array_fill(0, $m + 1, NULL);
+ $pen = array_fill(0, $m + 1, NULL);
+ $len = array_fill(0, $m + 1, NULL);
+ $opt = array_fill(0, $m + 1, NULL);
+ $o = new Opti();
+
+ $convc = array_fill(0, $m, NULL); $areac = array_fill(0, $m + 1, NULL);
+
+ for ($i=0; $i<$m; $i++) {
+ if ($curve->tag[$i] == "CURVE") {
+ $convc[$i] = $sign($dpara($vert[$mod($i-1,$m)], $vert[$i], $vert[$mod($i+1,$m)]));
+ } else {
+ $convc[$i] = 0;
+ }
+ }
+
+ $area = 0.0;
+ $areac[0] = 0.0;
+ $p0 = $curve->vertex[0];
+ for ($i=0; $i<$m; $i++) {
+ $i1 = $mod($i+1, $m);
+ if ($curve->tag[$i1] == "CURVE") {
+ $alpha = $curve->alpha[$i1];
+ $area += 0.3 * $alpha * (4-$alpha) *
+ $dpara($curve->c[$i * 3 + 2], $vert[$i1], $curve->c[$i1 * 3 + 2])/2;
+ $area += $dpara($p0, $curve->c[$i * 3 + 2], $curve->c[$i1 * 3 + 2])/2;
+ }
+ $areac[$i+1] = $area;
+ }
+
+ $pt[0] = -1;
+ $pen[0] = 0;
+ $len[0] = 0;
+
+
+ for ($j=1; $j<=$m; $j++) {
+ $pt[$j] = $j-1;
+ $pen[$j] = $pen[$j-1];
+ $len[$j] = $len[$j-1]+1;
+
+ for ($i=$j-2; $i>=0; $i--) {
+ $r = $opti_penalty($path, $i, $mod($j,$m), $o, $info->opttolerance, $convc,
+ $areac);
+ if ($r) {
+ break;
+ }
+ if ($len[$j] > $len[$i]+1 ||
+ ($len[$j] == $len[$i]+1 && $pen[$j] > $pen[$i] + $o->pen)) {
+ $pt[$j] = $i;
+ $pen[$j] = $pen[$i] + $o->pen;
+ $len[$j] = $len[$i] + 1;
+ $opt[$j] = $o;
+ $o = new Opti();
+ }
+ }
+ }
+ $om = $len[$m];
+ $ocurve = new Curve($om);
+ $s = array_fill(0, $om, NULL);
+ $t = array_fill(0, $om, NULL);
+
+ $j = $m;
+ for ($i=$om-1; $i>=0; $i--) {
+ if ($pt[$j]==$j-1) {
+ $ocurve->tag[$i] = $curve->tag[$mod($j,$m)];
+ $ocurve->c[$i * 3 + 0] = $curve->c[$mod($j,$m) * 3 + 0];
+ $ocurve->c[$i * 3 + 1] = $curve->c[$mod($j,$m) * 3 + 1];
+ $ocurve->c[$i * 3 + 2] = $curve->c[$mod($j,$m) * 3 + 2];
+ $ocurve->vertex[$i] = $curve->vertex[$mod($j,$m)];
+ $ocurve->alpha[$i] = $curve->alpha[$mod($j,$m)];
+ $ocurve->alpha0[$i] = $curve->alpha0[$mod($j,$m)];
+ $ocurve->beta[$i] = $curve->beta[$mod($j,$m)];
+ $s[$i] = $t[$i] = 1.0;
+ } else {
+ $ocurve->tag[$i] = "CURVE";
+ $ocurve->c[$i * 3 + 0] = $opt[$j]->c[0];
+ $ocurve->c[$i * 3 + 1] = $opt[$j]->c[1];
+ $ocurve->c[$i * 3 + 2] = $curve->c[$mod($j,$m) * 3 + 2];
+ $ocurve->vertex[$i] = $interval($opt[$j]->s, $curve->c[$mod($j,$m) * 3 + 2],
+ $vert[$mod($j,$m)]);
+ $ocurve->alpha[$i] = $opt[$j]->alpha;
+ $ocurve->alpha0[$i] = $opt[$j]->alpha;
+ $s[$i] = $opt[$j]->s;
+ $t[$i] = $opt[$j]->t;
+ }
+ $j = $pt[$j];
+ }
+
+ for ($i=0; $i<$om; $i++) {
+ $i1 = $mod($i+1,$om);
+ if(($s[$i] + $t[$i1]) != 0){
+ $ocurve->beta[$i] = $s[$i] / ($s[$i] + $t[$i1]);
+ }else{
+ $ocurve->beta[$i] = INF; // TODO Hack para evitar división por 0
+ }
+ }
+ $ocurve->alphacurve = 1;
+ $path->curve = $ocurve;
+ };
+
+ for ($i = 0; $i < count($this->pathlist); $i++) {
+ $path = &$this->pathlist[$i];
+ $calcSums($path);
+ $calcLon($path);
+ $bestPolygon($path);
+ $adjustVertices($path);
+
+ if ($path->sign === "-") {
+ $reverse($path);
+ }
+
+ $smooth($path);
+
+ if ($info->optcurve) {
+ $optiCurve($path);
+ }
+ }
+ }
+
+ public function process() {
+ $this->bmToPathlist();
+ $this->processPath();
+ }
+
+ public function clear() {
+ $this->bm = null;
+ $this->pathlist = array();
+ }
+
+ public function getSVG($size, $opt_type='') {
+ $bm = &$this->bm;
+ $pathlist = &$this->pathlist;
+ $path = function($curve) use($size) {
+
+ $bezier = function($i) use($curve, $size) {
+ $b = 'C ' . number_format($curve->c[$i * 3 + 0]->x * $size, 3, ".", "") . ' ' .
+ number_format($curve->c[$i * 3 + 0]->y * $size, 3, ".", "") . ',';
+ $b .= number_format($curve->c[$i * 3 + 1]->x * $size, 3, ".", "") . ' ' .
+ number_format($curve->c[$i * 3 + 1]->y * $size, 3, ".", "") . ',';
+ $b .= number_format($curve->c[$i * 3 + 2]->x * $size, 3, ".", "") . ' ' .
+ number_format($curve->c[$i * 3 + 2]->y * $size, 3, ".", "") . ' ';
+ return $b;
+ };
+
+ $segment = function($i) use ($curve, $size) {
+ $s = 'L ' . number_format($curve->c[$i * 3 + 1]->x * $size, 3, ".", "") . ' ' .
+ number_format($curve->c[$i * 3 + 1]->y * $size, 3, ".", "") . ' ';
+ $s .= number_format($curve->c[$i * 3 + 2]->x * $size, 3, ".", "") . ' ' .
+ number_format($curve->c[$i * 3 + 2]->y * $size, 3, ".", "") . ' ';
+ return $s;
+ };
+
+ $n = $curve->n;
+ $p = 'M' . number_format($curve->c[($n - 1) * 3 + 2]->x * $size, 3, ".", "") .
+ ' ' . number_format($curve->c[($n - 1) * 3 + 2]->y * $size, 3, ".", "") . ' ';
+
+ for ($i = 0; $i < $n; $i++) {
+ if ($curve->tag[$i] === "CURVE") {
+ $p .= $bezier($i);
+ } else if ($curve->tag[$i] === "CORNER") {
+ $p .= $segment($i);
+ }
+ }
+ //p +=
+ return $p;
+ };
+
+ $w = $bm->w * $size; $h = $bm->h * $size;
+ $len = count($pathlist);
+
+ $svg = '<svg id="svg" version="1.1" width="' . $w . '" height="' . $h .
+ '" xmlns="http://www.w3.org/2000/svg">';
+ $svg .= '<path d="';
+ for ($i = 0; $i < $len; $i++) {
+ $c = $pathlist[$i]->curve;
+ $svg .= $path($c);
+ }
+ if ($opt_type === "curve") {
+ $strokec = "black";
+ $fillc = "none";
+ $fillrule = '';
+ } else {
+ $strokec = "none";
+ $fillc = "black";
+ $fillrule = ' fill-rule="evenodd"';
+ }
+ $svg .= '" stroke="' . $strokec . '" fill="' . $fillc . '"' . $fillrule . '/></svg>';
+
+ return $svg;
+ }
+}
+?>
diff --git a/src/Form/AdminConfigure.php b/src/Form/AdminConfigure.php
index 3369998..21e2430 100644
--- a/src/Form/AdminConfigure.php
+++ b/src/Form/AdminConfigure.php
@@ -103,6 +103,13 @@ class AdminConfigure extends ConfigFormBase {
'#description' => $this->t('Serve the lazyloading script from a CDN instead of your own server'),
];
+ $form['use_traced_placeholder'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Use traced placeholder'),
+ '#default_value' => $config->get('use_traced_placeholder'),
+ '#description' => $this->t('Dynamically create SVG placeholders for each image. There will be a performance penalty on first load.'),
+ ];
+
$form['placeholder'] = [
'#type' => 'textfield',
'#title' => $this->t('Placeholder Image'),
diff --git a/src/Placeholder.php b/src/Placeholder.php
new file mode 100644
index 0000000..36f01f6
--- /dev/null
+++ b/src/Placeholder.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace Drupal\lazyloader;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+
+/**
+ * Support tracing placeholder creation.
+ */
+class Placeholder {
+
+ protected $cacheFolder = 'lazyloader_placeholder';
+
+ /**
+ * Config service.
+ *
+ * @var \Drupal\Core\Config\ConfigFactoryInterface
+ */
+ protected $configFactory;
+
+ /**
+ * Creates a new ThemePreprocess instance.
+ *
+ * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+ * The config factory.
+ */
+ public function __construct(ConfigFactoryInterface $config_factory) {
+ $this->configFactory = $config_factory;
+ }
+
+ /**
+ * Make file contents suitable for img src attribute.
+ */
+ protected function wrapImageSrc($uri) {
+ return file_create_url($uri);
+ }
+
+ /**
+ * Create an SVG placeholder.
+ */
+ protected function getSvg($image_path) {
+ // All this relies on GD being present.
+ if (!\function_exists('imagecreatefromjpeg')) {
+ return NULL;
+ }
+
+ $t = new Tracer($image_path);
+ return $t->getSvg();
+ }
+
+ /**
+ * Make a hash for key.
+ */
+ protected function getKey($path) {
+ return md5($path);
+ }
+
+ /**
+ * Get a URI for the cached image.
+ */
+ protected function getUri($path = NULL) {
+ $output = 'public://' . $this->cacheFolder;
+ if ($path) {
+ $output .= '/' . $this->getKey($path) . '.svg';
+ }
+ return $output;
+ }
+
+ /**
+ * Cache the image, saving it to a public:// location.
+ */
+ protected function cachePlaceholder($path, $svg) {
+ $cache_uri = $this->getUri();
+ if (file_prepare_directory($cache_uri, FILE_CREATE_DIRECTORY)) {
+ $uri = $this->getUri($path);
+ if (file_unmanaged_save_data($svg, $uri, FILE_EXISTS_REPLACE)) {
+ return $uri;
+ }
+ }
+ return FALSE;
+ }
+
+ /**
+ * Attempt to retrieve a cached image.
+ */
+ protected function getCachedPlaceholder($path) {
+ $uri = $this->getUri($path);
+ if (file_exists($uri)) {
+ return $uri;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Wrap traced placeholder creation / retrieval.
+ */
+ public function getTracedPlaceholder($uri) {
+ $parsed = parse_url($uri);
+ if (isset($parsed['path'])) {
+ $real_path = DRUPAL_ROOT . $parsed['path'];
+ if (file_exists($real_path)) {
+ if (!$file = $this->getCachedPlaceholder($real_path)) {
+ $svg = $this->getSvg($real_path);
+ $file = $this->cachePlaceholder($real_path, $svg);
+ }
+ return $this->wrapImageSrc($file);
+ }
+ }
+ return NULL;
+ }
+
+ /**
+ * Wrap all placeholder functions.
+ */
+ public function getPlaceholderSrc($uri = NULL) {
+ $config = $this->configFactory->get('lazyloader.configuration');
+
+ $image_placeholder_src = NULL;
+
+ // Create placeholder image with Potracio.
+ if (!$image_placeholder_src && $uri && $config->get('use_traced_placeholder')) {
+ $image_placeholder_src = $this->getTracedPlaceholder($uri);
+ }
+
+ // Set placeholder image from default config.
+ if (!$image_placeholder_src && $image_placeholder = trim($config->get('placeholder'))) {
+ // Path to dummy placeholder image, to be replaced by actual image.
+ $image_placeholder_src = base_path() . $image_placeholder;
+ }
+
+ // Default to a blank inline thing.
+ if (!$image_placeholder_src) {
+ $image_placeholder_src = 'data:image/gif;base64,R0lGODlhAQABAIAAAP7//wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==';
+ }
+
+ return $image_placeholder_src;
+ }
+
+}
diff --git a/src/Tracer.php b/src/Tracer.php
new file mode 100644
index 0000000..70414b0
--- /dev/null
+++ b/src/Tracer.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\lazyloader;
+
+require_once __DIR__ . "/../lib/Potracio.php";
+use Potracio\Potracio;
+use Potracio\Bitmap;
+
+/**
+ * Wrap Potracio with some customization.
+ */
+class Tracer extends Potracio {
+ /**
+ * Dynamic resize factor.
+ *
+ * @var float
+ */
+ protected $resizeFactor = 1;
+
+ /**
+ * Maximum size of image. Larger images are downscaled.
+ *
+ * @var int
+ */
+ protected $dimLimit = 100;
+
+ /**
+ * Max area of speckles.
+ *
+ * @var int
+ */
+ protected $turdSize = 25;
+
+ /**
+ * Trace an image.
+ *
+ * @param string $file
+ * Full path to file.
+ */
+ public function __construct($file) {
+ parent::__construct(['turdsize' => $this->turdSize]);
+
+ list($w, $h) = getimagesize($file);
+ $image = imagecreatefromstring(file_get_contents($file));
+
+ if ($w > $this->dimLimit) {
+ $this->resizeFactor = $w / $this->dimLimit;
+ }
+ elseif ($h > $this->dimLimit) {
+ $this->resizeFactor = $h / $this->dimLimit;
+ }
+
+ if ($this->resizeFactor != 1) {
+ $w = $w / $this->resizeFactor;
+ $h = $h / $this->resizeFactor;
+ $image = imagescale($image, $w, $h);
+ }
+
+ // Below is copied over from the original function.
+ $this->bm = new Bitmap($w, $h);
+
+ for ($i = 0; $i < $h; $i++) {
+ for ($j = 0; $j < $w; $j++) {
+ $rgb = imagecolorat($image, $j, $i);
+ $r = ($rgb >> 16) & 0xFF;
+ $g = ($rgb >> 8) & 0xFF;
+ $b = $rgb & 0xFF;
+ $color = (0.2126 * $r) + (0.7153 * $g) + (0.0721 * $b);
+ $this->bm->data[] = $color < 128 ? 1 : 0;
+ }
+ }
+ }
+
+ /**
+ * Return an SVG.
+ */
+ public function getSvg() {
+ $this->process();
+ return parent::getSVG($this->resizeFactor);
+ }
+
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment