Compare commits
2 Commits
main
...
android-mk
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
485f2b9d19 | ||
|
|
0e88bb1c3e |
@@ -1,2 +0,0 @@
|
||||
BasedOnStyle: Google
|
||||
DerivePointerAlignment: false
|
||||
29
.github/workflows/build.yml
vendored
@@ -1,29 +0,0 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: set up JDK 21
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 21
|
||||
|
||||
- uses: pre-commit/action@v3.0.0
|
||||
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
with:
|
||||
packages: "cmake;4.1.0"
|
||||
|
||||
- name: build samples
|
||||
run: |
|
||||
./gradlew build
|
||||
13
.gitignore
vendored
@@ -1,13 +0,0 @@
|
||||
.gradle
|
||||
.idea
|
||||
**/*.iml
|
||||
local.properties
|
||||
build
|
||||
*~
|
||||
.externalNativeBuild
|
||||
libwebp
|
||||
.DS_Store
|
||||
**/ndkHelperBin
|
||||
**/.cxx
|
||||
display-p3/third_party
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
status: PUBLISHED
|
||||
technologies:
|
||||
- Android
|
||||
- NDK
|
||||
- Platform
|
||||
categories:
|
||||
- NDK
|
||||
languages:
|
||||
- C++
|
||||
solutions:
|
||||
- Mobile
|
||||
github: android/ndk-samples
|
||||
license: apache2
|
||||
@@ -1,6 +0,0 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
@@ -1,96 +0,0 @@
|
||||
# Architecture
|
||||
|
||||
This document describes the layout of the samples repository.
|
||||
|
||||
The top level directory is a Gradle project. This directory can be opened in
|
||||
Android Studio (and that will be the easiest way to work with the samples).
|
||||
|
||||
## Directory structure
|
||||
|
||||
### Samples
|
||||
|
||||
Most subdirectories are the individual sample apps. These can't be opened in
|
||||
Android Studio individually, as they rely on common code from the top level
|
||||
project. These subdirectories have their own `build.gradle` (the Groovy DSL) or
|
||||
`build.gradle.kts` (the Kotlin DSL) files. In Gradle terms these are "projects".
|
||||
Android Studio calls them "modules". The documentation in this repository will
|
||||
typically call them "modules".
|
||||
|
||||
Each sample has its own README.md that explains what features it demonstrates,
|
||||
as well as any unique requirements.
|
||||
|
||||
To run a sample, use the configuration selector in the top panel of Android
|
||||
Studio to select the sample and then click run. For example, to run the
|
||||
endless-tunnel sample game:
|
||||
|
||||
![Select and run a sample][docs/run-sample.png]
|
||||
|
||||
#### Build types
|
||||
|
||||
Android Gradle modules have multiple "build types", sometimes called "variants".
|
||||
For most of the samples, there are only two build types: debug and release. A
|
||||
few samples, notably the sanitizers sample, have more. To view the build types
|
||||
for the modules in this repository, in the Android Studio application menu,
|
||||
select View -> Tool Windows -> Build Variants. A window will open that lets you
|
||||
select the active variant for each sample.
|
||||
|
||||
### build-logic
|
||||
|
||||
The `build-logic` directory contains Gradle convention plugins used by this
|
||||
repository. This is where Gradle policy decisions that apply to the whole
|
||||
repository are made.
|
||||
|
||||
See the README.md in that directory for more details.
|
||||
|
||||
### docs
|
||||
|
||||
Documentation and supporting artifacts for this repository. Yes, for now it's
|
||||
just images for the READMEs and this doc.
|
||||
|
||||
### gradle/libs.versions.toml
|
||||
|
||||
This is a Gradle [version catalog]. It's the central location that defines the
|
||||
library and plugin dependencies for each sample.
|
||||
|
||||
The Android Gradle Plugin does not support evaluating version catalog fields in
|
||||
its DSL, so versions for SDK and NDK tools (`ndkVersion`, `compileSdkVersion`,
|
||||
`targetSdkVersion`, etc) are all defined by the convention plugins in
|
||||
[build-logic](#build-logic).
|
||||
|
||||
[version catalog]: https://docs.gradle.org/current/userguide/platforms.html
|
||||
|
||||
### Gradle wrapper
|
||||
|
||||
The `gradlew` and `gradlew.bat` scripts are the Gradle wrappers, macOS/Linux-
|
||||
and Windows batch-compatible respectively. They will manage the Gradle
|
||||
installation used by this repository for you, ensuring that the correct versions
|
||||
and environment are used. These should be used instead of running `gradle`
|
||||
directly.
|
||||
|
||||
`gradle/wrapper/gradle-wrapper.properties` defines which version of Gradle will
|
||||
be used. This should rarely be modified manually. Android Studio's Upgrade
|
||||
Assistant will manage that file and upgrade to whatever version of Gradle it
|
||||
prefers for that version of Android Studio and Android Gradle.
|
||||
|
||||
### build.gradle and settings.gradle
|
||||
|
||||
The build.gradle (or build.gradle.kts) file is the top level build file. It
|
||||
doesn't do anything interesting but declares which plugins will be used by the
|
||||
child modules. Each sample's module has its own build.gradle file that defines
|
||||
properties of the app.
|
||||
|
||||
The settings.gradle (or settings.gradle.kts) file configures repositories for
|
||||
fetching dependencies, and declares each app module.
|
||||
|
||||
### Metadata directories
|
||||
|
||||
#### .github
|
||||
|
||||
This directory contains GitHub metadata files, such as GitHub Action workflows
|
||||
and Issue templates.
|
||||
|
||||
#### .google
|
||||
|
||||
The `packaging.yaml` file in this directory is used by Android Studio to enable
|
||||
the File -> New -> Import Sample feature. As this repository is a monolithic
|
||||
sample project, it should not need to be changed.
|
||||
@@ -8,12 +8,12 @@ have to jump a couple of legal hurdles.
|
||||
Please fill out either the individual or corporate Contributor License Agreement
|
||||
(CLA).
|
||||
|
||||
- If you are an individual writing original source code and you're sure you own
|
||||
the intellectual property, then you'll need to sign an
|
||||
[individual CLA](http://code.google.com/legal/individual-cla-v1.0.html).
|
||||
- If you work for a company that wants to allow you to contribute your work,
|
||||
then you'll need to sign a
|
||||
[corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html).
|
||||
* If you are an individual writing original source code and you're sure you
|
||||
own the intellectual property, then you'll need to sign an [individual CLA]
|
||||
(http://code.google.com/legal/individual-cla-v1.0.html).
|
||||
* If you work for a company that wants to allow you to contribute your work,
|
||||
then you'll need to sign a [corporate CLA]
|
||||
(http://code.google.com/legal/corporate-cla-v1.0.html).
|
||||
|
||||
Follow either of the two links above to access the appropriate CLA and
|
||||
instructions for how to sign and return it. Once we receive it, we'll be able to
|
||||
@@ -24,10 +24,33 @@ accept your pull requests.
|
||||
1. Sign a Contributor License Agreement, if you have not yet done so (see
|
||||
details above).
|
||||
1. Create your change to the repo in question.
|
||||
- Fork the desired repo, develop and test your code changes.
|
||||
- Ensure that your code is clear and comprehensible.
|
||||
- Ensure that your code has an appropriate set of unit tests which all pass.
|
||||
* Fork the desired repo, develop and test your code changes.
|
||||
* Ensure that your code is clear and comprehensible.
|
||||
* Ensure that your code has an appropriate set of unit tests which all pass.
|
||||
1. Submit a pull request.
|
||||
1. The repo owner will review your request. If it is approved, the change will
|
||||
be merged. If it needs additional work, the repo owner will respond with
|
||||
useful comments.
|
||||
|
||||
## Contributing a New Sample App
|
||||
|
||||
1. Sign a Contributor License Agreement, if you have not yet done so (see
|
||||
details above).
|
||||
1. Create your own repo for your app following this naming convention:
|
||||
* mirror-{app-name}-{language or plaform}
|
||||
* apps: quickstart, photohunt-server, photohunt-client
|
||||
* example: mirror-quickstart-android
|
||||
* For multi-language apps, concatenate the primary languages like this:
|
||||
mirror-photohunt-server-java-python.
|
||||
|
||||
1. Create your sample app in this repo.
|
||||
* Be sure to clone the README.md, CONTRIBUTING.md and LICENSE files from the
|
||||
googlecast repo.
|
||||
* Ensure that your code is clear and comprehensible.
|
||||
* Ensure that your code has an appropriate set of unit tests which all pass.
|
||||
* Instructional value is the top priority when evaluating new app proposals for
|
||||
this collection of repos.
|
||||
1. Submit a request to fork your repo in googlecast organization.
|
||||
1. The repo owner will review your request. If it is approved, the sample will
|
||||
be merged. If it needs additional work, the repo owner will respond with
|
||||
useful comments.
|
||||
|
||||
34
MoreTeapots/AndroidManifest.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.sample.moreteapots"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="11"
|
||||
android:targetSdkVersion="19" />
|
||||
<uses-feature android:glEsVersion="0x00020000"></uses-feature>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme"
|
||||
android:hasCode="true"
|
||||
android:name="com.sample.moreteapots.MoreTeapotsApplication"
|
||||
>
|
||||
|
||||
<!-- Our activity is the built-in NativeActivity framework class.
|
||||
This will take care of integrating with our NDK code. -->
|
||||
<activity android:name="com.sample.moreteapots.MoreTeapotsNativeActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="orientation|keyboardHidden">
|
||||
<!-- Tell NativeActivity the name of or .so -->
|
||||
<meta-data android:name="android.app.lib_name"
|
||||
android:value="MoreTeapotsNativeActivity" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -17,7 +17,7 @@
|
||||
//
|
||||
|
||||
uniform lowp vec3 vMaterialAmbient;
|
||||
uniform lowp vec4 vMaterialSpecular;
|
||||
uniform mediump vec4 vMaterialSpecular;
|
||||
|
||||
varying lowp vec4 colorDiffuse;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
uniform lowp vec4 vMaterialSpecular;
|
||||
uniform mediump vec4 vMaterialSpecular;
|
||||
uniform highp vec3 vLight0;
|
||||
|
||||
in lowp vec4 colorDiffuse;
|
||||
@@ -31,6 +31,7 @@ uniform highp vec3 vLight0;
|
||||
|
||||
uniform lowp vec4 vMaterialDiffuse;
|
||||
uniform lowp vec3 vMaterialAmbient;
|
||||
uniform lowp vec4 vMaterialSpecular;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
19
MoreTeapots/jni/Android.mk
Normal file
@@ -0,0 +1,19 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := MoreTeapotsNativeActivity
|
||||
LOCAL_SRC_FILES := MoreTeapotsNativeActivity.cpp \
|
||||
MoreTeapotsRenderer.cpp \
|
||||
|
||||
LOCAL_C_INCLUDES :=
|
||||
LOCAL_CFLAGS :=
|
||||
|
||||
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv2
|
||||
LOCAL_STATIC_LIBRARIES := cpufeatures android_native_app_glue ndk_helper
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
$(call import-module,android/ndk_helper)
|
||||
$(call import-module,android/native_app_glue)
|
||||
$(call import-module,android/cpufeatures)
|
||||
4
MoreTeapots/jni/Application.mk
Normal file
@@ -0,0 +1,4 @@
|
||||
APP_PLATFORM := android-9
|
||||
APP_ABI := all
|
||||
|
||||
APP_STL := stlport_static
|
||||
500
MoreTeapots/jni/MoreTeapotsNativeActivity.cpp
Normal file
@@ -0,0 +1,500 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Include files
|
||||
//--------------------------------------------------------------------------------
|
||||
#include <jni.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <vector>
|
||||
#include <EGL/egl.h>
|
||||
#include <GLES/gl.h>
|
||||
|
||||
#include <android/sensor.h>
|
||||
#include <android/log.h>
|
||||
#include <android_native_app_glue.h>
|
||||
#include <android/native_window_jni.h>
|
||||
#include <cpu-features.h>
|
||||
|
||||
#include "MoreTeapotsRenderer.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//Preprocessor
|
||||
//-------------------------------------------------------------------------
|
||||
#define HELPER_CLASS_NAME "com/sample/helper/NDKHelper" //Class name of helper function
|
||||
//-------------------------------------------------------------------------
|
||||
//Constants
|
||||
//-------------------------------------------------------------------------
|
||||
const int32_t NUM_TEAPOTS_X = 8;
|
||||
const int32_t NUM_TEAPOTS_Y = 8;
|
||||
const int32_t NUM_TEAPOTS_Z = 8;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//Shared state for our app.
|
||||
//-------------------------------------------------------------------------
|
||||
struct android_app;
|
||||
class Engine
|
||||
{
|
||||
MoreTeapotsRenderer renderer_;
|
||||
|
||||
ndk_helper::GLContext* gl_context_;
|
||||
|
||||
bool initialized_resources_;
|
||||
bool has_focus_;
|
||||
|
||||
ndk_helper::DoubletapDetector doubletap_detector_;
|
||||
ndk_helper::PinchDetector pinch_detector_;
|
||||
ndk_helper::DragDetector drag_detector_;
|
||||
ndk_helper::PerfMonitor monitor_;
|
||||
|
||||
ndk_helper::TapCamera tap_camera_;
|
||||
|
||||
android_app* app_;
|
||||
|
||||
ASensorManager* sensor_manager_;
|
||||
const ASensor* accelerometer_sensor_;
|
||||
ASensorEventQueue* sensor_event_queue_;
|
||||
|
||||
void UpdateFPS( float fps );
|
||||
void ShowUI();
|
||||
void TransformPosition( ndk_helper::Vec2& vec );
|
||||
|
||||
public:
|
||||
static void HandleCmd( struct android_app* app,
|
||||
int32_t cmd );
|
||||
static int32_t HandleInput( android_app* app,
|
||||
AInputEvent* event );
|
||||
|
||||
Engine();
|
||||
~Engine();
|
||||
void SetState( android_app* state );
|
||||
int InitDisplay();
|
||||
void LoadResources();
|
||||
void UnloadResources();
|
||||
void DrawFrame();
|
||||
void TermDisplay();
|
||||
void TrimMemory();
|
||||
bool IsReady();
|
||||
|
||||
void UpdatePosition( AInputEvent* event,
|
||||
int32_t index,
|
||||
float& x,
|
||||
float& y );
|
||||
|
||||
void InitSensors();
|
||||
void ProcessSensors( int32_t id );
|
||||
void SuspendSensors();
|
||||
void ResumeSensors();
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//Ctor
|
||||
//-------------------------------------------------------------------------
|
||||
Engine::Engine() :
|
||||
initialized_resources_( false ),
|
||||
has_focus_( false ),
|
||||
app_( NULL ),
|
||||
sensor_manager_( NULL ),
|
||||
accelerometer_sensor_( NULL ),
|
||||
sensor_event_queue_( NULL )
|
||||
{
|
||||
gl_context_ = ndk_helper::GLContext::GetInstance();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//Dtor
|
||||
//-------------------------------------------------------------------------
|
||||
Engine::~Engine()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Load resources
|
||||
*/
|
||||
void Engine::LoadResources()
|
||||
{
|
||||
renderer_.Init( NUM_TEAPOTS_X, NUM_TEAPOTS_Y, NUM_TEAPOTS_Z );
|
||||
renderer_.Bind( &tap_camera_ );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload resources
|
||||
*/
|
||||
void Engine::UnloadResources()
|
||||
{
|
||||
renderer_.Unload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize an EGL context for the current display.
|
||||
*/
|
||||
int Engine::InitDisplay()
|
||||
{
|
||||
if( !initialized_resources_ )
|
||||
{
|
||||
gl_context_->Init( app_->window );
|
||||
LoadResources();
|
||||
initialized_resources_ = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// initialize OpenGL ES and EGL
|
||||
if( EGL_SUCCESS != gl_context_->Resume( app_->window ) )
|
||||
{
|
||||
UnloadResources();
|
||||
LoadResources();
|
||||
}
|
||||
}
|
||||
|
||||
ShowUI();
|
||||
|
||||
// Initialize GL state.
|
||||
glEnable( GL_CULL_FACE );
|
||||
glEnable( GL_DEPTH_TEST );
|
||||
glDepthFunc( GL_LEQUAL );
|
||||
|
||||
//Note that screen size might have been changed
|
||||
glViewport( 0, 0, gl_context_->GetScreenWidth(), gl_context_->GetScreenHeight() );
|
||||
renderer_.UpdateViewport();
|
||||
|
||||
tap_camera_.SetFlip( 1.f, -1.f, -1.f );
|
||||
tap_camera_.SetPinchTransformFactor( 10.f, 10.f, 8.f );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just the current frame in the display.
|
||||
*/
|
||||
void Engine::DrawFrame()
|
||||
{
|
||||
float fps;
|
||||
if( monitor_.Update( fps ) )
|
||||
{
|
||||
UpdateFPS( fps );
|
||||
}
|
||||
double dTime = monitor_.GetCurrentTime();
|
||||
renderer_.Update( dTime );
|
||||
|
||||
// Just fill the screen with a color.
|
||||
glClearColor( 0.5f, 0.5f, 0.5f, 1.f );
|
||||
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
|
||||
renderer_.Render();
|
||||
|
||||
// Swap
|
||||
if( EGL_SUCCESS != gl_context_->Swap() )
|
||||
{
|
||||
UnloadResources();
|
||||
LoadResources();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tear down the EGL context currently associated with the display.
|
||||
*/
|
||||
void Engine::TermDisplay()
|
||||
{
|
||||
gl_context_->Suspend();
|
||||
|
||||
}
|
||||
|
||||
void Engine::TrimMemory()
|
||||
{
|
||||
LOGI( "Trimming memory" );
|
||||
gl_context_->Invalidate();
|
||||
}
|
||||
/**
|
||||
* Process the next input event.
|
||||
*/
|
||||
int32_t Engine::HandleInput( android_app* app,
|
||||
AInputEvent* event )
|
||||
{
|
||||
Engine* eng = (Engine*) app->userData;
|
||||
if( AInputEvent_getType( event ) == AINPUT_EVENT_TYPE_MOTION )
|
||||
{
|
||||
ndk_helper::GESTURE_STATE doubleTapState = eng->doubletap_detector_.Detect( event );
|
||||
ndk_helper::GESTURE_STATE dragState = eng->drag_detector_.Detect( event );
|
||||
ndk_helper::GESTURE_STATE pinchState = eng->pinch_detector_.Detect( event );
|
||||
|
||||
//Double tap detector has a priority over other detectors
|
||||
if( doubleTapState == ndk_helper::GESTURE_STATE_ACTION )
|
||||
{
|
||||
//Detect double tap
|
||||
eng->tap_camera_.Reset( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
//Handle drag state
|
||||
if( dragState & ndk_helper::GESTURE_STATE_START )
|
||||
{
|
||||
//Otherwise, start dragging
|
||||
ndk_helper::Vec2 v;
|
||||
eng->drag_detector_.GetPointer( v );
|
||||
eng->TransformPosition( v );
|
||||
eng->tap_camera_.BeginDrag( v );
|
||||
}
|
||||
else if( dragState & ndk_helper::GESTURE_STATE_MOVE )
|
||||
{
|
||||
ndk_helper::Vec2 v;
|
||||
eng->drag_detector_.GetPointer( v );
|
||||
eng->TransformPosition( v );
|
||||
eng->tap_camera_.Drag( v );
|
||||
}
|
||||
else if( dragState & ndk_helper::GESTURE_STATE_END )
|
||||
{
|
||||
eng->tap_camera_.EndDrag();
|
||||
}
|
||||
|
||||
//Handle pinch state
|
||||
if( pinchState & ndk_helper::GESTURE_STATE_START )
|
||||
{
|
||||
//Start new pinch
|
||||
ndk_helper::Vec2 v1;
|
||||
ndk_helper::Vec2 v2;
|
||||
eng->pinch_detector_.GetPointers( v1, v2 );
|
||||
eng->TransformPosition( v1 );
|
||||
eng->TransformPosition( v2 );
|
||||
eng->tap_camera_.BeginPinch( v1, v2 );
|
||||
}
|
||||
else if( pinchState & ndk_helper::GESTURE_STATE_MOVE )
|
||||
{
|
||||
//Multi touch
|
||||
//Start new pinch
|
||||
ndk_helper::Vec2 v1;
|
||||
ndk_helper::Vec2 v2;
|
||||
eng->pinch_detector_.GetPointers( v1, v2 );
|
||||
eng->TransformPosition( v1 );
|
||||
eng->TransformPosition( v2 );
|
||||
eng->tap_camera_.Pinch( v1, v2 );
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the next main command.
|
||||
*/
|
||||
void Engine::HandleCmd( struct android_app* app,
|
||||
int32_t cmd )
|
||||
{
|
||||
Engine* eng = (Engine*) app->userData;
|
||||
switch( cmd )
|
||||
{
|
||||
case APP_CMD_SAVE_STATE:
|
||||
break;
|
||||
case APP_CMD_INIT_WINDOW:
|
||||
// The window is being shown, get it ready.
|
||||
if( app->window != NULL )
|
||||
{
|
||||
eng->InitDisplay();
|
||||
eng->DrawFrame();
|
||||
}
|
||||
break;
|
||||
case APP_CMD_TERM_WINDOW:
|
||||
// The window is being hidden or closed, clean it up.
|
||||
eng->TermDisplay();
|
||||
eng->has_focus_ = false;
|
||||
break;
|
||||
case APP_CMD_STOP:
|
||||
break;
|
||||
case APP_CMD_GAINED_FOCUS:
|
||||
eng->ResumeSensors();
|
||||
//Start animation
|
||||
eng->has_focus_ = true;
|
||||
break;
|
||||
case APP_CMD_LOST_FOCUS:
|
||||
eng->SuspendSensors();
|
||||
// Also stop animating.
|
||||
eng->has_focus_ = false;
|
||||
eng->DrawFrame();
|
||||
break;
|
||||
case APP_CMD_LOW_MEMORY:
|
||||
//Free up GL resources
|
||||
eng->TrimMemory();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//Sensor handlers
|
||||
//-------------------------------------------------------------------------
|
||||
void Engine::InitSensors()
|
||||
{
|
||||
sensor_manager_ = ASensorManager_getInstance();
|
||||
accelerometer_sensor_ = ASensorManager_getDefaultSensor( sensor_manager_,
|
||||
ASENSOR_TYPE_ACCELEROMETER );
|
||||
sensor_event_queue_ = ASensorManager_createEventQueue( sensor_manager_, app_->looper,
|
||||
LOOPER_ID_USER, NULL, NULL );
|
||||
}
|
||||
|
||||
void Engine::ProcessSensors( int32_t id )
|
||||
{
|
||||
// If a sensor has data, process it now.
|
||||
if( id == LOOPER_ID_USER )
|
||||
{
|
||||
if( accelerometer_sensor_ != NULL )
|
||||
{
|
||||
ASensorEvent event;
|
||||
while( ASensorEventQueue_getEvents( sensor_event_queue_, &event, 1 ) > 0 )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::ResumeSensors()
|
||||
{
|
||||
// When our app gains focus, we start monitoring the accelerometer.
|
||||
if( accelerometer_sensor_ != NULL )
|
||||
{
|
||||
ASensorEventQueue_enableSensor( sensor_event_queue_, accelerometer_sensor_ );
|
||||
// We'd like to get 60 events per second (in us).
|
||||
ASensorEventQueue_setEventRate( sensor_event_queue_, accelerometer_sensor_,
|
||||
(1000L / 60) * 1000 );
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::SuspendSensors()
|
||||
{
|
||||
// When our app loses focus, we stop monitoring the accelerometer.
|
||||
// This is to avoid consuming battery while not being used.
|
||||
if( accelerometer_sensor_ != NULL )
|
||||
{
|
||||
ASensorEventQueue_disableSensor( sensor_event_queue_, accelerometer_sensor_ );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//Misc
|
||||
//-------------------------------------------------------------------------
|
||||
void Engine::SetState( android_app* state )
|
||||
{
|
||||
app_ = state;
|
||||
doubletap_detector_.SetConfiguration( app_->config );
|
||||
drag_detector_.SetConfiguration( app_->config );
|
||||
pinch_detector_.SetConfiguration( app_->config );
|
||||
}
|
||||
|
||||
bool Engine::IsReady()
|
||||
{
|
||||
if( has_focus_ )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Engine::TransformPosition( ndk_helper::Vec2& vec )
|
||||
{
|
||||
vec = ndk_helper::Vec2( 2.0f, 2.0f ) * vec
|
||||
/ ndk_helper::Vec2( gl_context_->GetScreenWidth(), gl_context_->GetScreenHeight() )
|
||||
- ndk_helper::Vec2( 1.f, 1.f );
|
||||
}
|
||||
|
||||
void Engine::ShowUI()
|
||||
{
|
||||
JNIEnv *jni;
|
||||
app_->activity->vm->AttachCurrentThread( &jni, NULL );
|
||||
|
||||
//Default class retrieval
|
||||
jclass clazz = jni->GetObjectClass( app_->activity->clazz );
|
||||
jmethodID methodID = jni->GetMethodID( clazz, "showUI", "()V" );
|
||||
jni->CallVoidMethod( app_->activity->clazz, methodID );
|
||||
|
||||
app_->activity->vm->DetachCurrentThread();
|
||||
return;
|
||||
}
|
||||
|
||||
void Engine::UpdateFPS( float fps )
|
||||
{
|
||||
JNIEnv *jni;
|
||||
app_->activity->vm->AttachCurrentThread( &jni, NULL );
|
||||
|
||||
//Default class retrieval
|
||||
jclass clazz = jni->GetObjectClass( app_->activity->clazz );
|
||||
jmethodID methodID = jni->GetMethodID( clazz, "updateFPS", "(F)V" );
|
||||
jni->CallVoidMethod( app_->activity->clazz, methodID, fps );
|
||||
|
||||
app_->activity->vm->DetachCurrentThread();
|
||||
return;
|
||||
}
|
||||
|
||||
Engine g_engine;
|
||||
|
||||
/**
|
||||
* This is the main entry point of a native application that is using
|
||||
* android_native_app_glue. It runs in its own thread, with its own
|
||||
* event loop for receiving input events and doing other things.
|
||||
*/
|
||||
void android_main( android_app* state )
|
||||
{
|
||||
app_dummy();
|
||||
|
||||
g_engine.SetState( state );
|
||||
|
||||
//Init helper functions
|
||||
ndk_helper::JNIHelper::GetInstance()->Init( state->activity, HELPER_CLASS_NAME );
|
||||
|
||||
state->userData = &g_engine;
|
||||
state->onAppCmd = Engine::HandleCmd;
|
||||
state->onInputEvent = Engine::HandleInput;
|
||||
|
||||
#ifdef USE_NDK_PROFILER
|
||||
monstartup("libMoreTeapotsNativeActivity.so");
|
||||
#endif
|
||||
|
||||
// Prepare to monitor accelerometer
|
||||
g_engine.InitSensors();
|
||||
|
||||
// loop waiting for stuff to do.
|
||||
while( 1 )
|
||||
{
|
||||
// Read all pending events.
|
||||
int id;
|
||||
int events;
|
||||
android_poll_source* source;
|
||||
|
||||
// If not animating, we will block forever waiting for events.
|
||||
// If animating, we loop until all events are read, then continue
|
||||
// to draw the next frame of animation.
|
||||
while( (id = ALooper_pollAll( g_engine.IsReady() ? 0 : -1, NULL, &events, (void**) &source ))
|
||||
>= 0 )
|
||||
{
|
||||
// Process this event.
|
||||
if( source != NULL )
|
||||
source->process( state, source );
|
||||
|
||||
g_engine.ProcessSensors( id );
|
||||
|
||||
// Check if we are exiting.
|
||||
if( state->destroyRequested != 0 )
|
||||
{
|
||||
g_engine.TermDisplay();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( g_engine.IsReady() )
|
||||
{
|
||||
// Drawing is throttled to the screen update rate, so there
|
||||
// is no need to do timing here.
|
||||
g_engine.DrawFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
555
MoreTeapots/jni/MoreTeapotsRenderer.cpp
Normal file
@@ -0,0 +1,555 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// MoreTeapotsRenderer.cpp
|
||||
// Render teapots
|
||||
//--------------------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------------------
|
||||
// Include files
|
||||
//--------------------------------------------------------------------------------
|
||||
#include "MoreTeapotsRenderer.h"
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Teapot model data
|
||||
//--------------------------------------------------------------------------------
|
||||
#include "teapot.inl"
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Ctor
|
||||
//--------------------------------------------------------------------------------
|
||||
MoreTeapotsRenderer::MoreTeapotsRenderer() :
|
||||
geometry_instancing_support_( false )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Dtor
|
||||
//--------------------------------------------------------------------------------
|
||||
MoreTeapotsRenderer::~MoreTeapotsRenderer()
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Init
|
||||
//--------------------------------------------------------------------------------
|
||||
void MoreTeapotsRenderer::Init( const int32_t numX,
|
||||
const int32_t numY,
|
||||
const int32_t numZ )
|
||||
{
|
||||
if( ndk_helper::GLContext::GetInstance()->GetGLVersion() >= 3.0 )
|
||||
{
|
||||
geometry_instancing_support_ = true;
|
||||
}
|
||||
else if( ndk_helper::GLContext::GetInstance()->CheckExtension( "GL_NV_draw_instanced" )
|
||||
&& ndk_helper::GLContext::GetInstance()->CheckExtension(
|
||||
"GL_NV_uniform_buffer_object" ) )
|
||||
{
|
||||
LOGI( "Supported via extension!" );
|
||||
//_bGeometryInstancingSupport = true;
|
||||
//_bARBSupport = true; //Need to patch shaders
|
||||
//Currently this has been disabled
|
||||
}
|
||||
|
||||
//Settings
|
||||
glFrontFace( GL_CCW );
|
||||
|
||||
//Create Index buffer
|
||||
num_indices_ = sizeof(teapotIndices) / sizeof(teapotIndices[0]);
|
||||
glGenBuffers( 1, &ibo_ );
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo_ );
|
||||
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(teapotIndices), teapotIndices, GL_STATIC_DRAW );
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
|
||||
|
||||
//Create VBO
|
||||
num_vertices_ = sizeof(teapotPositions) / sizeof(teapotPositions[0]) / 3;
|
||||
int32_t iStride = sizeof(TEAPOT_VERTEX);
|
||||
int32_t iIndex = 0;
|
||||
TEAPOT_VERTEX* p = new TEAPOT_VERTEX[num_vertices_];
|
||||
for( int32_t i = 0; i < num_vertices_; ++i )
|
||||
{
|
||||
p[i].pos[0] = teapotPositions[iIndex];
|
||||
p[i].pos[1] = teapotPositions[iIndex + 1];
|
||||
p[i].pos[2] = teapotPositions[iIndex + 2];
|
||||
|
||||
p[i].normal[0] = teapotNormals[iIndex];
|
||||
p[i].normal[1] = teapotNormals[iIndex + 1];
|
||||
p[i].normal[2] = teapotNormals[iIndex + 2];
|
||||
iIndex += 3;
|
||||
}
|
||||
glGenBuffers( 1, &vbo_ );
|
||||
glBindBuffer( GL_ARRAY_BUFFER, vbo_ );
|
||||
glBufferData( GL_ARRAY_BUFFER, iStride * num_vertices_, p, GL_STATIC_DRAW );
|
||||
glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
||||
delete[] p;
|
||||
|
||||
//Init Projection matrices
|
||||
teapot_x_ = numX;
|
||||
teapot_y_ = numY;
|
||||
teapot_z_ = numZ;
|
||||
vec_mat_models_.reserve( teapot_x_ * teapot_y_ * teapot_z_ );
|
||||
|
||||
UpdateViewport();
|
||||
|
||||
const float total_width = 500.f;
|
||||
float gap_x = total_width / (teapot_x_ - 1);
|
||||
float gap_y = total_width / (teapot_y_ - 1);
|
||||
float gap_z = total_width / (teapot_z_ - 1);
|
||||
float offset_x = -total_width / 2.f;
|
||||
float offset_y = -total_width / 2.f;
|
||||
float offset_z = -total_width / 2.f;
|
||||
|
||||
for( int32_t iX = 0; iX < teapot_x_; ++iX )
|
||||
for( int32_t iY = 0; iY < teapot_y_; ++iY )
|
||||
for( int32_t iZ = 0; iZ < teapot_z_; ++iZ )
|
||||
{
|
||||
vec_mat_models_.push_back(
|
||||
ndk_helper::Mat4::Translation( iX * gap_x + offset_x, iY * gap_y + offset_y,
|
||||
iZ * gap_z + offset_z ) );
|
||||
vec_colors_.push_back(
|
||||
ndk_helper::Vec3( random() / float( RAND_MAX * 1.1 ),
|
||||
random() / float( RAND_MAX * 1.1 ),
|
||||
random() / float( RAND_MAX * 1.1 ) ) );
|
||||
|
||||
float fX = random() / float( RAND_MAX ) - 0.5f;
|
||||
float fY = random() / float( RAND_MAX ) - 0.5f;
|
||||
vec_rotations_.push_back( ndk_helper::Vec2( fX * 0.05f, fY * 0.05f ) );
|
||||
vec_current_rotations_.push_back( ndk_helper::Vec2( fX * M_PI, fY * M_PI ) );
|
||||
}
|
||||
|
||||
if( geometry_instancing_support_ )
|
||||
{
|
||||
//
|
||||
//Create parameter dictionary for shader patch
|
||||
std::map<std::string, std::string> param;
|
||||
param[std::string( "%NUM_TEAPOT%" )] = ToString( teapot_x_ * teapot_y_ * teapot_z_ );
|
||||
param[std::string( "%LOCATION_VERTEX%" )] = ToString( ATTRIB_VERTEX );
|
||||
param[std::string( "%LOCATION_NORMAL%" )] = ToString( ATTRIB_NORMAL );
|
||||
if( arb_support_ )
|
||||
param[std::string( "%ARB%" )] = std::string( "ARB" );
|
||||
else
|
||||
param[std::string( "%ARB%" )] = std::string( "" );
|
||||
|
||||
//Load shader
|
||||
bool b = LoadShadersES3( &shader_param_, "Shaders/VS_ShaderPlainES3.vsh",
|
||||
"Shaders/ShaderPlainES3.fsh", param );
|
||||
if( b )
|
||||
{
|
||||
//
|
||||
//Create uniform buffer
|
||||
//
|
||||
GLuint bindingPoint = 1;
|
||||
GLuint blockIndex;
|
||||
blockIndex = glGetUniformBlockIndex( shader_param_.program_, "ParamBlock" );
|
||||
glUniformBlockBinding( shader_param_.program_, blockIndex, bindingPoint );
|
||||
|
||||
//Retrieve array stride value
|
||||
int32_t iNumIndices;
|
||||
glGetActiveUniformBlockiv( shader_param_.program_, blockIndex,
|
||||
GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &iNumIndices );
|
||||
GLint i[iNumIndices];
|
||||
GLint stride[iNumIndices];
|
||||
glGetActiveUniformBlockiv( shader_param_.program_, blockIndex,
|
||||
GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, i );
|
||||
glGetActiveUniformsiv( shader_param_.program_, iNumIndices, (GLuint*) i,
|
||||
GL_UNIFORM_ARRAY_STRIDE, stride );
|
||||
|
||||
ubo_matrix_stride_ = stride[0] / sizeof(float);
|
||||
ubo_vector_stride_ = stride[2] / sizeof(float);
|
||||
|
||||
glGenBuffers( 1, &ubo_ );
|
||||
glBindBuffer( GL_UNIFORM_BUFFER, ubo_ );
|
||||
glBindBufferBase( GL_UNIFORM_BUFFER, bindingPoint, ubo_ );
|
||||
|
||||
//Store color value which wouldn't be updated every frame
|
||||
int32_t iSize = teapot_x_ * teapot_y_ * teapot_z_
|
||||
* (ubo_matrix_stride_ + ubo_matrix_stride_ + ubo_vector_stride_); //Mat4 + Mat4 + Vec3 + 1 stride
|
||||
float* pBuffer = new float[iSize];
|
||||
float* pColor = pBuffer + teapot_x_ * teapot_y_ * teapot_z_ * ubo_matrix_stride_ * 2;
|
||||
for( int32_t i = 0; i < teapot_x_ * teapot_y_ * teapot_z_; ++i )
|
||||
{
|
||||
memcpy( pColor, &vec_colors_[i], 3 * sizeof(float) );
|
||||
pColor += ubo_vector_stride_; //Assuming std140 layout which is 4 DWORD stride for vectors
|
||||
}
|
||||
|
||||
glBufferData( GL_UNIFORM_BUFFER, iSize * sizeof(float), pBuffer, GL_DYNAMIC_DRAW );
|
||||
delete[] pBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGI( "Shader compilation failed!! Falls back to ES2.0 pass" );
|
||||
//This happens some devices.
|
||||
geometry_instancing_support_ = false;
|
||||
//Load shader for GLES2.0
|
||||
LoadShaders( &shader_param_, "Shaders/VS_ShaderPlain.vsh", "Shaders/ShaderPlain.fsh" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Load shader for GLES2.0
|
||||
LoadShaders( &shader_param_, "Shaders/VS_ShaderPlain.vsh", "Shaders/ShaderPlain.fsh" );
|
||||
}
|
||||
}
|
||||
|
||||
void MoreTeapotsRenderer::UpdateViewport()
|
||||
{
|
||||
int32_t viewport[4];
|
||||
glGetIntegerv( GL_VIEWPORT, viewport );
|
||||
float fAspect = (float) viewport[2] / (float) viewport[3];
|
||||
|
||||
const float CAM_NEAR = 5.f;
|
||||
const float CAM_FAR = 10000.f;
|
||||
bool bRotate = false;
|
||||
mat_projection_ = ndk_helper::Mat4::Perspective( fAspect, 1.f, CAM_NEAR, CAM_FAR );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Unload
|
||||
//--------------------------------------------------------------------------------
|
||||
void MoreTeapotsRenderer::Unload()
|
||||
{
|
||||
if( vbo_ )
|
||||
{
|
||||
glDeleteBuffers( 1, &vbo_ );
|
||||
vbo_ = 0;
|
||||
}
|
||||
if( ubo_ )
|
||||
{
|
||||
glDeleteBuffers( 1, &ubo_ );
|
||||
ubo_ = 0;
|
||||
}
|
||||
if( ibo_ )
|
||||
{
|
||||
glDeleteBuffers( 1, &ibo_ );
|
||||
ibo_ = 0;
|
||||
}
|
||||
if( shader_param_.program_ )
|
||||
{
|
||||
glDeleteProgram( shader_param_.program_ );
|
||||
shader_param_.program_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Update
|
||||
//--------------------------------------------------------------------------------
|
||||
void MoreTeapotsRenderer::Update( float fTime )
|
||||
{
|
||||
const float CAM_X = 0.f;
|
||||
const float CAM_Y = 0.f;
|
||||
const float CAM_Z = 2000.f;
|
||||
|
||||
mat_view_ = ndk_helper::Mat4::LookAt( ndk_helper::Vec3( CAM_X, CAM_Y, CAM_Z ),
|
||||
ndk_helper::Vec3( 0.f, 0.f, 0.f ), ndk_helper::Vec3( 0.f, 1.f, 0.f ) );
|
||||
|
||||
if( camera_ )
|
||||
{
|
||||
camera_->Update();
|
||||
mat_view_ = camera_->GetTransformMatrix() * mat_view_ * camera_->GetRotationMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Render
|
||||
//--------------------------------------------------------------------------------
|
||||
void MoreTeapotsRenderer::Render()
|
||||
{
|
||||
// Bind the VBO
|
||||
glBindBuffer( GL_ARRAY_BUFFER, vbo_ );
|
||||
|
||||
int32_t iStride = sizeof(TEAPOT_VERTEX);
|
||||
// Pass the vertex data
|
||||
glVertexAttribPointer( ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, iStride, BUFFER_OFFSET( 0 ) );
|
||||
glEnableVertexAttribArray( ATTRIB_VERTEX );
|
||||
|
||||
glVertexAttribPointer( ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, iStride,
|
||||
BUFFER_OFFSET( 3 * sizeof(GLfloat) ) );
|
||||
glEnableVertexAttribArray( ATTRIB_NORMAL );
|
||||
|
||||
// Bind the IB
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo_ );
|
||||
|
||||
glUseProgram( shader_param_.program_ );
|
||||
|
||||
TEAPOT_MATERIALS material = { { 1.0f, 1.0f, 1.0f, 10.f }, { 0.1f, 0.1f, 0.1f }, };
|
||||
|
||||
//Update uniforms
|
||||
//
|
||||
//using glUniform3fv here was troublesome..
|
||||
//
|
||||
glUniform4f( shader_param_.material_specular_, material.specular_color[0],
|
||||
material.specular_color[1], material.specular_color[2], material.specular_color[3] );
|
||||
glUniform3f( shader_param_.material_ambient_, material.ambient_color[0],
|
||||
material.ambient_color[1], material.ambient_color[2] );
|
||||
|
||||
glUniform3f( shader_param_.light0_, 100.f, -200.f, -600.f );
|
||||
|
||||
if( geometry_instancing_support_ )
|
||||
{
|
||||
//
|
||||
//Geometry instancing, new feature in GLES3.0
|
||||
//
|
||||
|
||||
//Update UBO
|
||||
glBindBuffer( GL_UNIFORM_BUFFER, ubo_ );
|
||||
float* p = (float*) glMapBufferRange( GL_UNIFORM_BUFFER, 0,
|
||||
teapot_x_ * teapot_y_ * teapot_z_ * (ubo_matrix_stride_ * 2) * sizeof(float),
|
||||
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT );
|
||||
float* pMVPMat = p;
|
||||
float* pMVMat = p + teapot_x_ * teapot_y_ * teapot_z_ * ubo_matrix_stride_;
|
||||
for( int32_t i = 0; i < teapot_x_ * teapot_y_ * teapot_z_; ++i )
|
||||
{
|
||||
//Rotation
|
||||
float fX, fY;
|
||||
vec_current_rotations_[i] += vec_rotations_[i];
|
||||
vec_current_rotations_[i].Value( fX, fY );
|
||||
ndk_helper::Mat4 mat_rotation = ndk_helper::Mat4::RotationX( fX )
|
||||
* ndk_helper::Mat4::RotationY( fY );
|
||||
|
||||
// Feed Projection and Model View matrices to the shaders
|
||||
ndk_helper::Mat4 mat_v = mat_view_ * vec_mat_models_[i] * mat_rotation;
|
||||
ndk_helper::Mat4 mat_vp = mat_projection_ * mat_v;
|
||||
|
||||
memcpy( pMVPMat, mat_vp.Ptr(), sizeof(mat_v) );
|
||||
pMVPMat += ubo_matrix_stride_;
|
||||
|
||||
memcpy( pMVMat, mat_v.Ptr(), sizeof(mat_v) );
|
||||
pMVMat += ubo_matrix_stride_;
|
||||
}
|
||||
glUnmapBuffer( GL_UNIFORM_BUFFER );
|
||||
|
||||
//Instanced rendering
|
||||
glDrawElementsInstanced( GL_TRIANGLES, num_indices_, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0),
|
||||
teapot_x_ * teapot_y_ * teapot_z_ );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//Regular rendering pass
|
||||
for( int32_t i = 0; i < teapot_x_ * teapot_y_ * teapot_z_; ++i )
|
||||
{
|
||||
//Set diffuse
|
||||
float x, y, z;
|
||||
vec_colors_[i].Value( x, y, z );
|
||||
glUniform4f( shader_param_.material_diffuse_, x, y, z, 1.f );
|
||||
|
||||
//Rotation
|
||||
vec_current_rotations_[i] += vec_rotations_[i];
|
||||
vec_current_rotations_[i].Value( x, y );
|
||||
ndk_helper::Mat4 mat_rotation = ndk_helper::Mat4::RotationX( x )
|
||||
* ndk_helper::Mat4::RotationY( y );
|
||||
|
||||
// Feed Projection and Model View matrices to the shaders
|
||||
ndk_helper::Mat4 mat_v = mat_view_ * vec_mat_models_[i] * mat_rotation;
|
||||
ndk_helper::Mat4 mat_vp = mat_projection_ * mat_v;
|
||||
glUniformMatrix4fv( shader_param_.matrix_projection_, 1, GL_FALSE, mat_vp.Ptr() );
|
||||
glUniformMatrix4fv( shader_param_.matrix_view_, 1, GL_FALSE, mat_v.Ptr() );
|
||||
|
||||
glDrawElements( GL_TRIANGLES, num_indices_, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0) );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// LoadShaders
|
||||
//--------------------------------------------------------------------------------
|
||||
bool MoreTeapotsRenderer::LoadShaders( SHADER_PARAMS* params,
|
||||
const char* strVsh,
|
||||
const char* strFsh )
|
||||
{
|
||||
//
|
||||
//Shader load for GLES2
|
||||
//In GLES2.0, shader attribute locations need to be explicitly specified before linking
|
||||
//
|
||||
GLuint program;
|
||||
GLuint vertShader, fragShader;
|
||||
char *vertShaderPathname, *fragShaderPathname;
|
||||
|
||||
// Create shader program
|
||||
program = glCreateProgram();
|
||||
LOGI( "Created Shader %d", program );
|
||||
|
||||
// Create and compile vertex shader
|
||||
if( !ndk_helper::shader::CompileShader( &vertShader, GL_VERTEX_SHADER, strVsh ) )
|
||||
{
|
||||
LOGI( "Failed to compile vertex shader" );
|
||||
glDeleteProgram( program );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create and compile fragment shader
|
||||
if( !ndk_helper::shader::CompileShader( &fragShader, GL_FRAGMENT_SHADER, strFsh ) )
|
||||
{
|
||||
LOGI( "Failed to compile fragment shader" );
|
||||
glDeleteProgram( program );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attach vertex shader to program
|
||||
glAttachShader( program, vertShader );
|
||||
|
||||
// Attach fragment shader to program
|
||||
glAttachShader( program, fragShader );
|
||||
|
||||
// Bind attribute locations
|
||||
// this needs to be done prior to linking
|
||||
glBindAttribLocation( program, ATTRIB_VERTEX, "myVertex" );
|
||||
glBindAttribLocation( program, ATTRIB_NORMAL, "myNormal" );
|
||||
|
||||
// Link program
|
||||
if( !ndk_helper::shader::LinkProgram( program ) )
|
||||
{
|
||||
LOGI( "Failed to link program: %d", program );
|
||||
|
||||
if( vertShader )
|
||||
{
|
||||
glDeleteShader( vertShader );
|
||||
vertShader = 0;
|
||||
}
|
||||
if( fragShader )
|
||||
{
|
||||
glDeleteShader( fragShader );
|
||||
fragShader = 0;
|
||||
}
|
||||
if( program )
|
||||
{
|
||||
glDeleteProgram( program );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get uniform locations
|
||||
params->matrix_projection_ = glGetUniformLocation( program, "uPMatrix" );
|
||||
params->matrix_view_ = glGetUniformLocation( program, "uMVMatrix" );
|
||||
|
||||
params->light0_ = glGetUniformLocation( program, "vLight0" );
|
||||
params->material_diffuse_ = glGetUniformLocation( program, "vMaterialDiffuse" );
|
||||
params->material_ambient_ = glGetUniformLocation( program, "vMaterialAmbient" );
|
||||
params->material_specular_ = glGetUniformLocation( program, "vMaterialSpecular" );
|
||||
|
||||
// Release vertex and fragment shaders
|
||||
if( vertShader )
|
||||
glDeleteShader( vertShader );
|
||||
if( fragShader )
|
||||
glDeleteShader( fragShader );
|
||||
|
||||
params->program_ = program;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoreTeapotsRenderer::LoadShadersES3( SHADER_PARAMS* params,
|
||||
const char* strVsh,
|
||||
const char* strFsh,
|
||||
std::map<std::string, std::string>&shaderParams )
|
||||
{
|
||||
//
|
||||
//Shader load for GLES3
|
||||
//In GLES3.0, shader attribute index can be described in a shader code directly with layout() attribute
|
||||
//
|
||||
GLuint program;
|
||||
GLuint vertShader, fragShader;
|
||||
char *vertShaderPathname, *fragShaderPathname;
|
||||
|
||||
// Create shader program
|
||||
program = glCreateProgram();
|
||||
LOGI( "Created Shader %d", program );
|
||||
|
||||
// Create and compile vertex shader
|
||||
if( !ndk_helper::shader::CompileShader( &vertShader, GL_VERTEX_SHADER, strVsh, shaderParams ) )
|
||||
{
|
||||
LOGI( "Failed to compile vertex shader" );
|
||||
glDeleteProgram( program );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create and compile fragment shader
|
||||
if( !ndk_helper::shader::CompileShader( &fragShader, GL_FRAGMENT_SHADER, strFsh,
|
||||
shaderParams ) )
|
||||
{
|
||||
LOGI( "Failed to compile fragment shader" );
|
||||
glDeleteProgram( program );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attach vertex shader to program
|
||||
glAttachShader( program, vertShader );
|
||||
|
||||
// Attach fragment shader to program
|
||||
glAttachShader( program, fragShader );
|
||||
|
||||
// Link program
|
||||
if( !ndk_helper::shader::LinkProgram( program ) )
|
||||
{
|
||||
LOGI( "Failed to link program: %d", program );
|
||||
|
||||
if( vertShader )
|
||||
{
|
||||
glDeleteShader( vertShader );
|
||||
vertShader = 0;
|
||||
}
|
||||
if( fragShader )
|
||||
{
|
||||
glDeleteShader( fragShader );
|
||||
fragShader = 0;
|
||||
}
|
||||
if( program )
|
||||
{
|
||||
glDeleteProgram( program );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get uniform locations
|
||||
params->light0_ = glGetUniformLocation( program, "vLight0" );
|
||||
params->material_ambient_ = glGetUniformLocation( program, "vMaterialAmbient" );
|
||||
params->material_specular_ = glGetUniformLocation( program, "vMaterialSpecular" );
|
||||
|
||||
// Release vertex and fragment shaders
|
||||
if( vertShader )
|
||||
glDeleteShader( vertShader );
|
||||
if( fragShader )
|
||||
glDeleteShader( fragShader );
|
||||
|
||||
params->program_ = program;
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Bind
|
||||
//--------------------------------------------------------------------------------
|
||||
bool MoreTeapotsRenderer::Bind( ndk_helper::TapCamera* camera )
|
||||
{
|
||||
camera_ = camera;
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Helper functions
|
||||
//--------------------------------------------------------------------------------
|
||||
std::string MoreTeapotsRenderer::ToString( const int32_t i )
|
||||
{
|
||||
char str[64];
|
||||
snprintf( str, sizeof(str), "%d", i );
|
||||
return std::string( str );
|
||||
}
|
||||
|
||||
126
MoreTeapots/jni/MoreTeapotsRenderer.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// MoreTeapotsRenderer.h
|
||||
// Renderer for teapots
|
||||
//--------------------------------------------------------------------------------
|
||||
#ifndef _MoreTeapotsRenderer_H
|
||||
#define _MoreTeapotsRenderer_H
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Include files
|
||||
//--------------------------------------------------------------------------------
|
||||
#include <jni.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <GLES/gl.h>
|
||||
|
||||
#include <android/sensor.h>
|
||||
#include <android/log.h>
|
||||
#include <android_native_app_glue.h>
|
||||
#include <android/native_window_jni.h>
|
||||
#include <cpu-features.h>
|
||||
|
||||
#define CLASS_NAME "android/app/NativeActivity"
|
||||
#define APPLICATION_CLASS_NAME "com/sample/moreteapotss/MoreTeapotsApplication"
|
||||
|
||||
#include "NDKHelper.h"
|
||||
|
||||
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
|
||||
|
||||
struct TEAPOT_VERTEX
|
||||
{
|
||||
float pos[3];
|
||||
float normal[3];
|
||||
};
|
||||
|
||||
enum SHADER_ATTRIBUTES
|
||||
{
|
||||
ATTRIB_VERTEX, ATTRIB_NORMAL, ATTRIB_COLOR, ATTRIB_UV
|
||||
};
|
||||
|
||||
struct SHADER_PARAMS
|
||||
{
|
||||
GLuint program_;
|
||||
GLuint light0_;
|
||||
GLuint material_diffuse_;
|
||||
GLuint material_ambient_;
|
||||
GLuint material_specular_;
|
||||
|
||||
GLuint matrix_projection_;
|
||||
GLuint matrix_view_;
|
||||
};
|
||||
|
||||
struct TEAPOT_MATERIALS
|
||||
{
|
||||
float specular_color[4];
|
||||
float ambient_color[3];
|
||||
};
|
||||
|
||||
class MoreTeapotsRenderer
|
||||
{
|
||||
int32_t num_indices_;
|
||||
int32_t num_vertices_;
|
||||
GLuint ibo_;
|
||||
GLuint vbo_;
|
||||
GLuint ubo_;
|
||||
|
||||
SHADER_PARAMS shader_param_;
|
||||
bool LoadShaders( SHADER_PARAMS* params,
|
||||
const char* strVsh,
|
||||
const char* strFsh );
|
||||
bool LoadShadersES3( SHADER_PARAMS* params,
|
||||
const char* strVsh,
|
||||
const char* strFsh,
|
||||
std::map<std::string, std::string>&shaderParameters );
|
||||
|
||||
ndk_helper::Mat4 mat_projection_;
|
||||
ndk_helper::Mat4 mat_view_;
|
||||
std::vector<ndk_helper::Mat4> vec_mat_models_;
|
||||
std::vector<ndk_helper::Vec3> vec_colors_;
|
||||
std::vector<ndk_helper::Vec2> vec_rotations_;
|
||||
std::vector<ndk_helper::Vec2> vec_current_rotations_;
|
||||
|
||||
ndk_helper::TapCamera* camera_;
|
||||
|
||||
int32_t teapot_x_;
|
||||
int32_t teapot_y_;
|
||||
int32_t teapot_z_;
|
||||
int32_t ubo_matrix_stride_;
|
||||
int32_t ubo_vector_stride_;
|
||||
bool geometry_instancing_support_;
|
||||
bool arb_support_;
|
||||
|
||||
std::string ToString( const int32_t i );
|
||||
public:
|
||||
MoreTeapotsRenderer();
|
||||
virtual ~MoreTeapotsRenderer();
|
||||
void Init( const int32_t numX,
|
||||
const int32_t numY,
|
||||
const int32_t numZ );
|
||||
void Render();
|
||||
void Update( float dTime );
|
||||
bool Bind( ndk_helper::TapCamera* camera );
|
||||
void Unload();
|
||||
void UpdateViewport();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
2057
MoreTeapots/jni/teapot.inl
Normal file
3
MoreTeapots/lint.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
</lint>
|
||||
14
MoreTeapots/project.properties
Normal file
@@ -0,0 +1,14 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=Google Inc.:Google APIs:19
|
||||
BIN
MoreTeapots/res/drawable-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
MoreTeapots/res/drawable-ldpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
MoreTeapots/res/drawable-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
MoreTeapots/res/drawable-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
@@ -9,8 +9,8 @@
|
||||
android:id="@+id/textViewFPS"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:text="@string/fps"
|
||||
android:gravity="right"
|
||||
android:text="0.0 FPS"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<resources>
|
||||
|
||||
<string name="app_name">More Teapots</string>
|
||||
<string name="fps">0.0 FPS</string>
|
||||
|
||||
</resources>
|
||||
202
MoreTeapots/src/com/sample/helper/NDKHelper.java
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.sample.helper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioTrack;
|
||||
import android.opengl.GLUtils;
|
||||
import android.util.Log;
|
||||
|
||||
public class NDKHelper
|
||||
{
|
||||
private static Context context;
|
||||
|
||||
public static void setContext(Context c)
|
||||
{
|
||||
Log.i("NDKHelper", "setContext:" + c);
|
||||
context = c;
|
||||
}
|
||||
|
||||
//
|
||||
// Load Bitmap
|
||||
// Java helper is useful decoding PNG, TIFF etc rather than linking libPng
|
||||
// etc separately
|
||||
//
|
||||
private int nextPOT(int i)
|
||||
{
|
||||
int pot = 1;
|
||||
while (pot < i)
|
||||
pot <<= 1;
|
||||
return pot;
|
||||
}
|
||||
|
||||
private Bitmap scaleBitmap(Bitmap bitmapToScale, float newWidth, float newHeight)
|
||||
{
|
||||
if (bitmapToScale == null)
|
||||
return null;
|
||||
// get the original width and height
|
||||
int width = bitmapToScale.getWidth();
|
||||
int height = bitmapToScale.getHeight();
|
||||
// create a matrix for the manipulation
|
||||
Matrix matrix = new Matrix();
|
||||
|
||||
// resize the bit map
|
||||
matrix.postScale(newWidth / width, newHeight / height);
|
||||
|
||||
// recreate the new Bitmap and set it back
|
||||
return Bitmap.createBitmap(bitmapToScale, 0, 0, bitmapToScale.getWidth(),
|
||||
bitmapToScale.getHeight(), matrix, true);
|
||||
}
|
||||
|
||||
public boolean loadTexture(String path)
|
||||
{
|
||||
Bitmap bitmap = null;
|
||||
try
|
||||
{
|
||||
String str = path;
|
||||
if (!path.startsWith("/"))
|
||||
{
|
||||
str = "/" + path;
|
||||
}
|
||||
|
||||
File file = new File(context.getExternalFilesDir(null), str);
|
||||
if (file.canRead())
|
||||
{
|
||||
bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
|
||||
} else
|
||||
{
|
||||
bitmap = BitmapFactory.decodeStream(context.getResources().getAssets()
|
||||
.open(path));
|
||||
}
|
||||
// Matrix matrix = new Matrix();
|
||||
// // resize the bit map
|
||||
// matrix.postScale(-1F, 1F);
|
||||
//
|
||||
// // recreate the new Bitmap and set it back
|
||||
// bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
|
||||
// bitmap.getHeight(), matrix, true);
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
Log.w("NDKHelper", "Coundn't load a file:" + path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bitmap != null)
|
||||
{
|
||||
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public Bitmap openBitmap(String path, boolean iScalePOT)
|
||||
{
|
||||
Bitmap bitmap = null;
|
||||
try
|
||||
{
|
||||
bitmap = BitmapFactory.decodeStream(context.getResources().getAssets()
|
||||
.open(path));
|
||||
if (iScalePOT)
|
||||
{
|
||||
int originalWidth = getBitmapWidth(bitmap);
|
||||
int originalHeight = getBitmapHeight(bitmap);
|
||||
int width = nextPOT(originalWidth);
|
||||
int height = nextPOT(originalHeight);
|
||||
if (originalWidth != width || originalHeight != height)
|
||||
{
|
||||
// Scale it
|
||||
bitmap = scaleBitmap(bitmap, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
Log.w("NDKHelper", "Coundn't load a file:" + path);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public int getBitmapWidth(Bitmap bmp)
|
||||
{
|
||||
return bmp.getWidth();
|
||||
}
|
||||
|
||||
public int getBitmapHeight(Bitmap bmp)
|
||||
{
|
||||
return bmp.getHeight();
|
||||
}
|
||||
|
||||
public void getBitmapPixels(Bitmap bmp, int[] pixels)
|
||||
{
|
||||
int w = bmp.getWidth();
|
||||
int h = bmp.getHeight();
|
||||
bmp.getPixels(pixels, 0, w, 0, 0, w, h);
|
||||
}
|
||||
|
||||
public void closeBitmap(Bitmap bmp)
|
||||
{
|
||||
bmp.recycle();
|
||||
}
|
||||
|
||||
public static String getNativeLibraryDirectory(Context appContext)
|
||||
{
|
||||
ApplicationInfo ai = context.getApplicationInfo();
|
||||
|
||||
Log.w("NDKHelper", "ai.nativeLibraryDir:" + ai.nativeLibraryDir);
|
||||
|
||||
if ((ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
|
||||
|| (ai.flags & ApplicationInfo.FLAG_SYSTEM) == 0)
|
||||
{
|
||||
return ai.nativeLibraryDir;
|
||||
}
|
||||
return "/system/lib/";
|
||||
}
|
||||
|
||||
public int getNativeAudioBufferSize()
|
||||
{
|
||||
int SDK_INT = android.os.Build.VERSION.SDK_INT;
|
||||
if (SDK_INT >= 17)
|
||||
{
|
||||
AudioManager am = (AudioManager) context
|
||||
.getSystemService(Context.AUDIO_SERVICE);
|
||||
String framesPerBuffer = am
|
||||
.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
|
||||
return Integer.parseInt(framesPerBuffer);
|
||||
} else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int getNativeAudioSampleRate()
|
||||
{
|
||||
return AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_SYSTEM);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -35,9 +35,8 @@ import android.widget.Toast;
|
||||
public class MoreTeapotsApplication extends Application {
|
||||
private static Context context;
|
||||
public void onCreate(){
|
||||
super.onCreate();
|
||||
|
||||
context=getApplicationContext();
|
||||
NDKHelper.setContext(context);
|
||||
Log.w("native-activity", "onCreate");
|
||||
|
||||
final PackageManager pm = getApplicationContext().getPackageManager();
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.sample.moreteapots;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.NativeActivity;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
@@ -53,7 +51,6 @@ public class MoreTeapotsNativeActivity extends NativeActivity {
|
||||
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
@@ -76,10 +73,14 @@ public class MoreTeapotsNativeActivity extends NativeActivity {
|
||||
protected void onPause()
|
||||
{
|
||||
super.onPause();
|
||||
if (_popupWindow != null) {
|
||||
|
||||
_popupWindow.dismiss();
|
||||
_popupWindow = null;
|
||||
}
|
||||
}
|
||||
// Our popup window, you will call it from your C/C++ code later
|
||||
|
||||
@TargetApi(19)
|
||||
void setImmersiveSticky() {
|
||||
View decorView = getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
@@ -94,7 +95,6 @@ public class MoreTeapotsNativeActivity extends NativeActivity {
|
||||
PopupWindow _popupWindow;
|
||||
TextView _label;
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
public void showUI()
|
||||
{
|
||||
if( _popupWindow != null )
|
||||
@@ -120,7 +120,7 @@ public class MoreTeapotsNativeActivity extends NativeActivity {
|
||||
_activity.setContentView(mainLayout, params);
|
||||
|
||||
// Show our UI over NativeActivity window
|
||||
_popupWindow.showAtLocation(mainLayout, Gravity.TOP | Gravity.START, 10, 10);
|
||||
_popupWindow.showAtLocation(mainLayout, Gravity.TOP | Gravity.LEFT, 10, 10);
|
||||
_popupWindow.update();
|
||||
|
||||
_label = (TextView)popupView.findViewById(R.id.textViewFPS);
|
||||
155
README.md
@@ -1,142 +1,51 @@
|
||||
# Android NDK Samples
|
||||
NDK Samples
|
||||
===========
|
||||
|
||||
This repository contains sample apps that use the [Android NDK].
|
||||
This repository contains samples for [Android NDK][0].
|
||||
|
||||
For an explanation of the layout of this repository, see
|
||||
[ARCHITECTURE.md](ARCHITECTURE.md).
|
||||
Pre-requisites
|
||||
--------------
|
||||
|
||||
## Build and run
|
||||
- [Android NDK][0]
|
||||
|
||||
[](https://github.com/android/ndk-samples/actions)
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
1. Clone the repository
|
||||
2. Open the whole project in Android Studio
|
||||
3. Install CMake 4.1.0 via the SDK Manager (must be done manually until
|
||||
https://issuetracker.google.com/443137057 is fixed).
|
||||
4. Select the sample you want to run in the top bar (you may need to sync gradle
|
||||
first)
|
||||
5. Click the play button to run the sample
|
||||
These samples use the NDK build system, you can build them by
|
||||
following the instructions in the
|
||||
[NDK documentation](https://developer.android.com/tools/sdk/ndk/index.html#Samples).
|
||||
|
||||
You can also build the samples from the command line if you prefer. Use
|
||||
`./gradlew build` to build everything (if you're on Windows, use `.\gradlew.bat`
|
||||
instead of `./gradlew`). For individual tasks, see `./gradlew tasks`. To see the
|
||||
tasks for an individual sample, run the `tasks` task for that directory. For
|
||||
example, `./gradlew :camera:basic:tasks` will show the tasks for the
|
||||
`camera/basic` app.
|
||||
Support
|
||||
-------
|
||||
|
||||
## I just want something to copy from as a starting point
|
||||
- [Google+ Community](https://plus.google.com/communities/105153134372062985968)
|
||||
- [Stack Overflow](http://stackoverflow.com/questions/tagged/android)
|
||||
|
||||
The samples in this repository are generally not a good starting point for a
|
||||
production quality app. They aim to demonstrate individual NDK APIs, but often
|
||||
make sacrifices to be succinct that make them unsuitable for a production app.
|
||||
This is gradually changing, but for now you should not do this.
|
||||
If you've found an error in this sample, please [file an issue](https://github.com/googlesamples/android-ndk/issues/new).
|
||||
|
||||
[Now in Android](https://github.com/android/nowinandroid/) is an excellent
|
||||
resource for production quality apps in general, but does not touch on NDK-
|
||||
specific issues. https://github.com/DanAlbert/ndk-app-template can help some
|
||||
with that until this repository is able to.
|
||||
Patches are encouraged, and may be submitted by [forking this project](https://github.com/googlesamples/android-ndk/fork) and
|
||||
submitting a pull request through GitHub. Please see [CONTRIBUTING.md](CONTRIBUTING.md) for more details.
|
||||
|
||||
You're most likely best served by using the New Project wizard in Android Studio
|
||||
to create a new app, then using those resources and the samples here as a
|
||||
reference. Android Studio's "Native C++" template is a good starting point for
|
||||
typical applications that need to use some C++ via JNI. The "Game Activity"
|
||||
template is a good starting point for game-like apps (that is, apps that do not
|
||||
use the Android UI, but instead render their own UI using OpenGL or Vulkan).
|
||||
|
||||
## Best practices shown here
|
||||
|
||||
There are a few best practices shown throughout this repository that should be
|
||||
explained, but there's no central place to discuss those in the code, so we'll
|
||||
discuss those here.
|
||||
|
||||
### `RegisterNatives`
|
||||
|
||||
We prefer using `RegisterNatives()` via `JNI_OnLoad()` over the name-based
|
||||
matching that Studio's New Project Wizard will typically create, e.g. JNI
|
||||
functions that follow the pattern `JNIEXPORT void JNICALL
|
||||
Java_com_etc_ClassName_methodName()`. That approach to matching C/C++ functions
|
||||
to their Java `native` function (or Kotlin `external fun`) makes for a shorter
|
||||
demo when there are only a small number of functions, but it has a number of
|
||||
disadvantages. See the [JNI tips] guide for details.
|
||||
|
||||
[JNI tips]: https://developer.android.com/ndk/guides/jni-tips#native-libraries
|
||||
|
||||
### Version scripts
|
||||
|
||||
All of the app libraries shown here are built using a version script. This is a
|
||||
file that explicitly lists which symbols should be exported from the library,
|
||||
and hides all the others. Version scripts function similarly to
|
||||
`-fvisibility=hidden`, but can go a step further and are capable of hiding
|
||||
symbols in static libraries that are used by your app. Hiding as many symbols as
|
||||
possible results in smaller binaries that load faster, as there are fewer
|
||||
relocations required and LTO can do a better job. They also run faster as
|
||||
same-library function calls do not need to be made through the PLT. There are no
|
||||
good reasons to not use a version script for your NDK code. See the NDK
|
||||
documentation on [controlling symbol visibility] for more information.
|
||||
|
||||
You can find these in each sample as the `lib<name>.map.txt` file (where
|
||||
`<name>` is the name of the library passed to `add_app_library()` in the
|
||||
`CMakeLists.txt` file). The build plumbing that uses the version scripts is in
|
||||
the definition of `add_app_library()` in `cmake/AppLibrary.cmake`.
|
||||
|
||||
[controlling symbol visibility]: https://developer.android.com/ndk/guides/symbol-visibility
|
||||
|
||||
## Additional documentation
|
||||
|
||||
- [Add Native Code to Your Project](https://developer.android.com/studio/projects/add-native-code.html)
|
||||
- [Configure NDK for Android Studio/Gradle Plugin](https://developer.android.com/studio/projects/configure-agp-ndk)
|
||||
- [CMake for NDK](https://developer.android.com/ndk/guides/cmake.html)
|
||||
|
||||
## Support
|
||||
|
||||
If you've found an issue with a sample and you know how to fix it, please
|
||||
[send us a PR!](CONTRIBUTING.md).
|
||||
|
||||
If you need to report a bug, where it needs to be filed depends on the type of
|
||||
issue:
|
||||
|
||||
- Problems with the samples themselves:
|
||||
https://github.com/googlesamples/android-ndk/issues
|
||||
- Problems with the OS APIs: http://b.android.com (usually the Framework
|
||||
component)
|
||||
- Problems with NDK (that is, the compiler):
|
||||
https://github.com/android/ndk/issues
|
||||
|
||||
For questions about using the NDK or the platform APIs, you can ask on:
|
||||
|
||||
- [The NDK mailing list](https://groups.google.com/g/android-ndk) (best if
|
||||
you're not sure where else to ask)
|
||||
- The [Discussions](https://github.com/android/ndk-samples/discussions) tab of
|
||||
this repo (best for questions about the samples themselves)
|
||||
- The NDK's [Discussions](https://github.com/android/ndk/discussions) (best for
|
||||
questions about the NDK compilers and build systems)
|
||||
- [Stack Overflow](https://stackoverflow.com/questions/tagged/android)
|
||||
|
||||
## Additional NDK samples:
|
||||
|
||||
- [Google Play Game Samples with Android Studio](https://github.com/playgameservices/cpp-android-basic-samples)
|
||||
- [Google Android Vulkan Tutorials](https://github.com/googlesamples/android-vulkan-tutorials)
|
||||
- [Android Vulkan API Basic Samples](https://github.com/googlesamples/vulkan-basic-samples)
|
||||
- [Android High Performance Audio](https://github.com/googlesamples/android-audio-high-performance)
|
||||
|
||||
## License
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright 2015 The Android Open Source Project, Inc.
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more contributor
|
||||
license agreements. See the NOTICE file distributed with this work for
|
||||
additional information regarding copyright ownership. The ASF licenses this file
|
||||
to you 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
|
||||
license agreements. See the NOTICE file distributed with this work for
|
||||
additional information regarding copyright ownership. The ASF licenses this
|
||||
file to you 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
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
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.
|
||||
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.
|
||||
|
||||
[LICENSE](LICENSE)
|
||||
|
||||
[Android NDK]: https://developer.android.com/ndk
|
||||
[0]: https://developer.android.com/tools/sdk/ndk/
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
Android Studio/Gradle DSL References
|
||||
|
||||
| Name | Function | Type | Options | Default |
|
||||
| -------------- | ------------------- | :--: | ------------ | ------- |
|
||||
| debuggable | Debugging Java code | bool | true / false | |
|
||||
| ndk.debuggable | Debugging JNI code | bool | true / false | |
|
||||
|
||||
Notation: dot(".") notation is same as closure ("{}") notation
|
||||
34
Teapot/AndroidManifest.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.sample.teapot"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="11"
|
||||
android:targetSdkVersion="19" />
|
||||
<uses-feature android:glEsVersion="0x00020000"></uses-feature>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme"
|
||||
android:hasCode="true"
|
||||
android:name="com.sample.teapot.TeapotApplication"
|
||||
>
|
||||
|
||||
<!-- Our activity is the built-in NativeActivity framework class.
|
||||
This will take care of integrating with our NDK code. -->
|
||||
<activity android:name="com.sample.teapot.TeapotNativeActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="orientation|keyboardHidden">
|
||||
<!-- Tell NativeActivity the name of or .so -->
|
||||
<meta-data android:name="android.app.lib_name"
|
||||
android:value="TeapotNativeActivity" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -19,7 +19,7 @@
|
||||
#define USE_PHONG (1)
|
||||
|
||||
uniform lowp vec3 vMaterialAmbient;
|
||||
uniform lowp vec4 vMaterialSpecular;
|
||||
uniform mediump vec4 vMaterialSpecular;
|
||||
|
||||
varying lowp vec4 colorDiffuse;
|
||||
|
||||
20
Teapot/jni/Android.mk
Normal file
@@ -0,0 +1,20 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := TeapotNativeActivity
|
||||
LOCAL_SRC_FILES := TeapotNativeActivity.cpp \
|
||||
TeapotRenderer.cpp \
|
||||
|
||||
LOCAL_C_INCLUDES :=
|
||||
|
||||
LOCAL_CFLAGS :=
|
||||
|
||||
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv2
|
||||
LOCAL_STATIC_LIBRARIES := cpufeatures android_native_app_glue ndk_helper
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
$(call import-module,android/ndk_helper)
|
||||
$(call import-module,android/native_app_glue)
|
||||
$(call import-module,android/cpufeatures)
|
||||
4
Teapot/jni/Application.mk
Normal file
@@ -0,0 +1,4 @@
|
||||
APP_PLATFORM := android-9
|
||||
APP_ABI := all
|
||||
|
||||
APP_STL := stlport_static
|
||||
488
Teapot/jni/TeapotNativeActivity.cpp
Normal file
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Include files
|
||||
//--------------------------------------------------------------------------------
|
||||
#include <jni.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <android/sensor.h>
|
||||
#include <android/log.h>
|
||||
#include <android_native_app_glue.h>
|
||||
#include <android/native_window_jni.h>
|
||||
#include <cpu-features.h>
|
||||
|
||||
#include "TeapotRenderer.h"
|
||||
#include "NDKHelper.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//Preprocessor
|
||||
//-------------------------------------------------------------------------
|
||||
#define HELPER_CLASS_NAME "com/sample/helper/NDKHelper" //Class name of helper function
|
||||
//-------------------------------------------------------------------------
|
||||
//Shared state for our app.
|
||||
//-------------------------------------------------------------------------
|
||||
struct android_app;
|
||||
class Engine
|
||||
{
|
||||
TeapotRenderer renderer_;
|
||||
|
||||
ndk_helper::GLContext* gl_context_;
|
||||
|
||||
bool initialized_resources_;
|
||||
bool has_focus_;
|
||||
|
||||
ndk_helper::DoubletapDetector doubletap_detector_;
|
||||
ndk_helper::PinchDetector pinch_detector_;
|
||||
ndk_helper::DragDetector drag_detector_;
|
||||
ndk_helper::PerfMonitor monitor_;
|
||||
|
||||
ndk_helper::TapCamera tap_camera_;
|
||||
|
||||
android_app* app_;
|
||||
|
||||
ASensorManager* sensor_manager_;
|
||||
const ASensor* accelerometer_sensor_;
|
||||
ASensorEventQueue* sensor_event_queue_;
|
||||
|
||||
void UpdateFPS( float fFPS );
|
||||
void ShowUI();
|
||||
void TransformPosition( ndk_helper::Vec2& vec );
|
||||
|
||||
public:
|
||||
static void HandleCmd( struct android_app* app,
|
||||
int32_t cmd );
|
||||
static int32_t HandleInput( android_app* app,
|
||||
AInputEvent* event );
|
||||
|
||||
Engine();
|
||||
~Engine();
|
||||
void SetState( android_app* state );
|
||||
int InitDisplay();
|
||||
void LoadResources();
|
||||
void UnloadResources();
|
||||
void DrawFrame();
|
||||
void TermDisplay();
|
||||
void TrimMemory();
|
||||
bool IsReady();
|
||||
|
||||
void UpdatePosition( AInputEvent* event,
|
||||
int32_t iIndex,
|
||||
float& fX,
|
||||
float& fY );
|
||||
|
||||
void InitSensors();
|
||||
void ProcessSensors( int32_t id );
|
||||
void SuspendSensors();
|
||||
void ResumeSensors();
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//Ctor
|
||||
//-------------------------------------------------------------------------
|
||||
Engine::Engine() :
|
||||
initialized_resources_( false ),
|
||||
has_focus_( false ),
|
||||
app_( NULL ),
|
||||
sensor_manager_( NULL ),
|
||||
accelerometer_sensor_( NULL ),
|
||||
sensor_event_queue_( NULL )
|
||||
{
|
||||
gl_context_ = ndk_helper::GLContext::GetInstance();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//Dtor
|
||||
//-------------------------------------------------------------------------
|
||||
Engine::~Engine()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Load resources
|
||||
*/
|
||||
void Engine::LoadResources()
|
||||
{
|
||||
renderer_.Init();
|
||||
renderer_.Bind( &tap_camera_ );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload resources
|
||||
*/
|
||||
void Engine::UnloadResources()
|
||||
{
|
||||
renderer_.Unload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize an EGL context for the current display.
|
||||
*/
|
||||
int Engine::InitDisplay()
|
||||
{
|
||||
if( !initialized_resources_ )
|
||||
{
|
||||
gl_context_->Init( app_->window );
|
||||
LoadResources();
|
||||
initialized_resources_ = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// initialize OpenGL ES and EGL
|
||||
if( EGL_SUCCESS != gl_context_->Resume( app_->window ) )
|
||||
{
|
||||
UnloadResources();
|
||||
LoadResources();
|
||||
}
|
||||
}
|
||||
|
||||
ShowUI();
|
||||
|
||||
// Initialize GL state.
|
||||
glEnable( GL_CULL_FACE );
|
||||
glEnable( GL_DEPTH_TEST );
|
||||
glDepthFunc( GL_LEQUAL );
|
||||
|
||||
//Note that screen size might have been changed
|
||||
glViewport( 0, 0, gl_context_->GetScreenWidth(), gl_context_->GetScreenHeight() );
|
||||
renderer_.UpdateViewport();
|
||||
|
||||
tap_camera_.SetFlip( 1.f, -1.f, -1.f );
|
||||
tap_camera_.SetPinchTransformFactor( 2.f, 2.f, 8.f );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just the current frame in the display.
|
||||
*/
|
||||
void Engine::DrawFrame()
|
||||
{
|
||||
float fFPS;
|
||||
if( monitor_.Update( fFPS ) )
|
||||
{
|
||||
UpdateFPS( fFPS );
|
||||
}
|
||||
renderer_.Update( monitor_.GetCurrentTime() );
|
||||
|
||||
// Just fill the screen with a color.
|
||||
glClearColor( 0.5f, 0.5f, 0.5f, 1.f );
|
||||
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
|
||||
renderer_.Render();
|
||||
|
||||
// Swap
|
||||
if( EGL_SUCCESS != gl_context_->Swap() )
|
||||
{
|
||||
UnloadResources();
|
||||
LoadResources();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tear down the EGL context currently associated with the display.
|
||||
*/
|
||||
void Engine::TermDisplay()
|
||||
{
|
||||
gl_context_->Suspend();
|
||||
|
||||
}
|
||||
|
||||
void Engine::TrimMemory()
|
||||
{
|
||||
LOGI( "Trimming memory" );
|
||||
gl_context_->Invalidate();
|
||||
}
|
||||
/**
|
||||
* Process the next input event.
|
||||
*/
|
||||
int32_t Engine::HandleInput( android_app* app,
|
||||
AInputEvent* event )
|
||||
{
|
||||
Engine* eng = (Engine*) app->userData;
|
||||
if( AInputEvent_getType( event ) == AINPUT_EVENT_TYPE_MOTION )
|
||||
{
|
||||
ndk_helper::GESTURE_STATE doubleTapState = eng->doubletap_detector_.Detect( event );
|
||||
ndk_helper::GESTURE_STATE dragState = eng->drag_detector_.Detect( event );
|
||||
ndk_helper::GESTURE_STATE pinchState = eng->pinch_detector_.Detect( event );
|
||||
|
||||
//Double tap detector has a priority over other detectors
|
||||
if( doubleTapState == ndk_helper::GESTURE_STATE_ACTION )
|
||||
{
|
||||
//Detect double tap
|
||||
eng->tap_camera_.Reset( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
//Handle drag state
|
||||
if( dragState & ndk_helper::GESTURE_STATE_START )
|
||||
{
|
||||
//Otherwise, start dragging
|
||||
ndk_helper::Vec2 v;
|
||||
eng->drag_detector_.GetPointer( v );
|
||||
eng->TransformPosition( v );
|
||||
eng->tap_camera_.BeginDrag( v );
|
||||
}
|
||||
else if( dragState & ndk_helper::GESTURE_STATE_MOVE )
|
||||
{
|
||||
ndk_helper::Vec2 v;
|
||||
eng->drag_detector_.GetPointer( v );
|
||||
eng->TransformPosition( v );
|
||||
eng->tap_camera_.Drag( v );
|
||||
}
|
||||
else if( dragState & ndk_helper::GESTURE_STATE_END )
|
||||
{
|
||||
eng->tap_camera_.EndDrag();
|
||||
}
|
||||
|
||||
//Handle pinch state
|
||||
if( pinchState & ndk_helper::GESTURE_STATE_START )
|
||||
{
|
||||
//Start new pinch
|
||||
ndk_helper::Vec2 v1;
|
||||
ndk_helper::Vec2 v2;
|
||||
eng->pinch_detector_.GetPointers( v1, v2 );
|
||||
eng->TransformPosition( v1 );
|
||||
eng->TransformPosition( v2 );
|
||||
eng->tap_camera_.BeginPinch( v1, v2 );
|
||||
}
|
||||
else if( pinchState & ndk_helper::GESTURE_STATE_MOVE )
|
||||
{
|
||||
//Multi touch
|
||||
//Start new pinch
|
||||
ndk_helper::Vec2 v1;
|
||||
ndk_helper::Vec2 v2;
|
||||
eng->pinch_detector_.GetPointers( v1, v2 );
|
||||
eng->TransformPosition( v1 );
|
||||
eng->TransformPosition( v2 );
|
||||
eng->tap_camera_.Pinch( v1, v2 );
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the next main command.
|
||||
*/
|
||||
void Engine::HandleCmd( struct android_app* app,
|
||||
int32_t cmd )
|
||||
{
|
||||
Engine* eng = (Engine*) app->userData;
|
||||
switch( cmd )
|
||||
{
|
||||
case APP_CMD_SAVE_STATE:
|
||||
break;
|
||||
case APP_CMD_INIT_WINDOW:
|
||||
// The window is being shown, get it ready.
|
||||
if( app->window != NULL )
|
||||
{
|
||||
eng->InitDisplay();
|
||||
eng->DrawFrame();
|
||||
}
|
||||
break;
|
||||
case APP_CMD_TERM_WINDOW:
|
||||
// The window is being hidden or closed, clean it up.
|
||||
eng->TermDisplay();
|
||||
eng->has_focus_ = false;
|
||||
break;
|
||||
case APP_CMD_STOP:
|
||||
break;
|
||||
case APP_CMD_GAINED_FOCUS:
|
||||
eng->ResumeSensors();
|
||||
//Start animation
|
||||
eng->has_focus_ = true;
|
||||
break;
|
||||
case APP_CMD_LOST_FOCUS:
|
||||
eng->SuspendSensors();
|
||||
// Also stop animating.
|
||||
eng->has_focus_ = false;
|
||||
eng->DrawFrame();
|
||||
break;
|
||||
case APP_CMD_LOW_MEMORY:
|
||||
//Free up GL resources
|
||||
eng->TrimMemory();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//Sensor handlers
|
||||
//-------------------------------------------------------------------------
|
||||
void Engine::InitSensors()
|
||||
{
|
||||
sensor_manager_ = ASensorManager_getInstance();
|
||||
accelerometer_sensor_ = ASensorManager_getDefaultSensor( sensor_manager_,
|
||||
ASENSOR_TYPE_ACCELEROMETER );
|
||||
sensor_event_queue_ = ASensorManager_createEventQueue( sensor_manager_, app_->looper,
|
||||
LOOPER_ID_USER, NULL, NULL );
|
||||
}
|
||||
|
||||
void Engine::ProcessSensors( int32_t id )
|
||||
{
|
||||
// If a sensor has data, process it now.
|
||||
if( id == LOOPER_ID_USER )
|
||||
{
|
||||
if( accelerometer_sensor_ != NULL )
|
||||
{
|
||||
ASensorEvent event;
|
||||
while( ASensorEventQueue_getEvents( sensor_event_queue_, &event, 1 ) > 0 )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::ResumeSensors()
|
||||
{
|
||||
// When our app gains focus, we start monitoring the accelerometer.
|
||||
if( accelerometer_sensor_ != NULL )
|
||||
{
|
||||
ASensorEventQueue_enableSensor( sensor_event_queue_, accelerometer_sensor_ );
|
||||
// We'd like to get 60 events per second (in us).
|
||||
ASensorEventQueue_setEventRate( sensor_event_queue_, accelerometer_sensor_,
|
||||
(1000L / 60) * 1000 );
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::SuspendSensors()
|
||||
{
|
||||
// When our app loses focus, we stop monitoring the accelerometer.
|
||||
// This is to avoid consuming battery while not being used.
|
||||
if( accelerometer_sensor_ != NULL )
|
||||
{
|
||||
ASensorEventQueue_disableSensor( sensor_event_queue_, accelerometer_sensor_ );
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//Misc
|
||||
//-------------------------------------------------------------------------
|
||||
void Engine::SetState( android_app* state )
|
||||
{
|
||||
app_ = state;
|
||||
doubletap_detector_.SetConfiguration( app_->config );
|
||||
drag_detector_.SetConfiguration( app_->config );
|
||||
pinch_detector_.SetConfiguration( app_->config );
|
||||
}
|
||||
|
||||
bool Engine::IsReady()
|
||||
{
|
||||
if( has_focus_ )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Engine::TransformPosition( ndk_helper::Vec2& vec )
|
||||
{
|
||||
vec = ndk_helper::Vec2( 2.0f, 2.0f ) * vec
|
||||
/ ndk_helper::Vec2( gl_context_->GetScreenWidth(), gl_context_->GetScreenHeight() )
|
||||
- ndk_helper::Vec2( 1.f, 1.f );
|
||||
}
|
||||
|
||||
void Engine::ShowUI()
|
||||
{
|
||||
JNIEnv *jni;
|
||||
app_->activity->vm->AttachCurrentThread( &jni, NULL );
|
||||
|
||||
//Default class retrieval
|
||||
jclass clazz = jni->GetObjectClass( app_->activity->clazz );
|
||||
jmethodID methodID = jni->GetMethodID( clazz, "showUI", "()V" );
|
||||
jni->CallVoidMethod( app_->activity->clazz, methodID );
|
||||
|
||||
app_->activity->vm->DetachCurrentThread();
|
||||
return;
|
||||
}
|
||||
|
||||
void Engine::UpdateFPS( float fFPS )
|
||||
{
|
||||
JNIEnv *jni;
|
||||
app_->activity->vm->AttachCurrentThread( &jni, NULL );
|
||||
|
||||
//Default class retrieval
|
||||
jclass clazz = jni->GetObjectClass( app_->activity->clazz );
|
||||
jmethodID methodID = jni->GetMethodID( clazz, "updateFPS", "(F)V" );
|
||||
jni->CallVoidMethod( app_->activity->clazz, methodID, fFPS );
|
||||
|
||||
app_->activity->vm->DetachCurrentThread();
|
||||
return;
|
||||
}
|
||||
|
||||
Engine g_engine;
|
||||
|
||||
/**
|
||||
* This is the main entry point of a native application that is using
|
||||
* android_native_app_glue. It runs in its own thread, with its own
|
||||
* event loop for receiving input events and doing other things.
|
||||
*/
|
||||
void android_main( android_app* state )
|
||||
{
|
||||
app_dummy();
|
||||
|
||||
g_engine.SetState( state );
|
||||
|
||||
//Init helper functions
|
||||
ndk_helper::JNIHelper::Init( state->activity, HELPER_CLASS_NAME );
|
||||
|
||||
state->userData = &g_engine;
|
||||
state->onAppCmd = Engine::HandleCmd;
|
||||
state->onInputEvent = Engine::HandleInput;
|
||||
|
||||
#ifdef USE_NDK_PROFILER
|
||||
monstartup("libTeapotNativeActivity.so");
|
||||
#endif
|
||||
|
||||
// Prepare to monitor accelerometer
|
||||
g_engine.InitSensors();
|
||||
|
||||
// loop waiting for stuff to do.
|
||||
while( 1 )
|
||||
{
|
||||
// Read all pending events.
|
||||
int id;
|
||||
int events;
|
||||
android_poll_source* source;
|
||||
|
||||
// If not animating, we will block forever waiting for events.
|
||||
// If animating, we loop until all events are read, then continue
|
||||
// to draw the next frame of animation.
|
||||
while( (id = ALooper_pollAll( g_engine.IsReady() ? 0 : -1, NULL, &events, (void**) &source ))
|
||||
>= 0 )
|
||||
{
|
||||
// Process this event.
|
||||
if( source != NULL )
|
||||
source->process( state, source );
|
||||
|
||||
g_engine.ProcessSensors( id );
|
||||
|
||||
// Check if we are exiting.
|
||||
if( state->destroyRequested != 0 )
|
||||
{
|
||||
g_engine.TermDisplay();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( g_engine.IsReady() )
|
||||
{
|
||||
// Drawing is throttled to the screen update rate, so there
|
||||
// is no need to do timing here.
|
||||
g_engine.DrawFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
286
Teapot/jni/TeapotRenderer.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// TeapotRenderer.cpp
|
||||
// Render a teapot
|
||||
//--------------------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------------------
|
||||
// Include files
|
||||
//--------------------------------------------------------------------------------
|
||||
#include "TeapotRenderer.h"
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Teapot model data
|
||||
//--------------------------------------------------------------------------------
|
||||
#include "teapot.inl"
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Ctor
|
||||
//--------------------------------------------------------------------------------
|
||||
TeapotRenderer::TeapotRenderer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Dtor
|
||||
//--------------------------------------------------------------------------------
|
||||
TeapotRenderer::~TeapotRenderer()
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
|
||||
void TeapotRenderer::Init()
|
||||
{
|
||||
//Settings
|
||||
glFrontFace( GL_CCW );
|
||||
|
||||
//Load shader
|
||||
LoadShaders( &shader_param_, "Shaders/VS_ShaderPlain.vsh",
|
||||
"Shaders/ShaderPlain.fsh" );
|
||||
|
||||
//Create Index buffer
|
||||
num_indices_ = sizeof(teapotIndices) / sizeof(teapotIndices[0]);
|
||||
glGenBuffers( 1, &ibo_ );
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo_ );
|
||||
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(teapotIndices), teapotIndices,
|
||||
GL_STATIC_DRAW );
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
|
||||
|
||||
//Create VBO
|
||||
num_vertices_ = sizeof(teapotPositions) / sizeof(teapotPositions[0]) / 3;
|
||||
int32_t iStride = sizeof(TEAPOT_VERTEX);
|
||||
int32_t iIndex = 0;
|
||||
TEAPOT_VERTEX* p = new TEAPOT_VERTEX[num_vertices_];
|
||||
for( int32_t i = 0; i < num_vertices_; ++i )
|
||||
{
|
||||
p[i].pos[0] = teapotPositions[iIndex];
|
||||
p[i].pos[1] = teapotPositions[iIndex + 1];
|
||||
p[i].pos[2] = teapotPositions[iIndex + 2];
|
||||
|
||||
p[i].normal[0] = teapotNormals[iIndex];
|
||||
p[i].normal[1] = teapotNormals[iIndex + 1];
|
||||
p[i].normal[2] = teapotNormals[iIndex + 2];
|
||||
iIndex += 3;
|
||||
}
|
||||
glGenBuffers( 1, &vbo_ );
|
||||
glBindBuffer( GL_ARRAY_BUFFER, vbo_ );
|
||||
glBufferData( GL_ARRAY_BUFFER, iStride * num_vertices_, p, GL_STATIC_DRAW );
|
||||
glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
||||
|
||||
delete[] p;
|
||||
|
||||
UpdateViewport();
|
||||
mat_model_ = ndk_helper::Mat4::Translation( 0, 0, -15.f );
|
||||
|
||||
ndk_helper::Mat4 mat = ndk_helper::Mat4::RotationX( M_PI / 3 );
|
||||
mat_model_ = mat * mat_model_;
|
||||
}
|
||||
|
||||
void TeapotRenderer::UpdateViewport()
|
||||
{
|
||||
//Init Projection matrices
|
||||
int32_t viewport[4];
|
||||
glGetIntegerv( GL_VIEWPORT, viewport );
|
||||
float fAspect = (float) viewport[2] / (float) viewport[3];
|
||||
|
||||
const float CAM_NEAR = 5.f;
|
||||
const float CAM_FAR = 10000.f;
|
||||
bool bRotate = false;
|
||||
mat_projection_ = ndk_helper::Mat4::Perspective( fAspect, 1.f, CAM_NEAR, CAM_FAR );
|
||||
}
|
||||
|
||||
void TeapotRenderer::Unload()
|
||||
{
|
||||
if( vbo_ )
|
||||
{
|
||||
glDeleteBuffers( 1, &vbo_ );
|
||||
vbo_ = 0;
|
||||
}
|
||||
|
||||
if( ibo_ )
|
||||
{
|
||||
glDeleteBuffers( 1, &ibo_ );
|
||||
ibo_ = 0;
|
||||
}
|
||||
|
||||
if( shader_param_.program_ )
|
||||
{
|
||||
glDeleteProgram( shader_param_.program_ );
|
||||
shader_param_.program_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TeapotRenderer::Update( float fTime )
|
||||
{
|
||||
const float CAM_X = 0.f;
|
||||
const float CAM_Y = 0.f;
|
||||
const float CAM_Z = 700.f;
|
||||
|
||||
mat_view_ = ndk_helper::Mat4::LookAt( ndk_helper::Vec3( CAM_X, CAM_Y, CAM_Z ),
|
||||
ndk_helper::Vec3( 0.f, 0.f, 0.f ), ndk_helper::Vec3( 0.f, 1.f, 0.f ) );
|
||||
|
||||
if( camera_ )
|
||||
{
|
||||
camera_->Update();
|
||||
mat_view_ = camera_->GetTransformMatrix() * mat_view_
|
||||
* camera_->GetRotationMatrix() * mat_model_;
|
||||
}
|
||||
else
|
||||
{
|
||||
mat_view_ = mat_view_ * mat_model_;
|
||||
}
|
||||
}
|
||||
|
||||
void TeapotRenderer::Render()
|
||||
{
|
||||
//
|
||||
// Feed Projection and Model View matrices to the shaders
|
||||
ndk_helper::Mat4 mat_vp = mat_projection_ * mat_view_;
|
||||
|
||||
// Bind the VBO
|
||||
glBindBuffer( GL_ARRAY_BUFFER, vbo_ );
|
||||
|
||||
int32_t iStride = sizeof(TEAPOT_VERTEX);
|
||||
// Pass the vertex data
|
||||
glVertexAttribPointer( ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, iStride,
|
||||
BUFFER_OFFSET( 0 ) );
|
||||
glEnableVertexAttribArray( ATTRIB_VERTEX );
|
||||
|
||||
glVertexAttribPointer( ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, iStride,
|
||||
BUFFER_OFFSET( 3 * sizeof(GLfloat) ) );
|
||||
glEnableVertexAttribArray( ATTRIB_NORMAL );
|
||||
|
||||
// Bind the IB
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo_ );
|
||||
|
||||
glUseProgram( shader_param_.program_ );
|
||||
|
||||
TEAPOT_MATERIALS material = { { 1.0f, 0.5f, 0.5f }, { 1.0f, 1.0f, 1.0f, 10.f }, {
|
||||
0.1f, 0.1f, 0.1f }, };
|
||||
|
||||
//Update uniforms
|
||||
glUniform4f( shader_param_.material_diffuse_, material.diffuse_color[0],
|
||||
material.diffuse_color[1], material.diffuse_color[2], 1.f );
|
||||
|
||||
glUniform4f( shader_param_.material_specular_, material.specular_color[0],
|
||||
material.specular_color[1], material.specular_color[2],
|
||||
material.specular_color[3] );
|
||||
//
|
||||
//using glUniform3fv here was troublesome
|
||||
//
|
||||
glUniform3f( shader_param_.material_ambient_, material.ambient_color[0],
|
||||
material.ambient_color[1], material.ambient_color[2] );
|
||||
|
||||
glUniformMatrix4fv( shader_param_.matrix_projection_, 1, GL_FALSE, mat_vp.Ptr() );
|
||||
glUniformMatrix4fv( shader_param_.matrix_view_, 1, GL_FALSE, mat_view_.Ptr() );
|
||||
glUniform3f( shader_param_.light0_, 100.f, -200.f, -600.f );
|
||||
|
||||
glDrawElements( GL_TRIANGLES, num_indices_, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0) );
|
||||
|
||||
glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
||||
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
|
||||
}
|
||||
|
||||
bool TeapotRenderer::LoadShaders( SHADER_PARAMS* params,
|
||||
const char* strVsh,
|
||||
const char* strFsh )
|
||||
{
|
||||
GLuint program;
|
||||
GLuint vert_shader, frag_shader;
|
||||
char *vert_shader_pathname, *frag_shader_pathname;
|
||||
|
||||
// Create shader program
|
||||
program = glCreateProgram();
|
||||
LOGI( "Created Shader %d", program );
|
||||
|
||||
// Create and compile vertex shader
|
||||
if( !ndk_helper::shader::CompileShader( &vert_shader, GL_VERTEX_SHADER, strVsh ) )
|
||||
{
|
||||
LOGI( "Failed to compile vertex shader" );
|
||||
glDeleteProgram( program );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create and compile fragment shader
|
||||
if( !ndk_helper::shader::CompileShader( &frag_shader, GL_FRAGMENT_SHADER, strFsh ) )
|
||||
{
|
||||
LOGI( "Failed to compile fragment shader" );
|
||||
glDeleteProgram( program );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attach vertex shader to program
|
||||
glAttachShader( program, vert_shader );
|
||||
|
||||
// Attach fragment shader to program
|
||||
glAttachShader( program, frag_shader );
|
||||
|
||||
// Bind attribute locations
|
||||
// this needs to be done prior to linking
|
||||
glBindAttribLocation( program, ATTRIB_VERTEX, "myVertex" );
|
||||
glBindAttribLocation( program, ATTRIB_NORMAL, "myNormal" );
|
||||
glBindAttribLocation( program, ATTRIB_UV, "myUV" );
|
||||
|
||||
// Link program
|
||||
if( !ndk_helper::shader::LinkProgram( program ) )
|
||||
{
|
||||
LOGI( "Failed to link program: %d", program );
|
||||
|
||||
if( vert_shader )
|
||||
{
|
||||
glDeleteShader( vert_shader );
|
||||
vert_shader = 0;
|
||||
}
|
||||
if( frag_shader )
|
||||
{
|
||||
glDeleteShader( frag_shader );
|
||||
frag_shader = 0;
|
||||
}
|
||||
if( program )
|
||||
{
|
||||
glDeleteProgram( program );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get uniform locations
|
||||
params->matrix_projection_ = glGetUniformLocation( program, "uPMatrix" );
|
||||
params->matrix_view_ = glGetUniformLocation( program, "uMVMatrix" );
|
||||
|
||||
params->light0_ = glGetUniformLocation( program, "vLight0" );
|
||||
params->material_diffuse_ = glGetUniformLocation( program, "vMaterialDiffuse" );
|
||||
params->material_ambient_ = glGetUniformLocation( program, "vMaterialAmbient" );
|
||||
params->material_specular_ = glGetUniformLocation( program, "vMaterialSpecular" );
|
||||
|
||||
// Release vertex and fragment shaders
|
||||
if( vert_shader )
|
||||
glDeleteShader( vert_shader );
|
||||
if( frag_shader )
|
||||
glDeleteShader( frag_shader );
|
||||
|
||||
params->program_ = program;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TeapotRenderer::Bind( ndk_helper::TapCamera* camera )
|
||||
{
|
||||
camera_ = camera;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,75 +24,82 @@
|
||||
//--------------------------------------------------------------------------------
|
||||
// Include files
|
||||
//--------------------------------------------------------------------------------
|
||||
#include <EGL/egl.h>
|
||||
#include <GLES/gl.h>
|
||||
#include <android/log.h>
|
||||
#include <android/native_window_jni.h>
|
||||
#include <android/sensor.h>
|
||||
#include <android_native_app_glue.h>
|
||||
#include <errno.h>
|
||||
#include <jni.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <GLES/gl.h>
|
||||
|
||||
#include <android/sensor.h>
|
||||
#include <android/log.h>
|
||||
#include <android_native_app_glue.h>
|
||||
#include <android/native_window_jni.h>
|
||||
#include <cpu-features.h>
|
||||
|
||||
#define CLASS_NAME "android/app/NativeActivity"
|
||||
#define APPLICATION_CLASS_NAME "com/sample/teapot/TeapotApplication"
|
||||
|
||||
#include "NDKHelper.h"
|
||||
|
||||
struct TEAPOT_VERTEX {
|
||||
float pos[3];
|
||||
float normal[3];
|
||||
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
|
||||
|
||||
struct TEAPOT_VERTEX
|
||||
{
|
||||
float pos[3];
|
||||
float normal[3];
|
||||
};
|
||||
|
||||
enum SHADER_ATTRIBUTES {
|
||||
ATTRIB_VERTEX,
|
||||
ATTRIB_NORMAL,
|
||||
ATTRIB_UV,
|
||||
enum SHADER_ATTRIBUTES
|
||||
{
|
||||
ATTRIB_VERTEX, ATTRIB_NORMAL, ATTRIB_UV,
|
||||
};
|
||||
|
||||
struct SHADER_PARAMS {
|
||||
GLuint program_;
|
||||
GLuint light0_;
|
||||
GLuint material_diffuse_;
|
||||
GLuint material_ambient_;
|
||||
GLuint material_specular_;
|
||||
struct SHADER_PARAMS
|
||||
{
|
||||
GLuint program_;
|
||||
GLuint light0_;
|
||||
GLuint material_diffuse_;
|
||||
GLuint material_ambient_;
|
||||
GLuint material_specular_;
|
||||
|
||||
GLuint matrix_projection_;
|
||||
GLuint matrix_view_;
|
||||
GLuint matrix_projection_;
|
||||
GLuint matrix_view_;
|
||||
};
|
||||
|
||||
struct TEAPOT_MATERIALS {
|
||||
float diffuse_color[3];
|
||||
float specular_color[4];
|
||||
float ambient_color[3];
|
||||
struct TEAPOT_MATERIALS
|
||||
{
|
||||
float diffuse_color[3];
|
||||
float specular_color[4];
|
||||
float ambient_color[3];
|
||||
};
|
||||
|
||||
class TeapotRenderer {
|
||||
int32_t num_indices_;
|
||||
int32_t num_vertices_;
|
||||
GLuint ibo_;
|
||||
GLuint vbo_;
|
||||
class TeapotRenderer
|
||||
{
|
||||
int32_t num_indices_;
|
||||
int32_t num_vertices_;
|
||||
GLuint ibo_;
|
||||
GLuint vbo_;
|
||||
|
||||
SHADER_PARAMS shader_param_;
|
||||
bool LoadShaders(SHADER_PARAMS* params, const char* strVsh,
|
||||
const char* strFsh);
|
||||
SHADER_PARAMS shader_param_;
|
||||
bool LoadShaders( SHADER_PARAMS* params, const char* strVsh, const char* strFsh );
|
||||
|
||||
ndk_helper::Mat4 mat_projection_;
|
||||
ndk_helper::Mat4 mat_view_;
|
||||
ndk_helper::Mat4 mat_model_;
|
||||
ndk_helper::Mat4 mat_projection_;
|
||||
ndk_helper::Mat4 mat_view_;
|
||||
ndk_helper::Mat4 mat_model_;
|
||||
|
||||
ndk_helper::TapCamera* camera_;
|
||||
|
||||
public:
|
||||
TeapotRenderer();
|
||||
virtual ~TeapotRenderer();
|
||||
void Init();
|
||||
void Render(float r, float g, float b);
|
||||
void Update(double time);
|
||||
bool Bind(ndk_helper::TapCamera* camera);
|
||||
void Unload();
|
||||
void UpdateViewport();
|
||||
ndk_helper::TapCamera* camera_;
|
||||
public:
|
||||
TeapotRenderer();
|
||||
virtual ~TeapotRenderer();
|
||||
void Init();
|
||||
void Render();
|
||||
void Update( float dTime );
|
||||
bool Bind( ndk_helper::TapCamera* camera );
|
||||
void Unload();
|
||||
void UpdateViewport();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
2057
Teapot/jni/teapot.inl
Normal file
3
Teapot/lint.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
</lint>
|
||||
14
Teapot/project.properties
Normal file
@@ -0,0 +1,14 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=Google Inc.:Google APIs:19
|
||||
BIN
Teapot/res/drawable-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
Teapot/res/drawable-ldpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Teapot/res/drawable-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
Teapot/res/drawable-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
@@ -9,8 +9,8 @@
|
||||
android:id="@+id/textViewFPS"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:text="@string/fps"
|
||||
android:gravity="right"
|
||||
android:text="0.0 FPS"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<resources>
|
||||
|
||||
<string name="app_name">Teapot</string>
|
||||
<string name="fps">0.0 FPS</string>
|
||||
|
||||
</resources>
|
||||
203
Teapot/src/com/sample/helper/NDKHelper.java
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.sample.helper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioTrack;
|
||||
import android.opengl.GLUtils;
|
||||
import android.util.Log;
|
||||
|
||||
public class NDKHelper
|
||||
{
|
||||
private static Context context;
|
||||
|
||||
public static void setContext(Context c)
|
||||
{
|
||||
Log.i("NDKHelper", "setContext:" + c);
|
||||
context = c;
|
||||
}
|
||||
|
||||
//
|
||||
// Load Bitmap
|
||||
// Java helper is useful decoding PNG, TIFF etc rather than linking libPng
|
||||
// etc separately
|
||||
//
|
||||
private int nextPOT(int i)
|
||||
{
|
||||
int pot = 1;
|
||||
while (pot < i)
|
||||
pot <<= 1;
|
||||
return pot;
|
||||
}
|
||||
|
||||
private Bitmap scaleBitmap(Bitmap bitmapToScale, float newWidth, float newHeight)
|
||||
{
|
||||
if (bitmapToScale == null)
|
||||
return null;
|
||||
// get the original width and height
|
||||
int width = bitmapToScale.getWidth();
|
||||
int height = bitmapToScale.getHeight();
|
||||
// create a matrix for the manipulation
|
||||
Matrix matrix = new Matrix();
|
||||
|
||||
// resize the bit map
|
||||
matrix.postScale(newWidth / width, newHeight / height);
|
||||
|
||||
// recreate the new Bitmap and set it back
|
||||
return Bitmap.createBitmap(bitmapToScale, 0, 0, bitmapToScale.getWidth(),
|
||||
bitmapToScale.getHeight(), matrix, true);
|
||||
}
|
||||
|
||||
public boolean loadTexture(String path)
|
||||
{
|
||||
Bitmap bitmap = null;
|
||||
try
|
||||
{
|
||||
String str = path;
|
||||
if (!path.startsWith("/"))
|
||||
{
|
||||
str = "/" + path;
|
||||
}
|
||||
|
||||
File file = new File(context.getExternalFilesDir(null), str);
|
||||
if (file.canRead())
|
||||
{
|
||||
bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
|
||||
} else
|
||||
{
|
||||
bitmap = BitmapFactory.decodeStream(context.getResources().getAssets()
|
||||
.open(path));
|
||||
}
|
||||
// Matrix matrix = new Matrix();
|
||||
// // resize the bit map
|
||||
// matrix.postScale(-1F, 1F);
|
||||
//
|
||||
// // recreate the new Bitmap and set it back
|
||||
// bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
|
||||
// bitmap.getHeight(), matrix, true);
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
Log.w("NDKHelper", "Coundn't load a file:" + path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bitmap != null)
|
||||
{
|
||||
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public Bitmap openBitmap(String path, boolean iScalePOT)
|
||||
{
|
||||
Bitmap bitmap = null;
|
||||
try
|
||||
{
|
||||
bitmap = BitmapFactory.decodeStream(context.getResources().getAssets()
|
||||
.open(path));
|
||||
if (iScalePOT)
|
||||
{
|
||||
int originalWidth = getBitmapWidth(bitmap);
|
||||
int originalHeight = getBitmapHeight(bitmap);
|
||||
int width = nextPOT(originalWidth);
|
||||
int height = nextPOT(originalHeight);
|
||||
if (originalWidth != width || originalHeight != height)
|
||||
{
|
||||
// Scale it
|
||||
bitmap = scaleBitmap(bitmap, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
Log.w("NDKHelper", "Coundn't load a file:" + path);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public int getBitmapWidth(Bitmap bmp)
|
||||
{
|
||||
return bmp.getWidth();
|
||||
}
|
||||
|
||||
public int getBitmapHeight(Bitmap bmp)
|
||||
{
|
||||
return bmp.getHeight();
|
||||
}
|
||||
|
||||
public void getBitmapPixels(Bitmap bmp, int[] pixels)
|
||||
{
|
||||
int w = bmp.getWidth();
|
||||
int h = bmp.getHeight();
|
||||
bmp.getPixels(pixels, 0, w, 0, 0, w, h);
|
||||
}
|
||||
|
||||
public void closeBitmap(Bitmap bmp)
|
||||
{
|
||||
bmp.recycle();
|
||||
}
|
||||
|
||||
public static String getNativeLibraryDirectory(Context appContext)
|
||||
{
|
||||
ApplicationInfo ai = context.getApplicationInfo();
|
||||
|
||||
Log.w("NDKHelper", "ai.nativeLibraryDir:" + ai.nativeLibraryDir);
|
||||
|
||||
if ((ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
|
||||
|| (ai.flags & ApplicationInfo.FLAG_SYSTEM) == 0)
|
||||
{
|
||||
return ai.nativeLibraryDir;
|
||||
}
|
||||
return "/system/lib/";
|
||||
}
|
||||
|
||||
public int getNativeAudioBufferSize()
|
||||
{
|
||||
int SDK_INT = android.os.Build.VERSION.SDK_INT;
|
||||
if (SDK_INT >= 17)
|
||||
{
|
||||
AudioManager am = (AudioManager) context
|
||||
.getSystemService(Context.AUDIO_SERVICE);
|
||||
String framesPerBuffer = am
|
||||
.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
|
||||
return Integer.parseInt(framesPerBuffer);
|
||||
} else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int getNativeAudioSampleRate()
|
||||
{
|
||||
return AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_SYSTEM);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,7 +32,6 @@ import android.widget.Toast;
|
||||
|
||||
public class TeapotApplication extends Application {
|
||||
public void onCreate(){
|
||||
super.onCreate();
|
||||
Log.w("native-activity", "onCreate");
|
||||
|
||||
final PackageManager pm = getApplicationContext().getPackageManager();
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.sample.teapot;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.NativeActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
@@ -51,7 +49,6 @@ public class TeapotNativeActivity extends NativeActivity {
|
||||
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
@@ -73,7 +70,6 @@ public class TeapotNativeActivity extends NativeActivity {
|
||||
}
|
||||
// Our popup window, you will call it from your C/C++ code later
|
||||
|
||||
@TargetApi(19)
|
||||
void setImmersiveSticky() {
|
||||
View decorView = getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
@@ -88,7 +84,6 @@ public class TeapotNativeActivity extends NativeActivity {
|
||||
PopupWindow _popupWindow;
|
||||
TextView _label;
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
public void showUI()
|
||||
{
|
||||
if( _popupWindow != null )
|
||||
@@ -114,7 +109,7 @@ public class TeapotNativeActivity extends NativeActivity {
|
||||
_activity.setContentView(mainLayout, params);
|
||||
|
||||
// Show our UI over NativeActivity window
|
||||
_popupWindow.showAtLocation(mainLayout, Gravity.TOP | Gravity.START, 10, 10);
|
||||
_popupWindow.showAtLocation(mainLayout, Gravity.TOP | Gravity.LEFT, 10, 10);
|
||||
_popupWindow.update();
|
||||
|
||||
_label = (TextView)popupView.findViewById(R.id.textViewFPS);
|
||||
@@ -125,6 +120,10 @@ public class TeapotNativeActivity extends NativeActivity {
|
||||
protected void onPause()
|
||||
{
|
||||
super.onPause();
|
||||
if (_popupWindow != null) {
|
||||
_popupWindow.dismiss();
|
||||
_popupWindow = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateFPS(final float fFPS)
|
||||
@@ -1,6 +0,0 @@
|
||||
# Sample removed
|
||||
|
||||
This sample has been removed because the API it demonstrated (OpenSLES) is
|
||||
deprecated. New apps should instead use [Oboe], which has its own samples.
|
||||
|
||||
[Oboe]: https://github.com/google/oboe
|
||||
1
base/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -1,32 +0,0 @@
|
||||
plugins {
|
||||
id("ndksamples.android.library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.android.ndk.samples.base"
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path = file("src/main/cpp/CMakeLists.txt")
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
prefabPublishing = true
|
||||
}
|
||||
|
||||
prefab {
|
||||
create("base") {
|
||||
headers = "src/main/cpp/include"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(libs.appcompat)
|
||||
implementation(libs.material)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.ext.junit)
|
||||
androidTestImplementation(libs.espresso.core)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
project(Base LANGUAGES CXX)
|
||||
|
||||
include(AppLibrary)
|
||||
|
||||
add_app_library(base
|
||||
STATIC
|
||||
logging.cpp
|
||||
)
|
||||
|
||||
target_compile_features(base PRIVATE cxx_std_23)
|
||||
target_compile_options(base PRIVATE -Wno-vla-cxx-extension)
|
||||
target_include_directories(base PUBLIC include)
|
||||
target_link_libraries(base PUBLIC log)
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
namespace ndksamples::base {
|
||||
|
||||
class ErrnoRestorer {
|
||||
public:
|
||||
ErrnoRestorer() : saved_errno_(errno) {}
|
||||
ErrnoRestorer(const ErrnoRestorer&) = delete;
|
||||
|
||||
~ErrnoRestorer() { errno = saved_errno_; }
|
||||
|
||||
ErrnoRestorer& operator=(const ErrnoRestorer&) = delete;
|
||||
|
||||
// Allow this object to be used as part of && operation.
|
||||
explicit operator bool() const { return true; }
|
||||
|
||||
private:
|
||||
const int saved_errno_;
|
||||
};
|
||||
|
||||
} // namespace ndksamples::base
|
||||
@@ -1,483 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file logging.h
|
||||
* @brief Logging and assertion utilities.
|
||||
*
|
||||
* This is a modified version of AOSP's android-base/logging.h:
|
||||
* https://cs.android.com/android/platform/superproject/main/+/main:system/libbase/include/android-base/logging.h
|
||||
*
|
||||
* The original file contained a lot of dependencies for things we don't need
|
||||
* (kernel logging, support for non-Android platforms, non-logd loggers, etc).
|
||||
* That's all been removed so we don't need to pull in those dependencies.
|
||||
*
|
||||
* If you copy from this sample, you may want to replace this with something
|
||||
* like absl, which provides a very similar (if not identical) interface for all
|
||||
* platforms. absl was not used here because we didn't need much, and it was
|
||||
* preferable to avoid an additional dependency.
|
||||
*/
|
||||
|
||||
//
|
||||
// Google-style C++ logging.
|
||||
//
|
||||
|
||||
// This header provides a C++ stream interface to logging.
|
||||
//
|
||||
// To log:
|
||||
//
|
||||
// LOG(INFO) << "Some text; " << some_value;
|
||||
//
|
||||
// Replace `INFO` with any severity from `enum LogSeverity`.
|
||||
// Most devices filter out VERBOSE logs by default, run
|
||||
// `adb shell setprop log.tag.<TAG> V` to see them in adb logcat.
|
||||
//
|
||||
// To log the result of a failed function and include the string
|
||||
// representation of `errno` at the end:
|
||||
//
|
||||
// PLOG(ERROR) << "Write failed";
|
||||
//
|
||||
// The output will be something like `Write failed: I/O error`.
|
||||
// Remember this as 'P' as in perror(3).
|
||||
//
|
||||
// To output your own types, simply implement operator<< as normal.
|
||||
//
|
||||
// By default, output goes to logcat on Android and stderr on the host.
|
||||
// A process can use `SetLogger` to decide where all logging goes.
|
||||
// Implementations are provided for logcat, stderr, and dmesg.
|
||||
//
|
||||
// By default, the process' name is used as the log tag.
|
||||
// Code can choose a specific log tag by defining LOG_TAG
|
||||
// before including this header.
|
||||
|
||||
// This header also provides assertions:
|
||||
//
|
||||
// CHECK(must_be_true);
|
||||
// CHECK_EQ(a, b) << z_is_interesting_too;
|
||||
|
||||
#include <base/errno_restorer.h>
|
||||
#include <base/macros.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string_view>
|
||||
|
||||
// Note: DO NOT USE DIRECTLY. Use LOG_TAG instead.
|
||||
#ifdef _LOG_TAG_INTERNAL
|
||||
#error "_LOG_TAG_INTERNAL must not be defined"
|
||||
#endif
|
||||
#ifdef LOG_TAG
|
||||
#define _LOG_TAG_INTERNAL LOG_TAG
|
||||
#else
|
||||
#define _LOG_TAG_INTERNAL nullptr
|
||||
#endif
|
||||
|
||||
namespace ndksamples::base {
|
||||
|
||||
enum LogSeverity {
|
||||
VERBOSE,
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARNING,
|
||||
ERROR,
|
||||
FATAL_WITHOUT_ABORT, // For loggability tests, this is considered identical
|
||||
// to FATAL.
|
||||
FATAL,
|
||||
};
|
||||
|
||||
enum LogId {
|
||||
DEFAULT,
|
||||
MAIN,
|
||||
SYSTEM,
|
||||
RADIO,
|
||||
CRASH,
|
||||
};
|
||||
|
||||
using LogFunction = std::function<void(
|
||||
LogId /*log_buffer_id*/, LogSeverity /*severity*/, const char* /*tag*/,
|
||||
const char* /*file*/, unsigned int /*line*/, const char* /*message*/)>;
|
||||
using AbortFunction = std::function<void(const char* /*abort_message*/)>;
|
||||
|
||||
void DefaultAborter(const char* abort_message);
|
||||
|
||||
void SetDefaultTag(const std::string& tag);
|
||||
|
||||
// The LogdLogger sends chunks of up to ~4000 bytes at a time to logd. It does
|
||||
// not prevent other threads from writing to logd between sending each chunk, so
|
||||
// other threads may interleave their messages. If preventing interleaving is
|
||||
// required, then a custom logger that takes a lock before calling this logger
|
||||
// should be provided.
|
||||
class LogdLogger {
|
||||
public:
|
||||
explicit LogdLogger(LogId default_log_id = ndksamples::base::MAIN);
|
||||
|
||||
void operator()(LogId, LogSeverity, const char* tag, const char* file,
|
||||
unsigned int line, const char* message);
|
||||
|
||||
private:
|
||||
LogId default_log_id_;
|
||||
};
|
||||
|
||||
// Configure logging based on ANDROID_LOG_TAGS environment variable.
|
||||
// We need to parse a string that looks like
|
||||
//
|
||||
// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
|
||||
//
|
||||
// The tag (or '*' for the global level) comes first, followed by a colon and a
|
||||
// letter indicating the minimum priority level we're expected to log. This can
|
||||
// be used to reveal or conceal logs with specific tags.
|
||||
#define INIT_LOGGING_DEFAULT_LOGGER LogdLogger()
|
||||
void InitLogging(const std::optional<std::string_view> default_tag = {},
|
||||
std::optional<LogSeverity> log_level = {},
|
||||
LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER,
|
||||
AbortFunction&& aborter = DefaultAborter);
|
||||
#undef INIT_LOGGING_DEFAULT_LOGGER
|
||||
|
||||
// Replace the current logger and return the old one.
|
||||
LogFunction SetLogger(LogFunction&& logger);
|
||||
|
||||
// Replace the current aborter and return the old one.
|
||||
AbortFunction SetAborter(AbortFunction&& aborter);
|
||||
|
||||
// A helper macro that produces an expression that accepts both a qualified name
|
||||
// and an unqualified name for a LogSeverity, and returns a LogSeverity value.
|
||||
// Note: DO NOT USE DIRECTLY. This is an implementation detail.
|
||||
#define SEVERITY_LAMBDA(severity) \
|
||||
([&]() { \
|
||||
using ::ndksamples::base::VERBOSE; \
|
||||
using ::ndksamples::base::DEBUG; \
|
||||
using ::ndksamples::base::INFO; \
|
||||
using ::ndksamples::base::WARNING; \
|
||||
using ::ndksamples::base::ERROR; \
|
||||
using ::ndksamples::base::FATAL_WITHOUT_ABORT; \
|
||||
using ::ndksamples::base::FATAL; \
|
||||
return (severity); \
|
||||
}())
|
||||
|
||||
#define ABORT_AFTER_LOG_FATAL
|
||||
#define ABORT_AFTER_LOG_EXPR_IF(c, x) (x)
|
||||
#define MUST_LOG_MESSAGE(severity) false
|
||||
#define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
|
||||
|
||||
// Defines whether the given severity will be logged or silently swallowed.
|
||||
#define WOULD_LOG(severity) \
|
||||
(UNLIKELY(::ndksamples::base::ShouldLog(SEVERITY_LAMBDA(severity), \
|
||||
_LOG_TAG_INTERNAL)) || \
|
||||
MUST_LOG_MESSAGE(severity))
|
||||
|
||||
// Get an ostream that can be used for logging at the given severity and to the
|
||||
// default destination.
|
||||
//
|
||||
// Notes:
|
||||
// 1) This will not check whether the severity is high enough. One should use
|
||||
// WOULD_LOG to filter
|
||||
// usage manually.
|
||||
// 2) This does not save and restore errno.
|
||||
#define LOG_STREAM(severity) \
|
||||
::ndksamples::base::LogMessage( \
|
||||
__FILE__, __LINE__, SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, -1) \
|
||||
.stream()
|
||||
|
||||
// Logs a message to logcat on Android otherwise to stderr. If the severity is
|
||||
// FATAL it also causes an abort. For example:
|
||||
//
|
||||
// LOG(FATAL) << "We didn't expect to reach here";
|
||||
#define LOG(severity) LOGGING_PREAMBLE(severity) && LOG_STREAM(severity)
|
||||
|
||||
// Checks if we want to log something, and sets up appropriate RAII objects if
|
||||
// so.
|
||||
// Note: DO NOT USE DIRECTLY. This is an implementation detail.
|
||||
#define LOGGING_PREAMBLE(severity) \
|
||||
(WOULD_LOG(severity) && \
|
||||
ABORT_AFTER_LOG_EXPR_IF( \
|
||||
(SEVERITY_LAMBDA(severity)) == ::ndksamples::base::FATAL, true) && \
|
||||
::ndksamples::base::ErrnoRestorer())
|
||||
|
||||
// A variant of LOG that also logs the current errno value. To be used when
|
||||
// library calls fail.
|
||||
#define PLOG(severity) \
|
||||
LOGGING_PREAMBLE(severity) && \
|
||||
::ndksamples::base::LogMessage(__FILE__, __LINE__, \
|
||||
SEVERITY_LAMBDA(severity), \
|
||||
_LOG_TAG_INTERNAL, errno) \
|
||||
.stream()
|
||||
|
||||
// Marker that code is yet to be implemented.
|
||||
#define UNIMPLEMENTED(level) \
|
||||
LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
|
||||
|
||||
// Check whether condition x holds and LOG(FATAL) if not. The value of the
|
||||
// expression x is only evaluated once. Extra logging can be appended using <<
|
||||
// after. For example:
|
||||
//
|
||||
// CHECK(false == true) results in a log message of
|
||||
// "Check failed: false == true".
|
||||
#define CHECK(x) \
|
||||
LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) || \
|
||||
::ndksamples::base::LogMessage(__FILE__, __LINE__, \
|
||||
::ndksamples::base::FATAL, \
|
||||
_LOG_TAG_INTERNAL, -1) \
|
||||
.stream() \
|
||||
<< "Check failed: " #x << " "
|
||||
|
||||
// clang-format off
|
||||
// Helper for CHECK_xx(x,y) macros.
|
||||
#define CHECK_OP(LHS, RHS, OP) \
|
||||
for (auto _values = ::ndksamples::base::MakeEagerEvaluator(LHS, RHS); \
|
||||
UNLIKELY(!(_values.lhs.v OP _values.rhs.v)); \
|
||||
/* empty */) \
|
||||
ABORT_AFTER_LOG_FATAL \
|
||||
::ndksamples::base::LogMessage(__FILE__, __LINE__, ::ndksamples::base::FATAL, _LOG_TAG_INTERNAL, -1) \
|
||||
.stream() \
|
||||
<< "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" \
|
||||
<< ::ndksamples::base::LogNullGuard<decltype(_values.lhs.v)>::Guard(_values.lhs.v) \
|
||||
<< ", " #RHS "=" \
|
||||
<< ::ndksamples::base::LogNullGuard<decltype(_values.rhs.v)>::Guard(_values.rhs.v) \
|
||||
<< ") "
|
||||
// clang-format on
|
||||
|
||||
// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
|
||||
// of the expressions x and y is evaluated once. Extra logging can be appended
|
||||
// using << after. For example:
|
||||
//
|
||||
// CHECK_NE(0 == 1, false) results in
|
||||
// "Check failed: false != false (0==1=false, false=false) ".
|
||||
#define CHECK_EQ(x, y) CHECK_OP(x, y, ==)
|
||||
#define CHECK_NE(x, y) CHECK_OP(x, y, !=)
|
||||
#define CHECK_LE(x, y) CHECK_OP(x, y, <=)
|
||||
#define CHECK_LT(x, y) CHECK_OP(x, y, <)
|
||||
#define CHECK_GE(x, y) CHECK_OP(x, y, >=)
|
||||
#define CHECK_GT(x, y) CHECK_OP(x, y, >)
|
||||
|
||||
// clang-format off
|
||||
// Helper for CHECK_STRxx(s1,s2) macros.
|
||||
#define CHECK_STROP(s1, s2, sense) \
|
||||
while (UNLIKELY((strcmp(s1, s2) == 0) != (sense))) \
|
||||
ABORT_AFTER_LOG_FATAL \
|
||||
::ndksamples::base::LogMessage(__FILE__, __LINE__, ::ndksamples::base::FATAL, \
|
||||
_LOG_TAG_INTERNAL, -1) \
|
||||
.stream() \
|
||||
<< "Check failed: " << "\"" << (s1) << "\"" \
|
||||
<< ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
|
||||
// clang-format on
|
||||
|
||||
// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
|
||||
#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
|
||||
#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
|
||||
|
||||
// Perform the pthread function call(args), LOG(FATAL) on error.
|
||||
#define CHECK_PTHREAD_CALL(call, args, what) \
|
||||
do { \
|
||||
int rc = call args; \
|
||||
if (rc != 0) { \
|
||||
errno = rc; \
|
||||
ABORT_AFTER_LOG_FATAL \
|
||||
PLOG(FATAL) << #call << " failed for " << (what); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
|
||||
// CHECK should be used unless profiling identifies a CHECK as being in
|
||||
// performance critical code.
|
||||
#if defined(NDEBUG) && !defined(__clang_analyzer__)
|
||||
static constexpr bool kEnableDChecks = false;
|
||||
#else
|
||||
static constexpr bool kEnableDChecks = true;
|
||||
#endif
|
||||
|
||||
#define DCHECK(x) \
|
||||
if (::ndksamples::base::kEnableDChecks) CHECK(x)
|
||||
#define DCHECK_EQ(x, y) \
|
||||
if (::ndksamples::base::kEnableDChecks) CHECK_EQ(x, y)
|
||||
#define DCHECK_NE(x, y) \
|
||||
if (::ndksamples::base::kEnableDChecks) CHECK_NE(x, y)
|
||||
#define DCHECK_LE(x, y) \
|
||||
if (::ndksamples::base::kEnableDChecks) CHECK_LE(x, y)
|
||||
#define DCHECK_LT(x, y) \
|
||||
if (::ndksamples::base::kEnableDChecks) CHECK_LT(x, y)
|
||||
#define DCHECK_GE(x, y) \
|
||||
if (::ndksamples::base::kEnableDChecks) CHECK_GE(x, y)
|
||||
#define DCHECK_GT(x, y) \
|
||||
if (::ndksamples::base::kEnableDChecks) CHECK_GT(x, y)
|
||||
#define DCHECK_STREQ(s1, s2) \
|
||||
if (::ndksamples::base::kEnableDChecks) CHECK_STREQ(s1, s2)
|
||||
#define DCHECK_STRNE(s1, s2) \
|
||||
if (::ndksamples::base::kEnableDChecks) CHECK_STRNE(s1, s2)
|
||||
|
||||
namespace log_detail {
|
||||
|
||||
// Temporary storage for a single eagerly evaluated check expression operand.
|
||||
template <typename T>
|
||||
struct Storage {
|
||||
template <typename U>
|
||||
explicit constexpr Storage(U&& u) : v(std::forward<U>(u)) {}
|
||||
explicit Storage(const Storage& t) = delete;
|
||||
explicit Storage(Storage&& t) = delete;
|
||||
T v;
|
||||
};
|
||||
|
||||
// Partial specialization for smart pointers to avoid copying.
|
||||
template <typename T>
|
||||
struct Storage<std::unique_ptr<T>> {
|
||||
explicit constexpr Storage(const std::unique_ptr<T>& ptr) : v(ptr.get()) {}
|
||||
const T* v;
|
||||
};
|
||||
template <typename T>
|
||||
struct Storage<std::shared_ptr<T>> {
|
||||
explicit constexpr Storage(const std::shared_ptr<T>& ptr) : v(ptr.get()) {}
|
||||
const T* v;
|
||||
};
|
||||
|
||||
// Type trait that checks if a type is a (potentially const) char pointer.
|
||||
template <typename T>
|
||||
struct IsCharPointer {
|
||||
using Pointee = std::remove_cv_t<std::remove_pointer_t<T>>;
|
||||
static constexpr bool value =
|
||||
std::is_pointer_v<T> &&
|
||||
(std::is_same_v<Pointee, char> || std::is_same_v<Pointee, signed char> ||
|
||||
std::is_same_v<Pointee, unsigned char>);
|
||||
};
|
||||
|
||||
// Counterpart to Storage that depends on both operands. This is used to prevent
|
||||
// char pointers being treated as strings in the log output - they might point
|
||||
// to buffers of unprintable binary data.
|
||||
template <typename LHS, typename RHS>
|
||||
struct StorageTypes {
|
||||
static constexpr bool voidptr =
|
||||
IsCharPointer<LHS>::value && IsCharPointer<RHS>::value;
|
||||
using LHSType = std::conditional_t<voidptr, const void*, LHS>;
|
||||
using RHSType = std::conditional_t<voidptr, const void*, RHS>;
|
||||
};
|
||||
|
||||
// Temporary class created to evaluate the LHS and RHS, used with
|
||||
// MakeEagerEvaluator to infer the types of LHS and RHS.
|
||||
template <typename LHS, typename RHS>
|
||||
struct EagerEvaluator {
|
||||
template <typename A, typename B>
|
||||
constexpr EagerEvaluator(A&& l, B&& r)
|
||||
: lhs(std::forward<A>(l)), rhs(std::forward<B>(r)) {}
|
||||
const Storage<typename StorageTypes<LHS, RHS>::LHSType> lhs;
|
||||
const Storage<typename StorageTypes<LHS, RHS>::RHSType> rhs;
|
||||
};
|
||||
|
||||
} // namespace log_detail
|
||||
|
||||
// Converts std::nullptr_t and null char pointers to the string "null"
|
||||
// when writing the failure message.
|
||||
template <typename T>
|
||||
struct LogNullGuard {
|
||||
static const T& Guard(const T& v) { return v; }
|
||||
};
|
||||
template <>
|
||||
struct LogNullGuard<std::nullptr_t> {
|
||||
static const char* Guard(const std::nullptr_t&) { return "(null)"; }
|
||||
};
|
||||
template <>
|
||||
struct LogNullGuard<char*> {
|
||||
static const char* Guard(const char* v) { return v ? v : "(null)"; }
|
||||
};
|
||||
template <>
|
||||
struct LogNullGuard<const char*> {
|
||||
static const char* Guard(const char* v) { return v ? v : "(null)"; }
|
||||
};
|
||||
|
||||
// Helper function for CHECK_xx.
|
||||
template <typename LHS, typename RHS>
|
||||
constexpr auto MakeEagerEvaluator(LHS&& lhs, RHS&& rhs) {
|
||||
return log_detail::EagerEvaluator<std::decay_t<LHS>, std::decay_t<RHS>>(
|
||||
std::forward<LHS>(lhs), std::forward<RHS>(rhs));
|
||||
}
|
||||
|
||||
// Data for the log message, not stored in LogMessage to avoid increasing the
|
||||
// stack size.
|
||||
class LogMessageData;
|
||||
|
||||
// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
|
||||
// of a CHECK. The destructor will abort if the severity is FATAL.
|
||||
class LogMessage {
|
||||
public:
|
||||
// LogId has been deprecated, but this constructor must exist for prebuilts.
|
||||
LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
|
||||
const char* tag, int error);
|
||||
LogMessage(const char* file, unsigned int line, LogSeverity severity,
|
||||
const char* tag, int error);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LogMessage);
|
||||
|
||||
~LogMessage();
|
||||
|
||||
// Returns the stream associated with the message, the LogMessage performs
|
||||
// output when it goes out of scope.
|
||||
std::ostream& stream();
|
||||
|
||||
// The routine that performs the actual logging.
|
||||
static void LogLine(const char* file, unsigned int line, LogSeverity severity,
|
||||
const char* tag, const char* msg);
|
||||
|
||||
private:
|
||||
const std::unique_ptr<LogMessageData> data_;
|
||||
};
|
||||
|
||||
// Get the minimum severity level for logging.
|
||||
LogSeverity GetMinimumLogSeverity();
|
||||
|
||||
// Set the minimum severity level for logging, returning the old severity.
|
||||
LogSeverity SetMinimumLogSeverity(LogSeverity new_severity);
|
||||
|
||||
// Return whether or not a log message with the associated tag should be logged.
|
||||
bool ShouldLog(LogSeverity severity, const char* tag);
|
||||
|
||||
// Allows to temporarily change the minimum severity level for logging.
|
||||
class ScopedLogSeverity {
|
||||
public:
|
||||
explicit ScopedLogSeverity(LogSeverity level);
|
||||
~ScopedLogSeverity();
|
||||
|
||||
private:
|
||||
LogSeverity old_;
|
||||
};
|
||||
|
||||
} // namespace ndksamples::base
|
||||
|
||||
namespace std { // NOLINT(cert-dcl58-cpp)
|
||||
|
||||
// Emit a warning of ostream<< with std::string*. The intention was most likely
|
||||
// to print *string.
|
||||
//
|
||||
// Note: for this to work, we need to have this in a namespace.
|
||||
// Note: using a pragma because "-Wgcc-compat" (included in "-Weverything")
|
||||
// complains about
|
||||
// diagnose_if.
|
||||
// Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)"
|
||||
// instead. Note: a not-recommended alternative is to let Clang ignore the
|
||||
// warning by adding
|
||||
// -Wno-user-defined-warnings to CPPFLAGS.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgcc-compat"
|
||||
#define OSTREAM_STRING_POINTER_USAGE_WARNING \
|
||||
__attribute__(( \
|
||||
diagnose_if(true, "Unexpected logging of string pointer", "warning")))
|
||||
inline OSTREAM_STRING_POINTER_USAGE_WARNING std::ostream& operator<<(
|
||||
std::ostream& stream, const std::string* string_pointer) {
|
||||
return stream << static_cast<const void*>(string_pointer);
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
} // namespace std
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
|
||||
#include <utility>
|
||||
|
||||
// A macro to disallow the copy constructor and operator= functions
|
||||
// This must be placed in the private: declarations for a class.
|
||||
//
|
||||
// For disallowing only assign or copy, delete the relevant operator or
|
||||
// constructor, for example:
|
||||
// void operator=(const TypeName&) = delete;
|
||||
// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
|
||||
// semantically, one should either use disallow both or neither. Try to
|
||||
// avoid these in new code.
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
void operator=(const TypeName&) = delete
|
||||
|
||||
// A macro to disallow all the implicit constructors, namely the
|
||||
// default constructor, copy constructor and operator= functions.
|
||||
//
|
||||
// This should be used in the private: declarations for a class
|
||||
// that wants to prevent anyone from instantiating it. This is
|
||||
// especially useful for classes containing only static methods.
|
||||
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||
TypeName() = delete; \
|
||||
DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||
|
||||
// The arraysize(arr) macro returns the # of elements in an array arr.
|
||||
// The expression is a compile-time constant, and therefore can be
|
||||
// used in defining new arrays, for example. If you use arraysize on
|
||||
// a pointer by mistake, you will get a compile-time error.
|
||||
//
|
||||
// One caveat is that arraysize() doesn't accept any array of an
|
||||
// anonymous type or a type defined inside a function. In these rare
|
||||
// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
|
||||
// due to a limitation in C++'s template system. The limitation might
|
||||
// eventually be removed, but it hasn't happened yet.
|
||||
|
||||
// This template function declaration is used in defining arraysize.
|
||||
// Note that the function doesn't need an implementation, as we only
|
||||
// use its type.
|
||||
template <typename T, size_t N>
|
||||
char (&ArraySizeHelper(T (&array)[N]))[N]; // NOLINT(readability/casting)
|
||||
|
||||
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
|
||||
|
||||
#define SIZEOF_MEMBER(t, f) sizeof(std::declval<t>().f)
|
||||
|
||||
// Changing this definition will cause you a lot of pain. A majority of
|
||||
// vendor code defines LIKELY and UNLIKELY this way, and includes
|
||||
// this header through an indirect path.
|
||||
#define LIKELY(exp) (__builtin_expect((exp) != 0, true))
|
||||
#define UNLIKELY(exp) (__builtin_expect((exp) != 0, false))
|
||||
|
||||
/// True if the (runtime) version of the OS is at least x.
|
||||
///
|
||||
/// Clang is very particular about how __builtin_available is used. Logical
|
||||
/// operations (including negation) may not be combined with
|
||||
/// __builtin_available, so to negate this check you must do:
|
||||
///
|
||||
/// if (API_AT_LEAST(x)) {
|
||||
/// } else {
|
||||
/// // do negated stuff
|
||||
/// }
|
||||
#define API_AT_LEAST(x) __builtin_available(android x, *)
|
||||
|
||||
/// Marks a function as not callable on OS versions older than x.
|
||||
///
|
||||
/// This is a minor abuse of Clang's __attribute__((availability)), so the
|
||||
/// diagnostic for this will be a little odd, but it allows us to extract
|
||||
/// functions from code that already has an API_AT_LEAST guard without rewriting
|
||||
/// the guard in every called function.
|
||||
#define REQUIRES_API(x) __INTRODUCED_IN(x)
|
||||
@@ -1,294 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "base/logging.h"
|
||||
|
||||
#include <android/log.h>
|
||||
#include <android/set_abort_message.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <libgen.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "logging_splitters.h"
|
||||
|
||||
namespace ndksamples::base {
|
||||
|
||||
static const char* GetFileBasename(const char* file) {
|
||||
// We can't use basename(3) even on Unix because the Mac doesn't
|
||||
// have a non-modifying basename.
|
||||
const char* last_slash = strrchr(file, '/');
|
||||
if (last_slash != nullptr) {
|
||||
return last_slash + 1;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
static int32_t LogIdTolog_id_t(LogId log_id) {
|
||||
switch (log_id) {
|
||||
case MAIN:
|
||||
return LOG_ID_MAIN;
|
||||
case SYSTEM:
|
||||
return LOG_ID_SYSTEM;
|
||||
case RADIO:
|
||||
return LOG_ID_RADIO;
|
||||
case CRASH:
|
||||
return LOG_ID_CRASH;
|
||||
case DEFAULT:
|
||||
default:
|
||||
return LOG_ID_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t LogSeverityToPriority(LogSeverity severity) {
|
||||
switch (severity) {
|
||||
case VERBOSE:
|
||||
return ANDROID_LOG_VERBOSE;
|
||||
case DEBUG:
|
||||
return ANDROID_LOG_DEBUG;
|
||||
case INFO:
|
||||
return ANDROID_LOG_INFO;
|
||||
case WARNING:
|
||||
return ANDROID_LOG_WARN;
|
||||
case ERROR:
|
||||
return ANDROID_LOG_ERROR;
|
||||
case FATAL_WITHOUT_ABORT:
|
||||
case FATAL:
|
||||
default:
|
||||
return ANDROID_LOG_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
static LogFunction& Logger() {
|
||||
static auto& logger = *new LogFunction(LogdLogger());
|
||||
return logger;
|
||||
}
|
||||
|
||||
static AbortFunction& Aborter() {
|
||||
static auto& aborter = *new AbortFunction(DefaultAborter);
|
||||
return aborter;
|
||||
}
|
||||
|
||||
// Only used for Q fallback.
|
||||
static std::recursive_mutex& TagLock() {
|
||||
static auto& tag_lock = *new std::recursive_mutex();
|
||||
return tag_lock;
|
||||
}
|
||||
|
||||
static std::string* gDefaultTag;
|
||||
|
||||
void SetDefaultTag(const std::string_view tag) {
|
||||
std::lock_guard<std::recursive_mutex> lock(TagLock());
|
||||
if (gDefaultTag != nullptr) {
|
||||
delete gDefaultTag;
|
||||
gDefaultTag = nullptr;
|
||||
}
|
||||
if (!tag.empty()) {
|
||||
gDefaultTag = new std::string(tag);
|
||||
}
|
||||
}
|
||||
|
||||
static bool gInitialized = false;
|
||||
|
||||
// Only used for Q fallback.
|
||||
static LogSeverity gMinimumLogSeverity = INFO;
|
||||
|
||||
void DefaultAborter(const char* abort_message) {
|
||||
android_set_abort_message(abort_message);
|
||||
abort();
|
||||
}
|
||||
|
||||
static void LogdLogChunk(LogId id, LogSeverity severity, const char* tag,
|
||||
const char* message) {
|
||||
int32_t lg_id = LogIdTolog_id_t(id);
|
||||
int32_t priority = LogSeverityToPriority(severity);
|
||||
|
||||
__android_log_buf_print(lg_id, priority, tag, "%s", message);
|
||||
}
|
||||
|
||||
LogdLogger::LogdLogger(LogId default_log_id)
|
||||
: default_log_id_(default_log_id) {}
|
||||
|
||||
void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
|
||||
const char* file, unsigned int line,
|
||||
const char* message) {
|
||||
if (id == DEFAULT) {
|
||||
id = default_log_id_;
|
||||
}
|
||||
|
||||
SplitByLogdChunks(id, severity, tag, file, line, message, LogdLogChunk);
|
||||
}
|
||||
|
||||
void InitLogging(const std::optional<std::string_view> default_tag,
|
||||
std::optional<LogSeverity> log_level, LogFunction&& logger,
|
||||
AbortFunction&& aborter) {
|
||||
SetLogger(std::forward<LogFunction>(logger));
|
||||
SetAborter(std::forward<AbortFunction>(aborter));
|
||||
|
||||
if (gInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
gInitialized = true;
|
||||
|
||||
if (default_tag.has_value()) {
|
||||
SetDefaultTag(default_tag.value());
|
||||
}
|
||||
|
||||
const char* tags = getenv("ANDROID_LOG_TAGS");
|
||||
if (tags == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (log_level.has_value()) {
|
||||
SetMinimumLogSeverity(log_level.value());
|
||||
}
|
||||
}
|
||||
|
||||
LogFunction SetLogger(LogFunction&& logger) {
|
||||
LogFunction old_logger = std::move(Logger());
|
||||
Logger() = std::move(logger);
|
||||
return old_logger;
|
||||
}
|
||||
|
||||
AbortFunction SetAborter(AbortFunction&& aborter) {
|
||||
AbortFunction old_aborter = std::move(Aborter());
|
||||
Aborter() = std::move(aborter);
|
||||
return old_aborter;
|
||||
}
|
||||
|
||||
// This indirection greatly reduces the stack impact of having lots of
|
||||
// checks/logging in a function.
|
||||
class LogMessageData {
|
||||
public:
|
||||
LogMessageData(const char* file, unsigned int line, LogSeverity severity,
|
||||
const char* tag, int error)
|
||||
: file_(GetFileBasename(file)),
|
||||
line_number_(line),
|
||||
severity_(severity),
|
||||
tag_(tag),
|
||||
error_(error) {}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LogMessageData);
|
||||
|
||||
const char* GetFile() const { return file_; }
|
||||
|
||||
unsigned int GetLineNumber() const { return line_number_; }
|
||||
|
||||
LogSeverity GetSeverity() const { return severity_; }
|
||||
|
||||
const char* GetTag() const { return tag_; }
|
||||
|
||||
int GetError() const { return error_; }
|
||||
|
||||
std::ostream& GetBuffer() { return buffer_; }
|
||||
|
||||
std::string ToString() const { return buffer_.str(); }
|
||||
|
||||
private:
|
||||
std::ostringstream buffer_;
|
||||
const char* const file_;
|
||||
const unsigned int line_number_;
|
||||
const LogSeverity severity_;
|
||||
const char* const tag_;
|
||||
const int error_;
|
||||
};
|
||||
|
||||
LogMessage::LogMessage(const char* file, unsigned int line, LogId,
|
||||
LogSeverity severity, const char* tag, int error)
|
||||
: LogMessage(file, line, severity, tag, error) {}
|
||||
|
||||
LogMessage::LogMessage(const char* file, unsigned int line,
|
||||
LogSeverity severity, const char* tag, int error)
|
||||
: data_(new LogMessageData(file, line, severity, tag, error)) {}
|
||||
|
||||
LogMessage::~LogMessage() {
|
||||
// Check severity again. This is duplicate work wrt/ LOG macros, but not
|
||||
// LOG_STREAM.
|
||||
if (!WOULD_LOG(data_->GetSeverity())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Finish constructing the message.
|
||||
if (data_->GetError() != -1) {
|
||||
data_->GetBuffer() << ": " << strerror(data_->GetError());
|
||||
}
|
||||
std::string msg(data_->ToString());
|
||||
|
||||
if (data_->GetSeverity() == FATAL) {
|
||||
// Set the bionic abort message early to avoid liblog doing it
|
||||
// with the individual lines, so that we get the whole message.
|
||||
android_set_abort_message(msg.c_str());
|
||||
}
|
||||
|
||||
LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(),
|
||||
data_->GetTag(), msg.c_str());
|
||||
|
||||
// Abort if necessary.
|
||||
if (data_->GetSeverity() == FATAL) {
|
||||
Aborter()(msg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& LogMessage::stream() { return data_->GetBuffer(); }
|
||||
|
||||
void LogMessage::LogLine(const char* file, unsigned int line,
|
||||
LogSeverity severity, const char* tag,
|
||||
const char* message) {
|
||||
if (tag == nullptr) {
|
||||
std::lock_guard<std::recursive_mutex> lock(TagLock());
|
||||
if (gDefaultTag == nullptr) {
|
||||
gDefaultTag = new std::string(getprogname());
|
||||
}
|
||||
|
||||
Logger()(DEFAULT, severity, gDefaultTag->c_str(), file, line, message);
|
||||
} else {
|
||||
Logger()(DEFAULT, severity, tag, file, line, message);
|
||||
}
|
||||
}
|
||||
|
||||
LogSeverity GetMinimumLogSeverity() { return gMinimumLogSeverity; }
|
||||
|
||||
bool ShouldLog(LogSeverity severity, const char*) {
|
||||
return severity >= gMinimumLogSeverity;
|
||||
}
|
||||
|
||||
LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
|
||||
LogSeverity old_severity = gMinimumLogSeverity;
|
||||
gMinimumLogSeverity = new_severity;
|
||||
return old_severity;
|
||||
}
|
||||
|
||||
ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) {
|
||||
old_ = SetMinimumLogSeverity(new_severity);
|
||||
}
|
||||
|
||||
ScopedLogSeverity::~ScopedLogSeverity() { SetMinimumLogSeverity(old_); }
|
||||
|
||||
} // namespace ndksamples::base
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <format>
|
||||
|
||||
#include "base/logging.h"
|
||||
|
||||
#define LOGGER_ENTRY_MAX_PAYLOAD 4068 // This constant is not in the NDK.
|
||||
|
||||
namespace ndksamples::base {
|
||||
|
||||
// This splits the message up line by line, by calling log_function with a
|
||||
// pointer to the start of each line and the size up to the newline character.
|
||||
// It sends size = -1 for the final line.
|
||||
template <typename F, typename... Args>
|
||||
static void SplitByLines(const char* msg, const F& log_function,
|
||||
Args&&... args) {
|
||||
const char* newline = strchr(msg, '\n');
|
||||
while (newline != nullptr) {
|
||||
log_function(msg, newline - msg, args...);
|
||||
msg = newline + 1;
|
||||
newline = strchr(msg, '\n');
|
||||
}
|
||||
|
||||
log_function(msg, -1, args...);
|
||||
}
|
||||
|
||||
// This splits the message up into chunks that logs can process delimited by new
|
||||
// lines. It calls log_function with the exact null terminated message that
|
||||
// should be sent to logd. Note, despite the loops and snprintf's, if severity
|
||||
// is not fatal and there are no new lines, this function simply calls
|
||||
// log_function with msg without any extra overhead.
|
||||
template <typename F>
|
||||
static void SplitByLogdChunks(LogId log_id, LogSeverity severity,
|
||||
const char* tag, const char* file,
|
||||
unsigned int line, const char* msg,
|
||||
const F& log_function) {
|
||||
// The maximum size of a payload, after the log header that logd will accept
|
||||
// is LOGGER_ENTRY_MAX_PAYLOAD, so subtract the other elements in the payload
|
||||
// to find the size of the string that we can log in each pass. The protocol
|
||||
// is documented in liblog/README.protocol.md. Specifically we subtract a byte
|
||||
// for the priority, the length of the tag + its null terminator, and an
|
||||
// additional byte for the null terminator on the payload. We subtract an
|
||||
// additional 32 bytes for slack, similar to java/android/util/Log.java.
|
||||
ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
|
||||
if (max_size <= 0) {
|
||||
abort();
|
||||
}
|
||||
// If we're logging a fatal message, we'll append the file and line numbers.
|
||||
bool add_file =
|
||||
file != nullptr && (severity == FATAL || severity == FATAL_WITHOUT_ABORT);
|
||||
|
||||
std::string file_header;
|
||||
if (add_file) {
|
||||
file_header = std::format("{}:{}]", file, line);
|
||||
}
|
||||
int file_header_size = file_header.size();
|
||||
|
||||
__attribute__((uninitialized)) char logd_chunk[max_size + 1];
|
||||
ptrdiff_t chunk_position = 0;
|
||||
|
||||
auto call_log_function = [&]() {
|
||||
log_function(log_id, severity, tag, logd_chunk);
|
||||
chunk_position = 0;
|
||||
};
|
||||
|
||||
auto write_to_logd_chunk = [&](const char* message, int length) {
|
||||
int size_written = 0;
|
||||
const char* new_line = chunk_position > 0 ? "\n" : "";
|
||||
if (add_file) {
|
||||
size_written = snprintf(logd_chunk + chunk_position,
|
||||
sizeof(logd_chunk) - chunk_position, "%s%s%.*s",
|
||||
new_line, file_header.c_str(), length, message);
|
||||
} else {
|
||||
size_written = snprintf(logd_chunk + chunk_position,
|
||||
sizeof(logd_chunk) - chunk_position, "%s%.*s",
|
||||
new_line, length, message);
|
||||
}
|
||||
|
||||
// This should never fail, if it does and we set size_written to 0, which
|
||||
// will skip this line and move to the next one.
|
||||
if (size_written < 0) {
|
||||
size_written = 0;
|
||||
}
|
||||
chunk_position += size_written;
|
||||
};
|
||||
|
||||
const char* newline = strchr(msg, '\n');
|
||||
while (newline != nullptr) {
|
||||
// If we have data in the buffer and this next line doesn't fit, write the
|
||||
// buffer.
|
||||
if (chunk_position != 0 &&
|
||||
chunk_position + (newline - msg) + 1 + file_header_size > max_size) {
|
||||
call_log_function();
|
||||
}
|
||||
|
||||
// Otherwise, either the next line fits or we have any empty buffer and too
|
||||
// large of a line to ever fit, in both cases, we add it to the buffer and
|
||||
// continue.
|
||||
write_to_logd_chunk(msg, newline - msg);
|
||||
|
||||
msg = newline + 1;
|
||||
newline = strchr(msg, '\n');
|
||||
}
|
||||
|
||||
// If we have left over data in the buffer and we can fit the rest of msg, add
|
||||
// it to the buffer then write the buffer.
|
||||
if (chunk_position != 0 &&
|
||||
chunk_position + static_cast<int>(strlen(msg)) + 1 + file_header_size <=
|
||||
max_size) {
|
||||
write_to_logd_chunk(msg, -1);
|
||||
call_log_function();
|
||||
} else {
|
||||
// If the buffer is not empty and we can't fit the rest of msg into it,
|
||||
// write its contents.
|
||||
if (chunk_position != 0) {
|
||||
call_log_function();
|
||||
}
|
||||
// Then write the rest of the msg.
|
||||
if (add_file) {
|
||||
snprintf(logd_chunk, sizeof(logd_chunk), "%s%s", file_header.c_str(),
|
||||
msg);
|
||||
log_function(log_id, severity, tag, logd_chunk);
|
||||
} else {
|
||||
log_function(log_id, severity, tag, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ndksamples::base
|
||||
16
bitmap-plasma/AndroidManifest.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.plasma"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
<application android:label="@string/app_name" android:debuggable="true">
|
||||
<activity android:name=".Plasma"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
<uses-sdk android:minSdkVersion="8"/>
|
||||
</manifest>
|
||||
@@ -1,10 +0,0 @@
|
||||
# Bitmap Plasma
|
||||
|
||||
Bitmap Plasma is an Android sample that uses JNI to render a plasma effect in an
|
||||
Android
|
||||
[Bitmap](http://developer.android.com/reference/android/graphics/Bitmap.html)
|
||||
from C code.
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
@@ -1,23 +0,0 @@
|
||||
plugins {
|
||||
id "ndksamples.android.application"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.example.plasma'
|
||||
defaultConfig {
|
||||
applicationId 'com.example.plasma'
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path 'src/main/cpp/CMakeLists.txt'
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
prefab true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(":base")
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name">
|
||||
<activity android:name=".Plasma"
|
||||
android:label="@string/app_name"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -1,15 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
project(BitmapPlasma LANGUAGES CXX)
|
||||
|
||||
include(AppLibrary)
|
||||
find_package(base CONFIG REQUIRED)
|
||||
|
||||
add_app_library(plasma SHARED jni.cpp plasma.cpp)
|
||||
|
||||
target_link_libraries(plasma
|
||||
base::base
|
||||
android
|
||||
jnigraphics
|
||||
log
|
||||
m
|
||||
)
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (C) 2025 The Android Open Source Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <base/macros.h>
|
||||
#include <jni.h>
|
||||
|
||||
#include "plasma.h"
|
||||
|
||||
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* _Nonnull vm, void* _Nullable) {
|
||||
JNIEnv* env;
|
||||
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
jclass c = env->FindClass("com/example/plasma/PlasmaView");
|
||||
if (c == nullptr) return JNI_ERR;
|
||||
|
||||
static const JNINativeMethod methods[] = {
|
||||
{"renderPlasma", "(Landroid/graphics/Bitmap;J)V",
|
||||
reinterpret_cast<void*>(RenderPlasma)},
|
||||
};
|
||||
int rc = env->RegisterNatives(c, methods, arraysize(methods));
|
||||
if (rc != JNI_OK) return rc;
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
LIBPLASMA {
|
||||
global:
|
||||
JNI_OnLoad;
|
||||
local:
|
||||
*;
|
||||
};
|
||||
@@ -1,365 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <android/bitmap.h>
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#define LOG_TAG "libplasma"
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
/* Set to 1 to enable debug log traces. */
|
||||
#define DEBUG 0
|
||||
|
||||
/* Set to 1 to optimize memory stores when generating plasma. */
|
||||
#define OPTIMIZE_WRITES 1
|
||||
|
||||
/* Return current time in milliseconds */
|
||||
static double now_ms(void) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_sec * 1000. + tv.tv_usec / 1000.;
|
||||
}
|
||||
|
||||
/* We're going to perform computations for every pixel of the target
|
||||
* bitmap. floating-point operations are very slow on ARMv5, and not
|
||||
* too bad on ARMv7 with the exception of trigonometric functions.
|
||||
*
|
||||
* For better performance on all platforms, we're going to use fixed-point
|
||||
* arithmetic and all kinds of tricks
|
||||
*/
|
||||
|
||||
typedef int32_t Fixed;
|
||||
|
||||
#define FIXED_BITS 16
|
||||
#define FIXED_ONE (1 << FIXED_BITS)
|
||||
#define FIXED_AVERAGE(x, y) (((x) + (y)) >> 1)
|
||||
|
||||
#define FIXED_FROM_INT(x) ((x) << FIXED_BITS)
|
||||
#define FIXED_TO_INT(x) ((x) >> FIXED_BITS)
|
||||
|
||||
#define FIXED_FROM_FLOAT(x) ((Fixed)((x) * FIXED_ONE))
|
||||
#define FIXED_TO_FLOAT(x) ((x) / (1. * FIXED_ONE))
|
||||
|
||||
#define FIXED_MUL(x, y) (((int64_t)(x) * (y)) >> FIXED_BITS)
|
||||
#define FIXED_DIV(x, y) (((int64_t)(x) * FIXED_ONE) / (y))
|
||||
|
||||
#define FIXED_DIV2(x) ((x) >> 1)
|
||||
#define FIXED_AVERAGE(x, y) (((x) + (y)) >> 1)
|
||||
|
||||
#define FIXED_FRAC(x) ((x) & ((1 << FIXED_BITS) - 1))
|
||||
#define FIXED_TRUNC(x) ((x) & ~((1 << FIXED_BITS) - 1))
|
||||
|
||||
#define FIXED_FROM_INT_FLOAT(x, f) (Fixed)((x) * (FIXED_ONE * (f)))
|
||||
|
||||
typedef int32_t Angle;
|
||||
|
||||
#define ANGLE_BITS 9
|
||||
|
||||
#if ANGLE_BITS < 8
|
||||
#error ANGLE_BITS must be at least 8
|
||||
#endif
|
||||
|
||||
#define ANGLE_2PI (1 << ANGLE_BITS)
|
||||
#define ANGLE_PI (1 << (ANGLE_BITS - 1))
|
||||
#define ANGLE_PI2 (1 << (ANGLE_BITS - 2))
|
||||
#define ANGLE_PI4 (1 << (ANGLE_BITS - 3))
|
||||
|
||||
#define ANGLE_FROM_FLOAT(x) (Angle)((x) * ANGLE_PI / M_PI)
|
||||
#define ANGLE_TO_FLOAT(x) ((x) * M_PI / ANGLE_PI)
|
||||
|
||||
#if ANGLE_BITS <= FIXED_BITS
|
||||
#define ANGLE_FROM_FIXED(x) (Angle)((x) >> (FIXED_BITS - ANGLE_BITS))
|
||||
#define ANGLE_TO_FIXED(x) (Fixed)((x) << (FIXED_BITS - ANGLE_BITS))
|
||||
#else
|
||||
#define ANGLE_FROM_FIXED(x) (Angle)((x) << (ANGLE_BITS - FIXED_BITS))
|
||||
#define ANGLE_TO_FIXED(x) (Fixed)((x) >> (ANGLE_BITS - FIXED_BITS))
|
||||
#endif
|
||||
|
||||
static Fixed angle_sin_tab[ANGLE_2PI + 1];
|
||||
|
||||
static void init_angles(void) {
|
||||
int nn;
|
||||
for (nn = 0; nn < ANGLE_2PI + 1; nn++) {
|
||||
double radians = nn * M_PI / ANGLE_PI;
|
||||
angle_sin_tab[nn] = FIXED_FROM_FLOAT(sin(radians));
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ Fixed angle_sin(Angle a) {
|
||||
return angle_sin_tab[(uint32_t)a & (ANGLE_2PI - 1)];
|
||||
}
|
||||
|
||||
static __inline__ Fixed fixed_sin(Fixed f) {
|
||||
return angle_sin(ANGLE_FROM_FIXED(f));
|
||||
}
|
||||
|
||||
/* Color palette used for rendering the plasma */
|
||||
#define PALETTE_BITS 8
|
||||
#define PALETTE_SIZE (1 << PALETTE_BITS)
|
||||
|
||||
#if PALETTE_BITS > FIXED_BITS
|
||||
#error PALETTE_BITS must be smaller than FIXED_BITS
|
||||
#endif
|
||||
|
||||
static uint16_t palette[PALETTE_SIZE];
|
||||
|
||||
static uint16_t make565(int red, int green, int blue) {
|
||||
return (uint16_t)(((red << 8) & 0xf800) | ((green << 3) & 0x07e0) |
|
||||
((blue >> 3) & 0x001f));
|
||||
}
|
||||
|
||||
static void init_palette(void) {
|
||||
int nn, mm = 0;
|
||||
/* fun with colors */
|
||||
for (nn = 0; nn < PALETTE_SIZE / 4; nn++) {
|
||||
int jj = (nn - mm) * 4 * 255 / PALETTE_SIZE;
|
||||
palette[nn] = make565(255, jj, 255 - jj);
|
||||
}
|
||||
|
||||
for (mm = nn; nn < PALETTE_SIZE / 2; nn++) {
|
||||
int jj = (nn - mm) * 4 * 255 / PALETTE_SIZE;
|
||||
palette[nn] = make565(255 - jj, 255, jj);
|
||||
}
|
||||
|
||||
for (mm = nn; nn < PALETTE_SIZE * 3 / 4; nn++) {
|
||||
int jj = (nn - mm) * 4 * 255 / PALETTE_SIZE;
|
||||
palette[nn] = make565(0, 255 - jj, 255);
|
||||
}
|
||||
|
||||
for (mm = nn; nn < PALETTE_SIZE; nn++) {
|
||||
int jj = (nn - mm) * 4 * 255 / PALETTE_SIZE;
|
||||
palette[nn] = make565(jj, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ uint16_t palette_from_fixed(Fixed x) {
|
||||
if (x < 0) x = -x;
|
||||
if (x >= FIXED_ONE) x = FIXED_ONE - 1;
|
||||
int idx = FIXED_FRAC(x) >> (FIXED_BITS - PALETTE_BITS);
|
||||
return palette[idx & (PALETTE_SIZE - 1)];
|
||||
}
|
||||
|
||||
/* Angles expressed as fixed point radians */
|
||||
|
||||
static void init_tables(void) {
|
||||
init_palette();
|
||||
init_angles();
|
||||
}
|
||||
|
||||
static void fill_plasma(AndroidBitmapInfo* info, void* pixels, double t) {
|
||||
Fixed yt1 = FIXED_FROM_FLOAT(t / 1230.);
|
||||
Fixed yt2 = yt1;
|
||||
Fixed xt10 = FIXED_FROM_FLOAT(t / 3000.);
|
||||
Fixed xt20 = xt10;
|
||||
|
||||
#define YT1_INCR FIXED_FROM_FLOAT(1 / 100.)
|
||||
#define YT2_INCR FIXED_FROM_FLOAT(1 / 163.)
|
||||
|
||||
for (uint32_t yy = 0; yy < info->height; yy++) {
|
||||
uint16_t* line = (uint16_t*)pixels;
|
||||
Fixed base = fixed_sin(yt1) + fixed_sin(yt2);
|
||||
Fixed xt1 = xt10;
|
||||
Fixed xt2 = xt20;
|
||||
|
||||
yt1 += YT1_INCR;
|
||||
yt2 += YT2_INCR;
|
||||
|
||||
#define XT1_INCR FIXED_FROM_FLOAT(1 / 173.)
|
||||
#define XT2_INCR FIXED_FROM_FLOAT(1 / 242.)
|
||||
|
||||
#if OPTIMIZE_WRITES
|
||||
/* optimize memory writes by generating one aligned 32-bit store
|
||||
* for every pair of pixels.
|
||||
*/
|
||||
uint16_t* line_end = line + info->width;
|
||||
|
||||
if (line < line_end) {
|
||||
if (((uint32_t)(uintptr_t)line & 3) != 0) {
|
||||
Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
|
||||
xt1 += XT1_INCR;
|
||||
xt2 += XT2_INCR;
|
||||
|
||||
line[0] = palette_from_fixed(ii >> 2);
|
||||
line++;
|
||||
}
|
||||
|
||||
while (line + 2 <= line_end) {
|
||||
Fixed i1 = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
xt1 += XT1_INCR;
|
||||
xt2 += XT2_INCR;
|
||||
|
||||
Fixed i2 = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
xt1 += XT1_INCR;
|
||||
xt2 += XT2_INCR;
|
||||
|
||||
uint32_t pixel = ((uint32_t)palette_from_fixed(i1 >> 2) << 16) |
|
||||
(uint32_t)palette_from_fixed(i2 >> 2);
|
||||
|
||||
((uint32_t*)line)[0] = pixel;
|
||||
line += 2;
|
||||
}
|
||||
|
||||
if (line < line_end) {
|
||||
Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
line[0] = palette_from_fixed(ii >> 2);
|
||||
line++;
|
||||
}
|
||||
}
|
||||
#else /* !OPTIMIZE_WRITES */
|
||||
int xx;
|
||||
for (xx = 0; xx < info->width; xx++) {
|
||||
Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
|
||||
xt1 += XT1_INCR;
|
||||
xt2 += XT2_INCR;
|
||||
|
||||
line[xx] = palette_from_fixed(ii / 4);
|
||||
}
|
||||
#endif /* !OPTIMIZE_WRITES */
|
||||
|
||||
// go to next line
|
||||
pixels = (char*)pixels + info->stride;
|
||||
}
|
||||
}
|
||||
|
||||
/* simple stats management */
|
||||
typedef struct {
|
||||
double renderTime;
|
||||
double frameTime;
|
||||
} FrameStats;
|
||||
|
||||
#define MAX_FRAME_STATS 200
|
||||
#define MAX_PERIOD_MS 1500
|
||||
|
||||
typedef struct {
|
||||
double firstTime;
|
||||
double lastTime;
|
||||
double frameTime;
|
||||
|
||||
int firstFrame;
|
||||
int numFrames;
|
||||
FrameStats frames[MAX_FRAME_STATS];
|
||||
} Stats;
|
||||
|
||||
static void stats_init(Stats* s) {
|
||||
s->lastTime = now_ms();
|
||||
s->firstTime = 0.;
|
||||
s->firstFrame = 0;
|
||||
s->numFrames = 0;
|
||||
}
|
||||
|
||||
static void stats_startFrame(Stats* s) { s->frameTime = now_ms(); }
|
||||
|
||||
static void stats_endFrame(Stats* s) {
|
||||
double now = now_ms();
|
||||
double renderTime = now - s->frameTime;
|
||||
double frameTime = now - s->lastTime;
|
||||
int nn;
|
||||
|
||||
if (now - s->firstTime >= MAX_PERIOD_MS) {
|
||||
if (s->numFrames > 0) {
|
||||
double minRender, maxRender, avgRender;
|
||||
double minFrame, maxFrame, avgFrame;
|
||||
int count;
|
||||
|
||||
nn = s->firstFrame;
|
||||
minRender = maxRender = avgRender = s->frames[nn].renderTime;
|
||||
minFrame = maxFrame = avgFrame = s->frames[nn].frameTime;
|
||||
for (count = s->numFrames; count > 0; count--) {
|
||||
nn += 1;
|
||||
if (nn >= MAX_FRAME_STATS) nn -= MAX_FRAME_STATS;
|
||||
double render = s->frames[nn].renderTime;
|
||||
if (render < minRender) minRender = render;
|
||||
if (render > maxRender) maxRender = render;
|
||||
double frame = s->frames[nn].frameTime;
|
||||
if (frame < minFrame) minFrame = frame;
|
||||
if (frame > maxFrame) maxFrame = frame;
|
||||
avgRender += render;
|
||||
avgFrame += frame;
|
||||
}
|
||||
avgRender /= s->numFrames;
|
||||
avgFrame /= s->numFrames;
|
||||
|
||||
LOGI(
|
||||
"frame/s (avg,min,max) = (%.1f,%.1f,%.1f) "
|
||||
"render time ms (avg,min,max) = (%.1f,%.1f,%.1f)\n",
|
||||
1000. / avgFrame, 1000. / maxFrame, 1000. / minFrame, avgRender,
|
||||
minRender, maxRender);
|
||||
}
|
||||
s->numFrames = 0;
|
||||
s->firstFrame = 0;
|
||||
s->firstTime = now;
|
||||
}
|
||||
|
||||
nn = s->firstFrame + s->numFrames;
|
||||
if (nn >= MAX_FRAME_STATS) nn -= MAX_FRAME_STATS;
|
||||
|
||||
s->frames[nn].renderTime = renderTime;
|
||||
s->frames[nn].frameTime = frameTime;
|
||||
|
||||
if (s->numFrames < MAX_FRAME_STATS) {
|
||||
s->numFrames += 1;
|
||||
} else {
|
||||
s->firstFrame += 1;
|
||||
if (s->firstFrame >= MAX_FRAME_STATS) s->firstFrame -= MAX_FRAME_STATS;
|
||||
}
|
||||
|
||||
s->lastTime = now;
|
||||
}
|
||||
|
||||
void RenderPlasma(JNIEnv* env, jclass, jobject bitmap, jlong time_ms) {
|
||||
AndroidBitmapInfo info;
|
||||
void* pixels;
|
||||
int ret;
|
||||
static Stats stats;
|
||||
static int init;
|
||||
|
||||
if (!init) {
|
||||
init_tables();
|
||||
stats_init(&stats);
|
||||
init = 1;
|
||||
}
|
||||
|
||||
if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
|
||||
LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.format != ANDROID_BITMAP_FORMAT_RGB_565) {
|
||||
LOGE("Bitmap format is not RGB_565 !");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
|
||||
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
|
||||
}
|
||||
|
||||
stats_startFrame(&stats);
|
||||
|
||||
/* Now fill the values with a nice little plasma */
|
||||
fill_plasma(&info, pixels, time_ms);
|
||||
|
||||
AndroidBitmap_unlockPixels(env, bitmap);
|
||||
|
||||
stats_endFrame(&stats);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright (C) 2025 The Android Open Source Project
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
void RenderPlasma(JNIEnv* env, jclass, jobject bitmap, jlong time_ms);
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
11
bitmap-plasma/default.properties
Normal file
@@ -0,0 +1,11 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system use,
|
||||
# "build.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
|
||||
# Project target.
|
||||
target=android-8
|
||||
9
bitmap-plasma/jni/Android.mk
Normal file
@@ -0,0 +1,9 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := plasma
|
||||
LOCAL_SRC_FILES := plasma.c
|
||||
LOCAL_LDLIBS := -lm -llog -ljnigraphics
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
2
bitmap-plasma/jni/Application.mk
Normal file
@@ -0,0 +1,2 @@
|
||||
APP_ABI := all
|
||||
APP_PLATFORM := android-8
|
||||
399
bitmap-plasma/jni/plasma.c
Normal file
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include <time.h>
|
||||
#include <android/log.h>
|
||||
#include <android/bitmap.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#define LOG_TAG "libplasma"
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
|
||||
|
||||
/* Set to 1 to enable debug log traces. */
|
||||
#define DEBUG 0
|
||||
|
||||
/* Set to 1 to optimize memory stores when generating plasma. */
|
||||
#define OPTIMIZE_WRITES 1
|
||||
|
||||
/* Return current time in milliseconds */
|
||||
static double now_ms(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_sec*1000. + tv.tv_usec/1000.;
|
||||
}
|
||||
|
||||
/* We're going to perform computations for every pixel of the target
|
||||
* bitmap. floating-point operations are very slow on ARMv5, and not
|
||||
* too bad on ARMv7 with the exception of trigonometric functions.
|
||||
*
|
||||
* For better performance on all platforms, we're going to use fixed-point
|
||||
* arithmetic and all kinds of tricks
|
||||
*/
|
||||
|
||||
typedef int32_t Fixed;
|
||||
|
||||
#define FIXED_BITS 16
|
||||
#define FIXED_ONE (1 << FIXED_BITS)
|
||||
#define FIXED_AVERAGE(x,y) (((x) + (y)) >> 1)
|
||||
|
||||
#define FIXED_FROM_INT(x) ((x) << FIXED_BITS)
|
||||
#define FIXED_TO_INT(x) ((x) >> FIXED_BITS)
|
||||
|
||||
#define FIXED_FROM_FLOAT(x) ((Fixed)((x)*FIXED_ONE))
|
||||
#define FIXED_TO_FLOAT(x) ((x)/(1.*FIXED_ONE))
|
||||
|
||||
#define FIXED_MUL(x,y) (((int64_t)(x) * (y)) >> FIXED_BITS)
|
||||
#define FIXED_DIV(x,y) (((int64_t)(x) * FIXED_ONE) / (y))
|
||||
|
||||
#define FIXED_DIV2(x) ((x) >> 1)
|
||||
#define FIXED_AVERAGE(x,y) (((x) + (y)) >> 1)
|
||||
|
||||
#define FIXED_FRAC(x) ((x) & ((1 << FIXED_BITS)-1))
|
||||
#define FIXED_TRUNC(x) ((x) & ~((1 << FIXED_BITS)-1))
|
||||
|
||||
#define FIXED_FROM_INT_FLOAT(x,f) (Fixed)((x)*(FIXED_ONE*(f)))
|
||||
|
||||
typedef int32_t Angle;
|
||||
|
||||
#define ANGLE_BITS 9
|
||||
|
||||
#if ANGLE_BITS < 8
|
||||
# error ANGLE_BITS must be at least 8
|
||||
#endif
|
||||
|
||||
#define ANGLE_2PI (1 << ANGLE_BITS)
|
||||
#define ANGLE_PI (1 << (ANGLE_BITS-1))
|
||||
#define ANGLE_PI2 (1 << (ANGLE_BITS-2))
|
||||
#define ANGLE_PI4 (1 << (ANGLE_BITS-3))
|
||||
|
||||
#define ANGLE_FROM_FLOAT(x) (Angle)((x)*ANGLE_PI/M_PI)
|
||||
#define ANGLE_TO_FLOAT(x) ((x)*M_PI/ANGLE_PI)
|
||||
|
||||
#if ANGLE_BITS <= FIXED_BITS
|
||||
# define ANGLE_FROM_FIXED(x) (Angle)((x) >> (FIXED_BITS - ANGLE_BITS))
|
||||
# define ANGLE_TO_FIXED(x) (Fixed)((x) << (FIXED_BITS - ANGLE_BITS))
|
||||
#else
|
||||
# define ANGLE_FROM_FIXED(x) (Angle)((x) << (ANGLE_BITS - FIXED_BITS))
|
||||
# define ANGLE_TO_FIXED(x) (Fixed)((x) >> (ANGLE_BITS - FIXED_BITS))
|
||||
#endif
|
||||
|
||||
static Fixed angle_sin_tab[ANGLE_2PI+1];
|
||||
|
||||
static void init_angles(void)
|
||||
{
|
||||
int nn;
|
||||
for (nn = 0; nn < ANGLE_2PI+1; nn++) {
|
||||
double radians = nn*M_PI/ANGLE_PI;
|
||||
angle_sin_tab[nn] = FIXED_FROM_FLOAT(sin(radians));
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ Fixed angle_sin( Angle a )
|
||||
{
|
||||
return angle_sin_tab[(uint32_t)a & (ANGLE_2PI-1)];
|
||||
}
|
||||
|
||||
static __inline__ Fixed angle_cos( Angle a )
|
||||
{
|
||||
return angle_sin(a + ANGLE_PI2);
|
||||
}
|
||||
|
||||
static __inline__ Fixed fixed_sin( Fixed f )
|
||||
{
|
||||
return angle_sin(ANGLE_FROM_FIXED(f));
|
||||
}
|
||||
|
||||
static __inline__ Fixed fixed_cos( Fixed f )
|
||||
{
|
||||
return angle_cos(ANGLE_FROM_FIXED(f));
|
||||
}
|
||||
|
||||
/* Color palette used for rendering the plasma */
|
||||
#define PALETTE_BITS 8
|
||||
#define PALETTE_SIZE (1 << PALETTE_BITS)
|
||||
|
||||
#if PALETTE_BITS > FIXED_BITS
|
||||
# error PALETTE_BITS must be smaller than FIXED_BITS
|
||||
#endif
|
||||
|
||||
static uint16_t palette[PALETTE_SIZE];
|
||||
|
||||
static uint16_t make565(int red, int green, int blue)
|
||||
{
|
||||
return (uint16_t)( ((red << 8) & 0xf800) |
|
||||
((green << 2) & 0x03e0) |
|
||||
((blue >> 3) & 0x001f) );
|
||||
}
|
||||
|
||||
static void init_palette(void)
|
||||
{
|
||||
int nn, mm = 0;
|
||||
/* fun with colors */
|
||||
for (nn = 0; nn < PALETTE_SIZE/4; nn++) {
|
||||
int jj = (nn-mm)*4*255/PALETTE_SIZE;
|
||||
palette[nn] = make565(255, jj, 255-jj);
|
||||
}
|
||||
|
||||
for ( mm = nn; nn < PALETTE_SIZE/2; nn++ ) {
|
||||
int jj = (nn-mm)*4*255/PALETTE_SIZE;
|
||||
palette[nn] = make565(255-jj, 255, jj);
|
||||
}
|
||||
|
||||
for ( mm = nn; nn < PALETTE_SIZE*3/4; nn++ ) {
|
||||
int jj = (nn-mm)*4*255/PALETTE_SIZE;
|
||||
palette[nn] = make565(0, 255-jj, 255);
|
||||
}
|
||||
|
||||
for ( mm = nn; nn < PALETTE_SIZE; nn++ ) {
|
||||
int jj = (nn-mm)*4*255/PALETTE_SIZE;
|
||||
palette[nn] = make565(jj, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ uint16_t palette_from_fixed( Fixed x )
|
||||
{
|
||||
if (x < 0) x = -x;
|
||||
if (x >= FIXED_ONE) x = FIXED_ONE-1;
|
||||
int idx = FIXED_FRAC(x) >> (FIXED_BITS - PALETTE_BITS);
|
||||
return palette[idx & (PALETTE_SIZE-1)];
|
||||
}
|
||||
|
||||
/* Angles expressed as fixed point radians */
|
||||
|
||||
static void init_tables(void)
|
||||
{
|
||||
init_palette();
|
||||
init_angles();
|
||||
}
|
||||
|
||||
static void fill_plasma( AndroidBitmapInfo* info, void* pixels, double t )
|
||||
{
|
||||
Fixed yt1 = FIXED_FROM_FLOAT(t/1230.);
|
||||
Fixed yt2 = yt1;
|
||||
Fixed xt10 = FIXED_FROM_FLOAT(t/3000.);
|
||||
Fixed xt20 = xt10;
|
||||
|
||||
#define YT1_INCR FIXED_FROM_FLOAT(1/100.)
|
||||
#define YT2_INCR FIXED_FROM_FLOAT(1/163.)
|
||||
|
||||
int yy;
|
||||
for (yy = 0; yy < info->height; yy++) {
|
||||
uint16_t* line = (uint16_t*)pixels;
|
||||
Fixed base = fixed_sin(yt1) + fixed_sin(yt2);
|
||||
Fixed xt1 = xt10;
|
||||
Fixed xt2 = xt20;
|
||||
|
||||
yt1 += YT1_INCR;
|
||||
yt2 += YT2_INCR;
|
||||
|
||||
#define XT1_INCR FIXED_FROM_FLOAT(1/173.)
|
||||
#define XT2_INCR FIXED_FROM_FLOAT(1/242.)
|
||||
|
||||
#if OPTIMIZE_WRITES
|
||||
/* optimize memory writes by generating one aligned 32-bit store
|
||||
* for every pair of pixels.
|
||||
*/
|
||||
uint16_t* line_end = line + info->width;
|
||||
|
||||
if (line < line_end) {
|
||||
if (((uint32_t)(uintptr_t)line & 3) != 0) {
|
||||
Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
|
||||
xt1 += XT1_INCR;
|
||||
xt2 += XT2_INCR;
|
||||
|
||||
line[0] = palette_from_fixed(ii >> 2);
|
||||
line++;
|
||||
}
|
||||
|
||||
while (line + 2 <= line_end) {
|
||||
Fixed i1 = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
xt1 += XT1_INCR;
|
||||
xt2 += XT2_INCR;
|
||||
|
||||
Fixed i2 = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
xt1 += XT1_INCR;
|
||||
xt2 += XT2_INCR;
|
||||
|
||||
uint32_t pixel = ((uint32_t)palette_from_fixed(i1 >> 2) << 16) |
|
||||
(uint32_t)palette_from_fixed(i2 >> 2);
|
||||
|
||||
((uint32_t*)line)[0] = pixel;
|
||||
line += 2;
|
||||
}
|
||||
|
||||
if (line < line_end) {
|
||||
Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
line[0] = palette_from_fixed(ii >> 2);
|
||||
line++;
|
||||
}
|
||||
}
|
||||
#else /* !OPTIMIZE_WRITES */
|
||||
int xx;
|
||||
for (xx = 0; xx < info->width; xx++) {
|
||||
|
||||
Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
|
||||
xt1 += XT1_INCR;
|
||||
xt2 += XT2_INCR;
|
||||
|
||||
line[xx] = palette_from_fixed(ii / 4);
|
||||
}
|
||||
#endif /* !OPTIMIZE_WRITES */
|
||||
|
||||
// go to next line
|
||||
pixels = (char*)pixels + info->stride;
|
||||
}
|
||||
}
|
||||
|
||||
/* simple stats management */
|
||||
typedef struct {
|
||||
double renderTime;
|
||||
double frameTime;
|
||||
} FrameStats;
|
||||
|
||||
#define MAX_FRAME_STATS 200
|
||||
#define MAX_PERIOD_MS 1500
|
||||
|
||||
typedef struct {
|
||||
double firstTime;
|
||||
double lastTime;
|
||||
double frameTime;
|
||||
|
||||
int firstFrame;
|
||||
int numFrames;
|
||||
FrameStats frames[ MAX_FRAME_STATS ];
|
||||
} Stats;
|
||||
|
||||
static void
|
||||
stats_init( Stats* s )
|
||||
{
|
||||
s->lastTime = now_ms();
|
||||
s->firstTime = 0.;
|
||||
s->firstFrame = 0;
|
||||
s->numFrames = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
stats_startFrame( Stats* s )
|
||||
{
|
||||
s->frameTime = now_ms();
|
||||
}
|
||||
|
||||
static void
|
||||
stats_endFrame( Stats* s )
|
||||
{
|
||||
double now = now_ms();
|
||||
double renderTime = now - s->frameTime;
|
||||
double frameTime = now - s->lastTime;
|
||||
int nn;
|
||||
|
||||
if (now - s->firstTime >= MAX_PERIOD_MS) {
|
||||
if (s->numFrames > 0) {
|
||||
double minRender, maxRender, avgRender;
|
||||
double minFrame, maxFrame, avgFrame;
|
||||
int count;
|
||||
|
||||
nn = s->firstFrame;
|
||||
minRender = maxRender = avgRender = s->frames[nn].renderTime;
|
||||
minFrame = maxFrame = avgFrame = s->frames[nn].frameTime;
|
||||
for (count = s->numFrames; count > 0; count-- ) {
|
||||
nn += 1;
|
||||
if (nn >= MAX_FRAME_STATS)
|
||||
nn -= MAX_FRAME_STATS;
|
||||
double render = s->frames[nn].renderTime;
|
||||
if (render < minRender) minRender = render;
|
||||
if (render > maxRender) maxRender = render;
|
||||
double frame = s->frames[nn].frameTime;
|
||||
if (frame < minFrame) minFrame = frame;
|
||||
if (frame > maxFrame) maxFrame = frame;
|
||||
avgRender += render;
|
||||
avgFrame += frame;
|
||||
}
|
||||
avgRender /= s->numFrames;
|
||||
avgFrame /= s->numFrames;
|
||||
|
||||
LOGI("frame/s (avg,min,max) = (%.1f,%.1f,%.1f) "
|
||||
"render time ms (avg,min,max) = (%.1f,%.1f,%.1f)\n",
|
||||
1000./avgFrame, 1000./maxFrame, 1000./minFrame,
|
||||
avgRender, minRender, maxRender);
|
||||
}
|
||||
s->numFrames = 0;
|
||||
s->firstFrame = 0;
|
||||
s->firstTime = now;
|
||||
}
|
||||
|
||||
nn = s->firstFrame + s->numFrames;
|
||||
if (nn >= MAX_FRAME_STATS)
|
||||
nn -= MAX_FRAME_STATS;
|
||||
|
||||
s->frames[nn].renderTime = renderTime;
|
||||
s->frames[nn].frameTime = frameTime;
|
||||
|
||||
if (s->numFrames < MAX_FRAME_STATS) {
|
||||
s->numFrames += 1;
|
||||
} else {
|
||||
s->firstFrame += 1;
|
||||
if (s->firstFrame >= MAX_FRAME_STATS)
|
||||
s->firstFrame -= MAX_FRAME_STATS;
|
||||
}
|
||||
|
||||
s->lastTime = now;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_example_plasma_PlasmaView_renderPlasma(JNIEnv * env, jobject obj, jobject bitmap, jlong time_ms)
|
||||
{
|
||||
AndroidBitmapInfo info;
|
||||
void* pixels;
|
||||
int ret;
|
||||
static Stats stats;
|
||||
static int init;
|
||||
|
||||
if (!init) {
|
||||
init_tables();
|
||||
stats_init(&stats);
|
||||
init = 1;
|
||||
}
|
||||
|
||||
if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
|
||||
LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.format != ANDROID_BITMAP_FORMAT_RGB_565) {
|
||||
LOGE("Bitmap format is not RGB_565 !");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
|
||||
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
|
||||
}
|
||||
|
||||
stats_startFrame(&stats);
|
||||
|
||||
/* Now fill the values with a nice little plasma */
|
||||
fill_plasma(&info, pixels, time_ms );
|
||||
|
||||
AndroidBitmap_unlockPixels(env, bitmap);
|
||||
|
||||
stats_endFrame(&stats);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 690 KiB |
@@ -15,9 +15,7 @@
|
||||
*/
|
||||
package com.example.plasma;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.graphics.Point;
|
||||
import android.os.Bundle;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
@@ -28,33 +26,26 @@ import android.view.WindowManager;
|
||||
|
||||
public class Plasma extends Activity
|
||||
{
|
||||
// Called when the activity is first created.
|
||||
/** Called when the activity is first created. */
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
Point displaySize = new Point();
|
||||
display.getSize(displaySize);
|
||||
setContentView(new PlasmaView(this, displaySize.x, displaySize.y));
|
||||
setContentView(new PlasmaView(this, display.getWidth(), display.getHeight()));
|
||||
}
|
||||
|
||||
// load our native library
|
||||
/* load our native library */
|
||||
static {
|
||||
System.loadLibrary("plasma");
|
||||
}
|
||||
}
|
||||
|
||||
// Custom view for rendering plasma.
|
||||
//
|
||||
// Note: suppressing lint wrarning for ViewConstructor since it is
|
||||
// manually set from the activity and not used in any layout.
|
||||
@SuppressLint("ViewConstructor")
|
||||
class PlasmaView extends View {
|
||||
private Bitmap mBitmap;
|
||||
private long mStartTime;
|
||||
|
||||
// implementend by libplasma.so
|
||||
/* implementend by libplasma.so */
|
||||
private static native void renderPlasma(Bitmap bitmap, long time_ms);
|
||||
|
||||
public PlasmaView(Context context, int width, int height) {
|
||||
@@ -64,14 +55,10 @@ class PlasmaView extends View {
|
||||
}
|
||||
|
||||
@Override protected void onDraw(Canvas canvas) {
|
||||
//canvas.drawColor(0xFFCCCCCC);
|
||||
renderPlasma(mBitmap, System.currentTimeMillis() - mStartTime);
|
||||
canvas.drawBitmap(mBitmap, 0, 0, null);
|
||||
// force a redraw, with a different time-based pattern.
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
|
||||
}
|
||||
|
||||
}
|
||||
1
build-logic/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/bin
|
||||
@@ -1,11 +0,0 @@
|
||||
# Convention plugins
|
||||
|
||||
This directory contains [convention plugins] used by the NDK samples. These are
|
||||
used to remove Gradle boiler plate from individual samples in favor of common
|
||||
configuration here. Using convention plugins for single module projects is
|
||||
overkill, but any non-trivial app will likely need their own eventually. See
|
||||
[Now In Android's build-logic][nia-build-logic] for a more thorough example of
|
||||
building convention plugins for Android projects.
|
||||
|
||||
[convention plugins]: https://docs.gradle.org/current/samples/sample_convention_plugins.html
|
||||
[nia-build-logic]: https://github.com/android/nowinandroid/blob/main/build-logic/README.md
|
||||
@@ -1,40 +0,0 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
id("java-gradle-plugin")
|
||||
`kotlin-dsl`
|
||||
alias(libs.plugins.jetbrains.kotlin.jvm)
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_17.toString()
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.android.gradlePlugin)
|
||||
compileOnly(libs.kotlin.gradlePlugin)
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
register("androidApplication") {
|
||||
id = "ndksamples.android.application"
|
||||
implementationClass = "com.android.ndk.samples.buildlogic.AndroidApplicationConventionPlugin"
|
||||
}
|
||||
register("androidLibrary") {
|
||||
id = "ndksamples.android.library"
|
||||
implementationClass = "com.android.ndk.samples.buildlogic.AndroidLibraryConventionPlugin"
|
||||
}
|
||||
register("kotlinAndroid") {
|
||||
id = "ndksamples.android.kotlin"
|
||||
implementationClass = "com.android.ndk.samples.buildlogic.KotlinConventionPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "build-logic"
|
||||
@@ -1,72 +0,0 @@
|
||||
package com.android.ndk.samples.buildlogic
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
class AndroidApplicationConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
with(pluginManager) {
|
||||
apply("com.android.application")
|
||||
}
|
||||
|
||||
extensions.configure<ApplicationExtension> {
|
||||
compileSdk = Versions.COMPILE_SDK
|
||||
ndkVersion = Versions.NDK
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version = Versions.CMAKE
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.MIN_SDK
|
||||
targetSdk = Versions.TARGET_SDK
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments.add("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
|
||||
arguments.add("-DCMAKE_MODULE_PATH=${rootDir.resolve("cmake")}")
|
||||
}
|
||||
}
|
||||
|
||||
ndk {
|
||||
// riscv64 isn't a supported Android ABI yet (August 2025), but we're
|
||||
// enabling it here as part of that experiment. Until it's a supported ABI,
|
||||
// don't include this in your app, as Play will block uploads of APKs which
|
||||
// contain riscv64 libraries.
|
||||
abiFilters.addAll(
|
||||
listOf(
|
||||
"arm64-v8a",
|
||||
"armeabi-v7a",
|
||||
"riscv64",
|
||||
"x86",
|
||||
"x86_64",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = Versions.JAVA
|
||||
targetCompatibility = Versions.JAVA
|
||||
}
|
||||
|
||||
// Studio will not automatically pass logcat through ndk-stack, so we need to avoid
|
||||
// stripping debug binaries if we want the crash trace to be readable.
|
||||
buildTypes {
|
||||
debug {
|
||||
packaging {
|
||||
jniLibs {
|
||||
keepDebugSymbols += "**/*.so"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.android.ndk.samples.buildlogic
|
||||
|
||||
import com.android.build.api.dsl.LibraryExtension
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
class AndroidLibraryConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
with(pluginManager) {
|
||||
apply("com.android.library")
|
||||
}
|
||||
|
||||
extensions.configure<LibraryExtension> {
|
||||
compileSdk = Versions.COMPILE_SDK
|
||||
ndkVersion = Versions.NDK
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version = Versions.CMAKE
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.MIN_SDK
|
||||
lint {
|
||||
targetSdk = Versions.TARGET_SDK
|
||||
}
|
||||
testOptions {
|
||||
targetSdk = Versions.TARGET_SDK
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments.add("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
|
||||
arguments.add("-DCMAKE_MODULE_PATH=${rootDir.resolve("cmake")}")
|
||||
}
|
||||
}
|
||||
ndk {
|
||||
// riscv64 isn't a supported Android ABI yet (August 2025), but we're
|
||||
// enabling it here as part of that experiment. Until it's a supported ABI,
|
||||
// don't include this in your app, as Play will block uploads of APKs which
|
||||
// contain riscv64 libraries.
|
||||
abiFilters.addAll(
|
||||
listOf(
|
||||
"arm64-v8a",
|
||||
"armeabi-v7a",
|
||||
"riscv64",
|
||||
"x86",
|
||||
"x86_64",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = Versions.JAVA
|
||||
targetCompatibility = Versions.JAVA
|
||||
}
|
||||
|
||||
// Studio will not automatically pass logcat through ndk-stack, so we need to avoid
|
||||
// stripping debug binaries if we want the crash trace to be readable.
|
||||
buildTypes {
|
||||
debug {
|
||||
packaging {
|
||||
jniLibs {
|
||||
keepDebugSymbols += "**/*.so"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.android.ndk.samples.buildlogic
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
class KotlinConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
with(pluginManager) {
|
||||
apply("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
extensions.configure<ApplicationExtension> {
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = Versions.JAVA.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.android.ndk.samples.buildlogic
|
||||
|
||||
import org.gradle.api.JavaVersion
|
||||
|
||||
object Versions {
|
||||
const val COMPILE_SDK = 35
|
||||
const val TARGET_SDK = 35
|
||||
const val MIN_SDK = 21
|
||||
const val NDK = "28.2.13676358" // r28c
|
||||
const val CMAKE = "4.1.0"
|
||||
val JAVA = JavaVersion.VERSION_1_8
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.android.library) apply false
|
||||
alias(libs.plugins.jetbrainsKotlinAndroid) apply false
|
||||
}
|
||||
|
Before Width: | Height: | Size: 32 KiB |
@@ -1,23 +0,0 @@
|
||||
# NdkCamera Sample
|
||||
|
||||
Two API samples:
|
||||
|
||||
- texture-view: Preview NDK camera image with
|
||||
[Android TextureView](https://developer.android.com/reference/android/view/TextureView.html)
|
||||
- basic: A basic NdkCamera sample to preview camera images with AReadImage and
|
||||
take jpeg photos. Exposure and sensitivity are adjustable for preview, however
|
||||
capturing photos is in auto mode (it could be adjustable with similar method
|
||||
as used for preview).
|
||||
|
||||
## Other Resources
|
||||
|
||||
- Getting familiar with the 5 Camera2 objects
|
||||

|
||||
|
||||
- [Camera2 blogs](https://medium.com/androiddevelopers/camera-enumeration-on-android-9a053b910cb5)
|
||||
|
||||
- [Camera2 Java documentation](https://developer.android.com/reference/android/hardware/camera2/package-summary)
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
@@ -1,31 +0,0 @@
|
||||
plugins {
|
||||
id "ndksamples.android.application"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.sample.camera.basic'
|
||||
defaultConfig {
|
||||
applicationId 'com.sample.camera.basic'
|
||||
minSdkVersion 24
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments '-DANDROID_STL=c++_static'
|
||||
}
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path 'src/main/cpp/CMakeLists.txt'
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
prefab true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(":base")
|
||||
implementation libs.appcompat
|
||||
implementation project(":camera:camera-utils")
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:hasCode="true">
|
||||
<activity android:name="com.sample.camera.basic.CameraActivity"
|
||||
android:label="@string/app_name"
|
||||
android:exported="true">
|
||||
<meta-data android:name="android.app.lib_name"
|
||||
android:value="ndk_camera" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -1,46 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2017 The Android Open Source Project
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
project(CameraBasic LANGUAGES C CXX)
|
||||
|
||||
include(AppLibrary)
|
||||
include(AndroidNdkModules)
|
||||
find_package(base REQUIRED CONFIG)
|
||||
find_package(camera-utils REQUIRED CONFIG)
|
||||
|
||||
android_ndk_import_module_native_app_glue()
|
||||
|
||||
add_app_library(ndk_camera SHARED
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/android_main.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/camera_engine.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/camera_manager.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/camera_listeners.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/image_reader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/camera_ui.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(ndk_camera
|
||||
PRIVATE
|
||||
base::base
|
||||
camera-utils::camera-utils
|
||||
android
|
||||
log
|
||||
m
|
||||
$<LINK_LIBRARY:WHOLE_ARCHIVE,native_app_glue>
|
||||
camera2ndk
|
||||
mediandk
|
||||
)
|
||||