ComponentTransfer::contrast()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 12

Duplication

Lines 12
Ratio 100 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 0
Metric Value
dl 12
loc 12
ccs 8
cts 8
cp 1
rs 9.8666
c 0
b 0
f 0
cc 1
nc 1
nop 3
crap 1
1
<?php
2
namespace nstdio\svg\filter;
3
4
use nstdio\svg\container\ContainerInterface;
5
use nstdio\svg\traits\ElementTrait;
6
use nstdio\svg\util\KeyValueWriter;
7
8
/**
9
 * Class ComponentTransfer
10
 * This filter primitive performs component-wise remapping of data as follows:
11
 *
12
 * R' = feFuncR( R )
13
 * G' = feFuncG( G )
14
 * B' = feFuncB( B )
15
 * A' = feFuncA( A )
16
 * for every pixel. It allows operations like brightness adjustment, contrast adjustment, color balance or
17
 * thresholding.
18
 *
19
 * The calculations are performed on non-premultiplied color values. If the input graphics consists of premultiplied
20
 * color values, those values are automatically converted into non-premultiplied color values for this operation. (Note
21
 * that the undoing and redoing of the premultiplication can be avoided if feFuncA is the identity transform and all
22
 * alpha values on the source graphic are set to 1.)
23
 *
24
 * @link    https://www.w3.org/TR/SVG/filters.html#feComponentTransferElement
25
 * @property string type        = "identity | table | discrete | linear | gamma" Indicates the type of component
26
 *           transfer function. The type of function determines the applicability of the other attributes.
27
 * @property string tableValues = "(list of <number>s)" When type="table", the list of <number>s v0,v1,...vn, separated
28
 *           by white space and/or a comma, which define the lookup table. An empty list results in an identity
29
 *           transfer function. If the attribute is not specified, then the effect is as if an empty list were
30
 *           provided. In the following, C is the initial component (e.g., 'feFuncR'), C' is the remapped component;
31
 *           both in the closed interval [0,1].
32
 * @property float  slope       = "<number>" When type="linear", the slope of the linear function. If the attribute is
33
 *           not specified, then the effect is as if a value of 1 were specified.
34
 * @property float  intercept   = "<number>" When type="linear", the intercept of the linear function. If the attribute
35
 *           is not specified, then the effect is as if a value of 0 were specified.
36
 * @property float  amplitude   = "<number>" When type="gamma", the amplitude of the gamma function. If the attribute
37
 *           is not specified, then the effect is as if a value of 1 were specified.
38
 * @property float  exponent    = "<number>" When type="gamma", the exponent of the gamma function. If the attribute is
39
 *           not specified, then the effect is as if a value of 1 were specified.
40
 * @property float  offset      = "<number>" When type="gamma", the offset of the gamma function. If the attribute is
41
 *           not specified, then the effect is as if a value of 0 were specified.
42
 * @package nstdio\svg\filter
43
 * @author  Edgar Asatryan <[email protected]>
44
 */
