Skip to content

Commit

Permalink
simplify filters
Browse files Browse the repository at this point in the history
  • Loading branch information
MartkCz committed Nov 30, 2021
1 parent 198c8ee commit bc4732b
Show file tree
Hide file tree
Showing 122 changed files with 1,633 additions and 1,983 deletions.
9 changes: 4 additions & 5 deletions .docs/README.md
Expand Up @@ -5,11 +5,10 @@
- [Symfony](symfony.md)
- [Standalone](standalone.md)
- [Doctrine](doctrine.md)
- Filters (image manipulation)
- [Imagine filters (uses imagine/imagine)](imagine.md)
- [Nette image filters (uses nette/utils)](nette-image-filters.md)
- [Nette filters via neon config](nette-filters-config.md)
- [Filters with dynamic options](filters-options.md)
- [Filters](filters.md) (image manipulation)
- [Imagine filter processor (uses imagine/imagine)](imagine.md)
- [Nette filter processor (uses nette/utils)](nette-processor.md)
- [Filters in neon and latte](neon-filters.md)
- Examples
- [Nette + Gumlet + Google Storage](examples/nette-gumlet-googleStorage.md)

69 changes: 13 additions & 56 deletions .docs/examples/nette-gumlet-googleStorage.md
Expand Up @@ -12,82 +12,39 @@ extensions:
image.gumlet: Contributte\Imagist\Bridge\Nette\DI\GumletImageStorageExtension
image.gumlet:
bucket: bulios
token: 26982c29aeb19ac8ae94721a096dbe91
bucket: string
token: string|null
```

And that's all. Now we use google storage instead of default filesystem storage.
And that's all. Now we use Google storage instead of default filesystem storage.

```neon
services:
image.filesystem:
factory: Contributte\Imagist\Filesystem\GoogleStorageFilesystem('bucket', %appDir%/config/gcs.json)
```

## Gumlet - filters

GumletLinkGenerator uses Normalizers (converts filters to array) instead of Filters. So let's create one:
## Gumlet operation processor

```php
use Contributte\Imagist\Bridge\Gumlet\GumletBuilder;
use InvalidArgumentException;
use Contributte\Imagist\Entity\ImageInterface;
use Contributte\Imagist\Entity\Filter\ImageFilter;
use Contributte\Imagist\Filter\FilterNormalizerProcessorInterface;

