diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..30e3b31
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+/sandbox/**
+/.idea/**
+/*.iml
+/.git/**
+/jars/**
+/bundle/**
+/.gradle/**
+/vendor/**
+/build/**
+/examples/vendor/**
+/package.hub.yml
+/gradle
+/build.gradle
+/gradlew
+/gradlew.bat
+/package-lock.php.yml
+/out/**
+/dn-sources/**
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f49a4e1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/README.MD b/README.MD
new file mode 100644
index 0000000..96afda1
--- /dev/null
+++ b/README.MD
@@ -0,0 +1,49 @@
+# Webcam
+Расширение для работы с веб-камерой
+## Установка
+```
+jppm add jphp-webcam-ext
+```
+## Примеры
+### Получить веб-камеру по умолчанию:
+```php
+$webcam = Webcam::getDefault();
+```
+### Получить список веб-камер:
+```php
+$webcamList = Webcam::getWebcams();
+```
+### Сделать снимок с веб-камеры:
+```php
+$webcam = Webcam::getDefault();
+$webcam->open();
+$image = $webcam->getImage();
+$image->save('screenshot.png');
+```
+### Отобразить изображение с веб-камеры:
+```php
+$webcam = Webcam::getDefault();
+$webcam->open();
+
+$webcamView = new UXWebcamView();
+$webcamView->webcam = $webcam;
+$webcamView->size = [640, 480];
+$form->add($webcamView);
+$webcamView->play();
+```
+## Расширение для DevelNext
+[Скачать](https://github.com/jphp-group/jphp-webcam-ext/releases/latest)
+
+![](/screenshots/dn_view.png)
+
+## Скриншоты
+
+![](/screenshots/screenshot.png)
+
+## Дополнительно
+
+[Venity](https://vk.com/venity)
+
+[DevelNext - extensions & manuals.](https://vk.com/dn_extension)
+
+[Оригинальная библиотека](https://github.com/sarxos/webcam-capture)
\ No newline at end of file
diff --git a/api-docs/README.md b/api-docs/README.md
new file mode 100644
index 0000000..ef77325
--- /dev/null
+++ b/api-docs/README.md
@@ -0,0 +1,25 @@
+## jphp-webcam-ext
+> version 1.0.0, created by JPPM.
+
+
+### Install
+```
+jppm add jphp-webcam-ext@1.0.0
+```
+
+### API
+**Classes**
+
+#### `php\gui`
+
+- [`UXWebcamView`](https://github.com/jphp-group/jphp-webcam-ext/api-docs/classes/php/gui/UXWebcamView.md)
+
+#### `webcam\event`
+
+- [`WebcamDiscoveryEvent`](https://github.com/jphp-group/jphp-webcam-ext/api-docs/classes/webcam/event/WebcamDiscoveryEvent.md)
+- [`WebcamMotionEvent`](https://github.com/jphp-group/jphp-webcam-ext/api-docs/classes/webcam/event/WebcamMotionEvent.md)
+
+#### `webcam`
+
+- [`Webcam`](https://github.com/jphp-group/jphp-webcam-ext/api-docs/classes/webcam/Webcam.md)
+- [`WebcamMotionDetector`](https://github.com/jphp-group/jphp-webcam-ext/api-docs/classes/webcam/WebcamMotionDetector.md)
\ No newline at end of file
diff --git a/api-docs/classes/php/gui/UXWebcamView.md b/api-docs/classes/php/gui/UXWebcamView.md
new file mode 100644
index 0000000..df7697d
--- /dev/null
+++ b/api-docs/classes/php/gui/UXWebcamView.md
@@ -0,0 +1,44 @@
+# UXWebcamView
+
+- **class** `UXWebcamView` (`php\gui\UXWebcamView`) **extends** `UXControl` (`php\gui\UXControl`)
+- **source** `php/gui/UXWebcamView.php`
+
+---
+
+#### Properties
+
+- `->`[`webcam`](#prop-webcam) : [`Webcam`](https://github.com/jphp-group/jphp-webcam-ext/api-docs/classes/webcam/Webcam.md)
+- `->`[`placeholderColor`](#prop-placeholdercolor) : `UXColor`
+- `->`[`openMode`](#prop-openmode) : `string`
+- `->`[`closeMode`](#prop-closemode) : `string`
+- `->`[`stretch`](#prop-stretch) : `bool`
+- `->`[`centered`](#prop-centered) : `bool`
+- `->`[`stopped`](#prop-stopped) : `bool`
+- `->`[`flipX`](#prop-flipx) : `bool`
+- `->`[`flipY`](#prop-flipy) : `bool`
+
+---
+
+#### Methods
+
+- `->`[`play()`](#method-play)
+- `->`[`stop()`](#method-stop)
+
+---
+# Methods
+
+
+
+### play()
+```php
+play(): void
+```
+
+---
+
+
+
+### stop()
+```php
+stop(): void
+```
\ No newline at end of file
diff --git a/api-docs/classes/webcam/Webcam.md b/api-docs/classes/webcam/Webcam.md
new file mode 100644
index 0000000..509020d
--- /dev/null
+++ b/api-docs/classes/webcam/Webcam.md
@@ -0,0 +1,114 @@
+# Webcam
+
+- **class** `Webcam` (`webcam\Webcam`)
+- **source** `webcam/Webcam.php`
+
+---
+
+#### Properties
+
+- `->`[`name`](#prop-name) : `string`
+- `->`[`open`](#prop-open) : `bool`
+- `->`[`viewSize`](#prop-viewsize) : `array`
+
+---
+
+#### Static Methods
+
+- `Webcam ::`[`getDefault()`](#method-getdefault)
+- `Webcam ::`[`getWebcams()`](#method-getwebcams)
+- `Webcam ::`[`addWebcamFoundListener()`](#method-addwebcamfoundlistener)
+- `Webcam ::`[`addWebcamGoneListener()`](#method-addwebcamgonelistener)
+- `Webcam ::`[`removeWebcamFoundListener()`](#method-removewebcamfoundlistener)
+- `Webcam ::`[`removeWebcamGoneListener()`](#method-removewebcamgonelistener)
+
+---
+
+#### Methods
+
+- `->`[`open()`](#method-open)
+- `->`[`close()`](#method-close)
+- `->`[`getImage()`](#method-getimage)
+
+---
+# Static Methods
+
+
+
+### getDefault()
+```php
+Webcam::getDefault(int $timeout): Webcam
+```
+
+---
+
+
+
+### getWebcams()
+```php
+Webcam::getWebcams(int $timeout): Webcam[]
+```
+
+---
+
+
+
+### addWebcamFoundListener()
+```php
+Webcam::addWebcamFoundListener(callable $callable): Invoker
+```
+
+---
+
+
+
+### addWebcamGoneListener()
+```php
+Webcam::addWebcamGoneListener(callable $callable): Invoker
+```
+
+---
+
+
+
+### removeWebcamFoundListener()
+```php
+Webcam::removeWebcamFoundListener(php\lang\Invoker $callable): void
+```
+
+---
+
+
+
+### removeWebcamGoneListener()
+```php
+Webcam::removeWebcamGoneListener(php\lang\Invoker $callable): void
+```
+
+---
+# Methods
+
+
+
+### open()
+```php
+open(boolean $async): void
+```
+
+---
+
+
+
+### close()
+```php
+close(): void
+```
+
+---
+
+
+
+### getImage()
+```php
+getImage(): UXImage
+```
\ No newline at end of file
diff --git a/api-docs/classes/webcam/WebcamMotionDetector.md b/api-docs/classes/webcam/WebcamMotionDetector.md
new file mode 100644
index 0000000..14bd767
--- /dev/null
+++ b/api-docs/classes/webcam/WebcamMotionDetector.md
@@ -0,0 +1,111 @@
+# WebcamMotionDetector
+
+- **class** `WebcamMotionDetector` (`webcam\WebcamMotionDetector`)
+- **source** `webcam/WebcamMotionDetector.php`
+
+---
+
+#### Properties
+
+- `->`[`interval`](#prop-interval) : `int`
+- `->`[`maxMotionPoints`](#prop-maxmotionpoints) : `int`
+- `->`[`pointRange`](#prop-pointrange) : `int`
+- `->`[`motionArea`](#prop-motionarea) : `float`
+- `->`[`motion`](#prop-motion) : `bool`
+- `->`[`webcam`](#prop-webcam) : [`Webcam`](https://github.com/jphp-group/jphp-webcam-ext/api-docs/classes/webcam/Webcam.md)
+
+---
+
+#### Methods
+
+- `->`[`__construct()`](#method-__construct)
+- `->`[`start()`](#method-start)
+- `->`[`stop()`](#method-stop)
+- `->`[`setPixelThreshold()`](#method-setpixelthreshold)
+- `->`[`setAreaThreshold()`](#method-setareathreshold)
+- `->`[`setInertia()`](#method-setinertia)
+- `->`[`clearInertia()`](#method-clearinertia)
+- `->`[`addMotionListener()`](#method-addmotionlistener)
+- `->`[`removeMotionListener()`](#method-removemotionlistener)
+
+---
+# Methods
+
+
+
+### __construct()
+```php
+__construct(webcam\Webcam $webcam): void
+```
+
+---
+
+
+
+### start()
+```php
+start(): void
+```
+
+---
+
+
+
+### stop()
+```php
+stop(): void
+```
+
+---
+
+
+
+### setPixelThreshold()
+```php
+setPixelThreshold(int $value): void
+```
+
+---
+
+
+
+### setAreaThreshold()
+```php
+setAreaThreshold(double $value): void
+```
+
+---
+
+
+
+### setInertia()
+```php
+setInertia(int $value): void
+```
+
+---
+
+
+
+### clearInertia()
+```php
+clearInertia(): void
+```
+
+---
+
+
+
+### addMotionListener()
+```php
+addMotionListener(callable $callable): Invoker
+```
+
+---
+
+
+
+### removeMotionListener()
+```php
+removeMotionListener(php\lang\Invoker $invoker): void
+```
\ No newline at end of file
diff --git a/api-docs/classes/webcam/event/WebcamDiscoveryEvent.md b/api-docs/classes/webcam/event/WebcamDiscoveryEvent.md
new file mode 100644
index 0000000..48300ab
--- /dev/null
+++ b/api-docs/classes/webcam/event/WebcamDiscoveryEvent.md
@@ -0,0 +1,36 @@
+# WebcamDiscoveryEvent
+
+- **class** `WebcamDiscoveryEvent` (`webcam\event\WebcamDiscoveryEvent`)
+- **source** `webcam/event/WebcamDiscoveryEvent.php`
+
+---
+
+#### Properties
+
+- `->`[`webcam`](#prop-webcam) : `Webcam`
+
+---
+
+#### Methods
+
+- `->`[`isAdded()`](#method-isadded)
+- `->`[`isRemoved()`](#method-isremoved)
+
+---
+# Methods
+
+
+
+### isAdded()
+```php
+isAdded(): bool
+```
+
+---
+
+
+
+### isRemoved()
+```php
+isRemoved(): bool
+```
\ No newline at end of file
diff --git a/api-docs/classes/webcam/event/WebcamMotionEvent.md b/api-docs/classes/webcam/event/WebcamMotionEvent.md
new file mode 100644
index 0000000..05a235f
--- /dev/null
+++ b/api-docs/classes/webcam/event/WebcamMotionEvent.md
@@ -0,0 +1,15 @@
+# WebcamMotionEvent
+
+- **class** `WebcamMotionEvent` (`webcam\event\WebcamMotionEvent`)
+- **source** `webcam/event/WebcamMotionEvent.php`
+
+---
+
+#### Properties
+
+- `->`[`webcam`](#prop-webcam) : [`Webcam`](https://github.com/jphp-group/jphp-webcam-ext/api-docs/classes/webcam/Webcam.md)
+- `->`[`area`](#prop-area) : `float`
+- `->`[`cog`](#prop-cog) : `array`
+- `->`[`points`](#prop-points) : `array[]`
+- `->`[`currentImage`](#prop-currentimage) : `UXImage`
+- `->`[`previousImage`](#prop-previousimage) : `UXImage`
\ No newline at end of file
diff --git a/package.php.yml b/package.php.yml
new file mode 100644
index 0000000..3d516e1
--- /dev/null
+++ b/package.php.yml
@@ -0,0 +1,50 @@
+name: jphp-webcam-ext
+version: 1.0.3
+
+plugins:
+- Hub
+- Doc
+- Gradle
+
+deps:
+ jphp-runtime: '*'
+ jphp-gui-ext: '*'
+
+devDeps:
+ jphp-core: '*'
+ dn-bundle-plugin: '*'
+
+
+gradle:
+ deps:
+ - 'com.github.sarxos:webcam-capture:0.3.12'
+
+config:
+ ignore:
+ - /sandbox/**
+ - /.idea/**
+ - /*.iml
+ - /.git/**
+ - /package.hub.yml
+ - /bundle/**
+ - /src-bundle/**
+ - /build
+ - /out/**
+ - /examples/**
+ - /screenshots/**
+ - /dn-sources/**
+ - /.gitignore
+ - /settings.gradle
+
+doc:
+ url-prefix: 'https://github.com/jphp-group/%name%/api-docs/'
+
+
+develnext-bundle:
+ version: 1.0.2
+ name: Webcam
+ author: broelik
+ icon: "develnext/bundle/webcam/icon32.png"
+ description: "Пакет для работы с веб-камерой"
+ group: "other"
+ class: "develnext\\bundle\\webcam\\WebcamBundle"
\ No newline at end of file
diff --git a/screenshots/dn_view.png b/screenshots/dn_view.png
new file mode 100644
index 0000000..d1e8a7f
Binary files /dev/null and b/screenshots/dn_view.png differ
diff --git a/screenshots/screenshot.png b/screenshots/screenshot.png
new file mode 100644
index 0000000..43e96a7
Binary files /dev/null and b/screenshots/screenshot.png differ
diff --git a/sdk/php/gui/UXWebcamView.php b/sdk/php/gui/UXWebcamView.php
new file mode 100644
index 0000000..ec6b3a4
--- /dev/null
+++ b/sdk/php/gui/UXWebcamView.php
@@ -0,0 +1,50 @@
+getRegisteredFormat(GuiFormFormat::class);
+ if($format){
+ $format->registerInternalList('.dn/bundle/webcam/formComponents');
+ }
+ }
+ function onRemove(Project $project, AbstractBundle $owner = null){
+ parent::onRemove($project, $owner);
+ /** @var GuiFormFormat $format */
+ $format = Ide::get()->getRegisteredFormat(GuiFormFormat::class);
+ if($format){
+ $format->unregisterInternalList('.dn/bundle/webcam/formComponents');
+ }
+ }
+ public function onRegister(IdeLibraryBundleResource $resource)
+ {
+ parent::onRegister($resource);
+
+ $jars = ['bridj', 'slf4j-api', 'webcam-capture', 'jphp-webcam-ext'];
+ $files = fs::scan($resource->getPath(), null);
+ $filesToAdd = [];
+ foreach($files as $j => $file){
+ if(str::startsWith($file->getName(), 'dn-')){
+ continue;
+ }
+ foreach($jars as $i => $jar){
+ if(str::contains($file->getName(), $jar)){
+ $filesToAdd[$i] = $file;
+ unset($jars[$i]);
+ unset($files[$j]);
+ break;
+ }
+ }
+ }
+ $filesToAdd = arr::sortByKeys($filesToAdd);
+
+
+ try{
+ foreach($filesToAdd as $file){
+ Runtime::addJar($file);
+ }
+ }
+ catch(\Throwable $e){
+ file_put_contents('c:/users/broelik/desktop/result.txt', $e->getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src-bundle/develnext/bundle/webcam/elements/WebcamViewFormElement.php b/src-bundle/develnext/bundle/webcam/elements/WebcamViewFormElement.php
new file mode 100644
index 0000000..a1a31a3
--- /dev/null
+++ b/src-bundle/develnext/bundle/webcam/elements/WebcamViewFormElement.php
@@ -0,0 +1,44 @@
+placeholderColor = 'black';
+ return $view;
+ }
+ public function getDefaultSize(){
+ return [300, 200];
+ }
+ public function isOrigin($any){
+ return get_class($any) == UXWebcamView::class;
+ }
+}
\ No newline at end of file
diff --git a/src-bundle/develnext/bundle/webcam/elements/WebcamViewFormElement.xml b/src-bundle/develnext/bundle/webcam/elements/WebcamViewFormElement.xml
new file mode 100644
index 0000000..98b6ca2
--- /dev/null
+++ b/src-bundle/develnext/bundle/webcam/elements/WebcamViewFormElement.xml
@@ -0,0 +1,24 @@
+
+
+
+
+ Не открывать
+ Открывать синхронно
+ Открывать асинхронно
+
+
+
+
+ Не закрывать
+ Закрывать синхронно
+ Закрывать асинхронно
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src-bundle/develnext/bundle/webcam/tags/WebcamViewFormElementTag.php b/src-bundle/develnext/bundle/webcam/tags/WebcamViewFormElementTag.php
new file mode 100644
index 0000000..ee8123d
--- /dev/null
+++ b/src-bundle/develnext/bundle/webcam/tags/WebcamViewFormElementTag.php
@@ -0,0 +1,31 @@
+setAttribute('openMode', $node->openMode);
+ $element->setAttribute('closeMode', $node->closeMode);
+ $element->setAttribute('stretch', $node->stretch ? 'true' : 'false');
+ $element->setAttribute('centered', $node->centered ? 'true' : 'false');
+ $element->setAttribute('flipX', $node->flipX ? 'true' : 'false');
+ $element->setAttribute('flipY', $node->flipY ? 'true' : 'false');
+ if($node->placeholderColor){
+ $element->setAttribute('placeholderFill', $node->placeholderColor->getWebValue());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src-jvm/main/java/org/develnext/jphp/ext/webcam/WebcamExtension.java b/src-jvm/main/java/org/develnext/jphp/ext/webcam/WebcamExtension.java
new file mode 100644
index 0000000..89cdc7e
--- /dev/null
+++ b/src-jvm/main/java/org/develnext/jphp/ext/webcam/WebcamExtension.java
@@ -0,0 +1,38 @@
+package org.develnext.jphp.ext.webcam;
+
+import com.github.sarxos.webcam.Webcam;
+import com.github.sarxos.webcam.WebcamDiscoveryEvent;
+import com.github.sarxos.webcam.WebcamMotionDetector;
+import com.github.sarxos.webcam.WebcamMotionEvent;
+import org.develnext.jphp.ext.webcam.classes.WebcamMotionDetectorWrapper;
+import org.develnext.jphp.ext.webcam.classes.event.WebcamDiscoveryEventWrapper;
+import org.develnext.jphp.ext.webcam.classes.event.WebcamMotionEventWrapper;
+import org.develnext.jphp.ext.webcam.classes.gui.WebcamViewWrapper;
+import org.develnext.jphp.ext.webcam.classes.WebcamWrapper;
+import org.develnext.jphp.ext.webcam.support.control.WebcamView;
+import org.develnext.jphp.ext.webcam.support.memory.WebcamViewModeMemoryOperation;
+import php.runtime.env.CompileScope;
+import php.runtime.ext.support.Extension;
+import php.runtime.memory.support.MemoryOperation;
+
+public class WebcamExtension extends Extension {
+ public static final String NS = "webcam";
+
+ @Override
+ public Status getStatus() {
+ return Status.EXPERIMENTAL;
+ }
+
+ @Override
+ public void onRegister(CompileScope scope) {
+ registerWrapperClass(scope, WebcamView.class, WebcamViewWrapper.class);
+ MemoryOperation.register(new WebcamViewModeMemoryOperation());
+
+
+ registerWrapperClass(scope, Webcam.class, WebcamWrapper.class);
+ registerWrapperClass(scope, WebcamMotionDetector.class, WebcamMotionDetectorWrapper.class);
+
+ registerWrapperClass(scope, WebcamDiscoveryEvent.class, WebcamDiscoveryEventWrapper.class);
+ registerWrapperClass(scope, WebcamMotionEvent.class, WebcamMotionEventWrapper.class);
+ }
+}
\ No newline at end of file
diff --git a/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/WebcamMotionDetectorWrapper.java b/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/WebcamMotionDetectorWrapper.java
new file mode 100644
index 0000000..a733c13
--- /dev/null
+++ b/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/WebcamMotionDetectorWrapper.java
@@ -0,0 +1,80 @@
+package org.develnext.jphp.ext.webcam.classes;
+
+import com.github.sarxos.webcam.Webcam;
+import com.github.sarxos.webcam.WebcamDiscoveryListener;
+import com.github.sarxos.webcam.WebcamMotionDetector;
+import com.github.sarxos.webcam.WebcamMotionListener;
+import org.develnext.jphp.ext.webcam.WebcamExtension;
+import php.runtime.annotation.Reflection;
+import php.runtime.env.Environment;
+import php.runtime.ext.core.classes.WrapInvoker;
+import php.runtime.invoke.Invoker;
+import php.runtime.lang.BaseWrapper;
+import php.runtime.memory.ArrayMemory;
+import php.runtime.reflection.ClassEntity;
+
+import java.awt.*;
+
+@Reflection.Name("WebcamMotionDetector")
+@Reflection.Namespace(WebcamExtension.NS)
+public class WebcamMotionDetectorWrapper extends BaseWrapper {
+ public WebcamMotionDetectorWrapper(Environment env, WebcamMotionDetector wrappedObject) {
+ super(env, wrappedObject);
+ }
+ public WebcamMotionDetectorWrapper(Environment env, ClassEntity clazz) {
+ super(env, clazz);
+ }
+ interface WrappedInterface{
+ void start();
+ void stop();
+
+ @Reflection.Getter int getInterval();
+ @Reflection.Getter int getMaxMotionPoints();
+ @Reflection.Getter int getPointRange();
+ @Reflection.Getter double getMotionArea();
+ @Reflection.Getter("motion") boolean isMotion();
+ @Reflection.Getter Webcam getWebcam();
+
+ @Reflection.Setter void setInterval(int value);
+
+ @Reflection.Signature void setPixelThreshold(int value);
+ @Reflection.Signature void setMaxMotionPoints(int value);
+ @Reflection.Signature void setPointRange(int value);
+ @Reflection.Signature void setInertia(int value);
+ @Reflection.Signature void setAreaThreshold(double value);
+ }
+
+ @Reflection.Signature
+ public void __construct(Webcam webcam){
+ __wrappedObject = new WebcamMotionDetector(webcam);
+ }
+ @Reflection.Getter
+ public ArrayMemory getMotionCog(){
+ ArrayMemory result = new ArrayMemory();
+
+ Point cog = getWrappedObject().getMotionCog();
+
+ result.add(cog.getX());
+ result.add(cog.getY());
+
+ return result;
+ }
+
+ @Reflection.Signature
+ public WrapInvoker addMotionListener(Environment env, Invoker invoker){
+ WebcamMotionListener listener = (e) -> invoker.callAny(e);
+
+ getWrappedObject().addMotionListener(listener);
+
+ WrapInvoker wrapInvoker = new WrapInvoker(env, invoker);
+ invoker.setUserData(listener);
+ return wrapInvoker;
+ }
+ @Reflection.Signature
+ public boolean removeMotionListener(Invoker invoker){
+ if(invoker.getUserData() instanceof WebcamMotionListener){
+ return getWrappedObject().removeMotionListener((WebcamMotionListener)invoker.getUserData());
+ }
+ return false;
+ }
+}
diff --git a/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/WebcamWrapper.java b/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/WebcamWrapper.java
new file mode 100644
index 0000000..c4b6e10
--- /dev/null
+++ b/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/WebcamWrapper.java
@@ -0,0 +1,129 @@
+package org.develnext.jphp.ext.webcam.classes;
+
+import com.github.sarxos.webcam.Webcam;
+import com.github.sarxos.webcam.WebcamDiscoveryEvent;
+import com.github.sarxos.webcam.WebcamDiscoveryListener;
+import javafx.beans.value.ChangeListener;
+import javafx.embed.swing.SwingFXUtils;
+import javafx.scene.image.Image;
+import org.develnext.jphp.ext.javafx.JavaFXExtension;
+import org.develnext.jphp.ext.webcam.WebcamExtension;
+import php.runtime.Memory;
+import php.runtime.annotation.Reflection;
+import php.runtime.env.Environment;
+import php.runtime.ext.core.classes.WrapInvoker;
+import php.runtime.invoke.Invoker;
+import php.runtime.lang.BaseWrapper;
+import php.runtime.memory.ArrayMemory;
+import php.runtime.reflection.ClassEntity;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+
+@Reflection.Name("Webcam")
+@Reflection.Namespace(WebcamExtension.NS)
+@Reflection.Abstract
+public class WebcamWrapper extends BaseWrapper {
+ public WebcamWrapper(Environment env, Webcam wrappedObject) {
+ super(env, wrappedObject);
+ }
+ public WebcamWrapper(Environment env, ClassEntity clazz) {
+ super(env, clazz);
+ }
+ interface WrappedInterface{
+ boolean open();
+ boolean open(boolean async);
+ boolean close();
+
+ @Reflection.Getter("open") boolean isOpen();
+ @Reflection.Getter boolean getName();
+ }
+
+ @Reflection.Getter
+ public Memory getViewSize(){
+ Dimension dimension = getWrappedObject().getViewSize();
+ if(dimension == null){
+ return Memory.NULL;
+ }
+
+ ArrayMemory res = new ArrayMemory();
+ res.refOfIndex("width").assign(dimension.getWidth());
+ res.refOfIndex("height").assign(dimension.getHeight());
+
+ return res;
+ }
+
+ @Reflection.Signature
+ public Image getImage(){
+ BufferedImage bufferedImage = getWrappedObject().getImage();
+ if(bufferedImage == null){
+ return null;
+ }
+
+ return SwingFXUtils.toFXImage(bufferedImage, null);
+ }
+
+
+ @Reflection.Signature
+ public static Webcam getDefault(){
+ return Webcam.getDefault();
+ }
+ @Reflection.Signature
+ public static Webcam getDefault(long timeout) throws TimeoutException{
+ return Webcam.getDefault(timeout);
+ }
+
+ @Reflection.Signature
+ public static List getWebcams(){
+ return Webcam.getWebcams();
+ }
+ @Reflection.Signature
+ public static List getWebcams(long timeout) throws TimeoutException {
+ return Webcam.getWebcams(timeout);
+ }
+
+ private static final WebcamDiscoveryListener webcamDiscoveryListener = new WebcamDiscoveryListener(){
+ @Override
+ public void webcamFound(WebcamDiscoveryEvent event){
+ for(Invoker invoker : webcamFoundInvokers){
+ invoker.callAny(event);
+ }
+ }
+
+ @Override
+ public void webcamGone(WebcamDiscoveryEvent event){
+ for(Invoker invoker : webcamGoneInvokers){
+ invoker.callAny(event);
+ }
+ }
+ };
+ static{
+ Webcam.addDiscoveryListener(webcamDiscoveryListener);
+ }
+
+ private static final List webcamFoundInvokers = new ArrayList<>();
+ private static final List webcamGoneInvokers = new ArrayList<>();
+
+ @Reflection.Signature
+ public static WrapInvoker addWebcamFoundListener(Environment env, Invoker handler){
+ webcamFoundInvokers.add(handler);
+ return new WrapInvoker(env, handler);
+ }
+ @Reflection.Signature
+ public static WrapInvoker addWebcamGoneListener(Environment env, Invoker handler){
+ webcamGoneInvokers.add(handler);
+ return new WrapInvoker(env, handler);
+ }
+ @Reflection.Signature
+ public static boolean removeWebcamFoundListener(Invoker handler){
+ return webcamFoundInvokers.remove(handler);
+ }
+ @Reflection.Signature
+ public static boolean removeWebcamGoneListener(Invoker handler){
+ return webcamGoneInvokers.remove(handler);
+ }
+}
diff --git a/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/event/WebcamDiscoveryEventWrapper.java b/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/event/WebcamDiscoveryEventWrapper.java
new file mode 100644
index 0000000..d5e03b8
--- /dev/null
+++ b/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/event/WebcamDiscoveryEventWrapper.java
@@ -0,0 +1,36 @@
+package org.develnext.jphp.ext.webcam.classes.event;
+
+import com.github.sarxos.webcam.Webcam;
+import com.github.sarxos.webcam.WebcamDiscoveryEvent;
+
+import com.github.sarxos.webcam.WebcamDiscoveryListener;
+import org.develnext.jphp.ext.webcam.WebcamExtension;
+import php.runtime.annotation.Reflection;
+import php.runtime.env.Environment;
+import php.runtime.lang.BaseWrapper;
+import php.runtime.reflection.ClassEntity;
+
+@Reflection.Name("WebcamDiscoveryEvent")
+@Reflection.Namespace(WebcamExtension.NS+"\\event")
+@Reflection.Abstract
+public class WebcamDiscoveryEventWrapper extends BaseWrapper {
+ public WebcamDiscoveryEventWrapper(Environment env, WebcamDiscoveryEvent wrappedObject) {
+ super(env, wrappedObject);
+ }
+ public WebcamDiscoveryEventWrapper(Environment env, ClassEntity clazz) {
+ super(env, clazz);
+ }
+
+ interface WrappedInterface{
+ @Reflection.Getter Webcam getWebcam();
+ }
+
+ @Reflection.Signature
+ public boolean isAdded(){
+ return getWrappedObject().getType() == WebcamDiscoveryEvent.ADDED;
+ }
+ @Reflection.Signature
+ public boolean isRemoved(){
+ return getWrappedObject().getType() == WebcamDiscoveryEvent.REMOVED;
+ }
+}
diff --git a/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/event/WebcamMotionEventWrapper.java b/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/event/WebcamMotionEventWrapper.java
new file mode 100644
index 0000000..01f8b46
--- /dev/null
+++ b/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/event/WebcamMotionEventWrapper.java
@@ -0,0 +1,80 @@
+package org.develnext.jphp.ext.webcam.classes.event;
+
+import com.github.sarxos.webcam.Webcam;
+import com.github.sarxos.webcam.WebcamMotionEvent;
+import javafx.embed.swing.SwingFXUtils;
+import javafx.scene.image.Image;
+import org.develnext.jphp.ext.webcam.WebcamExtension;
+import php.runtime.Memory;
+import php.runtime.annotation.Reflection;
+import php.runtime.env.Environment;
+import php.runtime.lang.BaseWrapper;
+import php.runtime.memory.ArrayMemory;
+import php.runtime.reflection.ClassEntity;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+
+@Reflection.Name("WebcamMotionEvent")
+@Reflection.Namespace(WebcamExtension.NS+"\\event")
+@Reflection.Abstract
+public class WebcamMotionEventWrapper extends BaseWrapper {
+ public WebcamMotionEventWrapper(Environment env, WebcamMotionEvent wrappedObject) {
+ super(env, wrappedObject);
+ }
+ public WebcamMotionEventWrapper(Environment env, ClassEntity clazz) {
+ super(env, clazz);
+ }
+
+ interface WrappedInterface{
+ @Reflection.Getter Webcam getWebcam();
+ @Reflection.Getter double getArea();
+ }
+
+ @Reflection.Getter
+ public Memory getCog(){
+ Point cog = getWrappedObject().getCog();
+
+ ArrayMemory res = new ArrayMemory();
+
+ res.add(cog.getX());
+ res.add(cog.getY());
+
+ return res;
+ }
+
+ @Reflection.Getter
+ public Memory getPoints(){
+ ArrayMemory result = new ArrayMemory();
+
+ ArrayList pointList = getWrappedObject().getPoints();
+ if(pointList == null){
+ return result;
+ }
+ for(Point point : pointList){
+ ArrayMemory pointInfo = new ArrayMemory();
+
+ pointInfo.add(point.getX());
+ pointInfo.add(point.getY());
+
+ result.add(pointInfo);
+ }
+ return result;
+ }
+
+ @Reflection.Getter
+ public Image getCurrentImage(){
+ BufferedImage bufferedImage = getWrappedObject().getCurrentImage();
+
+ return bufferedImage == null ? null : SwingFXUtils.toFXImage(bufferedImage, null);
+ }
+ @Reflection.Getter
+ public Image getPreviousImage(){
+ BufferedImage bufferedImage = getWrappedObject().getPreviousImage();
+
+ return bufferedImage == null ? null : SwingFXUtils.toFXImage(bufferedImage, null);
+ }
+
+}
\ No newline at end of file
diff --git a/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/gui/WebcamViewWrapper.java b/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/gui/WebcamViewWrapper.java
new file mode 100644
index 0000000..36b24a0
--- /dev/null
+++ b/src-jvm/main/java/org/develnext/jphp/ext/webcam/classes/gui/WebcamViewWrapper.java
@@ -0,0 +1,49 @@
+package org.develnext.jphp.ext.webcam.classes.gui;
+
+import com.github.sarxos.webcam.Webcam;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.Paint;
+import org.develnext.jphp.ext.javafx.classes.UXControl;
+import org.develnext.jphp.ext.webcam.support.control.WebcamView;
+import php.runtime.annotation.Reflection;
+import php.runtime.env.Environment;
+import php.runtime.reflection.ClassEntity;
+
+@Reflection.Name("UXWebcamView")
+@Reflection.Namespace("php\\gui")
+public class WebcamViewWrapper extends UXControl {
+ public WebcamViewWrapper(Environment env, WebcamView wrappedObject) {
+ super(env, wrappedObject);
+ }
+ public WebcamViewWrapper(Environment env, ClassEntity clazz) {
+ super(env, clazz);
+ }
+
+ interface WrappedInterface{
+ void play();
+ void stop();
+
+ @Reflection.Property Webcam webcam();
+
+ @Reflection.Property WebcamView.WebcamViewMode closeMode();
+ @Reflection.Property WebcamView.WebcamViewMode openMode();
+ @Reflection.Property boolean stopped();
+ @Reflection.Property boolean stretch();
+ @Reflection.Property boolean centered();
+ @Reflection.Property boolean flipX();
+ @Reflection.Property boolean flipY();
+ }
+ @Reflection.Signature
+ public void __construct(){
+ __wrappedObject = new WebcamView();
+ }
+ @Reflection.Getter
+ public Color getPlaceholderColor(){
+ Paint paint = getWrappedObject().getPlaceholderFill();
+ return (paint instanceof Color) ? (Color)paint : null;
+ }
+ @Reflection.Setter
+ public void setPlaceholderColor(Color color){
+ getWrappedObject().setPlaceholderFill(color);
+ }
+}
diff --git a/src-jvm/main/java/org/develnext/jphp/ext/webcam/support/control/WebcamView.java b/src-jvm/main/java/org/develnext/jphp/ext/webcam/support/control/WebcamView.java
new file mode 100644
index 0000000..e9d299c
--- /dev/null
+++ b/src-jvm/main/java/org/develnext/jphp/ext/webcam/support/control/WebcamView.java
@@ -0,0 +1,170 @@
+package org.develnext.jphp.ext.webcam.support.control;
+
+import com.github.sarxos.webcam.Webcam;
+import javafx.application.Application;
+import javafx.beans.property.*;
+import javafx.scene.Scene;
+import javafx.scene.control.Control;
+import javafx.scene.control.Skin;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.Paint;
+import javafx.stage.Stage;
+import org.develnext.jphp.ext.webcam.support.skin.WebcamViewSkin;
+
+public class WebcamView extends Control{
+
+ public WebcamView(){
+ webcamProperty.addListener((obs, o, n) -> {
+
+ if(o != null){
+ switch(getCloseMode()){
+ case SYNC:
+ case ASYNC:
+ o.close();
+ }
+ }
+ if(n != null){
+ switch(getOpenMode()){
+ case SYNC:
+ n.open(false);
+ break;
+ case ASYNC:
+ n.open(true);
+ break;
+ }
+ }
+ });
+ }
+
+ public final void play(){
+ stopped.set(false);
+ }
+ public final void stop(){
+ stopped.set(true);
+ }
+
+
+ private final ObjectProperty webcamProperty = new SimpleObjectProperty<>();
+
+ public final void setWebcam(Webcam webcam){
+ webcamProperty.set(webcam);
+ }
+ public final Webcam getWebcam(){
+ return webcamProperty.get();
+ }
+ public final ObjectProperty webcamProperty(){
+ return webcamProperty;
+ }
+
+
+ private final ObjectProperty placeholderFillProperty = new SimpleObjectProperty<>();
+
+ public final void setPlaceholderFill(Paint paint){
+ placeholderFillProperty.set(paint);
+ }
+ public final Paint getPlaceholderFill(){
+ return placeholderFillProperty.get();
+ }
+ public final ObjectProperty placeholderFillProperty(){
+ return placeholderFillProperty;
+ }
+
+
+ private final ObjectProperty closeModeProperty = new SimpleObjectProperty<>(WebcamViewMode.NOT_USE);
+
+ public final void setCloseMode(WebcamViewMode value){
+ closeModeProperty.set(value);
+ }
+ public final WebcamViewMode getCloseMode(){
+ return closeModeProperty.get();
+ }
+ public final ObjectProperty closeModeProperty(){
+ return closeModeProperty;
+ }
+
+
+ private final ObjectProperty openModeProperty = new SimpleObjectProperty<>(WebcamViewMode.NOT_USE);
+
+ public final void setOpenMode(WebcamViewMode value){
+ openModeProperty.set(value);
+ }
+ public final WebcamViewMode getOpenMode(){
+ return openModeProperty.get();
+ }
+ public final ObjectProperty openModeProperty(){
+ return openModeProperty;
+ }
+
+
+ private final ReadOnlyBooleanWrapper stopped = new ReadOnlyBooleanWrapper(true);
+
+ public final boolean isStopped(){
+ return stopped.get();
+ }
+ public final ReadOnlyBooleanProperty stoppedProperty(){
+ return stopped.getReadOnlyProperty();
+ }
+
+
+ private final BooleanProperty stretchProperty = new SimpleBooleanProperty(false);
+
+ public final void setStretch(boolean value){
+ stretchProperty.set(value);
+ }
+ public final boolean isStretch(){
+ return stretchProperty.get();
+ }
+ public final BooleanProperty stretchProperty(){
+ return stretchProperty;
+ }
+
+
+ private final BooleanProperty centeredProperty = new SimpleBooleanProperty(false);
+
+ public final void setCentered(boolean value){
+ centeredProperty.set(value);
+ }
+ public final boolean isCentered(){
+ return centeredProperty.get();
+ }
+ public final BooleanProperty centeredProperty(){
+ return centeredProperty;
+ }
+
+ private final BooleanProperty flipXProperty = new SimpleBooleanProperty(false);
+
+ public final void setFlipX(boolean value){
+ flipXProperty.set(value);
+ }
+ public final boolean isFlipX(){
+ return flipXProperty.get();
+ }
+ public final BooleanProperty flipXProperty(){
+ return flipXProperty;
+ }
+
+ private final BooleanProperty flipYProperty = new SimpleBooleanProperty(false);
+
+ public final void setFlipY(boolean value){
+ flipYProperty.set(value);
+ }
+ public final boolean isFlipY(){
+ return flipYProperty.get();
+ }
+ public final BooleanProperty flipYProperty(){
+ return flipYProperty;
+ }
+
+
+ @Override
+ protected Skin> createDefaultSkin() {
+ return new WebcamViewSkin(this);
+ }
+
+
+ public enum WebcamViewMode{
+ SYNC,
+ ASYNC,
+ NOT_USE;
+ }
+}
\ No newline at end of file
diff --git a/src-jvm/main/java/org/develnext/jphp/ext/webcam/support/memory/WebcamViewModeMemoryOperation.java b/src-jvm/main/java/org/develnext/jphp/ext/webcam/support/memory/WebcamViewModeMemoryOperation.java
new file mode 100644
index 0000000..432772d
--- /dev/null
+++ b/src-jvm/main/java/org/develnext/jphp/ext/webcam/support/memory/WebcamViewModeMemoryOperation.java
@@ -0,0 +1,25 @@
+package org.develnext.jphp.ext.webcam.support.memory;
+
+import org.develnext.jphp.ext.webcam.support.control.WebcamView;
+import php.runtime.Memory;
+import php.runtime.env.Environment;
+import php.runtime.env.TraceInfo;
+import php.runtime.memory.StringMemory;
+import php.runtime.memory.support.MemoryOperation;
+
+public class WebcamViewModeMemoryOperation extends MemoryOperation {
+ @Override
+ public Class>[] getOperationClasses() {
+ return new Class[]{WebcamView.WebcamViewMode.class};
+ }
+
+ @Override
+ public WebcamView.WebcamViewMode convert(Environment environment, TraceInfo traceInfo, Memory memory) throws Throwable {
+ return WebcamView.WebcamViewMode.valueOf(memory.toString());
+ }
+
+ @Override
+ public Memory unconvert(Environment environment, TraceInfo traceInfo, WebcamView.WebcamViewMode mode) throws Throwable {
+ return new StringMemory(mode.toString());
+ }
+}
diff --git a/src-jvm/main/java/org/develnext/jphp/ext/webcam/support/skin/WebcamViewSkin.java b/src-jvm/main/java/org/develnext/jphp/ext/webcam/support/skin/WebcamViewSkin.java
new file mode 100644
index 0000000..763f2e8
--- /dev/null
+++ b/src-jvm/main/java/org/develnext/jphp/ext/webcam/support/skin/WebcamViewSkin.java
@@ -0,0 +1,136 @@
+package org.develnext.jphp.ext.webcam.support.skin;
+
+import com.github.sarxos.webcam.Webcam;
+import javafx.animation.AnimationTimer;
+import javafx.application.Application;
+import javafx.embed.swing.SwingFXUtils;
+import javafx.geometry.BoundingBox;
+import javafx.geometry.Bounds;
+import javafx.scene.Scene;
+import javafx.scene.canvas.Canvas;
+import javafx.scene.canvas.GraphicsContext;
+import javafx.scene.control.Button;
+import javafx.scene.control.SkinBase;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.effect.BoxBlur;
+import javafx.scene.image.Image;
+import javafx.scene.layout.*;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.Paint;
+import javafx.stage.Stage;
+import org.develnext.jphp.ext.webcam.support.control.WebcamView;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+public class WebcamViewSkin extends SkinBase {
+ private final Canvas canvas;
+ private final GraphicsContext gc;
+ private final AnimationTimer timer;
+
+ public WebcamViewSkin(WebcamView view) {
+ super(view);
+ canvas = new Canvas();
+ gc = canvas.getGraphicsContext2D();
+ view.widthProperty().addListener((obs, o, n) -> {
+ canvas.setWidth(n.doubleValue());
+ });
+ view.heightProperty().addListener((obs, o, n) -> {
+ canvas.setHeight(n.doubleValue());
+ });
+
+
+ timer = new AnimationTimer(){
+ @Override
+ public void handle(long now){
+ if(view.isStopped()){
+ fillPlaceholder();
+ return;
+ }
+
+ Webcam webcam = view.webcamProperty().get();
+
+ if(webcam == null){
+ fillPlaceholder();
+ return;
+ }
+
+ BufferedImage bufferedImage = webcam.getImage();
+ if(bufferedImage == null){
+ fillPlaceholder();
+ return;
+ }
+ Image image = SwingFXUtils.toFXImage(bufferedImage, null);
+ gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
+
+ Bounds bounds = calculateBounds(image.getWidth(), image.getHeight());
+
+ double w1 = bounds.getWidth();
+ double h1 = bounds.getHeight();
+
+ double x1 = bounds.getMinX();
+ double y1 = bounds.getMinY();
+
+ gc.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), x1, y1, w1, h1);
+ }
+ };
+ timer.start();
+
+ getChildren().add(canvas);
+ }
+ private Bounds calculateBounds(double sw, double sh){
+ return calculateBounds(sw, sh, false);
+ }
+ private Bounds calculateBounds(double sw, double sh, boolean forPlaceholder){
+ WebcamView view = getSkinnable();
+
+
+ double w = sw;
+ double h = sh;
+ double x = 0;
+ double y = 0;
+
+ if(view.isStretch()){
+ w = canvas.getWidth();
+ h = canvas.getHeight();
+ }
+ if(!forPlaceholder){
+ if(view.isFlipX()){
+ x = w;
+ w = -w;
+ }
+ if(view.isFlipY()){
+ y = h;
+ h = -h;
+ }
+ }
+ if(view.isCentered()){
+ x = (canvas.getWidth() - w) / 2;
+ y = (canvas.getHeight() - h) / 2;
+ }
+
+ return new BoundingBox(x, y, w, h);
+ }
+ private void fillPlaceholder(){
+ gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
+ Paint paint = getSkinnable().getPlaceholderFill();
+ if(paint != null){
+ double sw;
+ double sh;
+
+ Dimension dimension = getSkinnable().getWebcam() == null ? null : getSkinnable().getWebcam().getViewSize();
+ if(dimension != null){
+ sw = dimension.getWidth();
+ sh = dimension.getHeight();
+ }
+ else{
+ sw = canvas.getWidth();
+ sh = canvas.getHeight();
+ }
+
+ Bounds bounds = calculateBounds(sw, sh, true);
+ gc.setFill(paint);
+ gc.fillRect(bounds.getMinX(), bounds.getMinY(), bounds.getWidth(), bounds.getHeight());
+ }
+ }
+}
diff --git a/src-jvm/main/resources/META-INF/services/php.runtime.ext.support.Extension b/src-jvm/main/resources/META-INF/services/php.runtime.ext.support.Extension
new file mode 100644
index 0000000..0c12ae2
--- /dev/null
+++ b/src-jvm/main/resources/META-INF/services/php.runtime.ext.support.Extension
@@ -0,0 +1 @@
+org.develnext.jphp.ext.webcam.WebcamExtension
\ No newline at end of file