1
0
Fork 0
mirror of https://github.com/vanitasvitae/Spherical synced 2024-11-22 12:22:08 +01:00

Begin work on several bugfixes

This commit is contained in:
Simon Leistikow 2018-02-20 12:57:36 +01:00
parent 5b33d05f80
commit 7932d00cdc
9 changed files with 234 additions and 170 deletions

View file

@ -0,0 +1,31 @@
package de.trac.spherical;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
public class BroadcastHelper {
public enum BroadcastType {
PROGRESS_START,
PROGRESS_FINISHED
}
private static final String INTENT_ACTION = "de.spherical.internal";
private static final String INTENT_KEY_NAME = "broadcast_type";
public static final IntentFilter INTENT_FILTER = new IntentFilter(INTENT_ACTION); // TODO: useful?
static public void broadcast(Context context, BroadcastType type) {
Intent intent = new Intent(INTENT_ACTION);
intent.putExtra(INTENT_KEY_NAME, type.name());
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
static public BroadcastType getBroadcastType(Intent intent) {
if(!INTENT_ACTION.equals(intent.getAction()))
throw new IllegalArgumentException("Not a valid intent");
return (BroadcastType) intent.getSerializableExtra(INTENT_KEY_NAME);
}
}

View file

@ -52,10 +52,6 @@ public class FlatFragment extends ImageFragment {
return super.onOptionsItemSelected(item);
}
private MainActivity getMainActivity() {
return (MainActivity) getActivity();
}
@Override
public void updateBitmap(Bitmap bitmap) {
if (imageView == null) {

View file

@ -3,11 +3,11 @@ package de.trac.spherical;
import android.graphics.Bitmap;
import android.support.v4.app.Fragment;
/**
* Created by vanitas on 19.09.17.
*/
public abstract class ImageFragment extends Fragment {
public abstract void updateBitmap(Bitmap bitmap);
public MainActivity getMainActivity() {
return (MainActivity) getActivity();
}
}

View file

@ -1,6 +1,9 @@
package de.trac.spherical;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
@ -12,12 +15,13 @@ import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuInflater;
@ -30,10 +34,14 @@ import android.widget.RelativeLayout;
import android.widget.Toast;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import de.trac.spherical.parser.PhotoSphereMetadata;
import de.trac.spherical.parser.PhotoSphereParser;
import de.trac.spherical.rendering.PhotoSphereSurfaceView;
import static de.trac.spherical.BroadcastHelper.BroadcastType.PROGRESS_FINISHED;
import static de.trac.spherical.BroadcastHelper.BroadcastType.PROGRESS_START;
public class MainActivity extends AppCompatActivity {
@ -42,34 +50,73 @@ public class MainActivity extends AppCompatActivity {
public static final String MIME_PHOTO_SPHERE = "application/vnd.google.panorama360+jpg";
private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 387;
private FragmentManager fm;
// UI
private FloatingActionButton fab;
private FloatingActionButton actionButton;
private Toolbar toolbar;
private GestureDetectorCompat gestureDetector;
private ProgressFragment progressFragment = new ProgressFragment();
private FlatFragment flatFragment = new FlatFragment();
private SphereFragment sphereFragment = new SphereFragment();
private ImageFragment currentlyShownImageFragment;
// Cache
private Intent cachedIntent;
private Bitmap bitmap;
private PhotoSphereMetadata metadata;
// Fragments
private enum FragmentType {
PROGRESS("progress"),
SPHERE("sphere"),
FLAT("flat");
/// Optional tag.
String tag;
FragmentType(String tag) {
this.tag = tag;
}
}
private FragmentManager fragmentManager;
private Map<FragmentType, Fragment> fragments;
// Broadcast handling.
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (BroadcastHelper.getBroadcastType(intent)) {
case PROGRESS_START:
break;
case PROGRESS_FINISHED:
break;
default:
break;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupUI();
fm = getSupportFragmentManager();
// Init fragments.
fragmentManager = getSupportFragmentManager();
fragments = new HashMap<>();
fragments.put(FragmentType.PROGRESS, new ProgressFragment());
fragments.put(FragmentType.SPHERE, new SphereFragment());
fragments.put(FragmentType.FLAT, new FlatFragment());
// Intent handling.
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, BroadcastHelper.INTENT_FILTER);
handleIntent(getIntent());
}
@Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
super.onDestroy();
}
/**
* Initialize the user interface.
*/
@ -81,12 +128,12 @@ public class MainActivity extends AppCompatActivity {
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) toolbar.getLayoutParams();
lp.topMargin += getStatusBarHeight();
toolbar.bringToFront();
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
actionButton = (FloatingActionButton) findViewById(R.id.fab);
actionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sphereFragment.toggleUseTouchInput();
displayUI(false);
setUIVisibility(false);
}
});
@ -98,7 +145,7 @@ public class MainActivity extends AppCompatActivity {
gestureDetector = new GestureDetectorCompat(this, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapConfirmed(MotionEvent event) {
displayUI(!fab.isShown());
setUIVisibility(!actionButton.isShown());
return true;
}
@ -113,7 +160,7 @@ public class MainActivity extends AppCompatActivity {
switch (intent.getAction()) {
//Image was sent into the app
case Intent.ACTION_SEND:
showProgressFragment();
showFragment(FragmentType.SPHERE);
checkPermissionAndHandleSentImage(intent);
break;
@ -180,73 +227,35 @@ public class MainActivity extends AppCompatActivity {
}
// process image asynchronous.
new AsyncTask<Uri, Void, Void>() {
@Override
protected Void doInBackground(Uri... params) {
Uri uri = params[0];
try {
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
metadata = PhotoSphereParser.parse(getContentResolver().openInputStream(uri));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void result) {
switch (type) {
case MIME_PHOTO_SPHERE:
displayPhotoSphere();
break;
default:
if (metadata != null) {
displayPhotoSphere();
} else {
displayFlatImage();
}
break;
}
}
}.execute(imageUri);
new LoadImageTask(this, getContentResolver(), imageUri).execute();
}
/**
* Show/hide the FAB and toolbar.
* @param display show/hide
* @param visible show/hide
*/
private void displayUI(boolean display) {
if (display) {
fab.show();
private void setUIVisibility(boolean visible) {
if (visible) {
actionButton.show();
toolbar.setVisibility(View.VISIBLE);
} else {
fab.setVisibility(View.INVISIBLE);
actionButton.setVisibility(View.INVISIBLE);
toolbar.setVisibility(View.GONE);
}
}
private void showProgressFragment() {
fm.beginTransaction().replace(R.id.container_fragment, progressFragment, "prog").commit();
this.currentlyShownImageFragment = null;
}
private void showFlatImageFragment() {
fm.beginTransaction().replace(R.id.container_fragment, flatFragment, "flat").commit();
this.currentlyShownImageFragment = flatFragment;
}
private void showSphereFragment() {
fm.beginTransaction().replace(R.id.container_fragment, sphereFragment, "sphere").commit();
this.currentlyShownImageFragment = sphereFragment;
/**
* Will show the fragment of the given type.
* @param type fragment to be shown
*/
private void showFragment(FragmentType type) {
fragmentManager.beginTransaction().replace(R.id.container_fragment, fragments.get(type), type.tag).commit();
}
@Override
public void onResume() {
super.onResume();
displayUI(true);
setUIVisibility(true);
}
@Override
@ -271,7 +280,7 @@ public class MainActivity extends AppCompatActivity {
* Display a photo sphere.
*/
public void displayPhotoSphere() {
showSphereFragment();
showFragment(FragmentType.SPHERE);
currentlyShownImageFragment.updateBitmap(bitmap);
}
@ -279,7 +288,7 @@ public class MainActivity extends AppCompatActivity {
* Display a flat bitmap.
*/
public void displayFlatImage() {
showFlatImageFragment();
showFragment(FragmentType.FLAT);
currentlyShownImageFragment.updateBitmap(bitmap);
}
@ -301,7 +310,59 @@ public class MainActivity extends AppCompatActivity {
return gestureDetector;
}
public Bitmap getBitmap() {
return bitmap;
/**
* Dedicated async tasks to load an image.
* Takes a progress reporter and informs it about the changes.
*/
private static class LoadImageTask extends AsyncTask<Void, Void, Void> {
private ContentResolver contentResolver;
private Uri uri;
private Bitmap bitmap;
private PhotoSphereMetadata metadata;
private Context context;
LoadImageTask(Context context, ContentResolver contentResolver, Uri uri) {
this.context = context;
this.contentResolver = contentResolver;
this.uri = uri;
}
@Override
protected void onPreExecute() {
BroadcastHelper.broadcast(context, PROGRESS_START);
}
@Override
protected Void doInBackground(Void... params) {
try {
bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri));
metadata = PhotoSphereParser.parse(contentResolver.openInputStream(uri));
} catch (IOException | OutOfMemoryError e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void result) {
BroadcastHelper.broadcast(context, PROGRESS_FINISHED);
switch (type) {
case MIME_PHOTO_SPHERE:
showFragment(FragmentType.SPHERE);
break;
default:
if (metadata != null) {
displayPhotoSphere();
} else {
displayFlatImage();
}
break;
}
}
}
}

View file

@ -6,9 +6,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by vanitas on 18.09.17.
*/
public class ProgressFragment extends Fragment {
@Override

View file

@ -64,10 +64,6 @@ public class SphereFragment extends ImageFragment implements View.OnTouchListene
return super.onOptionsItemSelected(item);
}
private MainActivity getMainActivity() {
return (MainActivity) getActivity();
}
@Override
public void updateBitmap(Bitmap bitmap) {
if (surfaceView == null) {
@ -76,7 +72,7 @@ public class SphereFragment extends ImageFragment implements View.OnTouchListene
surfaceView.setBitmap(bitmap);
}
public void toggleUseTouchInput() {
surfaceView.setUseTouchInput(!surfaceView.getUseTouchInput());
public PhotoSphereSurfaceView getSurfaceView() {
return surfaceView;
}
}

View file

@ -11,58 +11,14 @@ import android.util.Log;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
import static android.opengl.GLES20.GL_COMPILE_STATUS;
import static android.opengl.GLES20.GL_CULL_FACE;
import static android.opengl.GLES20.GL_CW;
import static android.opengl.GLES20.GL_DEPTH_BUFFER_BIT;
import static android.opengl.GLES20.GL_DEPTH_TEST;
import static android.opengl.GLES20.GL_FLOAT;
import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
import static android.opengl.GLES20.GL_LINEAR;
import static android.opengl.GLES20.GL_LINK_STATUS;
import static android.opengl.GLES20.GL_NEAREST;
import static android.opengl.GLES20.GL_TEXTURE0;
import static android.opengl.GLES20.GL_TEXTURE_2D;
import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
import static android.opengl.GLES20.GL_TRIANGLES;
import static android.opengl.GLES20.GL_TRUE;
import static android.opengl.GLES20.GL_UNSIGNED_SHORT;
import static android.opengl.GLES20.GL_VERTEX_SHADER;
import static android.opengl.GLES20.glActiveTexture;
import static android.opengl.GLES20.glAttachShader;
import static android.opengl.GLES20.glBindTexture;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glCompileShader;
import static android.opengl.GLES20.glCreateProgram;
import static android.opengl.GLES20.glCreateShader;
import static android.opengl.GLES20.glDeleteProgram;
import static android.opengl.GLES20.glDeleteShader;
import static android.opengl.GLES20.glDisableVertexAttribArray;
import static android.opengl.GLES20.glDrawElements;
import static android.opengl.GLES20.glEnable;
import static android.opengl.GLES20.glEnableVertexAttribArray;
import static android.opengl.GLES20.glFrontFace;
import static android.opengl.GLES20.glGenTextures;
import static android.opengl.GLES20.glGetAttribLocation;
import static android.opengl.GLES20.glGetProgramInfoLog;
import static android.opengl.GLES20.glGetProgramiv;
import static android.opengl.GLES20.glGetShaderInfoLog;
import static android.opengl.GLES20.glGetShaderiv;
import static android.opengl.GLES20.glGetUniformLocation;
import static android.opengl.GLES20.glLinkProgram;
import static android.opengl.GLES20.glShaderSource;
import static android.opengl.GLES20.glTexParameteri;
import static android.opengl.GLES20.glUniform1i;
import static android.opengl.GLES20.glUniformMatrix4fv;
import static android.opengl.GLES20.glUseProgram;
import static android.opengl.GLES20.glVertexAttribPointer;
import static android.opengl.GLES20.glViewport;
import de.trac.spherical.BroadcastHelper;
import static android.opengl.GLES20.*;
public class PhotoSphereRenderer implements GLSurfaceView.Renderer {
private static final String TAG = "PhotoSphereRenderer";
/**
* Default vertex shader.
*
@ -97,7 +53,7 @@ public class PhotoSphereRenderer implements GLSurfaceView.Renderer {
// Sphere configuration.
public static final int SPHERE_POLY_COUNT_X = 32;
public static final int SPHERE_POLY_COUNT_Y = 32;
public static final float SPHERE_RADIUS = 10.0f;
public static final float SPHERE_RADIUS = 1.0f;
// Store a photoSphereGeometry geometry as framework for the photo texture.
private PhotoSphereGeometry photoSphereGeometry = null;
@ -105,14 +61,14 @@ public class PhotoSphereRenderer implements GLSurfaceView.Renderer {
// Store projection matrix.
private float projectionMatrix[] = new float [16];
// Store modelview matrix.
// Store model matrix.
private float modelMatrix[] = new float [16];
// Store view matrix.
private float viewMatrix [] = new float [16];
// Store the model view projection matrix.
private float mvpMatrix [] = new float [16];
private float mvpMatrix [] = new float [32];
// This array contains the current view matrix {x, y, width, height).
private int view [] = null;
@ -129,8 +85,8 @@ public class PhotoSphereRenderer implements GLSurfaceView.Renderer {
// Store texture.
private final int textureID [] = new int[1];
// Store bitmap for lazy loading.
private Bitmap bitmap = null;
// Store requested bitmap for lazy loading.
private Bitmap requestedBitmap = null;
// Store input handler instance to determine transformation.
private PhotoSphereSurfaceView surfaceView;
@ -155,20 +111,13 @@ public class PhotoSphereRenderer implements GLSurfaceView.Renderer {
public void onDrawFrame(GL10 unused) {
// Upload texture, if necessary.
if(bitmap != null) {
glBindTexture(GL_TEXTURE_2D, textureID[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// Release bitmap for garbage collection.
bitmap = null;
}
if(requestedBitmap != null)
uploadImage();
// Update transformation matrix.
Matrix.multiplyMM(mvpMatrix, 0, surfaceView.getRotationMatrix(), 0, modelMatrix, 0);
Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, mvpMatrix, 0);
//Matrix.multiplyMM(mvpMatrix, 0, surfaceView.getRotationMatrix(), 0, modelMatrix, 0);
//Matrix.multiplyMM(mvpMatrix, 16, viewMatrix, 0, mvpMatrix, 0);
Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, surfaceView.getRotationMatrix(), 0);
// Draw the frame.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@ -218,10 +167,11 @@ public class PhotoSphereRenderer implements GLSurfaceView.Renderer {
photoSphereGeometry = new PhotoSphereGeometry(SPHERE_RADIUS, SPHERE_POLY_COUNT_X, SPHERE_POLY_COUNT_Y);
// Set OpenGL state.
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CW);
//glEnable(GL_CULL_FACE);
glDisable(GL_CULL_FACE);
//glFrontFace(GL_CW);
glActiveTexture(GL_TEXTURE0);
// Build shader program.
@ -231,17 +181,51 @@ public class PhotoSphereRenderer implements GLSurfaceView.Renderer {
glGenTextures(1, textureID, 0);
// Initialize matrices.
Matrix.setRotateM(modelMatrix, 0, 90, 1.0f, 0.0f, 0.0f);
//Matrix.setRotateM(modelMatrix, 0, 90, 1.0f, 0.0f, 0.0f);
Matrix.setLookAtM(viewMatrix, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
}
/**
* Requests the renderer to uploads the image data of the given bitmap as texture.
* Requests the renderer to uploads the image data of the given requestedBitmap as texture.
* May not be done immediately.
* @param bitmap Bitmap to be set as texture
*/
public void requestBitmapUpload(Bitmap bitmap) {
this.bitmap = bitmap;
this.requestedBitmap = bitmap;
surfaceView.requestRender();
}
/**
* Uploads a requested image.
*/
private void uploadImage() {
// Tell the main activity we are going to do an expensive operation.
BroadcastHelper.broadcast(surfaceView.getContext(), BroadcastHelper.BroadcastType.PROGRESS_START);
// Check if requestedBitmap needs to be downsampled.
int [] maxTextureSize = new int[1];
glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
float maxSize = Math.max(requestedBitmap.getWidth(), requestedBitmap.getHeight());
if(maxSize > maxTextureSize[0]) { // TODO: implement tiling technique
Log.w(TAG, "Image too big, exceeding " + maxTextureSize[0] + " : will be downsampled");
int newWidth = (int) (requestedBitmap.getWidth() * maxTextureSize[0] / maxSize);
int newHeight = (int) (requestedBitmap.getHeight() * maxTextureSize[0] / maxSize);
requestedBitmap = Bitmap.createScaledBitmap(requestedBitmap, newWidth, newHeight, true);
}
// Upload texture.
glBindTexture(GL_TEXTURE_2D, textureID[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLUtils.texImage2D(GL_TEXTURE_2D, 0, requestedBitmap, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// Release requestedBitmap for garbage collection.
requestedBitmap = null;
// Tell main activity we are done.
BroadcastHelper.broadcast(surfaceView.getContext(), BroadcastHelper.BroadcastType.PROGRESS_FINISHED);
}
/**
@ -282,8 +266,8 @@ public class PhotoSphereRenderer implements GLSurfaceView.Renderer {
int[] linkStatus = new int[1];
glGetProgramiv(program, GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GL_TRUE) {
Log.e("Shader", "Could not link program: ");
Log.e("Shader", glGetProgramInfoLog(program));
Log.e(TAG, "Could not link program: ");
Log.e(TAG, glGetProgramInfoLog(program));
glDeleteProgram(program);
throw new RuntimeException("Could not link program");
}
@ -323,8 +307,8 @@ public class PhotoSphereRenderer implements GLSurfaceView.Renderer {
int[] compiled = new int[1];
glGetShaderiv(shader, GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e("Shader", "Could not compile shader " + type + ":");
Log.e("Shader", glGetShaderInfoLog(shader));
Log.e(TAG, "Could not compile shader " + type + ":");
Log.e(TAG, glGetShaderInfoLog(shader));
glDeleteShader(shader);
shader = 0;
}

View file

@ -30,7 +30,7 @@ public class PhotoSphereSurfaceView extends GLSurfaceView implements SensorEvent
private final float rayDirection [] = new float[4];
//
float oldAngleXZ, oldAngleY;
private float oldAngleXZ, oldAngleY;
// The renderer used by this view.
private PhotoSphereRenderer renderer;
@ -64,7 +64,7 @@ public class PhotoSphereSurfaceView extends GLSurfaceView implements SensorEvent
if(!useTouchInput)
return true;
/*
// Retrieve ray in world space.
renderer.getRay(event.getX(), event.getY(), rayStart, rayDirection);
@ -94,7 +94,7 @@ public class PhotoSphereSurfaceView extends GLSurfaceView implements SensorEvent
synchronized (rotationMatrix) {
Matrix.translateM(rotationMatrix, 0, px, py, pz);
}
*/
/*
// Calculate angles.
float angleY = (float) Math.toDegrees(Math.atan2(pz, px));

View file

@ -1,4 +1,3 @@
#Tue Dec 19 17:35:15 CET 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME