From 85fe852c25d1079504b4d0a997afb56e6bce7dc4 Mon Sep 17 00:00:00 2001 From: JD Hsu Date: Thu, 30 Apr 2020 13:04:25 -0700 Subject: [PATCH 01/39] Migrated to androidx, Android R preview --- core/build.gradle | 2 +- core/src/processing/android/PFragment.java | 14 ++++++++------ .../processing/android/PermissionRequestor.java | 2 +- core/src/processing/core/PApplet.java | 2 +- core/src/processing/core/PSurfaceNone.java | 9 +++++---- gradle.properties | 2 ++ gradle/wrapper/gradle-wrapper.properties | 3 ++- 7 files changed, 20 insertions(+), 14 deletions(-) create mode 100644 gradle.properties diff --git a/core/build.gradle b/core/build.gradle index 002621c9a..b45fede08 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -7,7 +7,7 @@ apply plugin: 'aar' dependencies { implementation name: "android" - implementationAar "com.android.support:support-v4:${supportLibsVersion}" + implementationAar 'androidx.legacy:legacy-support-v4:1.0.0' implementationAar "com.google.android.support:wearable:${wearVersion}" } diff --git a/core/src/processing/android/PFragment.java b/core/src/processing/android/PFragment.java index 1a2132ed3..859580dfd 100644 --- a/core/src/processing/android/PFragment.java +++ b/core/src/processing/android/PFragment.java @@ -22,12 +22,14 @@ package processing.android; -import android.support.annotation.IdRes; -import android.support.annotation.LayoutRes; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; + +import androidx.annotation.IdRes; +import androidx.annotation.LayoutRes; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; + import android.util.DisplayMetrics; import android.content.Intent; import android.content.pm.ActivityInfo; diff --git a/core/src/processing/android/PermissionRequestor.java b/core/src/processing/android/PermissionRequestor.java index 93c939238..fb8d59443 100644 --- a/core/src/processing/android/PermissionRequestor.java +++ b/core/src/processing/android/PermissionRequestor.java @@ -24,7 +24,7 @@ import android.app.Activity; import android.os.Bundle; -import android.support.v4.app.ActivityCompat; +import androidx.core.app.ActivityCompat; import android.support.v4.os.ResultReceiver; // A simple utility activity to request permissions in a service. diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 24e5dd3a7..8c0651e2c 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -45,7 +45,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.support.annotation.LayoutRes; +import androidx.annotation.LayoutRes; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.Menu; diff --git a/core/src/processing/core/PSurfaceNone.java b/core/src/processing/core/PSurfaceNone.java index cd350b873..901848ca4 100644 --- a/core/src/processing/core/PSurfaceNone.java +++ b/core/src/processing/core/PSurfaceNone.java @@ -33,8 +33,8 @@ import android.os.Handler; import android.os.Looper; import android.service.wallpaper.WallpaperService; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import android.support.wearable.watchface.WatchFaceService; import android.view.LayoutInflater; import android.view.SurfaceHolder; @@ -89,14 +89,15 @@ public AppComponent getComponent() { @Override public Context getContext() { - if (component.getKind() == AppComponent.FRAGMENT) { + return activity; + /*if (component.getKind() == AppComponent.FRAGMENT) { return activity; } else if (component.getKind() == AppComponent.WALLPAPER) { return wallpaper; } else if (component.getKind() == AppComponent.WATCHFACE) { return watchface; } - return null; + */ } diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..5465fec0e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2c2bbe5f9..f68335fce 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Thu Apr 30 13:02:23 PDT 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-all.zip From 8de9a4c9a319b4c9eff13040364ee5036a553be9 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar <46577873+rupesh-kumar-lpu@users.noreply.github.com> Date: Thu, 24 Dec 2020 05:57:33 +0530 Subject: [PATCH 02/39] Added core-androidx source assets --- .../src/assets/shaders/ColorFrag.glsl | 32 ++++ .../src/assets/shaders/ColorVert.glsl | 34 ++++ .../src/assets/shaders/LightFrag.glsl | 33 ++++ .../src/assets/shaders/LightVert.glsl | 151 +++++++++++++++++ .../src/assets/shaders/LineFrag.glsl | 32 ++++ .../src/assets/shaders/LineVert.glsl | 99 +++++++++++ .../src/assets/shaders/MaskFrag.glsl | 40 +++++ core-androidx/src/assets/shaders/P2DFrag.glsl | 14 ++ core-androidx/src/assets/shaders/P2DVert.glsl | 23 +++ .../src/assets/shaders/PointFrag.glsl | 32 ++++ .../src/assets/shaders/PointVert.glsl | 56 +++++++ core-androidx/src/assets/shaders/TexFrag.glsl | 37 +++++ .../src/assets/shaders/TexLightFrag.glsl | 37 +++++ .../src/assets/shaders/TexLightVert.glsl | 157 ++++++++++++++++++ core-androidx/src/assets/shaders/TexVert.glsl | 38 +++++ 15 files changed, 815 insertions(+) create mode 100644 core-androidx/src/assets/shaders/ColorFrag.glsl create mode 100644 core-androidx/src/assets/shaders/ColorVert.glsl create mode 100644 core-androidx/src/assets/shaders/LightFrag.glsl create mode 100644 core-androidx/src/assets/shaders/LightVert.glsl create mode 100644 core-androidx/src/assets/shaders/LineFrag.glsl create mode 100644 core-androidx/src/assets/shaders/LineVert.glsl create mode 100644 core-androidx/src/assets/shaders/MaskFrag.glsl create mode 100644 core-androidx/src/assets/shaders/P2DFrag.glsl create mode 100644 core-androidx/src/assets/shaders/P2DVert.glsl create mode 100644 core-androidx/src/assets/shaders/PointFrag.glsl create mode 100644 core-androidx/src/assets/shaders/PointVert.glsl create mode 100644 core-androidx/src/assets/shaders/TexFrag.glsl create mode 100644 core-androidx/src/assets/shaders/TexLightFrag.glsl create mode 100644 core-androidx/src/assets/shaders/TexLightVert.glsl create mode 100644 core-androidx/src/assets/shaders/TexVert.glsl diff --git a/core-androidx/src/assets/shaders/ColorFrag.glsl b/core-androidx/src/assets/shaders/ColorFrag.glsl new file mode 100644 index 000000000..5f86d92b4 --- /dev/null +++ b/core-androidx/src/assets/shaders/ColorFrag.glsl @@ -0,0 +1,32 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +varying vec4 vertColor; + +void main() { + gl_FragColor = vertColor; +} \ No newline at end of file diff --git a/core-androidx/src/assets/shaders/ColorVert.glsl b/core-androidx/src/assets/shaders/ColorVert.glsl new file mode 100644 index 000000000..65fd55716 --- /dev/null +++ b/core-androidx/src/assets/shaders/ColorVert.glsl @@ -0,0 +1,34 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +uniform mat4 transformMatrix; + +attribute vec4 position; +attribute vec4 color; + +varying vec4 vertColor; + +void main() { + gl_Position = transformMatrix * position; + + vertColor = color; +} \ No newline at end of file diff --git a/core-androidx/src/assets/shaders/LightFrag.glsl b/core-androidx/src/assets/shaders/LightFrag.glsl new file mode 100644 index 000000000..6b60d4039 --- /dev/null +++ b/core-androidx/src/assets/shaders/LightFrag.glsl @@ -0,0 +1,33 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +varying vec4 vertColor; +varying vec4 backVertColor; + +void main() { + gl_FragColor = gl_FrontFacing ? vertColor : backVertColor; +} \ No newline at end of file diff --git a/core-androidx/src/assets/shaders/LightVert.glsl b/core-androidx/src/assets/shaders/LightVert.glsl new file mode 100644 index 000000000..adc4bf9ad --- /dev/null +++ b/core-androidx/src/assets/shaders/LightVert.glsl @@ -0,0 +1,151 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +uniform mat4 modelviewMatrix; +uniform mat4 transformMatrix; +uniform mat3 normalMatrix; + +uniform int lightCount; +uniform vec4 lightPosition[8]; +uniform vec3 lightNormal[8]; +uniform vec3 lightAmbient[8]; +uniform vec3 lightDiffuse[8]; +uniform vec3 lightSpecular[8]; +uniform vec3 lightFalloff[8]; +uniform vec2 lightSpot[8]; + +attribute vec4 position; +attribute vec4 color; +attribute vec3 normal; + +attribute vec4 ambient; +attribute vec4 specular; +attribute vec4 emissive; +attribute float shininess; + +varying vec4 vertColor; +varying vec4 backVertColor; + +const float zero_float = 0.0; +const float one_float = 1.0; +const vec3 zero_vec3 = vec3(0); + +float falloffFactor(vec3 lightPos, vec3 vertPos, vec3 coeff) { + vec3 lpv = lightPos - vertPos; + vec3 dist = vec3(one_float); + dist.z = dot(lpv, lpv); + dist.y = sqrt(dist.z); + return one_float / dot(dist, coeff); +} + +float spotFactor(vec3 lightPos, vec3 vertPos, vec3 lightNorm, float minCos, float spotExp) { + vec3 lpv = normalize(lightPos - vertPos); + vec3 nln = -one_float * lightNorm; + float spotCos = dot(nln, lpv); + return spotCos <= minCos ? zero_float : pow(spotCos, spotExp); +} + +float lambertFactor(vec3 lightDir, vec3 vecNormal) { + return max(zero_float, dot(lightDir, vecNormal)); +} + +float blinnPhongFactor(vec3 lightDir, vec3 vertPos, vec3 vecNormal, float shine) { + vec3 np = normalize(vertPos); + vec3 ldp = normalize(lightDir - np); + return pow(max(zero_float, dot(ldp, vecNormal)), shine); +} + +void main() { + // Vertex in clip coordinates + gl_Position = transformMatrix * position; + + // Vertex in eye coordinates + vec3 ecVertex = vec3(modelviewMatrix * position); + + // Normal vector in eye coordinates + vec3 ecNormal = normalize(normalMatrix * normal); + vec3 ecNormalInv = ecNormal * -one_float; + + // Light calculations + vec3 totalAmbient = vec3(0, 0, 0); + + vec3 totalFrontDiffuse = vec3(0, 0, 0); + vec3 totalFrontSpecular = vec3(0, 0, 0); + + vec3 totalBackDiffuse = vec3(0, 0, 0); + vec3 totalBackSpecular = vec3(0, 0, 0); + + for (int i = 0; i < 8; i++) { + if (lightCount == i) break; + + vec3 lightPos = lightPosition[i].xyz; + bool isDir = lightPosition[i].w < one_float; + float spotCos = lightSpot[i].x; + float spotExp = lightSpot[i].y; + + vec3 lightDir; + float falloff; + float spotf; + + if (isDir) { + falloff = one_float; + lightDir = -one_float * lightNormal[i]; + } else { + falloff = falloffFactor(lightPos, ecVertex, lightFalloff[i]); + lightDir = normalize(lightPos - ecVertex); + } + + spotf = spotExp > zero_float ? spotFactor(lightPos, ecVertex, lightNormal[i], + spotCos, spotExp) + : one_float; + + if (any(greaterThan(lightAmbient[i], zero_vec3))) { + totalAmbient += lightAmbient[i] * falloff; + } + + if (any(greaterThan(lightDiffuse[i], zero_vec3))) { + totalFrontDiffuse += lightDiffuse[i] * falloff * spotf * + lambertFactor(lightDir, ecNormal); + totalBackDiffuse += lightDiffuse[i] * falloff * spotf * + lambertFactor(lightDir, ecNormalInv); + } + + if (any(greaterThan(lightSpecular[i], zero_vec3))) { + totalFrontSpecular += lightSpecular[i] * falloff * spotf * + blinnPhongFactor(lightDir, ecVertex, ecNormal, shininess); + totalBackSpecular += lightSpecular[i] * falloff * spotf * + blinnPhongFactor(lightDir, ecVertex, ecNormalInv, shininess); + } + } + + // Calculating final color as result of all lights (plus emissive term). + // Transparency is determined exclusively by the diffuse component. + vertColor = vec4(totalAmbient, 0) * ambient + + vec4(totalFrontDiffuse, 1) * color + + vec4(totalFrontSpecular, 0) * specular + + vec4(emissive.rgb, 0); + + backVertColor = vec4(totalAmbient, 0) * ambient + + vec4(totalBackDiffuse, 1) * color + + vec4(totalBackSpecular, 0) * specular + + vec4(emissive.rgb, 0); +} \ No newline at end of file diff --git a/core-androidx/src/assets/shaders/LineFrag.glsl b/core-androidx/src/assets/shaders/LineFrag.glsl new file mode 100644 index 000000000..9046e17e9 --- /dev/null +++ b/core-androidx/src/assets/shaders/LineFrag.glsl @@ -0,0 +1,32 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +varying vec4 vertColor; + +void main() { + gl_FragColor = vertColor; +} \ No newline at end of file diff --git a/core-androidx/src/assets/shaders/LineVert.glsl b/core-androidx/src/assets/shaders/LineVert.glsl new file mode 100644 index 000000000..15e7bf951 --- /dev/null +++ b/core-androidx/src/assets/shaders/LineVert.glsl @@ -0,0 +1,99 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-17 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +#define PROCESSING_LINE_SHADER + +uniform mat4 modelviewMatrix; +uniform mat4 projectionMatrix; + +uniform vec4 viewport; +uniform int perspective; +uniform vec3 scale; + +attribute vec4 position; +attribute vec4 color; +attribute vec4 direction; + +varying vec4 vertColor; + +void main() { + vec4 posp = modelviewMatrix * position; + vec4 posq = modelviewMatrix * (position + vec4(direction.xyz, 0)); + + // Moving vertices slightly toward the camera + // to avoid depth-fighting with the fill triangles. + // Discussed here: + // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=252848 + posp.xyz = posp.xyz * scale; + posq.xyz = posq.xyz * scale; + + vec4 p = projectionMatrix * posp; + vec4 q = projectionMatrix * posq; + + // formula to convert from clip space (range -1..1) to screen space (range 0..[width or height]) + // screen_p = (p.xy/p.w + <1,1>) * 0.5 * viewport.zw + + // prevent division by W by transforming the tangent formula (div by 0 causes + // the line to disappear, see https://github.com/processing/processing/issues/5183) + // t = screen_q - screen_p + // + // tangent is normalized and we don't care which direction it points to (+-) + // t = +- normalize( screen_q - screen_p ) + // t = +- normalize( (q.xy/q.w+<1,1>)*0.5*viewport.zw - (p.xy/p.w+<1,1>)*0.5*viewport.zw ) + // + // extract common factor, <1,1> - <1,1> cancels out + // t = +- normalize( (q.xy/q.w - p.xy/p.w) * 0.5 * viewport.zw ) + // + // convert to common divisor + // t = +- normalize( ((q.xy*p.w - p.xy*q.w) / (p.w*q.w)) * 0.5 * viewport.zw ) + // + // remove the common scalar divisor/factor, not needed due to normalize and +- + // (keep viewport - can't remove because it has different components for x and y + // and corrects for aspect ratio, see https://github.com/processing/processing/issues/5181) + // t = +- normalize( (q.xy*p.w - p.xy*q.w) * viewport.zw ) + + vec2 tangent = (q.xy*p.w - p.xy*q.w) * viewport.zw; + // don't normalize zero vector (line join triangles and lines perpendicular to the eye plane) + tangent = length(tangent) == 0.0 ? vec2(0.0, 0.0) : normalize(tangent); + + // flip tangent to normal (it's already normalized) + vec2 normal = vec2(-tangent.y, tangent.x); + + float thickness = direction.w; + vec2 offset = normal * thickness; + + // Perspective --- + // convert from world to clip by multiplying with projection scaling factor + // to get the right thickness (see https://github.com/processing/processing/issues/5182) + // invert Y, projections in Processing invert Y + vec2 perspScale = (projectionMatrix * vec4(1, -1, 0, 0)).xy; + + // No Perspective --- + // multiply by W (to cancel out division by W later in the pipeline) and + // convert from screen to clip (derived from clip to screen above) + vec2 noPerspScale = p.w / (0.5 * viewport.zw); + + gl_Position.xy = p.xy + offset.xy * mix(noPerspScale, perspScale, float(perspective > 0)); + gl_Position.zw = p.zw; + + vertColor = color; +} diff --git a/core-androidx/src/assets/shaders/MaskFrag.glsl b/core-androidx/src/assets/shaders/MaskFrag.glsl new file mode 100644 index 000000000..3d7e7dbf1 --- /dev/null +++ b/core-androidx/src/assets/shaders/MaskFrag.glsl @@ -0,0 +1,40 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +#define PROCESSING_TEXTURE_SHADER + +uniform sampler2D texture; +uniform sampler2D mask; + +varying vec4 vertTexCoord; + +void main() { + vec3 texColor = texture2D(texture, vertTexCoord.st).rgb; + vec3 maskColor = texture2D(mask, vertTexCoord.st).rgb; + float luminance = dot(maskColor, vec3(0.2126, 0.7152, 0.0722)); + gl_FragColor = vec4(texColor, luminance); +} \ No newline at end of file diff --git a/core-androidx/src/assets/shaders/P2DFrag.glsl b/core-androidx/src/assets/shaders/P2DFrag.glsl new file mode 100644 index 000000000..ce3a3fb27 --- /dev/null +++ b/core-androidx/src/assets/shaders/P2DFrag.glsl @@ -0,0 +1,14 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +varying vec4 vertColor; +varying vec2 vertTexCoord; +varying float vertTexFactor; + +uniform sampler2D texture; + +void main() { + gl_FragColor = mix(vertColor, vertColor * texture2D(texture, vertTexCoord), vertTexFactor); +} diff --git a/core-androidx/src/assets/shaders/P2DVert.glsl b/core-androidx/src/assets/shaders/P2DVert.glsl new file mode 100644 index 000000000..4e9a1f7e1 --- /dev/null +++ b/core-androidx/src/assets/shaders/P2DVert.glsl @@ -0,0 +1,23 @@ +attribute vec3 position; +attribute vec4 color; +attribute vec2 texCoord; +attribute float texFactor; + +varying vec4 vertColor; +varying vec2 vertTexCoord; +varying float vertTexFactor; + +uniform mat4 transform; +uniform vec2 texScale; + +void main() { + gl_Position = transform * vec4(position, 1); + + //we avoid affecting the Z component by the transform + //because it would mess up our depth testing + gl_Position.z = position.z; + + vertColor = color.zyxw; + vertTexCoord = texCoord * texScale; + vertTexFactor = texFactor; +} diff --git a/core-androidx/src/assets/shaders/PointFrag.glsl b/core-androidx/src/assets/shaders/PointFrag.glsl new file mode 100644 index 000000000..5f86d92b4 --- /dev/null +++ b/core-androidx/src/assets/shaders/PointFrag.glsl @@ -0,0 +1,32 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +varying vec4 vertColor; + +void main() { + gl_FragColor = vertColor; +} \ No newline at end of file diff --git a/core-androidx/src/assets/shaders/PointVert.glsl b/core-androidx/src/assets/shaders/PointVert.glsl new file mode 100644 index 000000000..292674615 --- /dev/null +++ b/core-androidx/src/assets/shaders/PointVert.glsl @@ -0,0 +1,56 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-17 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +uniform mat4 projectionMatrix; +uniform mat4 modelviewMatrix; + +uniform vec4 viewport; +uniform int perspective; + +attribute vec4 position; +attribute vec4 color; +attribute vec2 offset; + +varying vec4 vertColor; + +void main() { + vec4 pos = modelviewMatrix * position; + vec4 clip = projectionMatrix * pos; + + // Perspective --- + // convert from world to clip by multiplying with projection scaling factor + // invert Y, projections in Processing invert Y + vec2 perspScale = (projectionMatrix * vec4(1, -1, 0, 0)).xy; + + // formula to convert from clip space (range -1..1) to screen space (range 0..[width or height]) + // screen_p = (p.xy/p.w + <1,1>) * 0.5 * viewport.zw + + // No Perspective --- + // multiply by W (to cancel out division by W later in the pipeline) and + // convert from screen to clip (derived from clip to screen above) + vec2 noPerspScale = clip.w / (0.5 * viewport.zw); + + gl_Position.xy = clip.xy + offset.xy * mix(noPerspScale, perspScale, float(perspective > 0)); + gl_Position.zw = clip.zw; + + vertColor = color; +} \ No newline at end of file diff --git a/core-androidx/src/assets/shaders/TexFrag.glsl b/core-androidx/src/assets/shaders/TexFrag.glsl new file mode 100644 index 000000000..c8e183d49 --- /dev/null +++ b/core-androidx/src/assets/shaders/TexFrag.glsl @@ -0,0 +1,37 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +uniform sampler2D texture; + +uniform vec2 texOffset; + +varying vec4 vertColor; +varying vec4 vertTexCoord; + +void main() { + gl_FragColor = texture2D(texture, vertTexCoord.st) * vertColor; +} \ No newline at end of file diff --git a/core-androidx/src/assets/shaders/TexLightFrag.glsl b/core-androidx/src/assets/shaders/TexLightFrag.glsl new file mode 100644 index 000000000..fd9b61576 --- /dev/null +++ b/core-androidx/src/assets/shaders/TexLightFrag.glsl @@ -0,0 +1,37 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +uniform sampler2D texture; + +uniform vec2 texOffset; + +varying vec4 vertColor; +varying vec4 backVertColor; +varying vec4 vertTexCoord; + +void main() { + gl_FragColor = texture2D(texture, vertTexCoord.st) * (gl_FrontFacing ? vertColor : backVertColor); +} \ No newline at end of file diff --git a/core-androidx/src/assets/shaders/TexLightVert.glsl b/core-androidx/src/assets/shaders/TexLightVert.glsl new file mode 100644 index 000000000..57551edce --- /dev/null +++ b/core-androidx/src/assets/shaders/TexLightVert.glsl @@ -0,0 +1,157 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +uniform mat4 modelviewMatrix; +uniform mat4 transformMatrix; +uniform mat3 normalMatrix; +uniform mat4 texMatrix; + +uniform int lightCount; +uniform vec4 lightPosition[8]; +uniform vec3 lightNormal[8]; +uniform vec3 lightAmbient[8]; +uniform vec3 lightDiffuse[8]; +uniform vec3 lightSpecular[8]; +uniform vec3 lightFalloff[8]; +uniform vec2 lightSpot[8]; + +attribute vec4 position; +attribute vec4 color; +attribute vec3 normal; +attribute vec2 texCoord; + +attribute vec4 ambient; +attribute vec4 specular; +attribute vec4 emissive; +attribute float shininess; + +varying vec4 vertColor; +varying vec4 backVertColor; +varying vec4 vertTexCoord; + +const float zero_float = 0.0; +const float one_float = 1.0; +const vec3 zero_vec3 = vec3(0); + +float falloffFactor(vec3 lightPos, vec3 vertPos, vec3 coeff) { + vec3 lpv = lightPos - vertPos; + vec3 dist = vec3(one_float); + dist.z = dot(lpv, lpv); + dist.y = sqrt(dist.z); + return one_float / dot(dist, coeff); +} + +float spotFactor(vec3 lightPos, vec3 vertPos, vec3 lightNorm, float minCos, float spotExp) { + vec3 lpv = normalize(lightPos - vertPos); + vec3 nln = -one_float * lightNorm; + float spotCos = dot(nln, lpv); + return spotCos <= minCos ? zero_float : pow(spotCos, spotExp); +} + +float lambertFactor(vec3 lightDir, vec3 vecNormal) { + return max(zero_float, dot(lightDir, vecNormal)); +} + +float blinnPhongFactor(vec3 lightDir, vec3 vertPos, vec3 vecNormal, float shine) { + vec3 np = normalize(vertPos); + vec3 ldp = normalize(lightDir - np); + return pow(max(zero_float, dot(ldp, vecNormal)), shine); +} + +void main() { + // Vertex in clip coordinates + gl_Position = transformMatrix * position; + + // Vertex in eye coordinates + vec3 ecVertex = vec3(modelviewMatrix * position); + + // Normal vector in eye coordinates + vec3 ecNormal = normalize(normalMatrix * normal); + vec3 ecNormalInv = ecNormal * -one_float; + + // Light calculations + vec3 totalAmbient = vec3(0, 0, 0); + + vec3 totalFrontDiffuse = vec3(0, 0, 0); + vec3 totalFrontSpecular = vec3(0, 0, 0); + + vec3 totalBackDiffuse = vec3(0, 0, 0); + vec3 totalBackSpecular = vec3(0, 0, 0); + + for (int i = 0; i < 8; i++) { + if (lightCount == i) break; + + vec3 lightPos = lightPosition[i].xyz; + bool isDir = lightPosition[i].w < one_float; + float spotCos = lightSpot[i].x; + float spotExp = lightSpot[i].y; + + vec3 lightDir; + float falloff; + float spotf; + + if (isDir) { + falloff = one_float; + lightDir = -one_float * lightNormal[i]; + } else { + falloff = falloffFactor(lightPos, ecVertex, lightFalloff[i]); + lightDir = normalize(lightPos - ecVertex); + } + + spotf = spotExp > zero_float ? spotFactor(lightPos, ecVertex, lightNormal[i], + spotCos, spotExp) + : one_float; + + if (any(greaterThan(lightAmbient[i], zero_vec3))) { + totalAmbient += lightAmbient[i] * falloff; + } + + if (any(greaterThan(lightDiffuse[i], zero_vec3))) { + totalFrontDiffuse += lightDiffuse[i] * falloff * spotf * + lambertFactor(lightDir, ecNormal); + totalBackDiffuse += lightDiffuse[i] * falloff * spotf * + lambertFactor(lightDir, ecNormalInv); + } + + if (any(greaterThan(lightSpecular[i], zero_vec3))) { + totalFrontSpecular += lightSpecular[i] * falloff * spotf * + blinnPhongFactor(lightDir, ecVertex, ecNormal, shininess); + totalBackSpecular += lightSpecular[i] * falloff * spotf * + blinnPhongFactor(lightDir, ecVertex, ecNormalInv, shininess); + } + } + + // Calculating final color as result of all lights (plus emissive term). + // Transparency is determined exclusively by the diffuse component. + vertColor = vec4(totalAmbient, 0) * ambient + + vec4(totalFrontDiffuse, 1) * color + + vec4(totalFrontSpecular, 0) * specular + + vec4(emissive.rgb, 0); + + backVertColor = vec4(totalAmbient, 0) * ambient + + vec4(totalBackDiffuse, 1) * color + + vec4(totalBackSpecular, 0) * specular + + vec4(emissive.rgb, 0); + + // Calculating texture coordinates, with r and q set both to one + vertTexCoord = texMatrix * vec4(texCoord, 1.0, 1.0); +} diff --git a/core-androidx/src/assets/shaders/TexVert.glsl b/core-androidx/src/assets/shaders/TexVert.glsl new file mode 100644 index 000000000..c07dc30d7 --- /dev/null +++ b/core-androidx/src/assets/shaders/TexVert.glsl @@ -0,0 +1,38 @@ +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +uniform mat4 transformMatrix; +uniform mat4 texMatrix; + +attribute vec4 position; +attribute vec4 color; +attribute vec2 texCoord; + +varying vec4 vertColor; +varying vec4 vertTexCoord; + +void main() { + gl_Position = transformMatrix * position; + + vertColor = color; + vertTexCoord = texMatrix * vec4(texCoord, 1.0, 1.0); +} \ No newline at end of file From b48a88faae0416df008eb135a6351a952e99a841 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar <46577873+rupesh-kumar-lpu@users.noreply.github.com> Date: Thu, 24 Dec 2020 06:02:10 +0530 Subject: [PATCH 03/39] Added source processing inside core-androidx --- .../processing/a2d/PGraphicsAndroid2D.java | 2386 +++ .../src/processing/a2d/PShapeAndroid2D.java | 109 + .../src/processing/a2d/PSurfaceAndroid2D.java | 159 + .../src/processing/android/ActivityAPI.java | 65 + .../src/processing/android/AppComponent.java | 51 + .../src/processing/android/CompatUtils.java | 111 + .../src/processing/android/PFragment.java | 259 + .../src/processing/android/PWallpaper.java | 302 + .../processing/android/PWatchFaceCanvas.java | 367 + .../processing/android/PWatchFaceGLES.java | 392 + .../android/PermissionRequestor.java | 59 + .../src/processing/android/ServiceEngine.java | 49 + .../src/processing/core/PApplet.java | 12622 ++++++++++++++ .../src/processing/core/PConstants.java | 465 + core-androidx/src/processing/core/PFont.java | 907 + .../src/processing/core/PGraphics.java | 6134 +++++++ core-androidx/src/processing/core/PImage.java | 2881 ++++ .../src/processing/core/PMatrix.java | 170 + .../src/processing/core/PMatrix2D.java | 467 + .../src/processing/core/PMatrix3D.java | 806 + core-androidx/src/processing/core/PShape.java | 3571 ++++ .../src/processing/core/PShapeOBJ.java | 467 + .../src/processing/core/PShapeSVG.java | 2031 +++ core-androidx/src/processing/core/PStyle.java | 62 + .../src/processing/core/PSurface.java | 107 + .../src/processing/core/PSurfaceNone.java | 593 + .../src/processing/core/PVector.java | 1062 ++ .../src/processing/data/DoubleDict.java | 850 + .../src/processing/data/DoubleList.java | 928 + .../src/processing/data/FloatDict.java | 847 + .../src/processing/data/FloatList.java | 936 + .../src/processing/data/IntDict.java | 807 + .../src/processing/data/IntList.java | 936 + .../src/processing/data/JSONArray.java | 1260 ++ .../src/processing/data/JSONObject.java | 2282 +++ .../src/processing/data/JSONTokener.java | 435 + .../src/processing/data/LongDict.java | 802 + .../src/processing/data/LongList.java | 937 + core-androidx/src/processing/data/Sort.java | 46 + .../src/processing/data/StringDict.java | 613 + .../src/processing/data/StringList.java | 798 + core-androidx/src/processing/data/Table.java | 4934 ++++++ .../src/processing/data/TableRow.java | 198 + core-androidx/src/processing/data/XML.java | 1156 ++ core-androidx/src/processing/event/Event.java | 125 + .../src/processing/event/KeyEvent.java | 69 + .../src/processing/event/MouseEvent.java | 105 + .../src/processing/event/TouchEvent.java | 142 + .../src/processing/opengl/FontTexture.java | 379 + .../src/processing/opengl/FrameBuffer.java | 503 + .../src/processing/opengl/LinePath.java | 623 + .../src/processing/opengl/LineStroker.java | 685 + core-androidx/src/processing/opengl/PGL.java | 3363 ++++ .../src/processing/opengl/PGLES.java | 1608 ++ .../src/processing/opengl/PGraphics2D.java | 629 + .../src/processing/opengl/PGraphics2DX.java | 2056 +++ .../src/processing/opengl/PGraphics3D.java | 281 + .../processing/opengl/PGraphicsOpenGL.java | 14280 ++++++++++++++++ .../src/processing/opengl/PShader.java | 1478 ++ .../src/processing/opengl/PShapeOpenGL.java | 5250 ++++++ .../src/processing/opengl/PSurfaceGLES.java | 604 + .../src/processing/opengl/Texture.java | 1670 ++ .../src/processing/opengl/VertexBuffer.java | 88 + .../processing/opengl/tess/ActiveRegion.java | 70 + .../processing/opengl/tess/CachedVertex.java | 60 + .../src/processing/opengl/tess/Dict.java | 142 + .../src/processing/opengl/tess/DictNode.java | 61 + .../src/processing/opengl/tess/GLUface.java | 66 + .../processing/opengl/tess/GLUhalfEdge.java | 72 + .../src/processing/opengl/tess/GLUmesh.java | 63 + .../opengl/tess/GLUtessellatorImpl.java | 645 + .../src/processing/opengl/tess/GLUvertex.java | 67 + .../src/processing/opengl/tess/Geom.java | 340 + .../src/processing/opengl/tess/Mesh.java | 736 + .../src/processing/opengl/tess/Normal.java | 288 + .../src/processing/opengl/tess/PGLU.java | 158 + .../opengl/tess/PGLUtessellator.java | 70 + .../opengl/tess/PGLUtessellatorCallback.java | 359 + .../tess/PGLUtessellatorCallbackAdapter.java | 87 + .../src/processing/opengl/tess/PriorityQ.java | 102 + .../processing/opengl/tess/PriorityQHeap.java | 271 + .../processing/opengl/tess/PriorityQSort.java | 288 + .../src/processing/opengl/tess/Render.java | 558 + .../src/processing/opengl/tess/Sweep.java | 1354 ++ .../src/processing/opengl/tess/TessMono.java | 243 + .../src/processing/opengl/tess/TessState.java | 61 + 86 files changed, 95518 insertions(+) create mode 100644 core-androidx/src/processing/a2d/PGraphicsAndroid2D.java create mode 100644 core-androidx/src/processing/a2d/PShapeAndroid2D.java create mode 100644 core-androidx/src/processing/a2d/PSurfaceAndroid2D.java create mode 100644 core-androidx/src/processing/android/ActivityAPI.java create mode 100644 core-androidx/src/processing/android/AppComponent.java create mode 100644 core-androidx/src/processing/android/CompatUtils.java create mode 100644 core-androidx/src/processing/android/PFragment.java create mode 100644 core-androidx/src/processing/android/PWallpaper.java create mode 100644 core-androidx/src/processing/android/PWatchFaceCanvas.java create mode 100644 core-androidx/src/processing/android/PWatchFaceGLES.java create mode 100644 core-androidx/src/processing/android/PermissionRequestor.java create mode 100644 core-androidx/src/processing/android/ServiceEngine.java create mode 100644 core-androidx/src/processing/core/PApplet.java create mode 100644 core-androidx/src/processing/core/PConstants.java create mode 100644 core-androidx/src/processing/core/PFont.java create mode 100644 core-androidx/src/processing/core/PGraphics.java create mode 100644 core-androidx/src/processing/core/PImage.java create mode 100644 core-androidx/src/processing/core/PMatrix.java create mode 100644 core-androidx/src/processing/core/PMatrix2D.java create mode 100644 core-androidx/src/processing/core/PMatrix3D.java create mode 100644 core-androidx/src/processing/core/PShape.java create mode 100644 core-androidx/src/processing/core/PShapeOBJ.java create mode 100644 core-androidx/src/processing/core/PShapeSVG.java create mode 100644 core-androidx/src/processing/core/PStyle.java create mode 100644 core-androidx/src/processing/core/PSurface.java create mode 100644 core-androidx/src/processing/core/PSurfaceNone.java create mode 100644 core-androidx/src/processing/core/PVector.java create mode 100644 core-androidx/src/processing/data/DoubleDict.java create mode 100644 core-androidx/src/processing/data/DoubleList.java create mode 100644 core-androidx/src/processing/data/FloatDict.java create mode 100644 core-androidx/src/processing/data/FloatList.java create mode 100644 core-androidx/src/processing/data/IntDict.java create mode 100644 core-androidx/src/processing/data/IntList.java create mode 100644 core-androidx/src/processing/data/JSONArray.java create mode 100644 core-androidx/src/processing/data/JSONObject.java create mode 100644 core-androidx/src/processing/data/JSONTokener.java create mode 100644 core-androidx/src/processing/data/LongDict.java create mode 100644 core-androidx/src/processing/data/LongList.java create mode 100644 core-androidx/src/processing/data/Sort.java create mode 100644 core-androidx/src/processing/data/StringDict.java create mode 100644 core-androidx/src/processing/data/StringList.java create mode 100644 core-androidx/src/processing/data/Table.java create mode 100644 core-androidx/src/processing/data/TableRow.java create mode 100644 core-androidx/src/processing/data/XML.java create mode 100644 core-androidx/src/processing/event/Event.java create mode 100644 core-androidx/src/processing/event/KeyEvent.java create mode 100644 core-androidx/src/processing/event/MouseEvent.java create mode 100644 core-androidx/src/processing/event/TouchEvent.java create mode 100644 core-androidx/src/processing/opengl/FontTexture.java create mode 100644 core-androidx/src/processing/opengl/FrameBuffer.java create mode 100644 core-androidx/src/processing/opengl/LinePath.java create mode 100644 core-androidx/src/processing/opengl/LineStroker.java create mode 100644 core-androidx/src/processing/opengl/PGL.java create mode 100644 core-androidx/src/processing/opengl/PGLES.java create mode 100644 core-androidx/src/processing/opengl/PGraphics2D.java create mode 100644 core-androidx/src/processing/opengl/PGraphics2DX.java create mode 100644 core-androidx/src/processing/opengl/PGraphics3D.java create mode 100644 core-androidx/src/processing/opengl/PGraphicsOpenGL.java create mode 100644 core-androidx/src/processing/opengl/PShader.java create mode 100644 core-androidx/src/processing/opengl/PShapeOpenGL.java create mode 100644 core-androidx/src/processing/opengl/PSurfaceGLES.java create mode 100644 core-androidx/src/processing/opengl/Texture.java create mode 100644 core-androidx/src/processing/opengl/VertexBuffer.java create mode 100644 core-androidx/src/processing/opengl/tess/ActiveRegion.java create mode 100644 core-androidx/src/processing/opengl/tess/CachedVertex.java create mode 100644 core-androidx/src/processing/opengl/tess/Dict.java create mode 100644 core-androidx/src/processing/opengl/tess/DictNode.java create mode 100644 core-androidx/src/processing/opengl/tess/GLUface.java create mode 100644 core-androidx/src/processing/opengl/tess/GLUhalfEdge.java create mode 100644 core-androidx/src/processing/opengl/tess/GLUmesh.java create mode 100644 core-androidx/src/processing/opengl/tess/GLUtessellatorImpl.java create mode 100644 core-androidx/src/processing/opengl/tess/GLUvertex.java create mode 100644 core-androidx/src/processing/opengl/tess/Geom.java create mode 100644 core-androidx/src/processing/opengl/tess/Mesh.java create mode 100644 core-androidx/src/processing/opengl/tess/Normal.java create mode 100644 core-androidx/src/processing/opengl/tess/PGLU.java create mode 100644 core-androidx/src/processing/opengl/tess/PGLUtessellator.java create mode 100644 core-androidx/src/processing/opengl/tess/PGLUtessellatorCallback.java create mode 100644 core-androidx/src/processing/opengl/tess/PGLUtessellatorCallbackAdapter.java create mode 100644 core-androidx/src/processing/opengl/tess/PriorityQ.java create mode 100644 core-androidx/src/processing/opengl/tess/PriorityQHeap.java create mode 100644 core-androidx/src/processing/opengl/tess/PriorityQSort.java create mode 100644 core-androidx/src/processing/opengl/tess/Render.java create mode 100644 core-androidx/src/processing/opengl/tess/Sweep.java create mode 100644 core-androidx/src/processing/opengl/tess/TessMono.java create mode 100644 core-androidx/src/processing/opengl/tess/TessState.java diff --git a/core-androidx/src/processing/a2d/PGraphicsAndroid2D.java b/core-androidx/src/processing/a2d/PGraphicsAndroid2D.java new file mode 100644 index 000000000..53df26eb2 --- /dev/null +++ b/core-androidx/src/processing/a2d/PGraphicsAndroid2D.java @@ -0,0 +1,2386 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2005-12 Ben Fry and Casey Reas + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.a2d; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.nio.ByteBuffer; +import java.io.InputStream; +import java.util.zip.GZIPInputStream; + +import processing.android.AppComponent; +import processing.core.PApplet; +import processing.core.PFont; +import processing.core.PGraphics; +import processing.core.PImage; +import processing.core.PMatrix; +import processing.core.PMatrix2D; +import processing.core.PMatrix3D; +import processing.core.PShape; +import processing.core.PShapeSVG; +import processing.core.PSurface; +import processing.data.XML; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.ActivityManager; +import android.app.ActivityManager.MemoryInfo; +import android.content.Context; +import android.graphics.*; +import android.graphics.Bitmap.Config; +import android.graphics.Paint.Style; +import android.os.Build; +import android.os.Environment; +import android.view.SurfaceHolder; + +import static android.os.Environment.isExternalStorageRemovable; + + +/** + * Subclass for PGraphics that implements the graphics API using + * the Android 2D graphics model. Similar tradeoffs to JAVA2D mode + * with the original (desktop) version of Processing. + */ +public class PGraphicsAndroid2D extends PGraphics { + static public boolean useBitmap = true; + + public Canvas canvas; // like g2 for PGraphicsJava2D + + /// break the shape at the next vertex (next vertex() call is a moveto()) + boolean breakShape; + + /// coordinates for internal curve calculation + float[] curveCoordX; + float[] curveCoordY; + float[] curveDrawX; + float[] curveDrawY; + + static protected final int MATRIX_STACK_DEPTH = 32; + protected float[][] transformStack; + public PMatrix2D transform; + protected Matrix transformMatrix; + protected float[] transformArray; + int transformCount; + +// Line2D.Float line = new Line2D.Float(); +// Ellipse2D.Float ellipse = new Ellipse2D.Float(); +// Rectangle2D.Float rect = new Rectangle2D.Float(); +// Arc2D.Float arc = new Arc2D.Float(); + /** + * The temporary path object that does most of the drawing work. If there are + * any points in the path (meaning that moveto has been called), then + * vertexCount will be 1 (or more). In the POLYGON case, vertexCount is only + * set to 1 after the first point is drawn (to indicate a moveto) and not + * incremented after, since the variable isn't used for POLYGON paths. + */ + Path path; + + /** Temporary rectangle object. */ + RectF rect; + +// protected Color tintColorObject; + +// protected Color fillColorObject; +// public boolean fillGradient; +// public Paint fillGradientObject; + +// protected Color strokeColorObject; +// public boolean strokeGradient; +// public Paint strokeGradientObject; + + Paint fillPaint; + Paint strokePaint; + Paint tintPaint; + + + /** + * Marks when changes to the size have occurred, so that the backing bitmap + * can be recreated. + */ + protected boolean sized; + + /** + * Marks when some changes have occurred, to the surface view. + */ + protected boolean changed; + + ////////////////////////////////////////////////////////////// + + // INTERNAL + + + public PGraphicsAndroid2D() { + transformStack = new float[MATRIX_STACK_DEPTH][6]; + transform = new PMatrix2D(); + transformMatrix = new Matrix(); + transformArray = new float[9]; + + path = new Path(); + rect = new RectF(); + + fillPaint = new Paint(); + fillPaint.setStyle(Style.FILL); + strokePaint = new Paint(); + strokePaint.setStyle(Style.STROKE); + tintPaint = new Paint(Paint.FILTER_BITMAP_FLAG); + } + + + //public void setParent(PApplet parent) + + + //public void setPrimary(boolean primary) + + + //public void setPath(String path) + + @Override + public void surfaceChanged() { + changed = true; + } + + + @Override + public void setSize(int iwidth, int iheight) { + sized = iwidth != width || iheight != height; + super.setSize(iwidth, iheight); + } + + + @Override + public void dispose() { + if (bitmap != null) bitmap.recycle(); + } + + + @Override + public PSurface createSurface(AppComponent component, SurfaceHolder holder, boolean reset) { // ignore + return new PSurfaceAndroid2D(this, component, holder); + } + + + ////////////////////////////////////////////////////////////// + + // FRAME + + @SuppressLint("NewApi") + protected Canvas checkCanvas() { + if ((canvas == null || sized) && (useBitmap || !primaryGraphics)) { + if (bitmap == null || bitmap.getWidth() * bitmap.getHeight() < width * height || + Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + if (bitmap != null) bitmap.recycle(); + bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); + } else { + // reconfigure is only available in API level 19 or higher. + bitmap.reconfigure(width, height, bitmap.getConfig()); + } + canvas = new Canvas(bitmap); + sized = false; + } + restoreSurface(); + return canvas; + } + + + @Override + public void beginDraw() { + canvas = checkCanvas(); + + checkSettings(); + + resetMatrix(); // reset model matrix + + // reset vertices + vertexCount = 0; + } + + + @Override + public void endDraw() { + if (bitmap == null) return; + + if (primaryGraphics) { + SurfaceHolder holder = parent.getSurface().getSurfaceHolder(); + if (holder != null) { + Canvas screen = null; + try { + screen = holder.lockCanvas(null); + if (screen != null) { + screen.drawBitmap(bitmap, new Matrix(), null); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (screen != null) { + try { + holder.unlockCanvasAndPost(screen); + } catch (IllegalStateException ex) { + } catch (IllegalArgumentException ex) { + } + } + } + } + } else { + // TODO this is probably overkill for most tasks... + loadPixels(); + } + + // Marking as modified, and then calling updatePixels() in + // the super class, which just sets the mx1, my1, mx2, my2 + // coordinates of the modified area. This avoids doing the + // full copy of the pixels to the surface in this.updatePixels(). + setModified(); + super.updatePixels(); + } + + + + ////////////////////////////////////////////////////////////// + + // SETTINGS + + + //protected void checkSettings() + + + //protected void defaultSettings() + + + //protected void reapplySettings() + + + + ////////////////////////////////////////////////////////////// + + // HINT + + + //public void hint(int which) + + + + ////////////////////////////////////////////////////////////// + + // SHAPES + + + //public void beginShape(int kind) + + + @Override + public void beginShape(int kind) { + //super.beginShape(kind); + shape = kind; + vertexCount = 0; + curveVertexCount = 0; + + // reset the path, because when mixing curves and straight + // lines, vertexCount will be set back to zero, so vertexCount == 1 + // is no longer a good indicator of whether the shape is new. + // this way, just check to see if gpath is null, and if it isn't + // then just use it to continue the shape. + //path = null; +// path.reset(); +// pathReset = true; + } + + + //public boolean edge(boolean e) + + + //public void normal(float nx, float ny, float nz) { + + + //public void textureMode(int mode) + + + @Override + public void texture(PImage image) { + showMethodWarning("texture"); + } + + + @Override + public void vertex(float x, float y) { + // POLYGON and POINTS are broken out for efficiency + if (shape == POLYGON) { +// if (path == null) { +// path = new Path(); +// path.moveTo(x, y); + //if (pathReset) { + if (vertexCount == 0) { + path.reset(); + path.moveTo(x, y); + vertexCount = 1; +// pathReset = false; + } else if (breakShape) { + path.moveTo(x, y); + breakShape = false; + } else { + path.lineTo(x, y); + } + + // this is way too slow, otherwise what's the point of using beginShape() +// } else if (shape == POINTS) { +// point(x, y); + + } else { + curveVertexCount = 0; + + if (vertexCount == vertices.length) { + float temp[][] = new float[vertexCount<<1][VERTEX_FIELD_COUNT]; + System.arraycopy(vertices, 0, temp, 0, vertexCount); + vertices = temp; + } + // not everyone needs this, but just easier to store rather + // than adding another moving part to the code... + vertices[vertexCount][X] = x; + vertices[vertexCount][Y] = y; + vertexCount++; + + switch (shape) { + + case POINTS: + // store them for later + break; + + case LINES: + if ((vertexCount % 2) == 0) { + line(vertices[vertexCount-2][X], + vertices[vertexCount-2][Y], x, y); + vertexCount = 0; + } + break; + + case LINE_STRIP: + case LINE_LOOP: + if (vertexCount >= 2) { + line(vertices[vertexCount-2][X], + vertices[vertexCount-2][Y], x, y); + } + break; + + case TRIANGLES: + if ((vertexCount % 3) == 0) { + triangle(vertices[vertexCount - 3][X], + vertices[vertexCount - 3][Y], + vertices[vertexCount - 2][X], + vertices[vertexCount - 2][Y], + x, y); + vertexCount = 0; + } + break; + + case TRIANGLE_STRIP: + if (vertexCount >= 3) { + triangle(vertices[vertexCount - 2][X], + vertices[vertexCount - 2][Y], + x, //vertices[vertexCount - 1][X], + y, //vertices[vertexCount - 1][Y], + vertices[vertexCount - 3][X], + vertices[vertexCount - 3][Y]); + } + break; + + case TRIANGLE_FAN: + if (vertexCount >= 3) { + triangle(vertices[0][X], + vertices[0][Y], + vertices[vertexCount - 2][X], + vertices[vertexCount - 2][Y], + x, y); + } + break; + + case QUAD: + case QUADS: + if ((vertexCount % 4) == 0) { + quad(vertices[vertexCount - 4][X], + vertices[vertexCount - 4][Y], + vertices[vertexCount - 3][X], + vertices[vertexCount - 3][Y], + vertices[vertexCount - 2][X], + vertices[vertexCount - 2][Y], + x, y); + vertexCount = 0; + } + break; + + case QUAD_STRIP: + // 0---2---4 + // | | | + // 1---3---5 + if ((vertexCount >= 4) && ((vertexCount % 2) == 0)) { + quad(vertices[vertexCount - 4][X], + vertices[vertexCount - 4][Y], + vertices[vertexCount - 2][X], + vertices[vertexCount - 2][Y], + x, y, + vertices[vertexCount - 3][X], + vertices[vertexCount - 3][Y]); + } + break; + } + } + } + + + @Override + public void vertex(float x, float y, float z) { + showDepthWarningXYZ("vertex"); + } + + + @Override + public void vertex(float x, float y, float u, float v) { + showVariationWarning("vertex(x, y, u, v)"); + } + + + @Override + public void vertex(float x, float y, float z, float u, float v) { + showDepthWarningXYZ("vertex"); + } + + + @Override + public void breakShape() { + breakShape = true; + } + + + @Override + public void endShape(int mode) { + if (shape == POINTS && stroke && vertexCount > 0) { + Matrix m = getMatrixImp(); + if (strokeWeight == 1 && m.isIdentity()) { + if (screenPoint == null) { + screenPoint = new float[2]; + } + for (int i = 0; i < vertexCount; i++) { + screenPoint[0] = vertices[i][X]; + screenPoint[1] = vertices[i][Y]; + m.mapPoints(screenPoint); + set(PApplet.round(screenPoint[0]), PApplet.round(screenPoint[1]), strokeColor); + float x = vertices[i][X]; + float y = vertices[i][Y]; + set(PApplet.round(screenX(x, y)), PApplet.round(screenY(x, y)), strokeColor); + } + } else { + float sw = strokeWeight / 2; + // temporarily use the stroke Paint as a fill + strokePaint.setStyle(Style.FILL); + for (int i = 0; i < vertexCount; i++) { + float x = vertices[i][X]; + float y = vertices[i][Y]; + rect.set(x - sw, y - sw, x + sw, y + sw); + canvas.drawOval(rect, strokePaint); + } + strokePaint.setStyle(Style.STROKE); + } + } else if (shape == POLYGON) { + if (!path.isEmpty()) { + if (mode == CLOSE) { + path.close(); + } + drawPath(); + } + } else if (shape == LINE_LOOP && vertexCount >= 2) { + line(vertices[vertexCount-1][X], + vertices[vertexCount-1][Y], + vertices[0][X], + vertices[0][Y]); + } + shape = 0; + } + + + ////////////////////////////////////////////////////////////// + + // CLIPPING + + + @Override + protected void clipImpl(float x1, float y1, float x2, float y2) { + canvas.clipRect(x1, y1, x2, y2); + } + + + @Override + public void noClip() { + canvas.clipRect(0, 0, width, height, Region.Op.REPLACE); + } + + + ////////////////////////////////////////////////////////////// + + // BEZIER VERTICES + + + @Override + public void bezierVertex(float x1, float y1, + float x2, float y2, + float x3, float y3) { + // will check to make sure that vertexCount > 0 + bezierVertexCheck(); + path.cubicTo(x1, y1, x2, y2, x3, y3); + } + + + @Override + public void bezierVertex(float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4) { + showDepthWarningXYZ("bezierVertex"); + } + + + + ////////////////////////////////////////////////////////////// + + // QUADRATIC BEZIER VERTICES + + + @Override + public void quadraticVertex(float ctrlX, float ctrlY, + float endX, float endY) { + bezierVertexCheck(); + path.quadTo(ctrlX, ctrlY, endX, endY); + } + + + @Override + public void quadraticVertex(float x2, float y2, float z2, + float x4, float y4, float z4) { + showDepthWarningXYZ("quadVertex"); + } + + + + ////////////////////////////////////////////////////////////// + + // CURVE VERTICES + + + @Override + protected void curveVertexCheck() { + super.curveVertexCheck(); + + if (curveCoordX == null) { + curveCoordX = new float[4]; + curveCoordY = new float[4]; + curveDrawX = new float[4]; + curveDrawY = new float[4]; + } + } + + + @Override + protected void curveVertexSegment(float x1, float y1, + float x2, float y2, + float x3, float y3, + float x4, float y4) { + curveCoordX[0] = x1; + curveCoordY[0] = y1; + + curveCoordX[1] = x2; + curveCoordY[1] = y2; + + curveCoordX[2] = x3; + curveCoordY[2] = y3; + + curveCoordX[3] = x4; + curveCoordY[3] = y4; + + curveToBezierMatrix.mult(curveCoordX, curveDrawX); + curveToBezierMatrix.mult(curveCoordY, curveDrawY); + + // since the paths are continuous, + // only the first point needs the actual moveto + if (vertexCount == 0) { +// if (path == null) { +// path = new Path(); + path.moveTo(curveDrawX[0], curveDrawY[0]); + vertexCount = 1; + } + + path.cubicTo(curveDrawX[1], curveDrawY[1], + curveDrawX[2], curveDrawY[2], + curveDrawX[3], curveDrawY[3]); + } + + + @Override + public void curveVertex(float x, float y, float z) { + showDepthWarningXYZ("curveVertex"); + } + + + + ////////////////////////////////////////////////////////////// + + // RENDERER + + + //public void flush() + + + + ////////////////////////////////////////////////////////////// + + // POINT, LINE, TRIANGLE, QUAD + + + @Override + public void point(float x, float y) { + // this is a slow function to call on its own anyway. + // most people will use set(). + beginShape(POINTS); + vertex(x, y); + endShape(); +// if (strokeWeight > 1) { +// //line(x, y, x + EPSILON, y + EPSILON); +// float sw = strokeWeight / 2; +// rect.set(x - sw, y - sw, x + sw, y + sw); +// strokePaint.setStyle(Style.FILL); +// canvas.drawOval(rect, strokePaint); +// strokePaint.setStyle(Style.STROKE); +// } else { +// // TODO this isn't accurate, really we need to +// set(PApplet.round(screenX(x, y)), PApplet.round(screenY(x, y)), strokeColor); +// } + } + + + @Override + public void line(float x1, float y1, float x2, float y2) { +// line.setLine(x1, y1, x2, y2); +// strokeShape(line); + if (stroke) { + canvas.drawLine(x1, y1, x2, y2, strokePaint); + } + } + + + @Override + public void triangle(float x1, float y1, float x2, float y2, + float x3, float y3) { + path.reset(); + path.moveTo(x1, y1); + path.lineTo(x2, y2); + path.lineTo(x3, y3); + path.close(); + drawPath(); + } + + + @Override + public void quad(float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4) { + path.reset(); + path.moveTo(x1, y1); + path.lineTo(x2, y2); + path.lineTo(x3, y3); + path.lineTo(x4, y4); + path.close(); + drawPath(); + } + + + + ////////////////////////////////////////////////////////////// + + // RECT + + + //public void rectMode(int mode) + + + //public void rect(float a, float b, float c, float d) + + + @Override + protected void rectImpl(float x1, float y1, float x2, float y2) { +// rect.setFrame(x1, y1, x2-x1, y2-y1); +// drawShape(rect); + //rect.set(x1, y1, x2, y2); + if (fill) { + canvas.drawRect(x1, y1, x2, y2, fillPaint); + } + if (stroke) { + canvas.drawRect(x1, y1, x2, y2, strokePaint); + } + } + + + + ////////////////////////////////////////////////////////////// + + // ELLIPSE + + + //public void ellipseMode(int mode) + + + //public void ellipse(float a, float b, float c, float d) + + + @Override + protected void ellipseImpl(float x, float y, float w, float h) { +// ellipse.setFrame(x, y, w, h); +// drawShape(ellipse); + rect.set(x, y, x+w, y+h); + if (fill) { + canvas.drawOval(rect, fillPaint); + } + if (stroke) { + canvas.drawOval(rect, strokePaint); + } + } + + + + ////////////////////////////////////////////////////////////// + + // ARC + + + //public void arc(float a, float b, float c, float d, + // float start, float stop) + + + @Override + protected void arcImpl(float x, float y, float w, float h, + float start, float stop, int mode) { + // 0 to 90 in java would be 0 to -90 for p5 renderer + // but that won't work, so -90 to 0? + + if (stop - start >= TWO_PI) { + ellipseImpl(x, y, w, h); + + } else { + // Android agrees with us, so don't set start/stop negative like Java 2D + start = start * RAD_TO_DEG; + stop = stop * RAD_TO_DEG; + + // ok to do this because already checked for NaN + while (start < 0) { + start += 360; + stop += 360; + } + if (start > stop) { + float temp = start; + start = stop; + stop = temp; + } + + float sweep = stop - start; + rect.set(x, y, x+w, y+h); + + if (mode == 0) { + if (fill) { + canvas.drawArc(rect, start, sweep, true, fillPaint); + } + if (stroke) { + canvas.drawArc(rect, start, sweep, false, strokePaint); + } + } else if (mode == OPEN) { + if (fill) { + // Android does not support stroke and fill with different color + // after drawing the arc,draw the arc with Paint.Style.Stroke style + // again + canvas.drawArc(rect, start, sweep, false, fillPaint); + canvas.drawArc(rect, start, sweep, false, strokePaint); + } + if (stroke) { + canvas.drawArc(rect, start, sweep, false, strokePaint); + } + } else if (mode == CHORD) { + // Draw an extra line between start angle point and end point to + // achieve the chord + float endAngle = start + sweep; + float halfRectWidth = rect.width()/2; + float halfRectHeight = rect.height()/2; + float centerX = rect.centerX(); + float centerY = rect.centerY(); + + float startX = (float) (halfRectWidth* Math.cos(Math.toRadians(start))) + centerX; + float startY = (float) (halfRectHeight * Math.sin(Math.toRadians(start))) + centerY; + float endX = (float) (halfRectWidth * Math.cos(Math.toRadians(endAngle))) + centerX; + float endY = (float) (halfRectHeight * Math.sin(Math.toRadians(endAngle))) + centerY; + + if (fill) { + // draw the fill arc + canvas.drawArc(rect,start,sweep,false,fillPaint); + // draw the arc round border + canvas.drawArc(rect,start,sweep,false,strokePaint); + // draw the straight border + canvas.drawLine(startX,startY,endX,endY,strokePaint); + } + if (stroke) { + // draw the arc + canvas.drawArc(rect,start,sweep,false,strokePaint); + // draw the straight border + canvas.drawLine(startX,startY,endX,endY,strokePaint); + } + } else if (mode == PIE) { + if (fill) { + canvas.drawArc(rect, start, sweep, true, fillPaint); + } + if (stroke) { + canvas.drawArc(rect, start, sweep, true, strokePaint); + } + } + } + } + + + + ////////////////////////////////////////////////////////////// + + // JAVA2D SHAPE/PATH HANDLING + + +// protected void fillShape(Shape s) { +// if (fillGradient) { +// canvas.setPaint(fillGradientObject); +// canvas.fill(s); +// } else if (fill) { +// canvas.setColor(fillColorObject); +// canvas.fill(s); +// } +// } + + +// protected void strokeShape(Shape s) { +// if (strokeGradient) { +// canvas.setPaint(strokeGradientObject); +// canvas.draw(s); +// } else if (stroke) { +// canvas.setColor(strokeColorObject); +// canvas.draw(s); +// } +// } + + +// protected void drawShape(Shape s) { +// if (fillGradient) { +// canvas.setPaint(fillGradientObject); +// canvas.fill(s); +// } else if (fill) { +// canvas.setColor(fillColorObject); +// canvas.fill(s); +// } +// if (strokeGradient) { +// canvas.setPaint(strokeGradientObject); +// canvas.draw(s); +// } else if (stroke) { +// canvas.setColor(strokeColorObject); +// canvas.draw(s); +// } +// } + + + protected void drawPath() { + if (fill) { + canvas.drawPath(path, fillPaint); + } + if (stroke) { + canvas.drawPath(path, strokePaint); + } + } + + + + ////////////////////////////////////////////////////////////// + + // BOX + + + //public void box(float size) + + + @Override + public void box(float w, float h, float d) { + showMethodWarning("box"); + } + + + + ////////////////////////////////////////////////////////////// + + // SPHERE + + + //public void sphereDetail(int res) + + + //public void sphereDetail(int ures, int vres) + + + @Override + public void sphere(float r) { + showMethodWarning("sphere"); + } + + + + ////////////////////////////////////////////////////////////// + + // BEZIER + + + //public float bezierPoint(float a, float b, float c, float d, float t) + + + //public float bezierTangent(float a, float b, float c, float d, float t) + + + //protected void bezierInitCheck() + + + //protected void bezierInit() + + + /** Ignored (not needed) in Java 2D. */ + @Override + public void bezierDetail(int detail) { + } + + + //public void bezier(float x1, float y1, + // float x2, float y2, + // float x3, float y3, + // float x4, float y4) + + + //public void bezier(float x1, float y1, float z1, + // float x2, float y2, float z2, + // float x3, float y3, float z3, + // float x4, float y4, float z4) + + + + ////////////////////////////////////////////////////////////// + + // CURVE + + + //public float curvePoint(float a, float b, float c, float d, float t) + + + //public float curveTangent(float a, float b, float c, float d, float t) + + + /** Ignored (not needed) in Java 2D. */ + @Override + public void curveDetail(int detail) { + } + + //public void curveTightness(float tightness) + + + //protected void curveInitCheck() + + + //protected void curveInit() + + + //public void curve(float x1, float y1, + // float x2, float y2, + // float x3, float y3, + // float x4, float y4) + + + //public void curve(float x1, float y1, float z1, + // float x2, float y2, float z2, + // float x3, float y3, float z3, + // float x4, float y4, float z4) + + + + ////////////////////////////////////////////////////////////// + + // SMOOTH + + + @Override + public void smooth(int quality) { // ignore + super.smooth(quality); +// smooth = true; +// canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, +// RenderingHints.VALUE_ANTIALIAS_ON); +// canvas.setRenderingHint(RenderingHints.KEY_INTERPOLATION, +// RenderingHints.VALUE_INTERPOLATION_BICUBIC); + strokePaint.setAntiAlias(true); + fillPaint.setAntiAlias(true); + } + + + @Override + public void noSmooth() { + super.noSmooth(); +// smooth = false; +// canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, +// RenderingHints.VALUE_ANTIALIAS_OFF); +// canvas.setRenderingHint(RenderingHints.KEY_INTERPOLATION, +// RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + strokePaint.setAntiAlias(false); + fillPaint.setAntiAlias(false); + } + + + + ////////////////////////////////////////////////////////////// + + // IMAGE + + + //public void imageMode(int mode) + + + //public void image(PImage image, float x, float y) + + + //public void image(PImage image, float x, float y, float c, float d) + + + //public void image(PImage image, + // float a, float b, float c, float d, + // int u1, int v1, int u2, int v2) + + + Rect imageImplSrcRect; + RectF imageImplDstRect; + //android.widget.ImageView imv; + + /** + * Handle renderer-specific image drawing. + */ + @Override + protected void imageImpl(PImage src, + float x1, float y1, float x2, float y2, + int u1, int v1, int u2, int v2) { + Bitmap bitmap = (Bitmap)src.getNative(); + + if (bitmap != null && bitmap.isRecycled()) { + // Let's make sure it is recreated + bitmap = null; + } + + if (bitmap == null && src.format == ALPHA) { + // create an alpha bitmap for this feller + bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888); + int[] px = new int[src.pixels.length]; + for (int i = 0; i < px.length; i++) { + px[i] = src.pixels[i] << 24 | 0xFFFFFF; + } + bitmap.setPixels(px, 0, src.width, 0, 0, src.width, src.height); + modified = false; + src.setNative(bitmap); + } + + // this version's not usable because it doesn't allow you to set output w/h +// if (src.bitmap == null) { // format is ARGB or RGB +// int offset = v1*src.width + u1; +// canvas.drawBitmap(src.pixels, offset, src.width, +// x1, y1, u2-u1, v2-v1, +// src.format == ARGB, tint ? tintPaint : null); +// } else { + + if (bitmap == null || + src.width != bitmap.getWidth() || + src.height != bitmap.getHeight()) { + if (bitmap != null) bitmap.recycle(); + bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888); + modified = true; + src.setNative(bitmap); + } + + if (src.isModified()) { + //System.out.println("mutable, recycled = " + who.bitmap.isMutable() + ", " + who.bitmap.isRecycled()); + if (!bitmap.isMutable()) { + bitmap.recycle(); + bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888); + src.setNative(bitmap); + } + if (src.pixels != null) { + bitmap.setPixels(src.pixels, 0, src.width, 0, 0, src.width, src.height); + } + src.setModified(false); + } + + if (imageImplSrcRect == null) { + imageImplSrcRect = new Rect(u1, v1, u2, v2); + imageImplDstRect = new RectF(x1, y1, x2, y2); + } else { + imageImplSrcRect.set(u1, v1, u2, v2); + imageImplDstRect.set(x1, y1, x2, y2); + } + //canvas.drawBitmap(who.bitmap, imageImplSrcRect, imageImplDstRect, tint ? tintPaint : null); + //System.out.println(PApplet.hex(fillPaint.getColor())); + //canvas.drawBitmap(who.bitmap, imageImplSrcRect, imageImplDstRect, fillPaint); + // System.out.println("drawing lower, tint = " + tint + " " + PApplet.hex(tintPaint.getColor())); + canvas.drawBitmap(bitmap, imageImplSrcRect, imageImplDstRect, tint ? tintPaint : null); + + // If the OS things the memory is low, then recycles bitmaps automatically... + // but I don't think it is particularly efficient, as the bitmaps are stored + // in native heap for Android 10 and older. + MemoryInfo mi = new MemoryInfo(); + Activity activity = parent.getSurface().getActivity(); + if (activity == null) return; + ActivityManager activityManager = (ActivityManager) activity.getSystemService(android.content.Context.ACTIVITY_SERVICE); + activityManager.getMemoryInfo(mi); + if (mi.lowMemory) { + bitmap.recycle(); + src.setNative(null); + } + } + + + + ////////////////////////////////////////////////////////////// + + // SHAPE + + + //public void shapeMode(int mode) + + + //public void shape(PShape shape) + + + //public void shape(PShape shape, float x, float y) + + + //public void shape(PShape shape, float x, float y, float c, float d) + + + ////////////////////////////////////////////////////////////// + + // SHAPE I/O + + + @Override + public PShape loadShape(String filename) { + String extension = PApplet.getExtension(filename); + + PShapeSVG svg = null; + + if (extension.equals("svg")) { + svg = new PShapeSVG(parent.loadXML(filename)); + + } else if (extension.equals("svgz")) { + try { + InputStream input = new GZIPInputStream(parent.createInput(filename)); + XML xml = new XML(input); + svg = new PShapeSVG(xml); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + PGraphics.showWarning("Unsupported format"); + } + + return svg; + } + + + ////////////////////////////////////////////////////////////// + + // TEXT ATTRIBTUES + + + //public void textAlign(int align) + + + //public void textAlign(int alignX, int alignY) + + +// public float textAscent() { +// if (textFont == null) { +// defaultFontOrDeath("textAscent"); +// } +//// Font font = textFont.getFont(); +// Typeface font = textFont.getTypeface(); +// if (font == null) { +// return super.textAscent(); +// } +// return fillPaint.ascent(); +// } + + +// public float textDescent() { +// if (textFont == null) { +// defaultFontOrDeath("textDescent"); +// } +//// Font font = textFont.getFont(); +// Typeface font = textFont.getTypeface(); +// if (font == null) { +// return super.textDescent(); +// } +// return fillPaint.descent(); +// } + + + @Override + public void textFont(PFont which) { + super.textFont(which); + fillPaint.setTypeface((Typeface) which.getNative()); + fillPaint.setTextSize(which.getDefaultSize()); + } + + + @Override + public void textFont(PFont which, float size) { + super.textFont(which, size); + fillPaint.setTypeface((Typeface) which.getNative()); + fillPaint.setTextSize(size); + } + + + //public void textLeading(float leading) + + + //public void textMode(int mode) + + + @Override + protected boolean textModeCheck(int mode) { + return mode == MODEL; + } + + + /** + * Same as parent, but override for native version of the font. + *

