mirror of
https://github.com/defold/extension-camera
synced 2025-06-27 10:27:45 +02:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5db8d5c072 | ||
|
851f3dee87 | ||
|
73c3596156 | ||
|
d58f5ec46b | ||
|
89de4ccd59 | ||
|
e32dd144b6 | ||
|
6d420fc011 | ||
|
e07bd373ec | ||
|
6b01fcb6d6 | ||
|
cb4ab80e04 | ||
|
406edd5c37 | ||
|
8832fc1588 | ||
|
4e0625d64e | ||
|
a2e5d19ce8 | ||
|
9a3041402b | ||
|
f38c67fed8 | ||
|
87223b994e | ||
|
a459076dd2 |
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
github: defold
|
||||||
|
patreon: Defold
|
||||||
|
custom: ['https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NBNBHTUW4GS4C']
|
19
.github/workflows/trigger-site-rebuild.yml
vendored
Normal file
19
.github/workflows/trigger-site-rebuild.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
name: Trigger site rebuild
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
site-rebuild:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
name: 'Repository dispatch',
|
||||||
|
uses: defold/repository-dispatch@1.2.1,
|
||||||
|
with: {
|
||||||
|
repo: 'defold/defold.github.io',
|
||||||
|
token: '${{ secrets.SERVICES_GITHUB_TOKEN }}',
|
||||||
|
user: 'services@defold.se',
|
||||||
|
action: 'extension-camera'
|
||||||
|
}
|
||||||
|
}]
|
102
README.md
102
README.md
@ -1,101 +1,5 @@
|
|||||||
# extension-camera
|
# Camera extension for Defold
|
||||||
|
|
||||||
Native extension to use device camera to capture frames.
|
Defold [native extension](https://www.defold.com/manuals/extensions/) which provides access to the camera on macOS, iOS and Android.
|
||||||
|
|
||||||
|
[Manual, API and setup instructions](https://www.defold.com/extension-camera/) is available on the official Defold site.
|
||||||
# Installation
|
|
||||||
|
|
||||||
To use the camera extension in a Defold project this project has to be added as a [Defold library dependency](http://www.defold.com/manuals/libraries/). Open the **game.project** file and in the [Dependencies field in the Project section](https://defold.com/manuals/project-settings/#dependencies) add:
|
|
||||||
|
|
||||||
https://github.com/defold/extension-camera/archive/master.zip
|
|
||||||
|
|
||||||
Or point to the ZIP file of [a specific release](https://github.com/defold/extension-camera/releases).
|
|
||||||
|
|
||||||
|
|
||||||
# Supported platforms
|
|
||||||
|
|
||||||
The currently supported platforms are macOS, iOS and Android
|
|
||||||
|
|
||||||
|
|
||||||
# FAQ
|
|
||||||
|
|
||||||
## How do I reset macOS camera permission?
|
|
||||||
|
|
||||||
To test macOS camera permission popup multiple times you can reset the permission from the terminal:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tccutil reset Camera
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
# Lua API
|
|
||||||
|
|
||||||
## Type constants
|
|
||||||
|
|
||||||
Describes what camera should be used.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
camera.CAMERA_TYPE_FRONT -- Selfie
|
|
||||||
camera.CAMERA_TYPE_BACK
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Quality constants
|
|
||||||
|
|
||||||
```lua
|
|
||||||
camera.CAPTURE_QUALITY_HIGH
|
|
||||||
camera.CAPTURE_QUALITY_MEDIUM
|
|
||||||
camera.CAPTURE_QUALITY_LOW
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Status constants
|
|
||||||
|
|
||||||
```lua
|
|
||||||
camera.CAMERA_STARTED
|
|
||||||
camera.CAMERA_STOPPED
|
|
||||||
camera.CAMERA_NOT_PERMITTED
|
|
||||||
camera.CAMERA_ERROR
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## camera.start_capture(type, quality, callback)
|
|
||||||
|
|
||||||
Start camera capture using the specified camera (front/back) and capture quality. This may trigger a camera usage permission popup. When the popup has been dismissed the callback will be invoked with camera start status.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
camera.start_capture(camera.CAMERA_TYPE_BACK, camera.CAPTURE_QUALITY_HIGH, function(self, message)
|
|
||||||
if message == camera.CAMERA_STARTED then
|
|
||||||
-- do stuff
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## camera.stop_capture()
|
|
||||||
|
|
||||||
Stops a previously started capture session.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
camera.stop_capture()
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## camera.get_info()
|
|
||||||
|
|
||||||
Gets the info from the current capture session.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local info = camera.get_info()
|
|
||||||
print("width", info.width)
|
|
||||||
print("height", info.height)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## camera.get_frame()
|
|
||||||
|
|
||||||
Retrieves the camera pixel buffer. This buffer has one stream named "rgb", and is of type `buffer.VALUE_TYPE_UINT8` and has the value count of 1.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
self.cameraframe = camera.get_frame()
|
|
||||||
```
|
|
||||||
|
121
camera/api/extension-camera.script_api
Normal file
121
camera/api/extension-camera.script_api
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
- name: camera
|
||||||
|
type: table
|
||||||
|
desc: Provides functionality to capture images using the camera. Supported on macOS, iOS and Android.
|
||||||
|
[icon:ios] [icon:android]
|
||||||
|
members:
|
||||||
|
|
||||||
|
#*****************************************************************************************************
|
||||||
|
|
||||||
|
- name: start_capture
|
||||||
|
type: function
|
||||||
|
desc: Start camera capture using the specified camera (front/back) and capture quality. This may trigger a camera usage permission popup. When the popup has been dismissed the callback will be invoked with camera start status.
|
||||||
|
|
||||||
|
members:
|
||||||
|
- name: type
|
||||||
|
type: string
|
||||||
|
desc: Which camera to use, font or back.
|
||||||
|
- name: quality
|
||||||
|
type: string
|
||||||
|
desc: Quality of the captured image.
|
||||||
|
- name: callback
|
||||||
|
type: function
|
||||||
|
desc: |-
|
||||||
|
The function to call camera state has changed.
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- desc: |-
|
||||||
|
```lua
|
||||||
|
camera.start_capture(camera.CAMERA_TYPE_BACK, camera.CAPTURE_QUALITY_HIGH, function(self, message)
|
||||||
|
if message == camera.CAMERA_STARTED then
|
||||||
|
-- do stuff
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
#*****************************************************************************************************
|
||||||
|
|
||||||
|
- name: stop_capture
|
||||||
|
type: function
|
||||||
|
desc: Stops a previously started capture session.
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- desc: |-
|
||||||
|
```lua
|
||||||
|
camera.stop_capture()
|
||||||
|
```
|
||||||
|
|
||||||
|
#*****************************************************************************************************
|
||||||
|
|
||||||
|
- name: get_info
|
||||||
|
type: function
|
||||||
|
desc: Gets the info from the current capture session.
|
||||||
|
return:
|
||||||
|
- name: info
|
||||||
|
type: table
|
||||||
|
desc: Information about the camera.
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- desc: |-
|
||||||
|
```lua
|
||||||
|
local info = camera.get_info()
|
||||||
|
print("width", info.width)
|
||||||
|
print("height", info.height)
|
||||||
|
```
|
||||||
|
|
||||||
|
#*****************************************************************************************************
|
||||||
|
|
||||||
|
- name: get_frame
|
||||||
|
type: function
|
||||||
|
desc: Get captured frame.
|
||||||
|
return:
|
||||||
|
- name: frame
|
||||||
|
type: buffer
|
||||||
|
desc: Retrieves the camera pixel buffer. This buffer has one stream named "rgb", and is of type `buffer.VALUE_TYPE_UINT8` and has the value count of 1.
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- desc: |-
|
||||||
|
```lua
|
||||||
|
self.cameraframe = camera.get_frame()
|
||||||
|
```
|
||||||
|
|
||||||
|
#*****************************************************************************************************
|
||||||
|
|
||||||
|
- name: CAMERA_TYPE_FRONT
|
||||||
|
type: string
|
||||||
|
desc: Constant for the front camera.
|
||||||
|
|
||||||
|
- name: CAMERA_TYPE_BACK
|
||||||
|
type: string
|
||||||
|
desc: Constant for the back camera.
|
||||||
|
|
||||||
|
#*****************************************************************************************************
|
||||||
|
|
||||||
|
- name: CAPTURE_QUALITY_HIGH
|
||||||
|
type: string
|
||||||
|
desc: High quality capture session.
|
||||||
|
|
||||||
|
- name: CAPTURE_QUALITY_MEDIUM
|
||||||
|
type: string
|
||||||
|
desc: Medium quality capture session.
|
||||||
|
|
||||||
|
- name: CAPTURE_QUALITY_LOW
|
||||||
|
type: string
|
||||||
|
desc: Low quality capture session.
|
||||||
|
|
||||||
|
#*****************************************************************************************************
|
||||||
|
|
||||||
|
- name: CAMERA_STARTED
|
||||||
|
type: string
|
||||||
|
desc: The capture session has started.
|
||||||
|
|
||||||
|
- name: CAMERA_STOPPED
|
||||||
|
type: string
|
||||||
|
desc: The capture session has stopped.
|
||||||
|
|
||||||
|
- name: CAMERA_NOT_PERMITTED
|
||||||
|
type: string
|
||||||
|
desc: The user did not give permission to start the capture session.
|
||||||
|
|
||||||
|
- name: CAMERA_ERROR
|
||||||
|
type: string
|
||||||
|
desc: Something went wrong when starting the capture session.
|
@ -1,18 +1,10 @@
|
|||||||
name: "Camera"
|
name: "Camera"
|
||||||
|
|
||||||
platforms:
|
platforms:
|
||||||
x86_64-osx:
|
osx:
|
||||||
context:
|
|
||||||
frameworks: ["AVFoundation", "CoreMedia"]
|
|
||||||
|
|
||||||
x86-osx:
|
|
||||||
context:
|
context:
|
||||||
frameworks: ["AVFoundation", "CoreMedia"]
|
frameworks: ["AVFoundation", "CoreMedia"]
|
||||||
|
|
||||||
arm64-ios:
|
ios:
|
||||||
context:
|
|
||||||
frameworks: ["AVFoundation", "CoreMedia", "UIKit"]
|
|
||||||
|
|
||||||
armv7-ios:
|
|
||||||
context:
|
context:
|
||||||
frameworks: ["AVFoundation", "CoreMedia", "UIKit"]
|
frameworks: ["AVFoundation", "CoreMedia", "UIKit"]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="{{android.package}}">
|
||||||
<uses-feature android:name="android.hardware.camera" />
|
<uses-feature android:name="android.hardware.camera" />
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
dependencies {
|
repositories {
|
||||||
compile 'com.android.support:support-v4:28.+'
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'androidx.core:core:1.2.0'
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import android.app.FragmentManager;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.support.v4.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ public class AndroidCamera extends Fragment
|
|||||||
private boolean newFrame;
|
private boolean newFrame;
|
||||||
private int position;
|
private int position;
|
||||||
private int quality;
|
private int quality;
|
||||||
private Camera.Size size;
|
private Camera.Size previewSize;
|
||||||
|
|
||||||
private static Context context;
|
private static Context context;
|
||||||
|
|
||||||
@ -132,26 +132,43 @@ public class AndroidCamera extends Fragment
|
|||||||
|
|
||||||
Camera.Parameters params = camera.getParameters();
|
Camera.Parameters params = camera.getParameters();
|
||||||
|
|
||||||
List<Camera.Size> sizes = params.getSupportedPreviewSizes();
|
List<Camera.Size> previewSizes = params.getSupportedPreviewSizes();
|
||||||
|
List<String> focusModes = params.getSupportedFocusModes();
|
||||||
|
List<Camera.Size> pictureSizes = params.getSupportedPictureSizes();
|
||||||
|
List<Integer> supportedFormats = params.getSupportedPictureFormats();
|
||||||
|
|
||||||
|
Camera.Size pictureSize;
|
||||||
switch(this.quality)
|
switch(this.quality)
|
||||||
{
|
{
|
||||||
case CAPTURE_QUALITY_HIGH:
|
case CAPTURE_QUALITY_HIGH:
|
||||||
this.size = sizes.get(sizes.size() - 1);
|
this.previewSize = previewSizes.get(previewSizes.size() - 1);
|
||||||
|
pictureSize = pictureSizes.get(previewSizes.size() - 1);
|
||||||
break;
|
break;
|
||||||
case CAPTURE_QUALITY_LOW:
|
case CAPTURE_QUALITY_LOW:
|
||||||
this.size = sizes.get(0);
|
this.previewSize = previewSizes.get(0);
|
||||||
|
pictureSize = pictureSizes.get(0);
|
||||||
break;
|
break;
|
||||||
case CAPTURE_QUALITY_MEDIUM:
|
case CAPTURE_QUALITY_MEDIUM:
|
||||||
default:
|
default:
|
||||||
this.size = sizes.get((int)Math.ceil(sizes.size() / 2));
|
this.previewSize = previewSizes.get((int)Math.ceil(previewSizes.size() / 2));
|
||||||
|
pictureSize = pictureSizes.get((int)Math.ceil(previewSizes.size() / 2));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
params.setPreviewSize(this.size.width, this.size.height);
|
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
|
||||||
params.setPictureSize(this.size.width, this.size.height);
|
{
|
||||||
params.setPictureFormat(PixelFormat.JPEG);
|
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
|
||||||
params.setJpegQuality(90);
|
}
|
||||||
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
|
|
||||||
|
params.setPreviewSize(this.previewSize.width, this.previewSize.height);
|
||||||
|
params.setPictureSize(pictureSize.width, pictureSize.height);
|
||||||
|
|
||||||
|
if (supportedFormats.contains(PixelFormat.JPEG))
|
||||||
|
{
|
||||||
|
params.setPictureFormat(PixelFormat.JPEG);
|
||||||
|
params.setJpegQuality(90);
|
||||||
|
}
|
||||||
|
|
||||||
camera.setParameters(params);
|
camera.setParameters(params);
|
||||||
|
|
||||||
final Activity activity = (Activity)context;
|
final Activity activity = (Activity)context;
|
||||||
@ -166,7 +183,7 @@ public class AndroidCamera extends Fragment
|
|||||||
if(rotation == Surface.ROTATION_180 || rotation == Surface.ROTATION_270)
|
if(rotation == Surface.ROTATION_180 || rotation == Surface.ROTATION_270)
|
||||||
flip = true;
|
flip = true;
|
||||||
|
|
||||||
int[] pixels = convertYUV420_NV21toARGB8888(data, AndroidCamera.this.size.width, AndroidCamera.this.size.height, flip);
|
int[] pixels = convertYUV420_NV21toARGB8888(data, AndroidCamera.this.previewSize.width, AndroidCamera.this.previewSize.height, flip);
|
||||||
frameUpdate(pixels);
|
frameUpdate(pixels);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -179,7 +196,7 @@ public class AndroidCamera extends Fragment
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
captureStarted(this.size.width, this.size.height);
|
captureStarted(this.previewSize.width, this.previewSize.height);
|
||||||
camera.startPreview();
|
camera.startPreview();
|
||||||
queueMessage(CAMERA_STARTED);
|
queueMessage(CAMERA_STARTED);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#define MODULE_NAME "camera"
|
#define MODULE_NAME "camera"
|
||||||
|
|
||||||
// Defold SDK
|
// Defold SDK
|
||||||
#define DLIB_LOG_DOMAIN LIB_NAME
|
|
||||||
#include <dmsdk/sdk.h>
|
#include <dmsdk/sdk.h>
|
||||||
|
|
||||||
#if defined(DM_PLATFORM_IOS) || defined(DM_PLATFORM_OSX) || defined(DM_PLATFORM_ANDROID)
|
#if defined(DM_PLATFORM_IOS) || defined(DM_PLATFORM_OSX) || defined(DM_PLATFORM_ANDROID)
|
||||||
@ -48,21 +47,30 @@ void Camera_QueueMessage(CameraMessage message)
|
|||||||
|
|
||||||
static void Camera_ProcessQueue()
|
static void Camera_ProcessQueue()
|
||||||
{
|
{
|
||||||
DM_MUTEX_SCOPED_LOCK(g_DefoldCamera.m_Mutex);
|
if (g_DefoldCamera.m_MessageQueue.Empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i != g_DefoldCamera.m_MessageQueue.Size(); ++i)
|
dmArray<CameraMessage> tmp;
|
||||||
|
{
|
||||||
|
DM_MUTEX_SCOPED_LOCK(g_DefoldCamera.m_Mutex);
|
||||||
|
tmp.Swap(g_DefoldCamera.m_MessageQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i != tmp.Size(); ++i)
|
||||||
{
|
{
|
||||||
lua_State* L = dmScript::GetCallbackLuaContext(g_DefoldCamera.m_Callback);
|
lua_State* L = dmScript::GetCallbackLuaContext(g_DefoldCamera.m_Callback);
|
||||||
if (!dmScript::SetupCallback(g_DefoldCamera.m_Callback))
|
if (!dmScript::SetupCallback(g_DefoldCamera.m_Callback))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
CameraMessage message = g_DefoldCamera.m_MessageQueue[i];
|
CameraMessage message = tmp[i];
|
||||||
|
|
||||||
if (message == CAMERA_STARTED)
|
if (message == CAMERA_STARTED)
|
||||||
{
|
{
|
||||||
// Increase ref count
|
// Increase ref count
|
||||||
dmScript::LuaHBuffer luabuffer = {g_DefoldCamera.m_VideoBuffer, false};
|
dmScript::LuaHBuffer luabuffer(g_DefoldCamera.m_VideoBuffer, dmScript::OWNER_C);
|
||||||
dmScript::PushBuffer(L, luabuffer);
|
dmScript::PushBuffer(L, luabuffer);
|
||||||
g_DefoldCamera.m_VideoBufferLuaRef = dmScript::Ref(L, LUA_REGISTRYINDEX);
|
g_DefoldCamera.m_VideoBufferLuaRef = dmScript::Ref(L, LUA_REGISTRYINDEX);
|
||||||
}
|
}
|
||||||
@ -80,7 +88,6 @@ static void Camera_ProcessQueue()
|
|||||||
}
|
}
|
||||||
dmScript::TeardownCallback(g_DefoldCamera.m_Callback);
|
dmScript::TeardownCallback(g_DefoldCamera.m_Callback);
|
||||||
}
|
}
|
||||||
g_DefoldCamera.m_MessageQueue.SetSize(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Camera_DestroyCallback()
|
static void Camera_DestroyCallback()
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#if defined(DM_PLATFORM_ANDROID)
|
#if defined(DM_PLATFORM_ANDROID)
|
||||||
|
|
||||||
#include "camera_private.h"
|
#include "camera_private.h"
|
||||||
|
#include <dmsdk/dlib/android.h>
|
||||||
|
|
||||||
static jclass g_CameraClass = 0;
|
static jclass g_CameraClass = 0;
|
||||||
static jobject g_CameraObject = 0;
|
static jobject g_CameraObject = 0;
|
||||||
@ -20,53 +21,20 @@ static CaptureQuality g_Quality;
|
|||||||
|
|
||||||
static dmBuffer::HBuffer* g_Buffer = 0;
|
static dmBuffer::HBuffer* g_Buffer = 0;
|
||||||
|
|
||||||
|
|
||||||
static JNIEnv* Attach()
|
|
||||||
{
|
|
||||||
JNIEnv* env;
|
|
||||||
JavaVM* vm = dmGraphics::GetNativeAndroidJavaVM();
|
|
||||||
vm->AttachCurrentThread(&env, NULL);
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool Detach(JNIEnv* env)
|
|
||||||
{
|
|
||||||
bool exception = (bool) env->ExceptionCheck();
|
|
||||||
env->ExceptionClear();
|
|
||||||
JavaVM* vm = dmGraphics::GetNativeAndroidJavaVM();
|
|
||||||
vm->DetachCurrentThread();
|
|
||||||
return !exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
static jclass GetClass(JNIEnv* env, const char* classname)
|
|
||||||
{
|
|
||||||
jclass activity_class = env->FindClass("android/app/NativeActivity");
|
|
||||||
jmethodID get_class_loader = env->GetMethodID(activity_class,"getClassLoader", "()Ljava/lang/ClassLoader;");
|
|
||||||
jobject cls = env->CallObjectMethod(dmGraphics::GetNativeAndroidActivity(), get_class_loader);
|
|
||||||
jclass class_loader = env->FindClass("java/lang/ClassLoader");
|
|
||||||
jmethodID find_class = env->GetMethodID(class_loader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
|
||||||
|
|
||||||
jstring str_class_name = env->NewStringUTF(classname);
|
|
||||||
jclass outcls = (jclass)env->CallObjectMethod(cls, find_class, str_class_name);
|
|
||||||
env->DeleteLocalRef(str_class_name);
|
|
||||||
return outcls;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
JNIEXPORT void JNICALL Java_com_defold_android_camera_AndroidCamera_frameUpdate(JNIEnv * env, jobject jobj, jintArray data);
|
JNIEXPORT void JNICALL Java_com_defold_android_camera_AndroidCamera_frameUpdate(JNIEnv * env, jobject jobj, jintArray data);
|
||||||
JNIEXPORT void JNICALL Java_com_defold_android_camera_AndroidCamera_queueMessage(JNIEnv * env, jobject jobj, jint message);
|
JNIEXPORT void JNICALL Java_com_defold_android_camera_AndroidCamera_queueMessage(JNIEnv * env, jobject jobj, jint message);
|
||||||
JNIEXPORT void JNICALL Java_com_defold_android_camera_AndroidCamera_captureStarted(JNIEnv * env, jobject jobj, jint width, jint height);
|
JNIEXPORT void JNICALL Java_com_defold_android_camera_AndroidCamera_captureStarted(JNIEnv * env, jobject jobj, jint width, jint height);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_com_defold_android_camera_AndroidCamera_frameUpdate(JNIEnv * env, jobject jobj, jintArray data)
|
JNIEXPORT void JNICALL Java_com_defold_android_camera_AndroidCamera_frameUpdate(JNIEnv * env, jobject jobj, jintArray data)
|
||||||
{
|
{
|
||||||
if(!g_FrameLock)
|
if(!g_FrameLock)
|
||||||
{
|
{
|
||||||
env->GetIntArrayRegion(data, 0, g_Width * g_Height, g_Data);
|
env->GetIntArrayRegion(data, 0, g_Width * g_Height, g_Data);
|
||||||
g_FrameLock = true;
|
g_FrameLock = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_com_defold_android_camera_AndroidCamera_queueMessage(JNIEnv * env, jobject jobj, jint message)
|
JNIEXPORT void JNICALL Java_com_defold_android_camera_AndroidCamera_queueMessage(JNIEnv * env, jobject jobj, jint message)
|
||||||
@ -98,19 +66,15 @@ void CameraPlatform_GetCameraInfo(CameraInfo& outparams)
|
|||||||
|
|
||||||
int CameraPlatform_Initialize()
|
int CameraPlatform_Initialize()
|
||||||
{
|
{
|
||||||
JNIEnv* env = Attach();
|
dmAndroid::ThreadAttacher threadAttacher;
|
||||||
if(!env)
|
JNIEnv* env = threadAttacher.GetEnv();
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the AndroidCamera class
|
// get the AndroidCamera class
|
||||||
jclass tmp = GetClass(env, "com.defold.android.camera/AndroidCamera");
|
jclass tmp = dmAndroid::LoadClass(env, "com.defold.android.camera/AndroidCamera");
|
||||||
g_CameraClass = (jclass)env->NewGlobalRef(tmp);
|
g_CameraClass = (jclass)env->NewGlobalRef(tmp);
|
||||||
if(!g_CameraClass)
|
if(!g_CameraClass)
|
||||||
{
|
{
|
||||||
dmLogError("Could not find class 'com.defold.android.camera/AndroidCamera'.");
|
dmLogError("Could not find class 'com.defold.android.camera/AndroidCamera'.");
|
||||||
Detach(env);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +83,6 @@ int CameraPlatform_Initialize()
|
|||||||
if(!g_GetCameraMethodId)
|
if(!g_GetCameraMethodId)
|
||||||
{
|
{
|
||||||
dmLogError("Could not get static method 'getCamera'.");
|
dmLogError("Could not get static method 'getCamera'.");
|
||||||
Detach(env);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +91,6 @@ int CameraPlatform_Initialize()
|
|||||||
if(!g_CameraObject)
|
if(!g_CameraObject)
|
||||||
{
|
{
|
||||||
dmLogError("Could not create instance.");
|
dmLogError("Could not create instance.");
|
||||||
Detach(env);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,18 +99,15 @@ int CameraPlatform_Initialize()
|
|||||||
if(!g_StartPreviewMethodId)
|
if(!g_StartPreviewMethodId)
|
||||||
{
|
{
|
||||||
dmLogError("Could not get startPreview() method.");
|
dmLogError("Could not get startPreview() method.");
|
||||||
Detach(env);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
g_StopPreviewMethodId = env->GetMethodID(g_CameraClass, "stopPreview", "()V");
|
g_StopPreviewMethodId = env->GetMethodID(g_CameraClass, "stopPreview", "()V");
|
||||||
if(!g_StopPreviewMethodId)
|
if(!g_StopPreviewMethodId)
|
||||||
{
|
{
|
||||||
dmLogError("Could not get stopPreview() method.");
|
dmLogError("Could not get stopPreview() method.");
|
||||||
Detach(env);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Detach(env);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,9 +123,9 @@ void CameraPlatform_StartCapture(dmBuffer::HBuffer* buffer, CameraType type, Cap
|
|||||||
g_Type = type;
|
g_Type = type;
|
||||||
g_Quality = quality;
|
g_Quality = quality;
|
||||||
|
|
||||||
JNIEnv* env = Attach();
|
dmAndroid::ThreadAttacher threadAttacher;
|
||||||
env->CallVoidMethod(g_CameraObject, g_StartPreviewMethodId);
|
JNIEnv* env = threadAttacher.GetEnv();
|
||||||
Detach(env);
|
env->CallVoidMethod(g_CameraObject, g_StartPreviewMethodId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraPlatform_StopCapture()
|
void CameraPlatform_StopCapture()
|
||||||
@ -177,15 +136,15 @@ void CameraPlatform_StopCapture()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEnv* env = Attach();
|
dmAndroid::ThreadAttacher threadAttacher;
|
||||||
|
JNIEnv* env = threadAttacher.GetEnv();
|
||||||
env->CallVoidMethod(g_CameraObject, g_StopPreviewMethodId);
|
env->CallVoidMethod(g_CameraObject, g_StopPreviewMethodId);
|
||||||
Detach(env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraPlatform_UpdateCapture()
|
void CameraPlatform_UpdateCapture()
|
||||||
{
|
{
|
||||||
if(g_FrameLock)
|
if(g_FrameLock)
|
||||||
{
|
{
|
||||||
int width = g_Width;
|
int width = g_Width;
|
||||||
int height = g_Height;
|
int height = g_Height;
|
||||||
int numChannels = 4;
|
int numChannels = 4;
|
||||||
@ -209,8 +168,8 @@ void CameraPlatform_UpdateCapture()
|
|||||||
out[index+2] = (argb>>16)&0xFF; // B
|
out[index+2] = (argb>>16)&0xFF; // B
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_FrameLock = false;
|
g_FrameLock = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // DM_PLATFORM_ANDROID
|
#endif // DM_PLATFORM_ANDROID
|
||||||
|
37
docs/index.md
Normal file
37
docs/index.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: Defold camera extension API documentation
|
||||||
|
brief: This manual covers how to use the camera on macOS, iOS and Android in Defold.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Defold camera extension API documentation
|
||||||
|
|
||||||
|
This extension provides a unified, simple to use interface to capture images using the camera on macOS, iOS and Android.
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
To use this library in your Defold project, add the following URL to your `game.project` dependencies:
|
||||||
|
|
||||||
|
https://github.com/defold/extension-camera/archive/master.zip
|
||||||
|
|
||||||
|
We recommend using a link to a zip file of a [specific release](https://github.com/defold/extension-camera/releases).
|
||||||
|
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Refer to the [example project](https://github.com/defold/extension-camera/blob/master/main/main.script).
|
||||||
|
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### How do I reset macOS camera permission?
|
||||||
|
|
||||||
|
To test macOS camera permission popup multiple times you can reset the permission from the terminal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tccutil reset Camera
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Source code
|
||||||
|
|
||||||
|
The source code is available on [GitHub](https://github.com/defold/extension-camera)
|
8
main/default.font
Normal file
8
main/default.font
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
font: "/builtins/fonts/vera_mo_bd.ttf"
|
||||||
|
material: "/builtins/fonts/font-df.material"
|
||||||
|
size: 14
|
||||||
|
antialias: 1
|
||||||
|
alpha: 1.0
|
||||||
|
shadow_alpha: 0.0
|
||||||
|
shadow_blur: 0
|
||||||
|
output_format: TYPE_DISTANCE_FIELD
|
@ -16,14 +16,19 @@ embedded_instances {
|
|||||||
" z: 0.0\n"
|
" z: 0.0\n"
|
||||||
" w: 1.0\n"
|
" w: 1.0\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"sprite\"\n"
|
" id: \"sprite\"\n"
|
||||||
" type: \"sprite\"\n"
|
" type: \"sprite\"\n"
|
||||||
" data: \"tile_set: \\\"/main/camera.atlas\\\"\\n"
|
" data: \"default_animation: \\\"logo\\\"\\n"
|
||||||
"default_animation: \\\"logo\\\"\\n"
|
|
||||||
"material: \\\"/builtins/materials/sprite.material\\\"\\n"
|
"material: \\\"/builtins/materials/sprite.material\\\"\\n"
|
||||||
"blend_mode: BLEND_MODE_ALPHA\\n"
|
"blend_mode: BLEND_MODE_ALPHA\\n"
|
||||||
|
"textures {\\n"
|
||||||
|
" sampler: \\\"texture_sampler\\\"\\n"
|
||||||
|
" texture: \\\"/main/camera.atlas\\\"\\n"
|
||||||
|
"}\\n"
|
||||||
"\"\n"
|
"\"\n"
|
||||||
" position {\n"
|
" position {\n"
|
||||||
" x: 0.0\n"
|
" x: 0.0\n"
|
||||||
@ -60,10 +65,13 @@ embedded_instances {
|
|||||||
data: "embedded_components {\n"
|
data: "embedded_components {\n"
|
||||||
" id: \"sprite\"\n"
|
" id: \"sprite\"\n"
|
||||||
" type: \"sprite\"\n"
|
" type: \"sprite\"\n"
|
||||||
" data: \"tile_set: \\\"/main/logo.atlas\\\"\\n"
|
" data: \"default_animation: \\\"logo\\\"\\n"
|
||||||
"default_animation: \\\"logo\\\"\\n"
|
|
||||||
"material: \\\"/builtins/materials/sprite.material\\\"\\n"
|
"material: \\\"/builtins/materials/sprite.material\\\"\\n"
|
||||||
"blend_mode: BLEND_MODE_ALPHA\\n"
|
"blend_mode: BLEND_MODE_ALPHA\\n"
|
||||||
|
"textures {\\n"
|
||||||
|
" sampler: \\\"texture_sampler\\\"\\n"
|
||||||
|
" texture: \\\"/main/logo.atlas\\\"\\n"
|
||||||
|
"}\\n"
|
||||||
"\"\n"
|
"\"\n"
|
||||||
" position {\n"
|
" position {\n"
|
||||||
" x: 0.0\n"
|
" x: 0.0\n"
|
||||||
@ -86,12 +94,6 @@ embedded_instances {
|
|||||||
" z: 0.0\\n"
|
" z: 0.0\\n"
|
||||||
" w: 0.0\\n"
|
" w: 0.0\\n"
|
||||||
"}\\n"
|
"}\\n"
|
||||||
"scale {\\n"
|
|
||||||
" x: 1.0\\n"
|
|
||||||
" y: 1.0\\n"
|
|
||||||
" z: 1.0\\n"
|
|
||||||
" w: 0.0\\n"
|
|
||||||
"}\\n"
|
|
||||||
"color {\\n"
|
"color {\\n"
|
||||||
" x: 1.0\\n"
|
" x: 1.0\\n"
|
||||||
" y: 1.0\\n"
|
" y: 1.0\\n"
|
||||||
@ -116,8 +118,8 @@ embedded_instances {
|
|||||||
"blend_mode: BLEND_MODE_ALPHA\\n"
|
"blend_mode: BLEND_MODE_ALPHA\\n"
|
||||||
"line_break: false\\n"
|
"line_break: false\\n"
|
||||||
"text: \\\"Space/Touch to toggle capture\\\"\\n"
|
"text: \\\"Space/Touch to toggle capture\\\"\\n"
|
||||||
"font: \\\"/builtins/fonts/system_font.font\\\"\\n"
|
"font: \\\"/main/default.font\\\"\\n"
|
||||||
"material: \\\"/builtins/fonts/label.material\\\"\\n"
|
"material: \\\"/builtins/fonts/label-df.material\\\"\\n"
|
||||||
"\"\n"
|
"\"\n"
|
||||||
" position {\n"
|
" position {\n"
|
||||||
" x: 90.0\n"
|
" x: 90.0\n"
|
||||||
@ -140,12 +142,6 @@ embedded_instances {
|
|||||||
" z: 0.0\\n"
|
" z: 0.0\\n"
|
||||||
" w: 0.0\\n"
|
" w: 0.0\\n"
|
||||||
"}\\n"
|
"}\\n"
|
||||||
"scale {\\n"
|
|
||||||
" x: 1.0\\n"
|
|
||||||
" y: 1.0\\n"
|
|
||||||
" z: 1.0\\n"
|
|
||||||
" w: 0.0\\n"
|
|
||||||
"}\\n"
|
|
||||||
"color {\\n"
|
"color {\\n"
|
||||||
" x: 1.0\\n"
|
" x: 1.0\\n"
|
||||||
" y: 1.0\\n"
|
" y: 1.0\\n"
|
||||||
@ -172,8 +168,8 @@ embedded_instances {
|
|||||||
"text: \\\"label\\\\n"
|
"text: \\\"label\\\\n"
|
||||||
"\\\"\\n"
|
"\\\"\\n"
|
||||||
" \\\"\\\"\\n"
|
" \\\"\\\"\\n"
|
||||||
"font: \\\"/builtins/fonts/system_font.font\\\"\\n"
|
"font: \\\"/main/default.font\\\"\\n"
|
||||||
"material: \\\"/builtins/fonts/label.material\\\"\\n"
|
"material: \\\"/builtins/fonts/label-df.material\\\"\\n"
|
||||||
"\"\n"
|
"\"\n"
|
||||||
" position {\n"
|
" position {\n"
|
||||||
" x: 90.0\n"
|
" x: 90.0\n"
|
||||||
|
@ -3,7 +3,6 @@ local function stop_capture(self)
|
|||||||
if self.cameraframe == nil then
|
if self.cameraframe == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
self.cameraframe = nil
|
self.cameraframe = nil
|
||||||
camera.stop_capture()
|
camera.stop_capture()
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ local function start_capture(self)
|
|||||||
type = camera.CAMERA_TYPE_BACK
|
type = camera.CAMERA_TYPE_BACK
|
||||||
quality = camera.CAPTURE_QUALITY_MEDIUM
|
quality = camera.CAPTURE_QUALITY_MEDIUM
|
||||||
end
|
end
|
||||||
|
|
||||||
camera.start_capture(type, quality, function(self, status)
|
camera.start_capture(type, quality, function(self, status)
|
||||||
if status == camera.CAMERA_STARTED then
|
if status == camera.CAMERA_STARTED then
|
||||||
self.cameraframe = camera.get_frame()
|
self.cameraframe = camera.get_frame()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user