PHPackages                             vipertecpro/image-cropper - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Image &amp; Media](/categories/media)
4. /
5. vipertecpro/image-cropper

ActiveNativephp-plugin[Image &amp; Media](/categories/media)

vipertecpro/image-cropper
=========================

A fully native, hand-written freehand image cropper for NativePHP Mobile.

v1.1.0(today)07↑2900%MITPHP ^8.4

Since Jul 22Compare

[ Source](https://github.com/vipertecpro/image-cropper)[ Packagist](https://packagist.org/packages/vipertecpro/image-cropper)[ Docs](https://github.com/vipertecpro/image-cropper)[ RSS](/packages/vipertecpro-image-cropper/feed)WikiDiscussions Synced today

READMEChangelog (4)Dependencies (2)Versions (5)Used By (0)

ImageCropper — a native freehand image cropper for NativePHP Mobile
===================================================================

[](#imagecropper--a-native-freehand-image-cropper-for-nativephp-mobile)

[![Packagist Version](https://camo.githubusercontent.com/f91ff2cd47e9ac3acbd851dd03e87157c582dd6c929dba7f737e351866cd8c23/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f766970657274656370726f2f696d6167652d63726f707065722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/vipertecpro/image-cropper)[![Total Downloads](https://camo.githubusercontent.com/bdbb49f83f04ea384c9e97c781b2a852cf95712aca7bbdf89ae0eecd1b7a42e7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f766970657274656370726f2f696d6167652d63726f707065722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/vipertecpro/image-cropper)[![PHP Version](https://camo.githubusercontent.com/ba358c5dfd0737068ad2909baf35c58210d1d596c70dfe2596ff2b4cf0365e97/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f766970657274656370726f2f696d6167652d63726f707065722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/vipertecpro/image-cropper)[![License](https://camo.githubusercontent.com/4e004e09439850ac2903a0e0fa5dd99d2f04c4e6d3fe40f76be1be8552fc1ccc/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f766970657274656370726f2f696d6167652d63726f707065722e7376673f7374796c653d666c61742d737175617265)](LICENSE)[![Platforms](https://camo.githubusercontent.com/e0aee70d8862d842227892f1c15bc8822d8644036b7466b07114d1c5a178b1df/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d732d694f53253230253743253230416e64726f69642d626c75653f7374796c653d666c61742d737175617265)](#requirements)

A **fully native, hand-written** image cropper. It opens a real native screen (SwiftUI on iOS, Jetpack Compose on Android) where the user manipulates the image **freehand** — drag to reposition (2D), pinch to zoom, and rotate with two fingers, all at once — behind a crop frame. On confirm it renders the cropped region to a **real JPEG file** and hands the path back to PHP via an event.

No third-party libraries (no TOCropViewController, no uCrop) — every line is in this package and yours to extend.

Why a plugin? The on-device PHP runtime has **no image extensions** (no GD/Imagick), and EDGE's gesture layer has **no 2D drag or rotate gesture**. Native code is the only way to get freehand cropping *and* a real cropped file. This plugin provides both.

Features
--------

[](#features)

- ✂️ **Freehand crop** — 2D drag, pinch-zoom and two-finger rotate, all at once
- ⭕ **Shapes &amp; presets** — circle or rectangle; built-in Profile / Square / Portrait / 16:9 / Cover / Banner / Story, switchable live in-screen
- 🎨 **Adjust &amp; filter** — brightness / contrast / saturation and one-tap filter presets, baked into the output
- 🧩 **Fully configurable** — enable only the modes/tools you need (a bare crop-only editor, or adjust-only with no crop, …)
- 🌗 **Theme-aware** — follows the system light / dark theme
- 📦 **Zero dependencies** — no third-party native libraries, no permissions, no network (~65 KB of native source)
- 🍏 🤖 **iOS + Android** — hand-written SwiftUI + Jetpack Compose behind one PHP API

Demo app
--------

[](#demo-app)

Want to try it before wiring it into your own project? There's a full sample NativePHP app that showcases this plugin with **five ready-made examples**(full studio, locked profile avatar, locked cover, adjust-only, filter-only) and a [demo video](https://github.com/vipertecpro/supernativephp-image-manipulation/blob/main/art/demo.mp4):

**👉 [vipertecpro/supernativephp-image-manipulation](https://github.com/vipertecpro/supernativephp-image-manipulation)**

```
git clone https://github.com/vipertecpro/supernativephp-image-manipulation
cd supernativephp-image-manipulation
composer install
php artisan native:run ios   # or: android
```

Each example is a tiny `NativeComponent` that just declares its crop config, so it doubles as copy-paste reference for common use cases.

---

Requirements
------------

[](#requirements)

- PHP 8.4+
- NativePHP Mobile v3 or v4 (`nativephp/mobile: ^3.0|^4.0`)
- iOS 15+ / Android API 26+

---

Installation
------------

[](#installation)

### From a local path (development / this repo)

[](#from-a-local-path-development--this-repo)

1. Require it, publish the plugin service provider (once), then register it:

    ```
    composer require vipertecpro/image-cropper
    php artisan vendor:publish --tag=nativephp-plugins-provider   # once per app
    php artisan native:plugin:register vipertecpro/image-cropper
    php artisan native:plugin:list        # verify it shows "ImageCropper" + "ImageCropper.Open"
    ```

    **Local development** (working on the plugin next to your app) — point Composer at the checkout instead, then require the same way:

    ```
    "repositories": [
        { "type": "path", "url": "../image-cropper" }
    ]
    ```

    ```
    composer require vipertecpro/image-cropper:@dev
    ```
2. Rebuild so the native code compiles in (run in a terminal — pick your platform):

    ```
    php artisan native:run ios       # or: android
    ```

> Requiring with Composer is **not** enough — an unregistered plugin does nothing. Always run `native:plugin:register` and confirm with `native:plugin:list`.

---

Getting an image to crop
------------------------

[](#getting-an-image-to-crop)

This plugin's only input is a **file path** to an existing image — `ImageCropper::open($path)`. It does **not** capture or pick photos itself, so it has **no hard dependency** on any camera/gallery plugin. Any source of a path works: the filesystem, a bundled asset, a download, or a picker of your choice.

If you need a picker, [`nativephp/mobile-camera`](https://nativephp.com/plugins/nativephp/mobile-camera)is a convenient one (it's listed under `suggest` in this package, not `require`) — install and register it separately, then hand its result path to `ImageCropper::open()`:

```
// with nativephp/mobile-camera installed + registered
use Native\Mobile\Facades\Camera;

Camera::pickImages('images', false);   // fires MediaSelected → you get $files[0]
// …then: ImageCropper::open($files[0], ['preset' => 'profile']);
```

Usage (SuperNative — the v4 way)
--------------------------------

[](#usage-supernative--the-v4-way)

Call the facade from a `NativeComponent`, then handle the result event.

```
use Native\Mobile\Attributes\On;
use Native\Mobile\Edge\NativeComponent;
use Vipertecpro\ImageCropper\Events\CropCancelled;
use Vipertecpro\ImageCropper\Events\ImageCropped;
use Vipertecpro\ImageCropper\Facades\ImageCropper;

class Avatar extends NativeComponent
{
    public ?string $photo = null;   // an existing image path (e.g. from the camera plugin)
    public ?string $cropped = null;

    public function crop(): void
    {
        ImageCropper::open($this->photo, ['preset' => 'profile']); // circular avatar
    }

    #[On(ImageCropped::class)]
    public function onCropped(string $path): void
    {
        $this->cropped = $path;   // a brand-new cropped JPEG on disk
    }

    #[On(CropCancelled::class)]
    public function onCancelled(): void
    {
        // user backed out — nothing produced
    }
}
```

Display the result with `native:image`, e.g. a round avatar:

```

```

### API

[](#api)

```
ImageCropper::open(string $path, array $options = []);
```

The crop experience is **configurable** so one plugin covers many use cases.

OptionValuesDefault`preset``profile` (circle), `square`, `portrait`, `landscape`, `cover`, `banner`, `story`—`shape``circle` | `rect` (overrides the preset)`rect``aspectRatio`width / height, e.g. `16/9` (overrides the preset)`1.0``tools`any of `['zoom', 'rotate']` — only these crop fine-tune rulers showboth`modes`any of `['crop', 'adjust', 'filter']` — the editor modes offered; opens on the firstall three`presets`list of preset keys offered in the in-screen selector; `[]` hides it &amp; locks the cropall`outputSize`longest edge of the output, px`1024``id`correlation id echoed back on the event`null````
ImageCropper::open($path, ['preset' => 'profile']);                       // round avatar
ImageCropper::open($path, ['preset' => 'cover']);                         // wide banner
ImageCropper::open($path, ['shape' => 'rect', 'aspectRatio' => 3.0, 'tools' => ['zoom']]);

// A bare, locked cropper — no preset switching, no colour editing:
ImageCropper::open($path, [
    'preset' => 'profile',   // circle 1:1
    'presets' => [],         // lock it
    'modes' => ['crop'],     // crop only — no adjust / filter
]);

// No crop at all — operate on the WHOLE image and export the full photo:
ImageCropper::open($path, ['modes' => ['adjust']]);   // colour adjustments only
ImageCropper::open($path, ['modes' => ['filter']]);   // one-tap filters only
```

> When `modes` omits `crop`, the editor drops the crop frame and gestures entirely, shows the whole image, and exports the full photo (longest edge = `outputSize`) with the colour baked in.

The native screen is a small editor with three modes, chosen from the bottom bar (`Cancel · Crop / Adjust / Filter · Done`):

- **Crop** — drag / pinch-zoom / rotate behind a circle/rect mask, a live preset selector (Profile → circle, 16:9, Cover, Banner, Story…) and draggable **Zoom / Rotate** rulers.
- **Adjust** — **Brightness / Contrast / Saturation** via draggable rulers, live.
- **Filter** — one-tap presets (Original / Vivid / Mono / Noir / Soft / Punch).

Colour changes preview live (SwiftUI modifiers / Compose `ColorMatrix`) and are **baked into the output** on Done (CoreImage `CIColorControls` / Android `ColorMatrix`). **Cancel** shows a "Discard Changes" confirmation. Circle crops are written as transparent PNGs.

Restrict or hide the in-screen preset selector:

```
ImageCropper::open($path, ['presets' => ['square', 'landscape']]); // only these two
ImageCropper::open($path, ['presets' => []]);                       // hide it, lock the shape
```

EventPayloadFired when`Vipertecpro\ImageCropper\Events\ImageCropped``string $path`, `?string $id`The user confirms — `$path` is the new cropped JPEG.`Vipertecpro\ImageCropper\Events\CropCancelled``?string $id`The user cancels, or the source couldn't be read.Pass an `$id` when several crops could be in flight, to correlate the event.

### Usage (legacy web-view apps)

[](#usage-legacy-web-view-apps)

A JS wrapper is provided in `resources/js/imageCropper.js`. Because the result is async, subscribe to the native events with the `#nativephp` `On()` helper — see the file's header for an example.

---

How it works
------------

[](#how-it-works)

```
PHP  ImageCropper::open(path, aspectRatio, outputSize, id)
  └─ nativephp_call("ImageCropper.Open", {...})           ← synchronous bridge
        └─ Native  ImageCropperFunctions.Open.execute()
              ├─ present a full-screen crop UI over the current screen
              │     • iOS:     UIHostingController → SwiftUI CropView
              │     • Android: ComposeView overlay → CropScreen
              ├─ gestures (all simultaneous):
              │     • iOS:     DragGesture + MagnificationGesture + RotationGesture
              │     • Android: detectTransformGestures (pan + zoom + rotation)
              ├─ on "Done": render the crop region to a new JPEG (Core Graphics / Canvas+Matrix)
              └─ dispatch  ImageCropped { path }   (or CropCancelled)
PHP  #[On(ImageCropped::class)] handler receives the path

```

**The crop geometry** (identical on both platforms): the image-transform anchor and the crop frame share the same centre, so a source pixel `p` (measured from the image centre) lands in output space at

```
out = outputCentre + k·offset + (k · userScale · fitScale) · Rotate(θ) · p

```

where `k = outputWidth / cropFrameWidth` maps screen points to output pixels, `fitScale` fits the whole image into the container at zoom = 1, and `offset`/`θ`/`userScale`come from the gestures. That single affine map is set up once and the image is drawn through it — the on-screen preview uses the exact same math, so it's WYSIWYG.

### Files

[](#files)

```
src/ImageCropper.php                 PHP facade target — builds the bridge call
src/Facades/ImageCropper.php         ImageCropper facade
src/Events/ImageCropped.php          success event (path, id)
src/Events/CropCancelled.php         cancel event (id)
resources/ios/ImageCropperFunctions.swift     SwiftUI crop view + Core Graphics renderer
resources/android/ImageCropperFunctions.kt    Compose crop view + Canvas/Matrix renderer
resources/js/imageCropper.js         JS bridge for legacy web-view apps
nativephp.json                       manifest: bridge_functions + events

```

---

Extending it
------------

[](#extending-it)

- **Aspect ratio** is a parameter today. To offer an aspect picker inside the crop screen, add buttons that mutate the frame size (`viewport`) — the renderer already keys off it.
- **Draggable crop-rect corners** (resize the frame itself, not just move the image): add corner hit-testing + drag handlers that resize `viewport`; the render math is unchanged because it's driven by `viewport`.
- **Filters / adjustments**: apply a `CIFilter` (iOS) or `ColorMatrix` (Android) to the bitmap in the renderer before writing the file.

---

Publishing to the NativePHP plugin marketplace
----------------------------------------------

[](#publishing-to-the-nativephp-plugin-marketplace)

This package is published under the `vipertecpro/image-cropper` vendor (`Vipertecpro\ImageCropper` PHP namespace, `com.vipertecpro.plugins.image_cropper`Android package). To make it publicly installable:

1. Add `resources/icon.png` (a square logo) and, ideally, `screenshots`.
2. Validate: `php artisan native:plugin:validate` → must be **OK** with zero errors.
3. Push to the public Git repo (`https://github.com/vipertecpro/image-cropper`) and tag a release: `git tag v1.0.0 && git push --tags`.
4. **Packagist** — submit the repo at  so anyone can `composer require vipertecpro/image-cropper`.
5. **NativePHP marketplace** — submit at  for discovery.
6. Test on **physical** iOS and Android devices; provide TestFlight / Play test links.

See the checklist in NativePHP's *Creating Plugins* docs.

---

Caveats (please verify on-device)
---------------------------------

[](#caveats-please-verify-on-device)

This crop view is hand-written and was authored without a device compile, so treat the first build as a calibration pass:

- **Rotation direction / sign.** SwiftUI and Compose report clockwise-positive rotation, and the renderers use it directly. If a rotated crop comes out mirrored or turned the wrong way, negate the rotation in the renderer (`transform.rotation` in Swift / `state.rotationDeg` in Kotlin).
- **Very large images.** Cropping happens on a background thread; for enormous source images consider downsampling on decode.
- Report anything off and it's a small, localized fix in the two renderer functions.

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md) for the full version history and per-release notes.

License
-------

[](#license)

MIT

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance100

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

4

Last Release

0d ago

PHP version history (2 changes)v1.0.0PHP ^8.2

v1.1.0PHP ^8.4

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/11619188?v=4)[Vipul Walia](/maintainers/vipertecpro)[@vipertecpro](https://github.com/vipertecpro)

---

Tags

imagemobileeditorphotocropnativephpcropper

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/vipertecpro-image-cropper/health.svg)

```
[![Health](https://phpackages.com/badges/vipertecpro-image-cropper/health.svg)](https://phpackages.com/packages/vipertecpro-image-cropper)
```

###  Alternatives

[sybio/image-workshop

Powerful PHP class using GD library to work easily with images including layer notion (like Photoshop or GIMP)

854945.6k12](/packages/sybio-image-workshop)[dantsu/php-image-editor

PHP library to easily edit image with GD extension.

34174.0k2](/packages/dantsu-php-image-editor)[jonom/focuspoint

Smarter automatic image cropping for Silverstripe

111675.8k57](/packages/jonom-focuspoint)[ctessier/nova-advanced-image-field

An advanced image field for Nova with cropping and resizing.

102568.0k1](/packages/ctessier-nova-advanced-image-field)[jbzoo/image

A PHP class that simplifies working with images

174129.4k3](/packages/jbzoo-image)[stefangabos/zebra_image

A single-file, lightweight PHP library designed for efficient image manipulation featuring methods for modifying images and applying filters

138118.5k7](/packages/stefangabos-zebra-image)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
