Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ build/
*.swp
nobuild
nob
*.old
*.old
.idea
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
.idea
.idea/
.DS_Store
/build
/app/release
/captures
.externalNativeBuild
.cxx
local.properties
.kotlin
test.jks
33 changes: 33 additions & 0 deletions android/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Olive.c – Android Example

<p align="center">
<img src="../assets/olivec-200.png" width="160">
</p>

Minimal Android demo using **Olive.c** as a CPU renderer.

## Idea

Olive.c renders directly into the memory of an Android `Bitmap`.

* lock bitmap → get pixels pointer
* use pointer as `Olivec_Canvas`
* draw
* unlock → display

No copies. No return values. The bitmap memory is the render target.

## Loop

* refresh every ~10 ms
* render frame
* apply rotation

## Native

```
void *bitmapPixels;
AndroidBitmap_lockPixels(env, bmp, &bitmapPixels);
memcpy(bitmapPixels, pixels, sizeof(pixels));
AndroidBitmap_unlockPixels(env, bmp);
```
54 changes: 54 additions & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
@file:Suppress("UnstableApiUsage","NewerVersionAvailable","UseTomlInstead","GradleDependency")
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}

android {
namespace = "com.me.app"
compileSdk = 36

ndkVersion = "29.0.13846066"
defaultConfig {
applicationId = "com.me.app"
minSdk = 29
targetSdk = 36
versionCode = 1
versionName = "1.0"
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

kotlinOptions {
jvmTarget = "17"
}

buildTypes{
release {
isMinifyEnabled = true
isShrinkResources = true
}
}

buildFeatures {
viewBinding = true
}

externalNativeBuild {
cmake {
path = file("src/main/jni/CMakeLists.txt")
}
}
}

dependencies {
implementation("androidx.core:core-ktx:1.17.0")
implementation("androidx.appcompat:appcompat:1.7.1")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.constraintlayout:constraintlayout:2.2.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.9.3")
implementation("androidx.core:core-splashscreen:1.0.1")
}
22 changes: 22 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="DiscouragedApi,LockedOrientationActivity">

<application
android:icon="@mipmap/mpm_ic"
android:label="Olivest">
<activity
android:name=".MainActivity"
android:exported="true"
android:screenOrientation="portrait"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>

</manifest>
20 changes: 20 additions & 0 deletions android/app/src/main/jni/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.18.2)
project(olivest LANGUAGES C)

set(CMAKE_C_STANDARD 17)
set(CMAKE_CXX_STANDARD 17)

add_library(olivest SHARED
main.c
../../../../../olive.c
)

find_library(jnigraphics-lib jnigraphics)
find_library(android-lib android)
find_library(log-lib log)

target_link_libraries(olivest
${jnigraphics-lib}
${android-lib}
${log-lib}
)
145 changes: 145 additions & 0 deletions android/app/src/main/jni/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#include <jni.h>
#include <android/bitmap.h>
#include <android/log.h>
#include <string.h>
#include <math.h>

#include <stdio.h>

#define OLIVEC_IMPLEMENTATION

#include "../../../../../olive.c"

/* Canvas */
#define WIDTH 1100
#define HEIGHT 1100

/* Scene */
#define BACKGROUND_COLOR 0xFF181818
#define CIRCLE_RADIUS 100
#define CIRCLE_COLOR 0x99AA2020

#define PI 3.14159265359f

static float triangle_angle = 0.0f;
static float circle_x = WIDTH / 2.0f;
static float circle_y = HEIGHT / 2.0f;
static float circle_dx = 100.0f;
static float circle_dy = 100.0f;

/* Delta time placeholder */
static const float dt = 1.0f / 120.0f;

/* Rotate a point around the center */
static void rotate_point(float *x, float *y) {
float dx = *x - WIDTH / 2.0f;
float dy = *y - HEIGHT / 2.0f;
float mag = sqrtf(dx * dx + dy * dy);
float dir = atan2f(dy, dx) + triangle_angle;
*x = cosf(dir) * mag + WIDTH / 2.0f;
*y = sinf(dir) * mag + HEIGHT / 2.0f;
}



/*
-------------------- X
|
|
|
|
|
|
|
Y
*/

JNIEXPORT void JNICALL
Java_com_me_app_MainActivity_drawLines(JNIEnv *env, jobject thiz, jobject bmp) {
uint32_t pixels[WIDTH * HEIGHT];
const Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT, WIDTH);

olivec_fill(oc, 0xFFFFFFFF);

const int base_y = 50;