final class ImageNormalizer implements FilterNormalizerProcessorInterface
final class GumletOperationProcessor implements OperationProcessorInterface
{

public function avatar(ImageInterface $image): array
public function process(object $resource, OperationCollection $collection, ContextInterface $context): void
{
// resize to 200x200 and crop image
$gumlet = GumletBuilder::create()
->resize(200, 200, 'crop');

// if image matches scope avatar[/*]
if ($image->getScope()->startsWith('avatar')) {
$gumlet->crop('faces');
if (!$resource instanceof ArrayResource) {
return;
}

return $gumlet->build();
}

public function supports(ImageFilter $filter, ContextImageAware $context): bool
{
$method = $filter->getName();
$builder = new GumletBuilder();

return $context->has('gumlet') && !in_array($method, ['supports', 'normalize']) && method_exists($this, $method);
}

public function normalize(ImageFilter $filter, ContextImageAware $context): array
{
$method = $filter->getName();
if ($crop = $collection->get(CropOperation::class)) {
$builder->extract($crop->getLeft(), $crop->getTop(), $crop->getWidth(), $crop->getHeight());
}

return $this->$method($context->getImage());
$resource->merge($builder->build());
}

}
```

```neon
services:
- App\Images\Normalizer\ImageNormalizer
```

or with built-in config style configuration:
```neon
extensions:
image.filters: Contributte\Imagist\Bridge\Nette\DI\ImageStorageConfigFiltersExtension
image.filters:
sizeM: resize(600)
avatar:
- resize(200, 200, crop)
- crop(faces)
```

We can use image storage as we are used to:

```html
<img n:img="$image|filter:avatar">
```
72 changes: 0 additions & 72 deletions .docs/filters-options.md

This file was deleted.

205 changes: 205 additions & 0 deletions .docs/filters.md
@@ -0,0 +1,205 @@
# Filters

Filter is a set of operations that can be applied to an image.
We divide them into static filters (without arguments) and dynamic filters (with arguments).

Note: Operation can be filter too.

## Registration

Let's use `imagine/imagine` as FilterProcessor. We need this setup

```php
use Contributte\Imagist\Bridge\Imagine\ImagineOperationProcessor;
use Contributte\Imagist\Bridge\Imagine\ImagineResourceFactory;
use Contributte\Imagist\Filter\FilterProcessor;

$processor = new FilterProcessor(new ImagineResourceFactory(), [
new ImagineOperationProcessor(),
]);
```

### Nette
```neon
imagist:
extensions:
imagine:
enabled: true
nette:
enabled: false
```

## Image Filtering

Using filter in image is pretty easy, just look at it

```php
use Contributte\Imagist\Filter\Operation\ResizeOperation;

$image->withFilter(new ResizeOperation(20, 20)); // resize operation implements FilterInterface
```

Multiple filters are supported too

```php
use Contributte\Imagist\Filter\CompositeFilter;
use Contributte\Imagist\Filter\Operation\CropOperation;

$image->withFilter(new CompositeFilter(
'composite',
new ResizeOperation(20, 20),
new CropOperation(20, 20, 20, 20),
));
```

## Custom filter

Filter has collection of operations. Filter can have arguments, but filters without arguments are more recommended.

```php
use Contributte\Imagist\Filter\FilterIdentifier;
use Contributte\Imagist\Filter\FilterInterface;
use Contributte\Imagist\Filter\Operation\ResizeOperation;

final class CustomFilter implements FilterInterface
{

public function __construct(/* $arguments - if we need */)
{

}

public function getIdentifier(): FilterIdentifier
{
return new FilterIdentifier(
'customName',
/* $this->arguments - pass all arguments which we need */
); // it's important, because this defines name of folder
}

/**
* @return OperationInterface[]
*/
public function getOperations(): array
{
return [
new ResizeOperation(20, 20),
];
}

}
```

Usage:

```php
$image->withFilter(new CustomFilter(/* $arguments */));
```

## Filters with arguments

By default, filters with arguments aren't allowed. The main problem is name convention of directories.
We must use one of following resolvers instead of default.

### MD5FilterResolver

MD5FilterResolver will convert arguments to json and then to md5 hash

### SimpleFilterResolver

SimpleFilterResolver will convert arguments to an array of strings and then join array with `-` (maximum length is 255 and chars are limited)

### OriginalFilterResolver

OriginalFilterResolver throws away arguments, therefore by default throws exception

Disabling exception throwing

```php
use Contributte\Imagist\Resolver\FilterResolvers\OriginalFilterResolver;

new OriginalFilterResolver(false);
```

## Custom operations

Operations directly manipulate with images. We need create new operation and processor which process this operation.
Here we go:

```php
use Contributte\Imagist\Filter\Operation\OperationInterface;

class RotateOperation implements OperationInterface
{

public function __construct(public readonly int $degrees) {
}

}
```

And processor:

```php
use Contributte\Imagist\Filter\Operation\OperationProcessorInterface;
use Imagine\Image\ImageInterface;

class CustomOperationProcessor implements OperationProcessorInterface
{

public function process(object $resource, OperationCollection $collection, ContextInterface $context): void
{
if ($resource instanceof ImageInterface) { // supports only imagine/imagine
return;
}

if ($rotate = $collection->get(RotateOperation::class)) { // gets operation if exists, collection deletes this operation for future use
$resource->rotate($rotate->degrees);
}
}

}
```

And usage in filter:

```php
final class CustomFilter implements FilterInterface
{
// ...

public function getOperations(): array
{
return [
new RotateOperation(20),
];
}

// ...
}
```

What if we want to use operation as filter?
```php
$image->withFilter(new RotateOperation(20));
```

Exception is thrown, because filter must be instance of FilterInterface, let's fix it

```php
use Contributte\Imagist\Filter\FilterIdentifier;
use Contributte\Imagist\Filter\Operation\OperationAsFilter;

class RotateOperation extends OperationAsFilter
{

public function __construct(public readonly int $degrees) {
}

public function getIdentifier(): FilterIdentifier
{
return new FilterIdentifier('rotate', [$this->degrees]);
}

}
```

0 comments on commit bc4732b

Please sign in to comment.