+ * Also gets called by textFont, so the metrics + * will get recorded properly. + */ + @Override + public void textSize(float size) { + if (textFont == null) { + defaultFontOrDeath("textSize", size); + } + + Typeface font = (Typeface) textFont.getNative(); + if (font != null) { + fillPaint.setTextSize(size); + } + + handleTextSize(size); + } + + + protected void beginTextScreenMode() { + loadPixels(); + } + + protected void endTextScreenMode() { + updatePixels(); + } + + //public float textWidth(char c) + + + //public float textWidth(String str) + + + @Override + protected float textWidthImpl(char buffer[], int start, int stop) { +// Font font = textFont.getFont(); + Typeface font = (Typeface) textFont.getNative(); + if (font == null) { + return super.textWidthImpl(buffer, start, stop); + } + // maybe should use one of the newer/fancier functions for this? + int length = stop - start; +// FontMetrics metrics = canvas.getFontMetrics(font); +// return metrics.charsWidth(buffer, start, length); + return fillPaint.measureText(buffer, start, length); + } + + + + ////////////////////////////////////////////////////////////// + + // TEXT + + // None of the variations of text() are overridden from PGraphics. + + + + ////////////////////////////////////////////////////////////// + + // TEXT IMPL + + + //protected void textLineAlignImpl(char buffer[], int start, int stop, + // float x, float y) + + + @Override + protected void textLineImpl(char buffer[], int start, int stop, + float x, float y) { + Typeface font = (Typeface) textFont.getNative(); + if (font == null) { + showWarning("Inefficient font rendering: use createFont() with a TTF/OTF instead of loadFont()."); + //new Exception().printStackTrace(System.out); + super.textLineImpl(buffer, start, stop, x, y); + return; + } + + /* + // save the current setting for text smoothing. note that this is + // different from the smooth() function, because the font smoothing + // is controlled when the font is created, not now as it's drawn. + // fixed a bug in 0116 that handled this incorrectly. + Object textAntialias = + g2.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING); + + // override the current text smoothing setting based on the font + // (don't change the global smoothing settings) + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + textFont.smooth ? + RenderingHints.VALUE_ANTIALIAS_ON : + RenderingHints.VALUE_ANTIALIAS_OFF); + */ + +// Object antialias = +// canvas.getRenderingHint(RenderingHints.KEY_ANTIALIASING); +// if (antialias == null) { +// // if smooth() and noSmooth() not called, this will be null (0120) +// antialias = RenderingHints.VALUE_ANTIALIAS_DEFAULT; +// } + + // override the current smoothing setting based on the font + // also changes global setting for antialiasing, but this is because it's + // not possible to enable/disable them independently in some situations. +// canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, +// textFont.smooth ? +// RenderingHints.VALUE_ANTIALIAS_ON : +// RenderingHints.VALUE_ANTIALIAS_OFF); + fillPaint.setAntiAlias(textFont.isSmooth()); + + //System.out.println("setting frac metrics"); + //g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + // RenderingHints.VALUE_FRACTIONALMETRICS_ON); + +// canvas.setColor(fillColorObject); + int length = stop - start; +// canvas.drawChars(buffer, start, length, (int) (x + 0.5f), (int) (y + 0.5f)); + canvas.drawText(buffer, start, length, x, y, fillPaint); + + // return to previous smoothing state if it was changed +// canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialias); + fillPaint.setAntiAlias(0 < smooth); + +// textX = x + textWidthImpl(buffer, start, stop); +// textY = y; +// textZ = 0; // this will get set by the caller if non-zero + } + + + + ////////////////////////////////////////////////////////////// + + // MATRIX STACK + + + @Override + public void pushMatrix() { + if (transformCount == transformStack.length) { + throw new RuntimeException("pushMatrix() cannot use push more than " + + transformStack.length + " times"); + } + transform.get(transformStack[transformCount]); + transformCount++; + +// canvas.save(); + } + + + @Override + public void popMatrix() { + if (transformCount == 0) { + throw new RuntimeException("missing a popMatrix() " + + "to go with that pushMatrix()"); + } + transformCount--; + transform.set(transformStack[transformCount]); + updateTransformMatrix(); + + // Using canvas.restore() here and canvas.save() in popMatrix() and should achieve + // the same effect as setting copying transform into transformMatrix with updateTransformMatrix() + // and setting it below, although it has been reported that with the later approach, a push/pop + // would not result in the initial matrix state: + // https://github.com/processing/processing-android/issues/445 + // However, cannot find + canvas.setMatrix(transformMatrix); +// canvas.restore(); + } + + + ////////////////////////////////////////////////////////////// + + // MATRIX TRANSFORMS + + + @Override + public void translate(float tx, float ty) { + transform.translate(tx, ty); + canvas.translate(tx, ty); + } + + + @Override + public void rotate(float angle) { + transform.rotate(angle); + canvas.rotate(angle * RAD_TO_DEG); + } + + + @Override + public void rotateX(float angle) { + showDepthWarning("rotateX"); + } + + + @Override + public void rotateY(float angle) { + showDepthWarning("rotateY"); + } + + + @Override + public void rotateZ(float angle) { + showDepthWarning("rotateZ"); + } + + + @Override + public void rotate(float angle, float vx, float vy, float vz) { + showVariationWarning("rotate"); + } + + + @Override + public void scale(float s) { + transform.scale(s, s); + canvas.scale(s, s); + } + + + @Override + public void scale(float sx, float sy) { + transform.scale(sx, sy); + canvas.scale(sx, sy); + } + + + @Override + public void scale(float sx, float sy, float sz) { + showDepthWarningXYZ("scale"); + } + + + @Override + public void shearX(float angle) { + float t = (float) Math.tan(angle); + transform.apply(1, t, 0, 0, 1, 0); + canvas.skew(t, 0); + } + + + @Override + public void shearY(float angle) { + float t = (float) Math.tan(angle); + transform.apply(1, 0, 0, t, 1, 0); + canvas.skew(0, t); + } + + + ////////////////////////////////////////////////////////////// + + // MATRIX MORE + + + @Override + public void resetMatrix() { + transform.reset(); + canvas.setMatrix(null); + } + + + //public void applyMatrix(PMatrix2D source) + + + @Override + public void applyMatrix(float n00, float n01, float n02, + float n10, float n11, float n12) { + transform.apply(n00, n01, n02, n10, n11, n12); + updateTransformMatrix(); + canvas.concat(transformMatrix); + } + + + //public void applyMatrix(PMatrix3D source) + + + @Override + public void applyMatrix(float n00, float n01, float n02, float n03, + float n10, float n11, float n12, float n13, + float n20, float n21, float n22, float n23, + float n30, float n31, float n32, float n33) { + showVariationWarning("applyMatrix"); + } + + + + ////////////////////////////////////////////////////////////// + + // MATRIX GET/SET + + + @Override + public PMatrix getMatrix() { + return getMatrix((PMatrix2D) null); + } + + + @Override + public PMatrix2D getMatrix(PMatrix2D target) { + if (target == null) { + target = new PMatrix2D(); + } + target.set(transform); + return target; + } + + + @Override + public PMatrix3D getMatrix(PMatrix3D target) { + showVariationWarning("getMatrix"); + return target; + } + + + //public void setMatrix(PMatrix source) + + + @Override + public void setMatrix(PMatrix2D source) { + transform.set(source); + updateTransformMatrix(); + canvas.setMatrix(transformMatrix); + } + + + @Override + public void setMatrix(PMatrix3D source) { + showVariationWarning("setMatrix"); + } + + + @Override + public void printMatrix() { + getMatrix((PMatrix2D) null).print(); + } + + + protected Matrix getMatrixImp() { + Matrix m = new Matrix(); + updateTransformMatrix(); + m.set(transformMatrix); + return m; +// return canvas.getMatrix(); + } + + + public void updateTransformMatrix() { + transformArray[0] = transform.m00; + transformArray[1] = transform.m01; + transformArray[2] = transform.m02; + transformArray[3] = transform.m10; + transformArray[4] = transform.m11; + transformArray[5] = transform.m12; + transformArray[6] = 0; + transformArray[7] = 0; + transformArray[8] = 1; + transformMatrix.setValues(transformArray); + } + + + ////////////////////////////////////////////////////////////// + + // CAMERA and PROJECTION + + // Inherit the plaintive warnings from PGraphics + + + //public void beginCamera() + //public void endCamera() + //public void camera() + //public void camera(float eyeX, float eyeY, float eyeZ, + // float centerX, float centerY, float centerZ, + // float upX, float upY, float upZ) + //public void printCamera() + + //public void ortho() + //public void ortho(float left, float right, + // float bottom, float top, + // float near, float far) + //public void perspective() + //public void perspective(float fov, float aspect, float near, float far) + //public void frustum(float left, float right, + // float bottom, float top, + // float near, float far) + //public void printProjection() + + + + ////////////////////////////////////////////////////////////// + + // SCREEN and MODEL transforms + + float[] screenPoint; + + @Override + public float screenX(float x, float y) { + if (screenPoint == null) { + screenPoint = new float[2]; + } + screenPoint[0] = x; + screenPoint[1] = y; + getMatrixImp().mapPoints(screenPoint); + return screenPoint[0]; + } + + + @Override + public float screenY(float x, float y) { + if (screenPoint == null) { + screenPoint = new float[2]; + } + screenPoint[0] = x; + screenPoint[1] = y; + getMatrixImp().mapPoints(screenPoint); + return screenPoint[1]; + } + + + @Override + public float screenX(float x, float y, float z) { + showDepthWarningXYZ("screenX"); + return 0; + } + + + @Override + public float screenY(float x, float y, float z) { + showDepthWarningXYZ("screenY"); + return 0; + } + + + @Override + public float screenZ(float x, float y, float z) { + showDepthWarningXYZ("screenZ"); + return 0; + } + + + //public float modelX(float x, float y, float z) + + + //public float modelY(float x, float y, float z) + + + //public float modelZ(float x, float y, float z) + + + + ////////////////////////////////////////////////////////////// + + // STYLE + + // pushStyle(), popStyle(), style() and getStyle() inherited. + + + + ////////////////////////////////////////////////////////////// + + // STROKE CAP/JOIN/WEIGHT + + + @Override + public void strokeCap(int cap) { + super.strokeCap(cap); + + if (strokeCap == ROUND) { + strokePaint.setStrokeCap(Paint.Cap.ROUND); + } else if (strokeCap == PROJECT) { + strokePaint.setStrokeCap(Paint.Cap.SQUARE); + } else { + strokePaint.setStrokeCap(Paint.Cap.BUTT); + } + } + + + @Override + public void strokeJoin(int join) { + super.strokeJoin(join); + + if (strokeJoin == MITER) { + strokePaint.setStrokeJoin(Paint.Join.MITER); + } else if (strokeJoin == ROUND) { + strokePaint.setStrokeJoin(Paint.Join.ROUND); + } else { + strokePaint.setStrokeJoin(Paint.Join.BEVEL); + } + } + + + @Override + public void strokeWeight(float weight) { + super.strokeWeight(weight); + strokePaint.setStrokeWidth(weight); + } + + + + ////////////////////////////////////////////////////////////// + + // STROKE + + // noStroke() and stroke() inherited from PGraphics. + + + @Override + protected void strokeFromCalc() { + super.strokeFromCalc(); +// strokeColorObject = new Color(strokeColor, true); + strokePaint.setColor(strokeColor); +// strokeGradient = false; + strokePaint.setShader(null); + } + + + + ////////////////////////////////////////////////////////////// + + // TINT + + // noTint() and tint() inherited from PGraphics. + + + @Override + protected void tintFromCalc() { + super.tintFromCalc(); + tintPaint.setColorFilter(new PorterDuffColorFilter(tintColor, PorterDuff.Mode.MULTIPLY)); + } + + + + ////////////////////////////////////////////////////////////// + + // FILL + + // noFill() and fill() inherited from PGraphics. + + + @Override + protected void fillFromCalc() { + super.fillFromCalc(); +// fillColorObject = new Color(fillColor, true); + fillPaint.setColor(fillColor); +// fillGradient = false; + fillPaint.setShader(null); + } + + + + ////////////////////////////////////////////////////////////// + + // MATERIAL PROPERTIES + + + //public void ambient(int rgb) + //public void ambient(float gray) + //public void ambient(float x, float y, float z) + //protected void ambientFromCalc() + //public void specular(int rgb) + //public void specular(float gray) + //public void specular(float x, float y, float z) + //protected void specularFromCalc() + //public void shininess(float shine) + //public void emissive(int rgb) + //public void emissive(float gray) + //public void emissive(float x, float y, float z ) + //protected void emissiveFromCalc() + + + + ////////////////////////////////////////////////////////////// + + // LIGHTS + + + //public void lights() + //public void noLights() + //public void ambientLight(float red, float green, float blue) + //public void ambientLight(float red, float green, float blue, + // float x, float y, float z) + //public void directionalLight(float red, float green, float blue, + // float nx, float ny, float nz) + //public void pointLight(float red, float green, float blue, + // float x, float y, float z) + //public void spotLight(float red, float green, float blue, + // float x, float y, float z, + // float nx, float ny, float nz, + // float angle, float concentration) + //public void lightFalloff(float constant, float linear, float quadratic) + //public void lightSpecular(float x, float y, float z) + //protected void lightPosition(int num, float x, float y, float z) + //protected void lightDirection(int num, float x, float y, float z) + + + + ////////////////////////////////////////////////////////////// + + // BACKGROUND + + // background() methods inherited from PGraphics, along with the + // PImage version of backgroundImpl(), since it just calls set(). + + + //public void backgroundImpl(PImage image) + + +// int[] clearPixels; + + @Override + public void backgroundImpl() { + canvas.drawColor(backgroundColor); + +// if (backgroundAlpha) { +// WritableRaster raster = ((BufferedImage) image).getRaster(); +// if ((clearPixels == null) || (clearPixels.length < width)) { +// clearPixels = new int[width]; +// } +// java.util.Arrays.fill(clearPixels, backgroundColor); +// for (int i = 0; i < height; i++) { +// raster.setDataElements(0, i, width, 1, clearPixels); +// } +// } else { +// //new Exception().printStackTrace(System.out); +// // in case people do transformations before background(), +// // need to handle this with a push/reset/pop +// pushMatrix(); +// resetMatrix(); +// canvas.setColor(new Color(backgroundColor)); //, backgroundAlpha)); +// canvas.fillRect(0, 0, width, height); +// popMatrix(); +// } + } + + + + ////////////////////////////////////////////////////////////// + + // COLOR MODE + + // All colorMode() variations are inherited from PGraphics. + + + + ////////////////////////////////////////////////////////////// + + // COLOR CALC + + // colorCalc() and colorCalcARGB() inherited from PGraphics. + + + + ////////////////////////////////////////////////////////////// + + // COLOR DATATYPE STUFFING + + // final color() variations inherited. + + + + ////////////////////////////////////////////////////////////// + + // COLOR DATATYPE EXTRACTION + + // final methods alpha, red, green, blue, + // hue, saturation, and brightness all inherited. + + + + ////////////////////////////////////////////////////////////// + + // COLOR DATATYPE INTERPOLATION + + // both lerpColor variants inherited. + + + + ////////////////////////////////////////////////////////////// + + // BEGIN/END RAW + + + @Override + public void beginRaw(PGraphics recorderRaw) { + showMethodWarning("beginRaw"); + } + + + @Override + public void endRaw() { + showMethodWarning("endRaw"); + } + + + + ////////////////////////////////////////////////////////////// + + // WARNINGS and EXCEPTIONS + + // showWarning and showException inherited. + + + + ////////////////////////////////////////////////////////////// + + // RENDERER SUPPORT QUERIES + + + //public boolean displayable() // true + + + //public boolean is2D() // true + + + //public boolean is3D() // false + + + + ////////////////////////////////////////////////////////////// + + // PIMAGE METHODS + + + // getImage, setCache, getCache, removeCache, isModified, setModified + + + @Override + public void loadPixels() { + if (bitmap == null) { + throw new RuntimeException("The pixels array is not available in this " + + "renderer withouth a backing bitmap"); + } + + if ((pixels == null) || (pixels.length != width * height)) { + pixels = new int[width * height]; + } +// WritableRaster raster = ((BufferedImage) image).getRaster(); +// raster.getDataElements(0, 0, width, height, pixels); + bitmap.getPixels(pixels, 0, width, 0, 0, width, height); + } + + + /** + * Update the pixels[] buffer to the PGraphics image. + *