/* E */
// olivec_line(oc, 200, base_y, 300, base_y, 0xFFFF00FF);
// olivec_line(oc, 200, base_y * 2, 300, base_y * 2, 0xFF00FFFF);
// olivec_line(oc, 200, base_y * 3, 300, base_y * 3, 0xFFFF0000);
// olivec_line(oc, 200, base_y, 200, base_y * 3, 0xFFFF0000);
//
// /* A */
// olivec_line(oc, 400, base_y, 350, base_y * 3, 0xFFFF00FF);
// olivec_line(oc, 400, base_y, 450, base_y * 3, 0xFFFF00FF);
// olivec_line(oc, 350, base_y * 2, 450, base_y * 2, 0xFFFF00FF);
//



/* N */
olivec_line(oc, 600, 600, 600, 800, 0xFF00FF00);

olivec_line(oc, 650, 800, 600, 600, 0xFF00FF00);

olivec_line(oc, 650, 800, 650, 600, 0xFF00FF00);




/* Rectangle */
//olivec_rect(oc, 400, 200, 100, 150, 0xFF00FF00);

void *bitmapPixels;
AndroidBitmap_lockPixels(env, bmp, &bitmapPixels);
memcpy(bitmapPixels, pixels, sizeof(pixels));
AndroidBitmap_unlockPixels(env, bmp);
}

JNIEXPORT void JNICALL
Java_com_me_app_MainActivity_drawTriangle(JNIEnv *env, jobject thiz, jobject bmp) {
uint32_t pixels[WIDTH * HEIGHT];
const Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT, WIDTH);

olivec_fill(oc, BACKGROUND_COLOR);

/* Triangle */
triangle_angle += 0.7f * PI * dt;

float x1 = WIDTH / 2.0f, y1 = HEIGHT * 3.0f / 20;
float x2 = WIDTH * 3.0f / 20, y2 = HEIGHT / 2.0f;
float x3 = WIDTH * 17.0f / 20, y3 = HEIGHT * 17.0f / 20;

rotate_point(&x1, &y1);
rotate_point(&x2, &y2);
rotate_point(&x3, &y3);



olivec_triangle3c(oc, (int) x1, (int) y1, (int) x2, (int) y2, (int) x3, (int) y3,
0xFF2020FF, 0xFF20FF20, 0xFFFF2020);

/* Circle */
float nx = circle_x + circle_dx * dt;
if (nx - CIRCLE_RADIUS < 0 || nx + CIRCLE_RADIUS >= WIDTH) {
circle_dx *= -1;
} else {
circle_x = nx;
}

float ny = circle_y + circle_dy * dt;
if (ny - CIRCLE_RADIUS < 0 || ny + CIRCLE_RADIUS >= HEIGHT) {
circle_dy *= -1;
} else {
circle_y = ny;
}

// == DRAWING THE CIRCLE WITHIN THE TRIANGLE == //
olivec_circle(oc, (int) circle_x, (int) circle_y, CIRCLE_RADIUS, CIRCLE_COLOR);

// == CREATE THE BITMAP WITH THE GIVEN POINTER == //
void *bitmapPixels;
AndroidBitmap_lockPixels(env, bmp, &bitmapPixels);
memcpy(bitmapPixels, pixels, sizeof(pixels));
AndroidBitmap_unlockPixels(env, bmp);
}
68 changes: 68 additions & 0 deletions android/app/src/main/kotlin/com/me/app/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.me.app

import android.graphics.Bitmap
import android.os.Bundle
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.createBitmap
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.lifecycleScope
import com.me.app.databinding.ActivityMainBinding
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.lang.Thread.sleep


class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding

// == TAKEN FROM C SIDE == //
private val dimen = 1100

init {
System.loadLibrary("olivest")
}


external fun drawLines(bmp: Bitmap)
external fun drawTriangle(bmp: Bitmap)


override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen().setKeepOnScreenCondition {
sleep(800)
false
}
super.onCreate(savedInstanceState)

// == HIDE SYSTEM BARS == //
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
controller.hide(WindowInsetsCompat.Type.systemBars())
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}

// == KEEP THE SCREEN ON == //
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)


val bitmap = createBitmap(dimen, dimen)

// == CALL OLIVE.c AND FILL IN THE BUFFER == //
drawTriangle(bitmap)
binding.surface.setImageBitmap(bitmap)

lifecycleScope.launch {
repeat(Int.MAX_VALUE) {
delay(10)
drawTriangle(bitmap)
binding.surface.setImageBitmap(bitmap)
}
}
}
}
Loading