45
class ComponentTransfer extends BaseFilter implements ContainerInterface
46
{
47
    use ElementTrait;
48
49
    /**
50
     * @inheritdoc
51
     */
52 12
    public function getName()
53
    {
54 12
        return "feComponentTransfer";
55
    }
56
57 1
    public static function identity(ContainerInterface $container, $filterId = null)
58
    {
59 1
        return self::createAndAppend($container, [
60 1
            'id'   => $filterId,
61 1
            'type' => __FUNCTION__,
62 1
        ], true);
63
64
    }
65
66
    /**
67
     * @param ContainerInterface $container
68
     * @param                    $config
69
     *
70
     * @param bool               $alpha
71
     *
72
     * @return Filter
73
     */
74 11
    private static function createAndAppend(ContainerInterface $container, $config, $alpha = false)
75
    {
76 11
        $id = $config['id'];
77 11
        $type = $config['type'];
78 11
        unset($config['id'], $config['type']);
79
80 11
        $filter = self::defaultFilter($container, $id);
81 11
        $componentTransfer = new ComponentTransfer($filter);
82 11
        $componentTransfer->id = null;
83
84 11
        self::funcFactory($componentTransfer, $type, $config, $alpha);
85
86 11
        return $filter;
87
    }
88
89 11
    private static function funcFactory(ContainerInterface $container, $type, array $config, $alpha = false)
90
    {
91
        /** @var Func[] $ret */
92 11
        $ret = [new FuncR($container, $type), new FuncG($container, $type), new FuncB($container, $type)];
93
94 11
        if ($alpha === true) {
95 1
            $ret[] = new FuncA($container, $type);
96 1
        }
97 11
        foreach ($config as $key => $value) {
98 10
            KeyValueWriter::apply($ret[$key]->getElement(), $value);
0 ignored issues
show
Bug introduced by
It seems like $ret[$key]->getElement() targeting nstdio\svg\SVGElement::getElement() can also be of type object<DOMElement> or object<nstdio\svg\container\ContainerInterface>; however, nstdio\svg\util\KeyValueWriter::apply() does only seem to accept object<nstdio\svg\XMLDocumentInterface>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
99 11
        }
100
101 11
        return $ret;
102
    }
103
104 5
    public static function table(ContainerInterface $container, array $table, $filterId = null)
105
    {
106 5
        $table = self::padAttribute($table);
107 5
        $config = ['id' => $filterId, 'type' => __FUNCTION__];
108 5
        for ($i = 0; $i < 3; $i++) {
109 5
            $config[$i]['tableValues'] = implode(' ', $table[$i]);
110 5
        }
111
112 5
        return self::createAndAppend($container, $config);
113
    }
114
115 7
    private static function padAttribute(array $attributeValue, $max = 3)
116
    {
117 7
        $val = array_values(array_slice($attributeValue, 0, $max));
118 7
        $cnt = count($val);
119 7
        if ($cnt < $max) {
120 5
            $repeatValue = $val[$cnt - 1];
121 5
            for ($i = $cnt - 1; $i < $max; $i++) {
122 5
                $val[$i] = $repeatValue;
123 5
            }
124 5
        }
125
126 7
        return $val;
127
    }
128
129 2 View Code Duplication
    public static function linear(ContainerInterface $container, $slope, $intercept, $filterId = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
130
    {
131 2
        $config = ['id' => $filterId, 'type' => __FUNCTION__];
132
133 2
        self::buildConfig($config, [
134 2
            'slope'     => $slope,
135 2
            'intercept' => $intercept,
136 2
        ]);
137
138 2
        return self::createAndAppend($container, $config);
139
    }
140
141 5
    private static function buildConfig(array &$config, array $attributes)
142
    {
143 5
        foreach ($attributes as $key => $value) {
144 5
            if (is_array($value)) {
145 2
                $aligned = self::padAttribute($value);
146 2
                for ($i = 0; $i < 3; $i++) {
147 2
                    $config[$i][$key] = $aligned[$i];
148 2
                }
149 2
            } else {
150 4
                for ($i = 0; $i < 3; $i++) {
151 4
                    $config[$i][$key] = $value;
152 4
                }
153
            }
154 5
        }
155 5
    }
156
157 1 View Code Duplication
    public static function gamma(ContainerInterface $container, $amplitude, $exponent, $offset = 0, $filterId = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
158
    {
159 1
        $config = ['id' => $filterId, 'type' => __FUNCTION__];
160 1
        self::buildConfig($config, [
161 1
            'amplitude' => $amplitude,
162 1
            'exponent'  => $exponent,
163 1
            'offset'    => $offset,
164 1
        ]);
165
166 1
        return self::createAndAppend($container, $config);
167
    }
168
169 1 View Code Duplication
    public static function brightness(ContainerInterface $container, $amount, $filterId = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
170
    {
171 1
        $config = ['id' => $filterId, 'type' => 'linear'];
172 1
        self::buildConfig($config, [
173 1
            'slope' => $amount,
174 1
        ]);
175
176 1
        return self::createAndAppend($container, $config);
177
    }
178
179 1 View Code Duplication
    public static function contrast(ContainerInterface $container, $amount, $filterId = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
180
    {
181 1
        $config = ['id' => $filterId, 'type' => 'linear'];
182
183 1
        $intercept = 0.5 - (0.5 * $amount);
184 1
        self::buildConfig($config, [
185 1
            'slope'     => $amount,
186 1
            'intercept' => $intercept,
187 1
        ]);
188
189 1
        return self::createAndAppend($container, $config);
190
    }
191
}