+ * Unlike in PImage, where updatePixels() only requests that the + * update happens, in PGraphicsJava2D, this will happen immediately. + */ + @Override + public void updatePixels() { + if (bitmap == null) { + throw new RuntimeException("The pixels array is not available in this " + + "renderer withouth a backing bitmap"); + } + +// WritableRaster raster = ((BufferedImage) image).getRaster(); +// raster.setDataElements(0, 0, width, height, pixels); + bitmap.setPixels(pixels, 0, width, 0, 0, width, height); + } + + + /** + * Update the pixels[] buffer to the PGraphics image. + *

+ * Unlike in PImage, where updatePixels() only requests that the + * update happens, in PGraphicsJava2D, this will happen immediately. + */ + @Override + public void updatePixels(int x, int y, int c, int d) { + //if ((x == 0) && (y == 0) && (c == width) && (d == height)) { + if ((x != 0) || (y != 0) || (c != width) || (d != height)) { + // Show a warning message, but continue anyway. + showVariationWarning("updatePixels(x, y, w, h)"); + } + updatePixels(); + } + + + @Override + public void resize(int wide, int high) { + showMethodWarning("resize"); + } + + + @Override + protected void clearState() { + super.clearState(); + if (restoreFilename != null) { + File cacheFile = new File(restoreFilename); + cacheFile.delete(); + } + } + + + @Override + protected void saveState() { + super.saveState(); + + Context context = parent.getContext(); + if (context == null || bitmap == null || parent.getSurface().getComponent().isService()) return; + try { + // Saving current width and height to avoid restoring the screen after a screen rotation + restoreWidth = pixelWidth; + restoreHeight = pixelHeight; + + int size = bitmap.getHeight() * bitmap.getRowBytes(); + ByteBuffer restoreBitmap = ByteBuffer.allocate(size); + bitmap.copyPixelsToBuffer(restoreBitmap); + + // Tries to use external but if not mounted, falls back on internal storage, as shown in + // https://developer.android.com/topic/performance/graphics/cache-bitmap#java + File cacheDir = Environment.MEDIA_MOUNTED == Environment.getExternalStorageState() || !isExternalStorageRemovable() ? + context.getExternalCacheDir() : context.getCacheDir(); + File cacheFile = new File(cacheDir + File.separator + "restore_pixels"); + restoreFilename = cacheFile.getAbsolutePath(); + + FileOutputStream stream = new FileOutputStream(cacheFile); + ObjectOutputStream dout = new ObjectOutputStream(stream); + byte[] array = new byte[size]; + restoreBitmap.rewind(); + restoreBitmap.get(array); + dout.writeObject(array); + dout.flush(); + stream.getFD().sync(); + stream.close(); + } catch (Exception ex) { + PGraphics.showWarning("Could not save screen contents to cache"); + ex.printStackTrace(); + } + } + + + @Override + protected void restoreSurface() { + if (changed) { + changed = false; + if (restoreFilename != null && restoreWidth == pixelWidth && restoreHeight == pixelHeight) { + // Set the counter to 1 so the restore bitmap is drawn in the next frame. + restoreCount = 1; + } + } else if (restoreCount > 0) { + restoreCount--; + if (restoreCount == 0) { + Context context = parent.getContext(); + if (context == null) return; + try { + // Load cached bitmap and draw + File cacheFile = new File(restoreFilename); + FileInputStream inStream = new FileInputStream(cacheFile); + ObjectInputStream din = new ObjectInputStream(inStream); + byte[] array = (byte[]) din.readObject(); + ByteBuffer restoreBitmap = ByteBuffer.wrap(array); + if (restoreBitmap.capacity() == bitmap.getHeight() * bitmap.getRowBytes()) { + restoreBitmap.rewind(); + bitmap.copyPixelsFromBuffer(restoreBitmap); + } + inStream.close(); + cacheFile.delete(); + } catch (Exception ex) { + PGraphics.showWarning("Could not restore screen contents from cache"); + ex.printStackTrace(); + } finally { + restoreFilename = null; + restoreWidth = -1; + restoreHeight = -1; + restoredSurface = true; + } + } + } + super.restoreSurface(); + } + + + ////////////////////////////////////////////////////////////// + + // GET/SET + + + static int getset[] = new int[1]; + + + @Override + public int get(int x, int y) { + if ((bitmap == null) || (x < 0) || (y < 0) || (x >= width) || (y >= height)) return 0; +// WritableRaster raster = ((BufferedImage) image).getRaster(); +// raster.getDataElements(x, y, getset); +// return getset[0]; + return bitmap.getPixel(x, y); + } + + + //public PImage get(int x, int y, int w, int h) + + +// @Override +// public PImage getImpl(int x, int y, int w, int h) { +// PImage output = new PImage(); +// output.width = w; +// output.height = h; +// output.parent = parent; +// +// Bitmap bitsy = Bitmap.createBitmap(bitmap, x, y, w, h); +// // guessing it's more efficient to use Bitmap instead of pixels[] +// //bitsy.getPixels(output.pixels, 0, w, 0, 0, w, h); +// output.bitmap = bitsy; +// +// return output; +// } + + + @Override + public PImage get() { + return get(0, 0, width, height); + } + + + @Override + public void set(int x, int y, int argb) { + if ((bitmap == null) || (x < 0) || (y < 0) || (x >= width) || (y >= height)) return; +// getset[0] = argb; +// WritableRaster raster = ((BufferedImage) image).getRaster(); +// raster.setDataElements(x, y, getset); + bitmap.setPixel(x, y, argb); + } + + + @Override + public void set(int x, int y, PImage src) { + if (src.format == ALPHA) { + // set() doesn't really make sense for an ALPHA image, since it + // directly replaces pixels and does no blending. + throw new RuntimeException("set() not available for ALPHA images"); + } + + Bitmap bitmap = (Bitmap)src.getNative(); + if (bitmap == null) { + bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888); + src.setNative(bitmap); + src.setModified(); + } + if (src.width != bitmap.getWidth() || + src.height != bitmap.getHeight()) { + bitmap.recycle(); + bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888); + src.setNative(bitmap); + src.setModified(); + } + if (src.isModified()) { + if (!bitmap.isMutable()) { + bitmap.recycle(); + bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888); + setNative(bitmap); + } + bitmap.setPixels(src.pixels, 0, src.width, 0, 0, src.width, src.height); + src.setModified(false); + } + // set() happens in screen coordinates, so need to clear the ctm + pushMatrix(); + canvas.setMatrix(null); // set to identity + canvas.drawBitmap(bitmap, x, y, null); + popMatrix(); + } + + + // elaborate but silly version, since android will happily do this work +// private Rect setImplSrcRect; +// private Rect setImplDstRect; +// +// protected void setImpl(int dx, int dy, int sx, int sy, int sw, int sh, +// PImage src) { +// if (setImplSrcRect == null) { +// setImplSrcRect = new Rect(sx, sy, sx+sw, sy+sh); +// setImplDstRect = new Rect(dx, dy, dx+sw, dy+sh); +// } else { +// setImplSrcRect.set(sx, sy, sx+sw, sy+sh); +// setImplDstRect.set(dx, dy, dx+sw, dy+sh); +// } +// // set() happens in screen coordinates, so need to nuke the ctm +// canvas.save(Canvas.MATRIX_SAVE_FLAG); +// canvas.setMatrix(null); // set to identity +// canvas.drawBitmap(src.image, setImplSrcRect, setImplDstRect, null); +// canvas.restore(); +// } + + // PImage version +// int srcOffset = sy * src.width + sx; +// int dstOffset = dy * width + dx; +// +// for (int y = sy; y < sy + sh; y++) { +// System.arraycopy(src.pixels, srcOffset, pixels, dstOffset, sw); +// srcOffset += src.width; +// dstOffset += width; +// } +// updatePixelsImpl(sx, sy, sx+sw, sy+sh); + + // PGraphicsJava2D version +// WritableRaster raster = ((BufferedImage) image).getRaster(); +// if ((sx == 0) && (sy == 0) && (sw == src.width) && (sh == src.height)) { +// raster.setDataElements(dx, dy, src.width, src.height, src.pixels); +// } else { +// // TODO Optimize, incredibly inefficient to reallocate this much memory +// PImage temp = src.get(sx, sy, sw, sh); +// raster.setDataElements(dx, dy, temp.width, temp.height, temp.pixels); +// } + + + + ////////////////////////////////////////////////////////////// + + // MASK + + + @Override + public void mask(int alpha[]) { + showMethodWarning("mask"); + } + + + @Override + public void mask(PImage alpha) { + showMethodWarning("mask"); + } + + + + ////////////////////////////////////////////////////////////// + + // FILTER + + // Because the PImage versions call loadPixels() and + // updatePixels(), no need to override anything here. + + + //public void filter(int kind) + + + //public void filter(int kind, float param) + + + + ////////////////////////////////////////////////////////////// + + // COPY + + + @Override + public void copy(int sx, int sy, int sw, int sh, + int dx, int dy, int dw, int dh) { + if (bitmap == null) { + throw new RuntimeException("The pixels array is not available in this " + + "renderer withouth a backing bitmap"); + } + +// Bitmap bitsy = Bitmap.createBitmap(image, sx, sy, sw, sh); +// rect.set(dx, dy, dx + dw, dy + dh); +// canvas.drawBitmap(bitsy, + rect.set(dx, dy, dx+dw, dy+dh); + Rect src = new Rect(sx, sy, sx+sw, sy+sh); + canvas.drawBitmap(bitmap, src, rect, null); + +// if ((sw != dw) || (sh != dh)) { +// // use slow version if changing size +// copy(this, sx, sy, sw, sh, dx, dy, dw, dh); +// +// } else { +// dx = dx - sx; // java2d's "dx" is the delta, not dest +// dy = dy - sy; +// canvas.copyArea(sx, sy, sw, sh, dx, dy); +// } + } + + +// public void copy(PImage src, +// int sx1, int sy1, int sx2, int sy2, +// int dx1, int dy1, int dx2, int dy2) { +// loadPixels(); +// super.copy(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); +// updatePixels(); +// } + + + + ////////////////////////////////////////////////////////////// + + // BLEND + + +// static public int blendColor(int c1, int c2, int mode) + + +// public void blend(int sx, int sy, int sw, int sh, +// int dx, int dy, int dw, int dh, int mode) + + +// public void blend(PImage src, +// int sx, int sy, int sw, int sh, +// int dx, int dy, int dw, int dh, int mode) + + + + ////////////////////////////////////////////////////////////// + + // SAVE + + +// public void save(String filename) { +// loadPixels(); +// super.save(filename); +// } +} \ No newline at end of file diff --git a/core-androidx/src/processing/a2d/PShapeAndroid2D.java b/core-androidx/src/processing/a2d/PShapeAndroid2D.java new file mode 100644 index 000000000..e5ace2266 --- /dev/null +++ b/core-androidx/src/processing/a2d/PShapeAndroid2D.java @@ -0,0 +1,109 @@ +package processing.a2d; + +import android.graphics.Shader; +import processing.core.PGraphics; +import processing.core.PShapeSVG; +import processing.data.XML; + +public class PShapeAndroid2D extends PShapeSVG { + protected Shader strokeGradientPaint; + protected Shader fillGradientPaint; + + + public PShapeAndroid2D(XML svg) { + super(svg); + } + + + public PShapeAndroid2D(PShapeSVG parent, XML properties, boolean parseKids) { + super(parent, properties, parseKids); + } + + + @Override + protected void setParent(PShapeSVG parent) { + super.setParent(parent); + + if (parent instanceof PShapeAndroid2D) { + PShapeAndroid2D pj = (PShapeAndroid2D) parent; + fillGradientPaint = pj.fillGradientPaint; + strokeGradientPaint = pj.strokeGradientPaint; + + } else { // parent is null or not Android2D + fillGradientPaint = null; + strokeGradientPaint = null; + } + } + + + /** Factory method for subclasses. */ + @Override + protected PShapeSVG createShape(PShapeSVG parent, XML properties, boolean parseKids) { + return new PShapeAndroid2D(parent, properties, parseKids); + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + protected Shader calcGradientPaint(Gradient gradient) { + // TODO just do this with the other parsing + int[] colors = new int[gradient.count]; + int opacityMask = ((int) (opacity * 255)) << 24; + for (int i = 0; i < gradient.count; i++) { + colors[i] = opacityMask | (gradient.color[i] & 0xFFFFFF); + } + + if (gradient instanceof LinearGradient) { + LinearGradient grad = (LinearGradient) gradient; +// return new LinearGradientPaint(grad.x1, grad.y1, grad.x2, grad.y2, +// grad.offset, grad.color, grad.count, +// opacity); + return new android.graphics.LinearGradient(grad.x1, grad.y1, + grad.x2, grad.y2, + colors, grad.offset, + Shader.TileMode.CLAMP ); + + } else if (gradient instanceof RadialGradient) { + RadialGradient grad = (RadialGradient) gradient; +// return new RadialGradientPaint(grad.cx, grad.cy, grad.r, +// grad.offset, grad.color, grad.count, +// opacity); + return new android.graphics.RadialGradient(grad.cx, grad.cy, grad.r, + colors, grad.offset, + Shader.TileMode.CLAMP); + } + return null; + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + @Override + protected void styles(PGraphics g) { + super.styles(g); + + if (g instanceof PGraphicsAndroid2D) { + PGraphicsAndroid2D gg = (PGraphicsAndroid2D) g; + + if (strokeGradient != null) { +// gg.strokeGradient = true; + if (strokeGradientPaint == null) { + strokeGradientPaint = calcGradientPaint(strokeGradient); + } + gg.strokePaint.setShader(strokeGradientPaint); + } + if (fillGradient != null) { +// gg.fillGradient = true; + if (fillGradientPaint == null) { + fillGradientPaint = calcGradientPaint(fillGradient); + } + gg.fillPaint.setShader(fillGradientPaint); + } else { + // need to shut off, in case parent object has a gradient applied + //gg.fillGradient = false; + } + } + } +} diff --git a/core-androidx/src/processing/a2d/PSurfaceAndroid2D.java b/core-androidx/src/processing/a2d/PSurfaceAndroid2D.java new file mode 100644 index 000000000..47297ea25 --- /dev/null +++ b/core-androidx/src/processing/a2d/PSurfaceAndroid2D.java @@ -0,0 +1,159 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2016 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.a2d; + +import android.content.Context; + +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.service.wallpaper.WallpaperService; +import android.support.wearable.watchface.CanvasWatchFaceService; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import processing.android.AppComponent; +import processing.android.PFragment; +import processing.core.PApplet; +import processing.core.PGraphics; +import processing.core.PSurfaceNone; + +public class PSurfaceAndroid2D extends PSurfaceNone { + + public PSurfaceAndroid2D() { } + + public PSurfaceAndroid2D(PGraphics graphics, AppComponent component, SurfaceHolder holder) { + this.sketch = graphics.parent; + this.graphics = graphics; + this.component = component; + if (component.getKind() == AppComponent.FRAGMENT) { + PFragment frag = (PFragment)component; + activity = frag.getActivity(); + surfaceView = new SurfaceViewAndroid2D(activity, null); + } else if (component.getKind() == AppComponent.WALLPAPER) { + wallpaper = (WallpaperService)component; + surfaceView = new SurfaceViewAndroid2D(wallpaper, holder); + } else if (component.getKind() == AppComponent.WATCHFACE) { + watchface = (CanvasWatchFaceService)component; + surfaceView = null; + // Set as ready here, as watch faces don't have a surface view with a + // surfaceCreate() event to do it. + surfaceReady = true; + } + } + + /////////////////////////////////////////////////////////// + + // SurfaceView + + public class SurfaceViewAndroid2D extends SurfaceView implements SurfaceHolder.Callback { + SurfaceHolder holder; + + public SurfaceViewAndroid2D(Context context, SurfaceHolder holder) { + super(context); + this.holder = holder; + +// println("surface holder"); + // Install a SurfaceHolder.Callback so we get notified when the + // underlying surface is created and destroyed + SurfaceHolder h = getHolder(); + h.addCallback(this); +// surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_GPU); // no longer needed. + +// println("setting focusable, requesting focus"); + setFocusable(true); + setFocusableInTouchMode(true); + requestFocus(); +// println("done making surface view"); + + surfaceReady = false; // Will be ready when the surfaceCreated() event is called + + // Solves screen flickering: + // https://github.com/processing/processing-android/issues/570 + setBackgroundColor(Color.argb(0, 0, 0, 0)); + getHolder().setFormat(PixelFormat.TRANSPARENT); + } + + @Override + public SurfaceHolder getHolder() { + if (holder == null) { + return super.getHolder(); + } else { + return holder; + } + } + + // part of SurfaceHolder.Callback + public void surfaceCreated(SurfaceHolder holder) { + surfaceReady = true; + if (requestedThreadStart) { + // Only start the thread once the surface has been created, otherwise it will not be able to draw + startThread(); + } + if (PApplet.DEBUG) { + System.out.println("surfaceCreated()"); + } + } + + + // part of SurfaceHolder.Callback + public void surfaceDestroyed(SurfaceHolder holder) { + if (PApplet.DEBUG) { + System.out.println("surfaceDestroyed()"); + } + } + + + // part of SurfaceHolder.Callback + public void surfaceChanged(SurfaceHolder holder, int format, int iwidth, int iheight) { + if (PApplet.DEBUG) { + System.out.println("SketchSurfaceView.surfaceChanged() " + iwidth + " " + iheight); + } + + sketch.surfaceChanged(); + sketch.setSize(iwidth, iheight); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + sketch.surfaceWindowFocusChanged(hasFocus); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return sketch.surfaceTouchEvent(event); + } + + @Override + public boolean onKeyDown(int code, android.view.KeyEvent event) { + sketch.surfaceKeyDown(code, event); + return super.onKeyDown(code, event); + } + + @Override + public boolean onKeyUp(int code, android.view.KeyEvent event) { + sketch.surfaceKeyUp(code, event); + return super.onKeyUp(code, event); + } + } +} diff --git a/core-androidx/src/processing/android/ActivityAPI.java b/core-androidx/src/processing/android/ActivityAPI.java new file mode 100644 index 000000000..e668ea63d --- /dev/null +++ b/core-androidx/src/processing/android/ActivityAPI.java @@ -0,0 +1,65 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2016-17 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.android; + +import android.content.Intent; +import android.os.Bundle; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.app.FragmentManager; + + +// Methods that should be implemented in PApplet to maintain backward +// compatibility with (some) functionality available from Activity/Fragment +public interface ActivityAPI { + // Lifecycle events + public void onCreate(Bundle savedInstanceState); + public void onDestroy(); + public void onStart(); + public void onStop(); + public void onPause(); + public void onResume(); + + // Activity and intent events + public void onActivityResult(int requestCode, int resultCode, Intent data); + public void onNewIntent(Intent intent); + + // Menu API + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater); + public boolean onOptionsItemSelected(MenuItem item); + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo); + public boolean onContextItemSelected(MenuItem item); + public void setHasOptionsMenu(boolean hasMenu); + + // IO events + public void onBackPressed(); + + // Activity management + public FragmentManager getFragmentManager(); + public Window getWindow(); +} diff --git a/core-androidx/src/processing/android/AppComponent.java b/core-androidx/src/processing/android/AppComponent.java new file mode 100644 index 000000000..1574061ee --- /dev/null +++ b/core-androidx/src/processing/android/AppComponent.java @@ -0,0 +1,51 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2016-17 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.android; + +import android.content.Intent; +import processing.core.PApplet; +import processing.core.PConstants; + +abstract public interface AppComponent extends PConstants { + static public final int FRAGMENT = 0; + static public final int WALLPAPER = 1; + static public final int WATCHFACE = 2; + + public void initDimensions(); + public int getDisplayWidth(); + public int getDisplayHeight(); + public float getDisplayDensity(); + public int getKind(); + public void setSketch(PApplet sketch); + public PApplet getSketch(); + + public boolean isService(); + public ServiceEngine getEngine(); + + public void startActivity(Intent intent); + + public void requestDraw(); + public boolean canDraw(); + + public void dispose(); +} diff --git a/core-androidx/src/processing/android/CompatUtils.java b/core-androidx/src/processing/android/CompatUtils.java new file mode 100644 index 000000000..b4f8f55bd --- /dev/null +++ b/core-androidx/src/processing/android/CompatUtils.java @@ -0,0 +1,111 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2017 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.android; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicInteger; +import android.annotation.SuppressLint; +import android.os.Build; +import android.util.DisplayMetrics; +import android.view.Display; +import android.view.View; +import android.graphics.Point; + +/** + * Compatibility utilities that work across versions of Android. Even though + * the mode sets API level 17 (Android 4.2) as the minimum target, because the + * core library could be used from another IDE and lower targets, then this + * compatibility methods are still needed. + */ +public class CompatUtils { + // Start at 15,000,000, taking into account the comment from Singed + // http://stackoverflow.com/a/39307421 + private static final AtomicInteger nextId = new AtomicInteger(15000000); + + + /** + * This method retrieves the "real" display metrics and size, without + * subtracting any window decor or applying any compatibility scale factors. + * @param display the Display object + * @param metrics the metrics to retrieve + * @param size the size to retrieve + */ + static public void getDisplayParams(Display display, + DisplayMetrics metrics, Point size) { + if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT) { + display.getRealMetrics(metrics); + display.getRealSize(size); + } if (Build.VERSION_CODES.ICE_CREAM_SANDWICH <= Build.VERSION.SDK_INT) { + display.getMetrics(metrics); + // Use undocumented methods getRawWidth, getRawHeight + try { + size.x = (Integer) Display.class.getMethod("getRawWidth").invoke(display); + size.y = (Integer) Display.class.getMethod("getRawHeight").invoke(display); + } catch (Exception e) { + display.getSize(size); + } + } else { + display.getMetrics(metrics); + display.getSize(size); + } + } + + + /** + * This method generates a unique View ID's. Handles the lack of + * View.generateViewId() in Android versions lower than 17, using a technique + * based on fantouch's code at http://stackoverflow.com/a/21000252 + * @return view ID + */ + @SuppressLint("NewApi") + static public int getUniqueViewId() { + if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT) { + return View.generateViewId(); + } else { + for (;;) { + final int result = nextId.get(); + // aapt-generated IDs have the high byte nonzero; clamp to the range under that. + int newValue = result + 1; + if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0. + if (nextId.compareAndSet(result, newValue)) { + return result; + } + } + } + } + + + /** + * This method returns the UTF-8 charset + * @return UTF-8 charset + */ + @SuppressLint("NewApi") + static public Charset getCharsetUTF8() { + if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) { + return StandardCharsets.UTF_8; + } else { + return Charset.forName("UTF-8"); + } + } +} diff --git a/core-androidx/src/processing/android/PFragment.java b/core-androidx/src/processing/android/PFragment.java new file mode 100644 index 000000000..08e245bca --- /dev/null +++ b/core-androidx/src/processing/android/PFragment.java @@ -0,0 +1,259 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2016-17 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.android; + +import androidx.annotation.IdRes; +import androidx.annotation.LayoutRes; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import android.util.DisplayMetrics; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.graphics.Point; +import android.os.Bundle; +import android.view.ContextMenu; +import android.view.Display; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.ContextMenu.ContextMenuInfo; +import processing.core.PApplet; + +public class PFragment extends Fragment implements AppComponent { + private DisplayMetrics metrics; + private Point size; + private PApplet sketch; + private @LayoutRes int layout = -1; + + + public PFragment() { + super(); + } + + + public PFragment(PApplet sketch) { + super(); + setSketch(sketch); + } + + + public void initDimensions() { + metrics = new DisplayMetrics(); + size = new Point(); + WindowManager wm = getActivity().getWindowManager(); + Display display = wm.getDefaultDisplay(); + CompatUtils.getDisplayParams(display, metrics, size); + } + + + public int getDisplayWidth() { + return size.x; + } + + + public int getDisplayHeight() { + return size.y; + } + + + public float getDisplayDensity() { + return metrics.density; + } + + + public int getKind() { + return FRAGMENT; + } + + + public void setSketch(PApplet sketch) { + this.sketch = sketch; + if (layout != -1) { + sketch.parentLayout = layout; + } + } + + + public PApplet getSketch() { + return sketch; + } + + + public void setLayout(@LayoutRes int layout, @IdRes int id, FragmentActivity activity) { + this.layout = layout; + if (sketch != null) { + sketch.parentLayout = layout; + } + FragmentManager manager = activity.getSupportFragmentManager(); + FragmentTransaction transaction = manager.beginTransaction(); + transaction.add(id, this); + transaction.commit(); + } + + + public void setView(View view, FragmentActivity activity) { + FragmentManager manager = activity.getSupportFragmentManager(); + FragmentTransaction transaction = manager.beginTransaction(); + transaction.add(view.getId(), this); + transaction.commit(); + } + + + public boolean isService() { + return false; + } + + + public ServiceEngine getEngine() { + return null; + } + + + public void dispose() { + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + if (sketch != null) { + sketch.initSurface(inflater, container, savedInstanceState, this, null); + + // For compatibility with older sketches that run some hardware initialization + // inside onCreate(), don't call from Fragment.onCreate() because the surface + // will not be yet ready, and so the reference to the activity and other + // system variables will be null. In any case, onCreateView() is called + // immediately after onCreate(): + // https://developer.android.com/reference/android/app/Fragment.html#Lifecycle + sketch.onCreate(savedInstanceState); + + return sketch.getSurface().getRootView(); + } else { + return null; + } + } + + + @Override + public void onStart() { + super.onStart(); + if (sketch != null) { + sketch.onStart(); + } + } + + + @Override + public void onResume() { + super.onResume(); + if (sketch != null) { + sketch.onResume(); + } + } + + + @Override + public void onPause() { + super.onPause(); + if (sketch != null) { + sketch.onPause(); + } + } + + + @Override + public void onStop() { + super.onStop(); + if (sketch != null) { + sketch.onStop(); + } + } + + + @Override + public void onDestroy() { + super.onDestroy(); + if (sketch != null) { + sketch.onDestroy(); + } + } + + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (sketch != null) sketch.onActivityResult(requestCode, resultCode, data); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + if (sketch != null) sketch.onCreateOptionsMenu(menu, inflater); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item){ + if (sketch != null) return sketch.onOptionsItemSelected(item); + return super.onOptionsItemSelected(item); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + if (sketch != null) sketch.onCreateContextMenu(menu, v, menuInfo); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + if (sketch != null) return sketch.onContextItemSelected(item); + return super.onContextItemSelected(item); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + if (PApplet.DEBUG) System.out.println("configuration changed: " + newConfig); + super.onConfigurationChanged(newConfig); + } + + + public void setOrientation(int which) { + if (which == PORTRAIT) { + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } else if (which == LANDSCAPE) { + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } + } + + + public void requestDraw() { + } + + + public boolean canDraw() { + return sketch != null && sketch.isLooping(); + } +} diff --git a/core-androidx/src/processing/android/PWallpaper.java b/core-androidx/src/processing/android/PWallpaper.java new file mode 100644 index 000000000..a285fc90d --- /dev/null +++ b/core-androidx/src/processing/android/PWallpaper.java @@ -0,0 +1,302 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2016-17 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.android; + +import android.service.wallpaper.WallpaperService; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.WindowManager; +import processing.core.PApplet; +import android.util.DisplayMetrics; +import android.view.Display; +import android.graphics.Point; +import android.graphics.Rect; + + +public class PWallpaper extends WallpaperService implements AppComponent { + private Point size; + private DisplayMetrics metrics; + private WallpaperEngine engine; + + + public void initDimensions() { + metrics = new DisplayMetrics(); + size = new Point(); + WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + CompatUtils.getDisplayParams(display, metrics, size); + } + + + public int getDisplayWidth() { + return size.x; + } + + + public int getDisplayHeight() { + return size.y; + } + + + public float getDisplayDensity() { + return metrics.density; + } + + + public int getKind() { + return WALLPAPER; + } + + + public PApplet createSketch() { + return new PApplet(); + } + + + public void setSketch(PApplet sketch) { + engine.sketch = sketch; + } + + + public PApplet getSketch() { + return engine.sketch; + } + + + public boolean isService() { + return true; + } + + + public ServiceEngine getEngine() { + return engine; + } + + + public void requestDraw() { + } + + + public boolean canDraw() { + return true; + } + + + public void dispose() { + } + + + public void requestPermissions() { + } + + + @Override + public Engine onCreateEngine() { + engine = new WallpaperEngine(); + return engine; + } + + + @Override + public void onDestroy() { + super.onDestroy(); + + if (engine != null){ + //engine.sketch = null; + engine.onDestroy(); + } + } + + + public class WallpaperEngine extends Engine implements ServiceEngine { + PApplet sketch; + private float xOffset, xOffsetStep; + private float yOffset, yOffsetStep; + private int xPixelOffset, yPixelOffset; + + + @Override + public void onCreate(SurfaceHolder surfaceHolder) { + super.onCreate(surfaceHolder); + sketch = createSketch(); + sketch.initSurface(PWallpaper.this, getSurfaceHolder()); + if (isPreview()) requestPermissions(); + setTouchEventsEnabled(true); + } + + + @Override + public void onSurfaceCreated(SurfaceHolder surfaceHolder) { + super.onSurfaceCreated(surfaceHolder); + } + + + @Override + public void onSurfaceChanged(final SurfaceHolder holder, final int format, + final int width, final int height) { + // When the surface of a live wallpaper changes (eg: after a screen rotation) the same sketch + // continues to run (unlike the case of regular apps, where its re-created) so we need to + // force a reset of the renderer so the backing FBOs (in the case of the OpenGL renderers) + // get reinitalized with the correct size. + sketch.g.reset(); + super.onSurfaceChanged(holder, format, width, height); + } + + + @Override + public void onVisibilityChanged(boolean visible) { + if (sketch != null) { + if (visible) { + sketch.onResume(); + } else { + sketch.onPause(); + } + } + super.onVisibilityChanged(visible); + } + + + /* + * Store the position of the touch event so we can use it for drawing + * later + */ + @Override + public void onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + if (sketch != null) { + sketch.surfaceTouchEvent(event); + } + } + + + @Override + public void onOffsetsChanged(float xOffset, float yOffset, + float xOffsetStep, float yOffsetStep, + int xPixelOffset, int yPixelOffset) { + + if (sketch != null) { + this.xOffset = xOffset; + this.yOffset = yOffset; + this.xOffsetStep = xOffsetStep; + this.yOffsetStep = yOffsetStep; + this.xPixelOffset = xPixelOffset; + this.yPixelOffset = yPixelOffset; + } + } + + + @Override + public void onSurfaceDestroyed(SurfaceHolder holder) { + // This is called immediately before a surface is being destroyed. + // After returning from this call, you should no longer try to access this + // surface. If you have a rendering thread that directly accesses the + // surface, you must ensure that thread is no longer touching the Surface + // before returning from this function. + super.onSurfaceDestroyed(holder); + } + + + @Override + public void onDestroy() { + super.onDestroy(); + if (sketch != null) { + sketch.onDestroy(); + } + } + + + @Override + public float getXOffset() { + return xOffset; + } + + + @Override + public float getYOffset() { + return yOffset; + } + + + @Override + public float getXOffsetStep() { + return xOffsetStep; + } + + + @Override + public float getYOffsetStep() { + return yOffsetStep; + } + + + @Override + public int getXPixelOffset() { + return xPixelOffset; + } + + + @Override + public int getYPixelOffset() { + return yPixelOffset; + } + + + @Override + public boolean isInAmbientMode() { + return false; + } + + + @Override + public boolean isRound() { + return false; + } + + + @Override + public Rect getInsets() { + return null; + } + + + @Override + public boolean useLowBitAmbient() { + return false; + } + + + @Override + public boolean requireBurnInProtection() { + return false; + } + + @Override + public void onRequestPermissionsResult(int requestCode, + String permissions[], + int[] grantResults) { + if (sketch != null) { + sketch.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } + } +} diff --git a/core-androidx/src/processing/android/PWatchFaceCanvas.java b/core-androidx/src/processing/android/PWatchFaceCanvas.java new file mode 100644 index 000000000..227022cfe --- /dev/null +++ b/core-androidx/src/processing/android/PWatchFaceCanvas.java @@ -0,0 +1,367 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2016-17 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.android; + +import java.lang.reflect.Method; + +import android.annotation.TargetApi; +import android.graphics.Canvas; +import android.graphics.Point; +import android.os.Bundle; +import android.graphics.Rect; +import android.support.wearable.complications.ComplicationData; +import android.support.wearable.watchface.CanvasWatchFaceService; +import android.support.wearable.watchface.WatchFaceStyle; +import android.util.DisplayMetrics; +import android.view.Display; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.WindowInsets; +import android.view.WindowManager; +import processing.a2d.PGraphicsAndroid2D; +import processing.core.PApplet; + +@TargetApi(21) +public class PWatchFaceCanvas extends CanvasWatchFaceService implements AppComponent { + private Point size; + private DisplayMetrics metrics; + private CanvasEngine engine; + + public void initDimensions() { + metrics = new DisplayMetrics(); + size = new Point(); + WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + CompatUtils.getDisplayParams(display, metrics, size); + } + + + public int getDisplayWidth() { + return size.x; + } + + + public int getDisplayHeight() { + return size.y; + } + + + public float getDisplayDensity() { + return metrics.density; + } + + + public int getKind() { + return WATCHFACE; + } + + + public PApplet createSketch() { + return new PApplet(); + } + + + public void setSketch(PApplet sketch) { + engine.sketch = sketch; + } + + + public PApplet getSketch() { + return engine.sketch; + } + + + public boolean isService() { + return true; + } + + + public ServiceEngine getEngine() { + return engine; + } + + + public void requestDraw() { + if (engine != null) engine.invalidateIfNecessary(); + } + + + public boolean canDraw() { + // The rendering loop should never call handleDraw() directly, + // it only needs to invalidate the screen + return false; + } + + + public void dispose() { + } + + + public void requestPermissions() { + } + + + @Override + public Engine onCreateEngine() { + engine = new CanvasEngine(); + return engine; + } + + + @Override + public void onDestroy() { + super.onDestroy(); + if (engine != null) engine.onDestroy(); + } + + + private class CanvasEngine extends CanvasWatchFaceService.Engine implements ServiceEngine { + private PApplet sketch; + private Method compUpdatedMethod; + private Method tapCommandMethod; + private boolean isRound = false; + private Rect insets = new Rect(); + private boolean lowBitAmbient = false; + private boolean burnInProtection = false; + + @Override + public void onCreate(SurfaceHolder surfaceHolder) { + super.onCreate(surfaceHolder); + setWatchFaceStyle(new WatchFaceStyle.Builder(PWatchFaceCanvas.this) + .setAcceptsTapEvents(true) + .build()); + sketch = createSketch(); + PGraphicsAndroid2D.useBitmap = false; + sketch.initSurface(PWatchFaceCanvas.this, null); + initTapEvents(); + initComplications(); + requestPermissions(); + } + + + private void initTapEvents() { + try { + tapCommandMethod = sketch.getClass().getMethod("onTapCommand", + new Class[] {int.class, int.class, int.class, long.class}); + } catch (Exception e) { + tapCommandMethod = null; + } + } + + + private void initComplications() { + try { + compUpdatedMethod = sketch.getClass().getMethod("onComplicationDataUpdate", + new Class[] {int.class, ComplicationData.class}); + } catch (Exception e) { + compUpdatedMethod = null; + } + } + + + private void invalidateIfNecessary() { + if (isVisible() && !isInAmbientMode()) { + invalidate(); + } + } + + + @Override + public void onAmbientModeChanged(boolean inAmbientMode) { + super.onAmbientModeChanged(inAmbientMode); + invalidateIfNecessary(); + // call new event handlers in sketch (?) + } + + + @Override + public void onPropertiesChanged(Bundle properties) { + super.onPropertiesChanged(properties); + lowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false); + burnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false); + } + + + @Override + public void onApplyWindowInsets(WindowInsets insets) { + super.onApplyWindowInsets(insets); + isRound = insets.isRound(); + this.insets.set(insets.getSystemWindowInsetLeft(), + insets.getSystemWindowInsetTop(), + insets.getSystemWindowInsetRight(), + insets.getSystemWindowInsetBottom()); + } + + + @Override + public void onVisibilityChanged(boolean visible) { + super.onVisibilityChanged(visible); + if (sketch != null) { + if (visible) { + sketch.onResume(); + } else { + sketch.onPause(); + } + } + } + + + @Override + public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { + super.onSurfaceChanged(holder, format, width, height); + if (sketch != null) { + sketch.surfaceChanged(); + sketch.setSize(width, height); + } + } + + + @Override + public void onPeekCardPositionUpdate(Rect rect) { } + + + @Override + public void onTimeTick() { + invalidate(); + } + + + @Override + public void onDraw(Canvas canvas, Rect bounds) { + super.onDraw(canvas, bounds); + if (sketch != null) { + PGraphicsAndroid2D g2 = (PGraphicsAndroid2D)sketch.g; + g2.canvas = canvas; + sketch.handleDraw(); + } + } + + + @Override + public void onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + if (sketch != null) sketch.surfaceTouchEvent(event); + } + + + @Override + public void onTapCommand(@TapType int tapType, int x, int y, long eventTime) { + if (tapCommandMethod != null) { + try { + tapCommandMethod.invoke(tapType, x, y, eventTime); + } catch (Exception e) { } + invalidate(); + } + } + + + @Override + public void onComplicationDataUpdate(int complicationId, + ComplicationData complicationData) { + if (compUpdatedMethod != null) { + try { + compUpdatedMethod.invoke(complicationId, complicationData); + } catch (Exception e) { } + invalidate(); + } + } + + + @Override + public void onDestroy() { + super.onDestroy(); + if (sketch != null) { + sketch.onDestroy(); + } + } + + + @Override + public float getXOffset() { + return 0; + } + + + @Override + public float getYOffset() { + return 0; + } + + + @Override + public float getXOffsetStep() { + return 0; + } + + + @Override + public float getYOffsetStep() { + return 0; + } + + + @Override + public int getXPixelOffset() { + return 0; + } + + + @Override + public int getYPixelOffset() { + return 0; + } + + + @Override + public boolean isRound() { + return isRound; + } + + + @Override + public Rect getInsets() { + return insets; + } + + + @Override + public boolean useLowBitAmbient() { + return lowBitAmbient; + } + + + @Override + public boolean requireBurnInProtection() { + return burnInProtection; + } + + @Override + public void onRequestPermissionsResult(int requestCode, + String permissions[], + int[] grantResults) { + if (sketch != null) { + sketch.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } + } +} diff --git a/core-androidx/src/processing/android/PWatchFaceGLES.java b/core-androidx/src/processing/android/PWatchFaceGLES.java new file mode 100644 index 000000000..8d385d286 --- /dev/null +++ b/core-androidx/src/processing/android/PWatchFaceGLES.java @@ -0,0 +1,392 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2016-17 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.android; + +import android.annotation.TargetApi; +import android.graphics.Point; +import android.opengl.EGL14; +import android.opengl.EGLConfig; +import android.opengl.EGLDisplay; +import android.os.Bundle; +import android.view.Display; +import android.view.WindowInsets; +import android.support.wearable.complications.ComplicationData; +import android.support.wearable.watchface.Gles2WatchFaceService; +import android.support.wearable.watchface.WatchFaceStyle; +import android.util.DisplayMetrics; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.WindowManager; +import processing.core.PApplet; +import java.lang.reflect.Method; + +import android.graphics.Rect; + +@TargetApi(21) +public class PWatchFaceGLES extends Gles2WatchFaceService implements AppComponent { + private static final int[] CONFIG_ATTRIB_LIST = new int[]{ + EGL14.EGL_RENDERABLE_TYPE, 4, + EGL14.EGL_RED_SIZE, 8, + EGL14.EGL_GREEN_SIZE, 8, + EGL14.EGL_BLUE_SIZE, 8, + EGL14.EGL_ALPHA_SIZE, 8, + EGL14.EGL_DEPTH_SIZE, 16, // this was missing + EGL14.EGL_NONE}; + + private Point size; + private DisplayMetrics metrics; + private GLES2Engine engine; + + + public void initDimensions() { + metrics = new DisplayMetrics(); + size = new Point(); + WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + CompatUtils.getDisplayParams(display, metrics, size); + } + + + public int getDisplayWidth() { + return size.x; + } + + + public int getDisplayHeight() { + return size.y; + } + + + public float getDisplayDensity() { + return metrics.density; + } + + + public int getKind() { + return WATCHFACE; + } + + + public PApplet createSketch() { + return new PApplet(); + } + + + public void setSketch(PApplet sketch) { + engine.sketch = sketch; + } + + + public PApplet getSketch() { + return engine.sketch; + } + + + public boolean isService() { + return true; + } + + + public ServiceEngine getEngine() { + return engine; + } + + + public void requestDraw() { + if (engine != null) engine.invalidateIfNecessary(); + } + + + public boolean canDraw() { + // The rendering loop should never call handleDraw() directly, it only needs to invalidate the + // screen + return false; + } + + + public void dispose() { + } + + + public void requestPermissions() { + } + + + @Override + public Engine onCreateEngine() { + engine = new GLES2Engine(); + return engine; + } + + + @Override + public void onDestroy() { + super.onDestroy(); + if (engine != null) engine.onDestroy(); + } + + + private class GLES2Engine extends Gles2WatchFaceService.Engine implements ServiceEngine { + private PApplet sketch; + private Method compUpdatedMethod; + private Method tapCommandMethod; + private boolean isRound = false; + private Rect insets = new Rect(); + private boolean lowBitAmbient = false; + private boolean burnInProtection = false; + + @Override + public void onCreate(SurfaceHolder surfaceHolder) { + super.onCreate(surfaceHolder); + setWatchFaceStyle(new WatchFaceStyle.Builder(PWatchFaceGLES.this) + .setAcceptsTapEvents(true) + .build()); + sketch = createSketch(); + sketch.initSurface(PWatchFaceGLES.this, null); + initTapEvents(); + initComplications(); + requestPermissions(); + } + + + public EGLConfig chooseEglConfig(EGLDisplay eglDisplay) { + int[] numEglConfigs = new int[1]; + EGLConfig[] eglConfigs = new EGLConfig[1]; + if(!EGL14.eglChooseConfig(eglDisplay, CONFIG_ATTRIB_LIST, 0, eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) { + throw new RuntimeException("eglChooseConfig failed"); + } else if(numEglConfigs[0] == 0) { + throw new RuntimeException("no matching EGL configs"); + } else { + return eglConfigs[0]; + } + } + + @Override + public void onGlContextCreated() { + super.onGlContextCreated(); + } + + + @Override + public void onGlSurfaceCreated(int width, int height) { + super.onGlSurfaceCreated(width, height); + if (sketch != null) { + sketch.surfaceChanged(); + sketch.setSize(width, height); + } + } + + + private void initTapEvents() { + try { + tapCommandMethod = sketch.getClass().getMethod("onTapCommand", + new Class[] {int.class, int.class, int.class, long.class}); + } catch (Exception e) { + tapCommandMethod = null; + } + } + + + private void initComplications() { + try { + compUpdatedMethod = sketch.getClass().getMethod("onComplicationDataUpdate", + new Class[] {int.class, ComplicationData.class}); + } catch (Exception e) { + compUpdatedMethod = null; + } + } + + + private void invalidateIfNecessary() { + if (isVisible() && !isInAmbientMode()) { + invalidate(); + } + } + + + @Override + public void onAmbientModeChanged(boolean inAmbientMode) { + super.onAmbientModeChanged(inAmbientMode); + invalidateIfNecessary(); + // call new event handlers in sketch (?) + } + + + @Override + public void onPropertiesChanged(Bundle properties) { + super.onPropertiesChanged(properties); + lowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false); + burnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false); + } + + + @Override + public void onApplyWindowInsets(WindowInsets insets) { + super.onApplyWindowInsets(insets); + this.insets.set(insets.getSystemWindowInsetLeft(), + insets.getSystemWindowInsetTop(), + insets.getSystemWindowInsetRight(), + insets.getSystemWindowInsetBottom()); + } + + + @Override + public void onVisibilityChanged(boolean visible) { + super.onVisibilityChanged(visible); + if (sketch != null) { + if (visible) { + sketch.onResume(); + } else { + sketch.onPause(); + } + } + } + + + @Override + public void onPeekCardPositionUpdate(Rect rect) { } + + + @Override + public void onTimeTick() { + invalidate(); + } + + + @Override + public void onDraw() { + super.onDraw(); + if (sketch != null) sketch.handleDraw(); + } + + + @Override + public void onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + if (sketch != null) sketch.surfaceTouchEvent(event); + } + + + @Override + public void onTapCommand(@TapType int tapType, int x, int y, long eventTime) { + if (tapCommandMethod != null) { + try { + tapCommandMethod.invoke(tapType, x, y, eventTime); + } catch (Exception e) { } + invalidate(); + } + } + + + @Override + public void onComplicationDataUpdate(int complicationId, + ComplicationData complicationData) { + if (compUpdatedMethod != null) { + try { + compUpdatedMethod.invoke(complicationId, complicationData); + } catch (Exception e) { + } + invalidate(); + } + } + + + @Override + public void onDestroy() { + super.onDestroy(); + if (sketch != null) { + sketch.onDestroy(); + } + } + + + @Override + public float getXOffset() { + return 0; + } + + + @Override + public float getYOffset() { + return 0; + } + + + @Override + public float getXOffsetStep() { + return 0; + } + + + @Override + public float getYOffsetStep() { + return 0; + } + + + @Override + public int getXPixelOffset() { + return 0; + } + + + @Override + public int getYPixelOffset() { + return 0; + } + + + @Override + public boolean isRound() { + return isRound; + } + + + @Override + public Rect getInsets() { + return insets; + } + + + @Override + public boolean useLowBitAmbient() { + return lowBitAmbient; + } + + + @Override + public boolean requireBurnInProtection() { + return burnInProtection; + } + + + @Override + public void onRequestPermissionsResult(int requestCode, + String permissions[], + int[] grantResults) { + if (sketch != null) { + sketch.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } + } +} diff --git a/core-androidx/src/processing/android/PermissionRequestor.java b/core-androidx/src/processing/android/PermissionRequestor.java new file mode 100644 index 000000000..3c524da4b --- /dev/null +++ b/core-androidx/src/processing/android/PermissionRequestor.java @@ -0,0 +1,59 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2017 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.android; + +import android.app.Activity; +import android.os.Bundle; +import android.os.ResultReceiver; + +import androidx.core.app.ActivityCompat; + +// A simple utility activity to request permissions in a service. +public class PermissionRequestor extends Activity { + public static final String KEY_RESULT_RECEIVER = "resultReceiver"; + public static final String KEY_PERMISSIONS = "permissions"; + public static final String KEY_GRANT_RESULTS = "grantResults"; + public static final String KEY_REQUEST_CODE = "requestCode"; + + ResultReceiver resultReceiver; + String[] permissions; + int requestCode; + + @Override + protected void onStart() { + super.onStart(); + resultReceiver = this.getIntent().getParcelableExtra(KEY_RESULT_RECEIVER); + permissions = this.getIntent().getStringArrayExtra(KEY_PERMISSIONS); + requestCode = this.getIntent().getIntExtra(KEY_REQUEST_CODE, 0); + ActivityCompat.requestPermissions(this, permissions, requestCode); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + Bundle resultData = new Bundle(); + resultData.putStringArray(KEY_PERMISSIONS, permissions); + resultData.putIntArray(KEY_GRANT_RESULTS, grantResults); + resultReceiver.send(requestCode, resultData); + finish(); + } +} \ No newline at end of file diff --git a/core-androidx/src/processing/android/ServiceEngine.java b/core-androidx/src/processing/android/ServiceEngine.java new file mode 100644 index 000000000..4ca043075 --- /dev/null +++ b/core-androidx/src/processing/android/ServiceEngine.java @@ -0,0 +1,49 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2017 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.android; + +import android.graphics.Rect; +import processing.core.PConstants; + +public interface ServiceEngine extends PConstants { + // wallpapers + public boolean isPreview(); + public float getXOffset(); + public float getYOffset(); + public float getXOffsetStep(); + public float getYOffsetStep(); + public int getXPixelOffset(); + public int getYPixelOffset(); + + // wear + public boolean isInAmbientMode(); + public boolean isRound(); + public Rect getInsets(); + public boolean useLowBitAmbient(); + public boolean requireBurnInProtection(); + + // Service permissions + public void onRequestPermissionsResult(int requestCode, + String permissions[], + int[] grantResults); +} diff --git a/core-androidx/src/processing/core/PApplet.java b/core-androidx/src/processing/core/PApplet.java new file mode 100644 index 000000000..3c1406698 --- /dev/null +++ b/core-androidx/src/processing/core/PApplet.java @@ -0,0 +1,12622 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-17 The Processing Foundation + Copyright (c) 2004-12 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.core; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.text.NumberFormat; +import java.util.*; +import java.util.regex.*; +import java.util.zip.*; + +import android.app.FragmentManager; +import android.view.Window; +import android.view.inputmethod.InputMethodManager; +import android.app.Activity; +import android.content.*; +import android.content.pm.PackageManager; +import android.content.res.AssetManager; +import android.graphics.*; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.View; +import android.view.ViewGroup; +import android.view.ContextMenu.ContextMenuInfo; + +import androidx.annotation.LayoutRes; + +import processing.a2d.PGraphicsAndroid2D; +import processing.android.ActivityAPI; +import processing.android.AppComponent; +import processing.android.CompatUtils; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; + +public class PApplet extends Object implements ActivityAPI, PConstants { + + static final public boolean DEBUG = false; +// static final public boolean DEBUG = true; + + // Convenience public constant holding the SDK version, akin to platform in Java mode + static final public int SDK = Build.VERSION.SDK_INT; + + //static final public int SDK = Build.VERSION_CODES.ICE_CREAM_SANDWICH; // Forcing older SDK for testing + + /** + * The surface this sketch draws to. + */ + protected PSurface surface; + + /** + * The view group containing the surface view of the PApplet. + */ + public @LayoutRes int parentLayout = -1; + + /** The PGraphics renderer associated with this PApplet */ + public PGraphics g; + + /** + * The screen size when the sketch was started. This is initialized inside + * onCreate(). + *

+ * Note that this won't update if you change the resolution + * of your screen once the the applet is running. + *

+ * This variable is not static because in the desktop version of Processing, + * not all instances of PApplet will necessarily be started on a screen of + * the same size. + */ + public int displayWidth, displayHeight; + + /** + * Command line options passed in from main(). + *

+ * This does not include the arguments passed in to PApplet itself. + */ +// public String[] args; + + /** + * Path to where sketch can read/write files (read-only). + * Android: This is the writable area for the Activity, which is correct + * for purposes of how sketchPath is used in practice from a sketch, + * even though it's technically different than the desktop version. + */ + public String sketchPath; //folder; + + /** When debugging headaches */ +// static final boolean THREAD_DEBUG = false; + + /** Default width and height for applet when not specified */ + static public final int DEFAULT_WIDTH = -1; + static public final int DEFAULT_HEIGHT = -1; + + /** + * Set true when the surface dimensions have changed, so that the PGraphics + * object can be resized on the next trip through handleDraw(). + */ + protected boolean surfaceChanged; + + /** + * Pixel buffer from this applet's PGraphics. + *

+ * When used with OpenGL or Java2D, this value will + * be null until loadPixels() has been called. + */ + public int[] pixels; + + /** width of this applet's associated PGraphics */ + public int width = DEFAULT_WIDTH; + + /** height of this applet's associated PGraphics */ + public int height = DEFAULT_HEIGHT; + + /** The logical density of the display from getDisplayMetrics().density + * According to Android's documentation: + * This is a scaling factor for the Density Independent Pixel unit, + * where one DIP is one pixel on an approximately 160 dpi screen + * (for example a 240x320, 1.5"x2" screen), providing the baseline of the + * system's display. Thus on a 160dpi screen this density value will be 1; + * on a 120 dpi screen it would be .75; etc. + */ + public float displayDensity = 1; + + // For future use + public int pixelDensity = 1; + public int pixelWidth; + public int pixelHeight; + + /////////////////////////////////////////////////////////////// + // Mouse events + + /** absolute x position of input on screen */ + public int mouseX; + + /** absolute x position of input on screen */ + public int mouseY; + + + /** + * Previous x/y position of the mouse. This will be a different value + * when inside a mouse handler (like the mouseMoved() method) versus + * when inside draw(). Inside draw(), pmouseX is updated once each + * frame, but inside mousePressed() and friends, it's updated each time + * an event comes through. Be sure to use only one or the other type of + * means for tracking pmouseX and pmouseY within your sketch, otherwise + * you're gonna run into trouble. + */ + public int pmouseX, pmouseY; + + public int mouseButton; + + public boolean mousePressed; + + + public boolean touchIsStarted; + + + public TouchEvent.Pointer[] touches = new TouchEvent.Pointer[0]; + + + /** + * previous mouseX/Y for the draw loop, separated out because this is + * separate from the pmouseX/Y when inside the mouse event handlers. + */ + protected int dmouseX, dmouseY; + + + /** + * pmotionX/Y for the event handlers (motionPressed(), motionDragged() etc) + * these are different because motion events are queued to the end of + * draw, so the previous position has to be updated on each event, + * as opposed to the pmotionX/Y that's used inside draw, which is expected + * to be updated once per trip through draw(). + */ + protected int emouseX, emouseY; + + + /** + * ID of the pointer tracked for mouse events. + */ + protected int mousePointerId; + + + /** + * ID of the most recently touch pointer gone up or down. + */ + protected int touchPointerId; + + /////////////////////////////////////////////////////////////// + // Key events + + /** + * Last key pressed. + *

+ * If it's a coded key, i.e. UP/DOWN/CTRL/SHIFT/ALT, + * this will be set to CODED (0xffff or 65535). + */ + public char key; + + /** + * When "key" is set to CODED, this will contain a Java key code. + *

+ * For the arrow keys, keyCode will be one of UP, DOWN, LEFT and RIGHT. + * Also available are ALT, CONTROL and SHIFT. A full set of constants + * can be obtained from java.awt.event.KeyEvent, from the VK_XXXX variables. + */ + public int keyCode; + + /** + * true if the mouse is currently pressed. + */ + public boolean keyPressed; + + /** + * the last KeyEvent object passed into a mouse function. + */ +// public KeyEvent keyEvent; + + /** + * Gets set to true/false as the applet gains/loses focus. + */ + public boolean focused = false; + + /** + * Keeps track of ENABLE_KEY_REPEAT hint + */ + protected boolean keyRepeatEnabled = false; + + /** + * Set to open when openKeyboard() is called, and used to close the keyboard when the sketch is + * paused, otherwise it remains visible. + */ + boolean keyboardIsOpen = false; + + /** + * Flag to determine if the back key was pressed. + */ + private boolean requestedBackPress = false; + + /** + * Flag to determine if the user handled the back press. + */ + public boolean handledBackPressed = true; + + /////////////////////////////////////////////////////////////// + // Permission handling + + /** + * Callback methods to handle permission requests + */ + protected HashMap permissionMethods = new HashMap(); + + + /** + * Permissions requested during one frame + */ + protected ArrayList reqPermissions = new ArrayList(); + + + /////////////////////////////////////////////////////////////// + // Rendering/timing + + /** + * Time in milliseconds when the applet was started. + *

+ * Used by the millis() function. + */ + long millisOffset = System.currentTimeMillis(); + + protected boolean insideDraw; + + /** Last time in nanoseconds that frameRate was checked */ + protected long frameRateLastNanos = 0; + + /** + * The current value of frames per second. + *

+ * The initial value will be 10 fps, and will be updated with each + * frame thereafter. The value is not instantaneous (since that + * wouldn't be very useful since it would jump around so much), + * but is instead averaged (integrated) over several frames. + * As such, this value won't be valid until after 5-10 frames. + */ + public float frameRate = 10; + + protected boolean looping; + + /** flag set to true when a redraw is asked for by the user */ + protected boolean redraw; + + /** + * How many frames have been displayed since the applet started. + *

+ * This value is read-only do not attempt to set it, + * otherwise bad things will happen. + *

+ * Inside setup(), frameCount is 0. + * For the first iteration of draw(), frameCount will equal 1. + */ + public int frameCount; + + /** + * true if this applet has had it. + */ + public boolean finished; + + /** + * true if exit() has been called so that things shut down + * once the main thread kicks off. + */ + protected boolean exitCalled; + + boolean insideSettings; + + String renderer = JAVA2D; + + int smooth = 1; // default smoothing (whatever that means for the renderer) + + boolean fullScreen = false; + + int display = -1; // use default + + // Background default needs to be different from the default value in + // PGraphics.backgroundColor, otherwise size(100, 100) bg spills over. + // https://github.com/processing/processing/issues/2297 + int windowColor = 0xffDDDDDD; + + /////////////////////////////////////////////////////////////// + // Error messages + + static final String ERROR_MIN_MAX = + "Cannot use min() or max() on an empty array."; + + /////////////////////////////////////////////////////////////// + // Command line options + + /** + * Position of the upper-lefthand corner of the editor window + * that launched this applet. + */ + static public final String ARGS_EDITOR_LOCATION = "--editor-location"; + + /** + * Location for where to position the applet window on screen. + *

+ * This is used by the editor to when saving the previous applet + * location, or could be used by other classes to launch at a + * specific position on-screen. + */ + static public final String ARGS_EXTERNAL = "--external"; + + static public final String ARGS_LOCATION = "--location"; + + static public final String ARGS_DISPLAY = "--display"; + + static public final String ARGS_BGCOLOR = "--bgcolor"; + + static public final String ARGS_PRESENT = "--present"; + + static public final String ARGS_EXCLUSIVE = "--exclusive"; + + static public final String ARGS_STOP_COLOR = "--stop-color"; + + static public final String ARGS_HIDE_STOP = "--hide-stop"; + + /** + * Allows the user or PdeEditor to set a specific sketch folder path. + *

+ * Used by PdeEditor to pass in the location where saveFrame() + * and all that stuff should write things. + */ + static public final String ARGS_SKETCH_FOLDER = "--sketch-path"; + + /** + * When run externally to a PdeEditor, + * this is sent by the applet when it quits. + */ + //static public final String EXTERNAL_QUIT = "__QUIT__"; + static public final String EXTERNAL_STOP = "__STOP__"; + + /** + * When run externally to a PDE Editor, this is sent by the applet + * whenever the window is moved. + *

+ * This is used so that the editor can re-open the sketch window + * in the same position as the user last left it. + */ + static public final String EXTERNAL_MOVE = "__MOVE__"; + + /** true if this sketch is being run by the PDE */ + boolean external = false; + + + ////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////// + + /** + * Required empty constructor. + */ + public PApplet() { + + } + + + public PSurface getSurface() { + return surface; + } + + + public Context getContext() { + return surface.getContext(); + } + + + public Activity getActivity() { + return surface.getActivity(); + } + + + public void initSurface(AppComponent component, SurfaceHolder holder) { + parentLayout = -1; + initSurface(null, null, null, component, holder); + } + + + public void initSurface(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState, + AppComponent component, SurfaceHolder holder) { + if (DEBUG) println("initSurface() happening here: " + Thread.currentThread().getName()); + + component.initDimensions(); + displayWidth = component.getDisplayWidth(); + displayHeight = component.getDisplayHeight(); + displayDensity = component.getDisplayDensity(); + + handleSettings(); + + boolean parentSize = false; + if (parentLayout == -1) { + if (fullScreen || width == -1 || height == -1) { + // Either sketch explicitly set to full-screen mode, or not + // size/fullScreen provided, so sketch uses the entire display + width = displayWidth; + height = displayHeight; + } + } else { + if (fullScreen || width == -1 || height == -1) { + // Dummy weight and height to initialize the PGraphics, will be resized + // when the view associated to the parent layout is created + width = 100; + height = 100; + parentSize = true; + } + } + + pixelWidth = width * pixelDensity; + pixelHeight = height * pixelDensity; + + String rendererName = sketchRenderer(); + if (DEBUG) println("Renderer " + rendererName); + g = makeGraphics(width, height, rendererName, true); + if (DEBUG) println("Created renderer"); + surface = g.createSurface(component, holder, false); + if (DEBUG) println("Created surface"); + + if (parentLayout == -1) { + setFullScreenVisibility(); + surface.initView(width, height); + } else { + surface.initView(width, height, parentSize, + inflater, container, savedInstanceState); + } + + finished = false; // just for clarity + // this will be cleared by draw() if it is not overridden + looping = true; + redraw = true; // draw this guy once + + sketchPath = surface.getFilesDir().getAbsolutePath(); + + surface.startThread(); + + if (DEBUG) println("Done with init surface"); + } + + + private void setFullScreenVisibility() { + if (fullScreen) { + runOnUiThread(new Runnable() { + @Override + public void run() { + int visibility; + if (SDK < 19) { + // Pre-4.4 + visibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + } else { + // 4.4 and higher. Integer instead of constants defined in View so it can + // build with SDK < 4.4 + visibility = 256 | // View.SYSTEM_UI_FLAG_LAYOUT_STABLE + 512 | // View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + 1024 | // View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + 4 | // View.SYSTEM_UI_FLAG_FULLSCREEN + 4096; // View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + // However, this visibility does not fix a bug where the navigation area + // turns black after resuming the app: + // https://code.google.com/p/android/issues/detail?id=170752 + } + surface.setSystemUiVisibility(visibility); + } + }); + } + } + + + public void onResume() { + if (DEBUG) System.out.println("PApplet.onResume() called"); + if (parentLayout == -1) { + setFullScreenVisibility(); + } + + handleMethods("resume"); + + // Don't call resume() when the app is starting and setup() has not been called yet: + // https://github.com/processing/processing-android/issues/274 + // Also, there is no need to call resume() from anywhere else (for example, from + // onStart) since onResume() is always called in the activity lifecyle: + // https://developer.android.com/guide/components/activities/activity-lifecycle.html + if (0 < frameCount) { + resume(); + } + + // Set the handledBackPressed to true to handle the situation where a fragment is popping + // right back after pressing the back button (the sketch does not exit). + handledBackPressed = true; + + if (g != null) { + g.restoreState(); + } + + surface.resumeThread(); + } + + + public void onPause() { + surface.pauseThread(); + + // Make sure that the keyboard is not left open after leaving the app + closeKeyboard(); + + if (g != null) { + g.saveState(); + } + + handleMethods("pause"); + + pause(); // handler for others to write + } + + + public void onStart() { + start(); + } + + + public void onStop() { + stop(); + } + + + public void onCreate(Bundle savedInstanceState) { + create(); + } + + + public void onDestroy() { + handleMethods("onDestroy"); + + surface.stopThread(); + + dispose(); + } + + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + handleMethods("onActivityResult", new Object[] { requestCode, resultCode, data }); + } + + + public void onNewIntent(Intent intent) { + handleMethods("onNewIntent", new Object[] { intent }); + } + + + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){ + + } + + + public boolean onOptionsItemSelected(MenuItem item) { + return false; + } + + + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + + } + + + public boolean onContextItemSelected(MenuItem item) { + return false; + } + + + public void setHasOptionsMenu(boolean hasMenu) { + surface.setHasOptionsMenu(hasMenu); + } + + + synchronized public void onBackPressed() { + requestedBackPress = true; + } + + + public FragmentManager getFragmentManager() { + if (getActivity() != null) { + return getActivity().getFragmentManager(); + } + return null; + } + + + public Window getWindow(){ + if (getActivity() != null) { + return getActivity().getWindow(); + } + return null; + } + + + public void startActivity(Intent intent) { + surface.startActivity(intent); + } + + + public void runOnUiThread(Runnable action) { + surface.runOnUiThread(action); + } + + + public boolean hasPermission(String permission) { + return surface.hasPermission(permission); + } + + + public void requestPermission(String permission) { + if (!hasPermission(permission)) { + reqPermissions.add(permission); + } + } + + + public void requestPermission(String permission, String callback) { + requestPermission(permission, callback, this); + } + + + public void requestPermission(String permission, String callback, Object target) { + registerWithArgs(callback, target, new Class[] { boolean.class }); + if (hasPermission(permission)) { + // If the app already has permission, still call the handle method as it + // may be doing some initialization + handleMethods(callback, new Object[] { true }); + } else { + permissionMethods.put(permission, callback); + // Accumulating permissions so they requested all at once at the end + // of draw. + reqPermissions.add(permission); + } + } + + + public void onRequestPermissionsResult(int requestCode, + String permissions[], + int[] grantResults) { + if (requestCode == PSurface.REQUEST_PERMISSIONS) { + for (int i = 0; i < grantResults.length; i++) { + boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED; + handlePermissionsResult(permissions[i], granted); + } + } + } + + + private void handlePermissionsResult(String permission, final boolean granted) { + String methodName = permissionMethods.get(permission); + final RegisteredMethods meth = registerMap.get(methodName); + if (meth != null) { + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + meth.handle(new Object[] { granted }); + } + }); + } + } + + + private void handlePermissions() { + if (0 < reqPermissions.size()) { + String[] req = reqPermissions.toArray(new String[reqPermissions.size()]); + surface.requestPermissions(req); + reqPermissions.clear(); + } + } + + synchronized private void handleBackPressed() { + if (requestedBackPress) { + requestedBackPress = false; + backPressed(); + if (!handledBackPressed) { + if (getActivity() != null) { + // Services don't have an activity associated to them, but back press could not be triggered for those anyways + getActivity().finish(); + } + handledBackPressed = false; + } + } + } + + /** + * @param method "size" or "fullScreen" + * @param args parameters passed to the function so we can show the user + * @return true if safely inside the settings() method + */ + boolean insideSettings(String method, Object... args) { + if (insideSettings) { + return true; + } + final String url = "https://processing.org/reference/" + method + "_.html"; + if (!external) { // post a warning for users of Eclipse and other IDEs + StringList argList = new StringList(args); + System.err.println("When not using the PDE, " + method + "() can only be used inside settings()."); + System.err.println("Remove the " + method + "() method from setup(), and add the following:"); + System.err.println("public void settings() {"); + System.err.println(" " + method + "(" + argList.join(", ") + ");"); + System.err.println("}"); + } + throw new IllegalStateException(method + "() cannot be used here, see " + url); + } + + + void handleSettings() { + insideSettings = true; + //Do stuff + settings(); + insideSettings = false; + } + + + public void settings() { + //It'll be empty. Will be overridden by user's sketch class. + } + + + final public int sketchWidth() { + return width; + } + + + final public int sketchHeight() { + return height; + } + + + final public String sketchRenderer() { + return renderer; + } + + + public int sketchSmooth() { + return smooth; + } + + + final public boolean sketchFullScreen() { + return fullScreen; + } + + + final public int sketchDisplay() { + return display; + } + + + final public String sketchOutputPath() { + return null; + } + + + final public OutputStream sketchOutputStream() { + return null; + } + + + final public int sketchWindowColor() { + return windowColor; + } + + + final public int sketchPixelDensity() { + return pixelDensity; + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + public void surfaceChanged() { + surfaceChanged = true; + g.surfaceChanged(); + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + /** + * Called by the sketch surface view, thought it could conceivably be called + * by Android as well. + */ + public void surfaceWindowFocusChanged(boolean hasFocus) { + focused = hasFocus; + if (focused) { + focusGained(); + } else { + focusLost(); + } + } + + + /** + * If you override this function without calling super.onTouchEvent(), + * then motionX, motionY, motionPressed, and motionEvent will not be set. + */ + public boolean surfaceTouchEvent(MotionEvent event) { + nativeMotionEvent(event); + return true; + } + + + public void surfaceKeyDown(int code, android.view.KeyEvent event) { + nativeKeyEvent(event); + } + + + public void surfaceKeyUp(int code, android.view.KeyEvent event) { + nativeKeyEvent(event); + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + /** + * Called by the browser or applet viewer to inform this applet that it + * should start its execution. It is called after the init method and + * each time the applet is revisited in a Web page. + *

+ * Called explicitly via the first call to PApplet.paint(), because + * PAppletGL needs to have a usable screen before getting things rolling. + */ + public void start() { + } + + + /** + * Called by the browser or applet viewer to inform + * this applet that it should stop its execution. + *

+ * Unfortunately, there are no guarantees from the Java spec + * when or if stop() will be called (i.e. on browser quit, + * or when moving between web pages), and it's not always called. + */ + public void stop() { + } + + + /** + * Developers can override here to save state. The 'paused' variable will be + * set before this function is called. + */ + public void pause() { + } + + + /** + * Developers can override here to restore state. The 'paused' variable + * will be cleared before this function is called. + */ + public void resume() { + } + + + public void backPressed() { + handledBackPressed = false; + } + + ////////////////////////////////////////////////////////////// + + + /** Map of registered methods, stored by name. */ + HashMap registerMap = + new HashMap(); + + + class RegisteredMethods { + int count; + Object[] objects; + // Because the Method comes from the class being called, + // it will be unique for most, if not all, objects. + Method[] methods; + Object[] emptyArgs = new Object[] { }; + + + void handle() { + handle(emptyArgs); + } + + + void handle(Object[] args) { + for (int i = 0; i < count; i++) { + try { + methods[i].invoke(objects[i], args); + } catch (Exception e) { + // check for wrapped exception, get root exception + Throwable t; + if (e instanceof InvocationTargetException) { + InvocationTargetException ite = (InvocationTargetException) e; + t = ite.getCause(); + } else { + t = e; + } + // check for RuntimeException, and allow to bubble up + if (t instanceof RuntimeException) { + // re-throw exception + throw (RuntimeException) t; + } else { + // trap and print as usual + t.printStackTrace(); + } + } + } + } + + + void add(Object object, Method method) { + if (findIndex(object) == -1) { + if (objects == null) { + objects = new Object[5]; + methods = new Method[5]; + + } else if (count == objects.length) { + objects = (Object[]) PApplet.expand(objects); + methods = (Method[]) PApplet.expand(methods); + } + objects[count] = object; + methods[count] = method; + count++; + } else { + die(method.getName() + "() already added for this instance of " + + object.getClass().getName()); + } + } + + + /** + * Removes first object/method pair matched (and only the first, + * must be called multiple times if object is registered multiple times). + * Does not shrink array afterwards, silently returns if method not found. + */ +// public void remove(Object object, Method method) { +// int index = findIndex(object, method); + public void remove(Object object) { + int index = findIndex(object); + if (index != -1) { + // shift remaining methods by one to preserve ordering + count--; + for (int i = index; i < count; i++) { + objects[i] = objects[i+1]; + methods[i] = methods[i+1]; + } + // clean things out for the gc's sake + objects[count] = null; + methods[count] = null; + } + } + + +// protected int findIndex(Object object, Method method) { + protected int findIndex(Object object) { + for (int i = 0; i < count; i++) { + if (objects[i] == object) { +// if (objects[i] == object && methods[i].equals(method)) { + //objects[i].equals() might be overridden, so use == for safety + // since here we do care about actual object identity + //methods[i]==method is never true even for same method, so must use + // equals(), this should be safe because of object identity + return i; + } + } + return -1; + } + } + + + /** + * Register a built-in event so that it can be fired for libraries, etc. + * Supported events include: + *