Related
Good day, I am beginning to learn Java and one of the first projects that I thought might help me to begin learning is by designing a watch face for WearOS, I am an artist/illustrator (currently going back to school to learn computer programming) by birth and school, so I am currently attempting work that I have some idea about.
My question is this. I have designed and implemented bitmaps that are rotating for the watch's hands, and have tried to center them with "mCenterX and Y" and have even manually tried to center them with canvas.translate, and entering in coordinates. Sorry if this is an absolute newbie question, but I have been Googling and attempting for at least a week. (Would include image of floating hands, but can't embed.)
The "Hour Hand" is the only one that I have currently working, and I only was able to do that with manually entering the coordinates with canvas.translate. The other hands will come into frame every-once-in-a-while then float out of frame, rotating around some "unknown" center point.
Code is below: (I have included all code pertaining to the bitmaps for the hour/minute/second hands, to see if I have something that is conflicting. I am using bits of code from various projects, which probably explains the off-centeredness, but I don't have the knowledge yet to grasp why, which is my ultimate goal here.)
Thank you for looking and replying! If you need additional information, let me know.
private class Engine extends CanvasWatchFaceService.Engine {
private static final float HOUR_STROKE_WIDTH = 5f;
private static final float MINUTE_STROKE_WIDTH = 3f;
private static final float SECOND_TICK_STROKE_WIDTH = 2f;
private static final float CENTER_GAP_AND_CIRCLE_RADIUS = 4f;
private static final int SHADOW_RADIUS = 6;
/* Handler to update the time once a second in interactive mode. */
private final Handler mUpdateTimeHandler = new EngineHandler(this);
private Calendar mCalendar;
private final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
mCalendar.setTimeZone(TimeZone.getDefault());
invalidate();
}
};
private boolean mRegisteredTimeZoneReceiver = false;
private boolean mMuteMode;
private float mCenterX;
private float mCenterY;
private int mWatchHandColor;
private int mWatchHandHighlightColor;
private int mWatchHandShadowColor;
private Paint mHourPaint;
private Paint mMinutePaint;
private Paint mSecondPaint;
private Paint mTickAndCirclePaint;
private Paint mBackgroundPaint;
private Bitmap mHourBitmap;
private Bitmap mMinuteBitmap;
private Bitmap mSecondBitmap;
private Bitmap mBackgroundBitmap;
private Bitmap mGrayBackgroundBitmap;
private void initializeWatchFace() {
/* Set defaults for colors */
mWatchHandColor = Color.WHITE;
mWatchHandHighlightColor = Color.RED;
mWatchHandShadowColor = Color.BLACK;
mHourPaint = new Paint();
mHourPaint.setColor(mWatchHandColor);
mHourPaint.setStrokeWidth(HOUR_STROKE_WIDTH);
mHourPaint.setAntiAlias(true);
mHourPaint.setStrokeCap(Paint.Cap.ROUND);
mHourPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
mMinutePaint = new Paint();
mMinutePaint.setColor(mWatchHandColor);
mMinutePaint.setStrokeWidth(MINUTE_STROKE_WIDTH);
mMinutePaint.setAntiAlias(true);
mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
mMinutePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
mSecondPaint = new Paint();
mSecondPaint.setColor(mWatchHandHighlightColor);
mSecondPaint.setStrokeWidth(SECOND_TICK_STROKE_WIDTH);
mSecondPaint.setAntiAlias(true);
mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
mSecondPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
mTickAndCirclePaint = new Paint();
mTickAndCirclePaint.setColor(mWatchHandColor);
mTickAndCirclePaint.setStrokeWidth(SECOND_TICK_STROKE_WIDTH);
mTickAndCirclePaint.setAntiAlias(true);
mTickAndCirclePaint.setStyle(Paint.Style.STROKE);
mTickAndCirclePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
}
private void updateWatchHandStyle() {
if (mAmbient) {
mHourPaint.setColor(Color.WHITE);
mMinutePaint.setColor(Color.WHITE);
mSecondPaint.setColor(Color.WHITE);
mTickAndCirclePaint.setColor(Color.WHITE);
mHourPaint.setAntiAlias(false);
mMinutePaint.setAntiAlias(false);
mSecondPaint.setAntiAlias(false);
mTickAndCirclePaint.setAntiAlias(false);
mHourPaint.clearShadowLayer();
mMinutePaint.clearShadowLayer();
mSecondPaint.clearShadowLayer();
mTickAndCirclePaint.clearShadowLayer();
} else {
mHourPaint.setColor(mWatchHandColor);
mMinutePaint.setColor(mWatchHandColor);
mSecondPaint.setColor(mWatchHandHighlightColor);
mTickAndCirclePaint.setColor(mWatchHandColor);
mHourPaint.setAntiAlias(true);
mMinutePaint.setAntiAlias(true);
mSecondPaint.setAntiAlias(true);
mTickAndCirclePaint.setAntiAlias(true);
mHourPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
mMinutePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
mSecondPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
mTickAndCirclePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
}
#Override
public void onInterruptionFilterChanged(int interruptionFilter) {
super.onInterruptionFilterChanged(interruptionFilter);
boolean inMuteMode = (interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE);
/* Dim display in mute mode. */
if (mMuteMode != inMuteMode) {
mMuteMode = inMuteMode;
mHourPaint.setAlpha(inMuteMode ? 100 : 255);
mMinutePaint.setAlpha(inMuteMode ? 100 : 255);
mSecondPaint.setAlpha(inMuteMode ? 80 : 255);
invalidate();
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
/*
* Find the coordinates of the center point on the screen, and ignore the window
* insets, so that, on round watches with a "chin", the watch face is centered on the
* entire screen, not just the usable portion.
*/
mCenterX = width / 2f;
mCenterY = height / 2f;
/*
* Calculate lengths of different hands based on watch screen size.
*/
mSecondHandLength = (float) (mCenterX * 0.875);
sMinuteHandLength = (float) (mCenterX * 0.75);
sHourHandLength = (float) (mCenterX * 0.5);
/* Scale loaded background image (more efficient) if surface dimensions change. */
float scale = ((float) width) / (float) mBackgroundBitmap.getWidth();
mBackgroundBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,
(int) (mBackgroundBitmap.getWidth() * scale),
(int) (mBackgroundBitmap.getHeight() * scale), false);
float scale2 = ((float) width) / (float) mSecondBitmap.getWidth();
mSecondBitmap = Bitmap.createScaledBitmap(mSecondBitmap,
12,
222, false);
float scale3 = ((float) width) / (float) mHourBitmap.getWidth();
mHourBitmap = Bitmap.createScaledBitmap(mHourBitmap,
12,139,false);
float scale4 = ((float) width) / (float) mMinuteBitmap.getWidth();
mMinuteBitmap = Bitmap.createScaledBitmap(mMinuteBitmap,
12,
162, false);
private void drawWatchFace(Canvas canvas) {
/*
* Draw ticks. Usually you will want to bake this directly into the photo, but in
* cases where you want to allow users to select their own photos, this dynamically
* creates them on top of the photo.
*/
float innerTickRadius = mCenterX - 10;
float outerTickRadius = mCenterX;
for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
float tickRot = (float) (tickIndex * Math.PI * 2 / 12);
float innerX = (float) Math.sin(tickRot) * innerTickRadius;
float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
float outerX = (float) Math.sin(tickRot) * outerTickRadius;
float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
canvas.drawLine(mCenterX + innerX, mCenterY + innerY,
mCenterX + outerX, mCenterY + outerY, mTickAndCirclePaint);
}
/*
* These calculations reflect the rotation in degrees per unit of time, e.g.,
* 360 / 60 = 6 and 360 / 12 = 30.
*/
final float seconds =
(mCalendar.get(Calendar.SECOND) + mCalendar.get(Calendar.MILLISECOND) / 1000f);
final float secondsRotation = seconds * 6f;
final float minutesRotation = mCalendar.get(Calendar.MINUTE) * 6f;
final float hourHandOffset = mCalendar.get(Calendar.MINUTE) / 2f;
final float hoursRotation = (mCalendar.get(Calendar.HOUR) * 30) + hourHandOffset;
/*
* Save the canvas state before we can begin to rotate it.
*/
canvas.save();
canvas.rotate(hoursRotation, mCenterX, mCenterY);
Matrix matrixHour = new Matrix();
matrixHour.setRotate(0, mCenterX, mCenterY);
canvas.translate(175, 68);
canvas.drawBitmap(mHourBitmap, matrixHour, mHourPaint);
canvas.rotate(minutesRotation - hoursRotation, mCenterX, mCenterY);
Matrix matrixMinute = new Matrix();
matrixMinute.setRotate(0, mCenterX, mCenterY);
canvas.translate(175, -60);
canvas.drawBitmap(mMinuteBitmap, matrixMinute, mMinutePaint);
/*
* Ensure the "seconds" hand is drawn only when we are in interactive mode.
* Otherwise, we only update the watch face once a minute.
*/
if (!mAmbient) {
canvas.rotate(secondsRotation - minutesRotation, mCenterX, mCenterY);
Matrix matrix = new Matrix();
matrixHour.setRotate(0, mCenterX, mCenterY);
canvas.translate(-175,35);
canvas.drawBitmap(mSecondBitmap, matrix, mSecondPaint);
}
/* Restore the canvas' original orientation. */
canvas.restore();
}
I am trying to touch and drag the card and update its position. Firstly, I want the card class to detect the touch drag event when I click on the card.
I have provided the code below for the card class and want help on how to detect touch event. I want to know what methods I should add and how to implement the x and y coordinates.
public class Card extends Sprite {
// /////////////////////////////////////////////////////////////////////////
// Properties:
// /////////////////////////////////////////////////////////////////////////
// Define the default card width and height
private static final int DEFAULT_CARD_WIDTH = 180;
private static final int DEFAULT_CARD_HEIGHT = 260;
// Define the common card base
private Bitmap mCardBase;
// Define the card portrait image
private Bitmap mCardPortrait;
// Define the card digit images
private Bitmap[] mCardDigits = new Bitmap[10];
// Define the offset locations and scaling for the card portrait
// card attack and card health values - all measured relative
// to the centre of the object as a percentage of object size
private Vector2 mAttackOffset = new Vector2(-0.68f, -0.84f);
private Vector2 mAttackScale = new Vector2(0.1f, 0.1f);
private Vector2 mHealthOffset = new Vector2(0.72f, -0.84f);
private Vector2 mHealthScale = new Vector2(0.1f, 0.1f);
private Vector2 mPortraitOffset = new Vector2(0.0f, 0.3f);
private Vector2 mPortraitScale = new Vector2(0.55f, 0.55f);
// Define the health and attack values
private int mAttack;
private int mHealth;
// /////////////////////////////////////////////////////////////////////////
// Constructors
// /////////////////////////////////////////////////////////////////////////
/**
* Create a new platform.
*
* #param x Centre y location of the platform
* #param y Centre x location of the platform
* #param gameScreen Gamescreen to which this platform belongs
*/
public Card(float x, float y, GameScreen gameScreen) {
super(x, y, DEFAULT_CARD_WIDTH, DEFAULT_CARD_HEIGHT, null, gameScreen);
AssetManager assetManager = gameScreen.getGame().getAssetManager();
// Store the common card base image
mCardBase = assetManager.getBitmap("CardBackground");
// Store the card portrait image
mCardPortrait = assetManager.getBitmap("CardPortrait");
// Store each of the damage/health digits
for(int digit = 0; digit <= 9; digit++)
mCardDigits[digit] = assetManager.getBitmap(String.valueOf(digit));
// Set default attack and health values
mAttack = 1;
mHealth = 2;
}
// /////////////////////////////////////////////////////////////////////////
// Methods
// /////////////////////////////////////////////////////////////////////////
/**
* Draw the game platform
*
* #param elapsedTime Elapsed time information
* #param graphics2D Graphics instance
* #param layerViewport Game layer viewport
* #param screenViewport Screen viewport
*/
#Override
public void draw(ElapsedTime elapsedTime, IGraphics2D graphics2D,
LayerViewport layerViewport, ScreenViewport screenViewport) {
// Draw the portrait
drawBitmap(mCardPortrait, mPortraitOffset, mPortraitScale,
graphics2D, layerViewport, screenViewport);
// Draw the card base background
mBitmap = mCardBase;
super.draw(elapsedTime, graphics2D, layerViewport, screenViewport);
// Draw the attack value
drawBitmap(mCardDigits[mAttack], mAttackOffset, mAttackScale,
graphics2D, layerViewport, screenViewport);
// Draw the attack value
drawBitmap(mCardDigits[mHealth], mHealthOffset, mHealthScale,
graphics2D, layerViewport, screenViewport);
}
private BoundingBox bound = new BoundingBox();
/**
* Method to draw out a specified bitmap using a specific offset (relative to the
* position of this game object) and scaling (relative to the size of this game
* object).
*
* #param bitmap Bitmap to draw
* #param offset Offset vector
* #param scale Scaling vector
* #param graphics2D Graphics instance
* #param layerViewport Game layer viewport
* #param screenViewport Screen viewport
*/
private void drawBitmap(Bitmap bitmap, Vector2 offset, Vector2 scale,
IGraphics2D graphics2D, LayerViewport layerViewport, ScreenViewport screenViewport) {
// // Calculate a game layer bound for the bitmap to be drawn
// bound.set(position.x + mBound.halfWidth * offset.x,
// position.y + mBound.halfHeight * offset.y,
// mBound.halfWidth * scale.x,
// mBound.halfHeight * scale.y);
// Calculate the center position of the rotated offset point.
double rotation = Math.toRadians(-this.orientation);
float diffX = mBound.halfWidth * offset.x;
float diffY = mBound.halfHeight * offset.y;
float rotatedX = (float)(Math.cos(rotation) * diffX - Math.sin(rotation) * diffY + position.x);
float rotatedY = (float)(Math.sin(rotation) * diffX + Math.cos(rotation) * diffY + position.y);
// Calculate a game layer bound for the bitmap to be drawn
bound.set(rotatedX, rotatedY,
mBound.halfWidth * scale.x, mBound.halfHeight * scale.y);
// Draw out the specified bitmap using the calculated bound.
// The following code is based on the Sprite's draw method.
if (GraphicsHelper.getSourceAndScreenRect(
bound, bitmap, layerViewport, screenViewport, drawSourceRect, drawScreenRect)) {
// Build an appropriate transformation matrix
drawMatrix.reset();
float scaleX = (float) drawScreenRect.width() / (float) drawSourceRect.width();
float scaleY = (float) drawScreenRect.height() / (float) drawSourceRect.height();
drawMatrix.postScale(scaleX, scaleY);
drawMatrix.postRotate(orientation, scaleX * bitmap.getWidth()
/ 2.0f, scaleY * bitmap.getHeight() / 2.0f);
drawMatrix.postTranslate(drawScreenRect.left, drawScreenRect.top);
// Draw the bitmap
graphics2D.drawBitmap(bitmap, drawMatrix, null);
}
}
}
I was looking Cropper and image crooper open source App by Edmodo, it does a pretty good job except instead of Image cropping I want to use it to scale my images, my Logic Was everytime the cropper Window/View changes in size I would change the height and width of my imageview to match the Crop view but everytime I do this, nothing happens, but looking at LogCat it seems my height and width do change except the imageview still looks the same even after changes below is the Class, my changes are commented in the onSizeChanged()
/*
* Copyright 2013, Edmodo, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License.
* You may obtain a copy of the License in the LICENSE file, or at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package com.theartofdev.edmodo.cropper.cropwindow;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import com.theartofdev.edmodo.cropper.CropImageView;
import com.theartofdev.edmodo.cropper.cropwindow.edge.Edge;
import com.theartofdev.edmodo.cropper.cropwindow.handle.Handle;
import com.theartofdev.edmodo.cropper.util.AspectRatioUtil;
import com.theartofdev.edmodo.cropper.util.HandleUtil;
import com.theartofdev.edmodo.cropper.util.PaintUtil;
/**
* A custom View representing the crop window and the shaded background outside the crop window.
*/
public class CropOverlayView extends View {
//region: Fields and Consts
private static final int SNAP_RADIUS_DP = 6;
private static final float DEFAULT_SHOW_GUIDELINES_LIMIT = 100;
// Gets default values from PaintUtil, sets a bunch of values such that the
// corners will draw correctly
private static final float DEFAULT_CORNER_THICKNESS_DP = PaintUtil.getCornerThickness();
private static final float DEFAULT_LINE_THICKNESS_DP = PaintUtil.getLineThickness();
private static final float DEFAULT_CORNER_OFFSET_DP = (DEFAULT_CORNER_THICKNESS_DP / 2) - (DEFAULT_LINE_THICKNESS_DP / 2);
private static final float DEFAULT_CORNER_EXTENSION_DP = DEFAULT_CORNER_THICKNESS_DP / 2
+ DEFAULT_CORNER_OFFSET_DP;
private static final float DEFAULT_CORNER_LENGTH_DP = 20;
private static final int GUIDELINES_ON_TOUCH = 1;
private static final int GUIDELINES_ON = 2;
private static RectF mRectF = new RectF();
/**
* The Paint used to draw the white rectangle around the crop area.
*/
private Paint mBorderPaint;
/**
* The Paint used to draw the guidelines within the crop area when pressed.
*/
private Paint mGuidelinePaint;
/**
* The Paint used to draw the corners of the Border
*/
private Paint mCornerPaint;
/**
* The Paint used to darken the surrounding areas outside the crop area.
*/
private Paint mBackgroundPaint;
/**
* The bounding box around the Bitmap that we are cropping.
*/
private Rect mBitmapRect;
// The radius of the touch zone (in pixels) around a given Handle.
private float mHandleRadius;
// An edge of the crop window will snap to the corresponding edge of a
// specified bounding box when the crop window edge is less than or equal to
// this distance (in pixels) away from the bounding box edge.
private float mSnapRadius;
// Holds the x and y offset between the exact touch location and the exact
// handle location that is activated. There may be an offset because we
// allow for some leeway (specified by mHandleRadius) in activating a
// handle. However, we want to maintain these offset values while the handle
// is being dragged so that the handle doesn't jump.
private Pair<Float, Float> mTouchOffset;
// The Handle that is currently pressed; null if no Handle is pressed.
private Handle mPressedHandle;
// Flag indicating if the crop area should always be a certain aspect ratio
// (indicated by mTargetAspectRatio).
private boolean mFixAspectRatio = CropImageView.DEFAULT_FIXED_ASPECT_RATIO;
// Floats to save the current aspect ratio of the image
private int mAspectRatioX = CropImageView.DEFAULT_ASPECT_RATIO_X;
private int mAspectRatioY = CropImageView.DEFAULT_ASPECT_RATIO_Y;
// The aspect ratio that the crop area should maintain; this variable is
// only used when mMaintainAspectRatio is true.
private float mTargetAspectRatio = ((float) mAspectRatioX) / mAspectRatioY;
/**
* Instance variables for customizable attributes
*/
private int mGuidelines;
/**
* The shape of the cropping area - rectangle/circular.
*/
private CropImageView.CropShape mCropShape;
// Whether the Crop View has been initialized for the first time
private boolean initializedCropWindow = false;
// Instance variables for the corner values
private float mCornerExtension;
private float mCornerOffset;
private float mCornerLength;
//endregion
public CropOverlayView(Context context) {
super(context);
init(context);
}
public CropOverlayView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
/**
* Informs the CropOverlayView of the image's position relative to the
* ImageView. This is necessary to call in order to draw the crop window.
*
* #param bitmapRect the image's bounding box
*/
public void setBitmapRect(Rect bitmapRect) {
mBitmapRect = bitmapRect;
initCropWindow(mBitmapRect);
}
/**
* Resets the crop overlay view.
*/
public void resetCropOverlayView() {
if (initializedCropWindow) {
initCropWindow(mBitmapRect);
invalidate();
}
}
/**
* The shape of the cropping area - rectangle/circular.
*/
public void setCropShape(CropImageView.CropShape cropShape) {
mCropShape = cropShape;
invalidate();
}
/**
* Sets the guidelines for the CropOverlayView to be either on, off, or to
* show when resizing the application.
*
* #param guidelines Integer that signals whether the guidelines should be
* on, off, or only showing when resizing.
*/
public void setGuidelines(int guidelines) {
if (guidelines < 0 || guidelines > 2)
throw new IllegalArgumentException("Guideline value must be set between 0 and 2. See documentation.");
else {
mGuidelines = guidelines;
if (initializedCropWindow) {
initCropWindow(mBitmapRect);
invalidate();
}
}
}
/**
* Sets whether the aspect ratio is fixed or not; true fixes the aspect
* ratio, while false allows it to be changed.
*
* #param fixAspectRatio Boolean that signals whether the aspect ratio
* should be maintained.
*/
public void setFixedAspectRatio(boolean fixAspectRatio) {
mFixAspectRatio = fixAspectRatio;
if (initializedCropWindow) {
initCropWindow(mBitmapRect);
invalidate();
}
}
/**
* Sets the X value of the aspect ratio; is defaulted to 1.
*
* #param aspectRatioX int that specifies the new X value of the aspect
* ratio
*/
public void setAspectRatioX(int aspectRatioX) {
if (aspectRatioX <= 0)
throw new IllegalArgumentException("Cannot set aspect ratio value to a number less than or equal to 0.");
else {
mAspectRatioX = aspectRatioX;
mTargetAspectRatio = ((float) mAspectRatioX) / mAspectRatioY;
if (initializedCropWindow) {
initCropWindow(mBitmapRect);
invalidate();
}
}
}
/**
* Sets the Y value of the aspect ratio; is defaulted to 1.
*
* #param aspectRatioY int that specifies the new Y value of the aspect
* ratio
*/
public void setAspectRatioY(int aspectRatioY) {
if (aspectRatioY <= 0)
throw new IllegalArgumentException("Cannot set aspect ratio value to a number less than or equal to 0.");
else {
mAspectRatioY = aspectRatioY;
mTargetAspectRatio = ((float) mAspectRatioX) / mAspectRatioY;
if (initializedCropWindow) {
initCropWindow(mBitmapRect);
invalidate();
}
}
}
/**
* Sets all initial values, but does not call initCropWindow to reset the
* views. Used once at the very start to initialize the attributes.
*
* #param guidelines Integer that signals whether the guidelines should be
* on, off, or only showing when resizing.
* #param fixAspectRatio Boolean that signals whether the aspect ratio
* should be maintained.
* #param aspectRatioX float that specifies the new X value of the aspect
* ratio
* #param aspectRatioY float that specifies the new Y value of the aspect
* ratio
*/
public void setInitialAttributeValues(int guidelines, boolean fixAspectRatio, int aspectRatioX, int aspectRatioY) {
if (guidelines < 0 || guidelines > 2)
throw new IllegalArgumentException("Guideline value must be set between 0 and 2. See documentation.");
else
mGuidelines = guidelines;
mFixAspectRatio = fixAspectRatio;
if (aspectRatioX <= 0)
throw new IllegalArgumentException("Cannot set aspect ratio value to a number less than or equal to 0.");
else {
mAspectRatioX = aspectRatioX;
mTargetAspectRatio = ((float) mAspectRatioX) / mAspectRatioY;
}
if (aspectRatioY <= 0)
throw new IllegalArgumentException("Cannot set aspect ratio value to a number less than or equal to 0.");
else {
mAspectRatioY = aspectRatioY;
mTargetAspectRatio = ((float) mAspectRatioX) / mAspectRatioY;
}
}
//region: Private methods
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// Initialize the crop window here because we need the size of the view
// to have been determined.
//my changes to update imageview height and width
CropImageView.mImageView.getLayoutParams().height = h;
CropImageView.mImageView.getLayoutParams().width = w;
CropImageView.mImageView.requestLayout();
CropImageView.mImageView.invalidate();
initCropWindow(mBitmapRect);
// CropImageView.mImageView.invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Draw translucent background for the cropped area.
drawBackground(canvas, mBitmapRect);
if (showGuidelines()) {
// Determines whether guidelines should be drawn or not
if (mGuidelines == GUIDELINES_ON) {
drawRuleOfThirdsGuidelines(canvas);
} else if (mGuidelines == GUIDELINES_ON_TOUCH) {
// Draw only when resizing
if (mPressedHandle != null)
drawRuleOfThirdsGuidelines(canvas);
}
}
float w = mBorderPaint.getStrokeWidth();
float l = Edge.LEFT.getCoordinate() + w;
float t = Edge.TOP.getCoordinate() + w;
float r = Edge.RIGHT.getCoordinate() - w;
float b = Edge.BOTTOM.getCoordinate() - w;
if (mCropShape == CropImageView.CropShape.RECTANGLE) {
// Draw rectangle crop window border.
canvas.drawRect(l, t, r, b, mBorderPaint);
drawCorners(canvas);
} else {
// Draw circular crop window border
mRectF.set(l, t, r, b);
canvas.drawOval(mRectF, mBorderPaint);
}
}
#Override
public boolean onTouchEvent(#SuppressWarnings("NullableProblems") MotionEvent event) {
// If this View is not enabled, don't allow for touch interactions.
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
onActionDown(event.getX(), event.getY());
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
getParent().requestDisallowInterceptTouchEvent(false);
onActionUp();
return true;
case MotionEvent.ACTION_MOVE:
onActionMove(event.getX(), event.getY());
getParent().requestDisallowInterceptTouchEvent(true);
return true;
default:
return false;
}
}
private void init(Context context) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
mHandleRadius = HandleUtil.getTargetRadius(context);
mSnapRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
SNAP_RADIUS_DP,
displayMetrics);
mBorderPaint = PaintUtil.newBorderPaint(context);
mGuidelinePaint = PaintUtil.newGuidelinePaint();
mBackgroundPaint = PaintUtil.newBackgroundPaint(context);
mCornerPaint = PaintUtil.newCornerPaint(context);
// Sets the values for the corner sizes
mCornerOffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
DEFAULT_CORNER_OFFSET_DP,
displayMetrics);
mCornerExtension = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
DEFAULT_CORNER_EXTENSION_DP,
displayMetrics);
mCornerLength = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
DEFAULT_CORNER_LENGTH_DP,
displayMetrics);
// Sets guidelines to default until specified otherwise
mGuidelines = CropImageView.DEFAULT_GUIDELINES;
}
/**
* Set the initial crop window size and position. This is dependent on the
* size and position of the image being cropped.
*
* #param bitmapRect the bounding box around the image being cropped
*/
private void initCropWindow(Rect bitmapRect) {
if (bitmapRect.width() == 0 || bitmapRect.height() == 0) {
return;
}
// Tells the attribute functions the crop window has already been
// initialized
if (!initializedCropWindow) {
initializedCropWindow = true;
}
if (mFixAspectRatio
&& (bitmapRect.left != 0 || bitmapRect.right != 0
|| bitmapRect.top != 0 || bitmapRect.bottom != 0)) {
// If the image aspect ratio is wider than the crop aspect ratio,
// then the image height is the determining initial length. Else,
// vice-versa.
if (AspectRatioUtil.calculateAspectRatio(bitmapRect) > mTargetAspectRatio) {
Edge.TOP.setCoordinate(bitmapRect.top);
Edge.BOTTOM.setCoordinate(bitmapRect.bottom);
final float centerX = getWidth() / 2f;
//dirty fix for wrong crop overlay aspect ratio when using fixed aspect ratio
mTargetAspectRatio = (float) mAspectRatioX / mAspectRatioY;
// Limits the aspect ratio to no less than 40 wide or 40 tall
final float cropWidth = Math.max(Edge.MIN_CROP_LENGTH_PX,
AspectRatioUtil.calculateWidth(Edge.TOP.getCoordinate(),
Edge.BOTTOM.getCoordinate(),
mTargetAspectRatio));
// Create new TargetAspectRatio if the original one does not fit
// the screen
if (cropWidth == Edge.MIN_CROP_LENGTH_PX) {
mTargetAspectRatio = (Edge.MIN_CROP_LENGTH_PX) / (Edge.BOTTOM.getCoordinate() - Edge.TOP.getCoordinate());
}
final float halfCropWidth = cropWidth / 2f;
Edge.LEFT.setCoordinate(centerX - halfCropWidth);
Edge.RIGHT.setCoordinate(centerX + halfCropWidth);
} else {
Edge.LEFT.setCoordinate(bitmapRect.left);
Edge.RIGHT.setCoordinate(bitmapRect.right);
final float centerY = getHeight() / 2f;
// Limits the aspect ratio to no less than 40 wide or 40 tall
final float cropHeight = Math.max(Edge.MIN_CROP_LENGTH_PX,
AspectRatioUtil.calculateHeight(Edge.LEFT.getCoordinate(),
Edge.RIGHT.getCoordinate(),
mTargetAspectRatio));
// Create new TargetAspectRatio if the original one does not fit
// the screen
if (cropHeight == Edge.MIN_CROP_LENGTH_PX) {
mTargetAspectRatio = (Edge.RIGHT.getCoordinate() - Edge.LEFT.getCoordinate()) / Edge.MIN_CROP_LENGTH_PX;
}
final float halfCropHeight = cropHeight / 2f;
Edge.TOP.setCoordinate(centerY - halfCropHeight);
Edge.BOTTOM.setCoordinate(centerY + halfCropHeight);
}
} else { // ... do not fix aspect ratio...
// Initialize crop window to have 10% padding w/ respect to image.
final float horizontalPadding = 0.1f * bitmapRect.width();
final float verticalPadding = 0.1f * bitmapRect.height();
Edge.LEFT.setCoordinate(bitmapRect.left + horizontalPadding);
Edge.TOP.setCoordinate(bitmapRect.top + verticalPadding);
Edge.RIGHT.setCoordinate(bitmapRect.right - horizontalPadding);
Edge.BOTTOM.setCoordinate(bitmapRect.bottom - verticalPadding);
}
}
/**
* Indicates whether the crop window is small enough that the guidelines
* should be shown. Public because this function is also used to determine
* if the center handle should be focused.
*
* #return boolean Whether the guidelines should be shown or not
*/
public static boolean showGuidelines() {
if ((Math.abs(Edge.LEFT.getCoordinate() - Edge.RIGHT.getCoordinate()) < DEFAULT_SHOW_GUIDELINES_LIMIT)
|| (Math.abs(Edge.TOP.getCoordinate() - Edge.BOTTOM.getCoordinate()) < DEFAULT_SHOW_GUIDELINES_LIMIT)) {
return false;
} else {
return true;
}
}
private void drawRuleOfThirdsGuidelines(Canvas canvas) {
float w = mBorderPaint.getStrokeWidth();
float l = Edge.LEFT.getCoordinate() + w;
float t = Edge.TOP.getCoordinate() + w;
float r = Edge.RIGHT.getCoordinate() - w;
float b = Edge.BOTTOM.getCoordinate() - w;
if (mCropShape == CropImageView.CropShape.OVAL) {
l += 15 * mGuidelinePaint.getStrokeWidth();
t += 15 * mGuidelinePaint.getStrokeWidth();
r -= 15 * mGuidelinePaint.getStrokeWidth();
b -= 15 * mGuidelinePaint.getStrokeWidth();
}
// Draw vertical guidelines.
final float oneThirdCropWidth = Edge.getWidth() / 3;
final float x1 = l + oneThirdCropWidth;
canvas.drawLine(x1, t, x1, b, mGuidelinePaint);
final float x2 = r - oneThirdCropWidth;
canvas.drawLine(x2, t, x2, b, mGuidelinePaint);
// Draw horizontal guidelines.
final float oneThirdCropHeight = Edge.getHeight() / 3;
final float y1 = t + oneThirdCropHeight;
canvas.drawLine(l, y1, r, y1, mGuidelinePaint);
final float y2 = b - oneThirdCropHeight;
canvas.drawLine(l, y2, r, y2, mGuidelinePaint);
}
private void drawBackground(Canvas canvas, Rect bitmapRect) {
final float l = Edge.LEFT.getCoordinate();
final float t = Edge.TOP.getCoordinate();
final float r = Edge.RIGHT.getCoordinate();
final float b = Edge.BOTTOM.getCoordinate();
if (mCropShape == CropImageView.CropShape.RECTANGLE) {
canvas.drawRect(bitmapRect.left, bitmapRect.top, bitmapRect.right, t, mBackgroundPaint);
canvas.drawRect(bitmapRect.left, b, bitmapRect.right, bitmapRect.bottom, mBackgroundPaint);
canvas.drawRect(bitmapRect.left, t, l, b, mBackgroundPaint);
canvas.drawRect(r, t, bitmapRect.right, b, mBackgroundPaint);
} else {
Path circleSelectionPath = new Path();
mRectF.set(l, t, r, b);
circleSelectionPath.addOval(mRectF, Path.Direction.CW);
canvas.clipPath(circleSelectionPath, Region.Op.XOR);
canvas.drawRect(bitmapRect.left, bitmapRect.top, bitmapRect.right, bitmapRect.bottom, mBackgroundPaint);
canvas.restore();
}
}
private void drawCorners(Canvas canvas) {
float w = mBorderPaint.getStrokeWidth();
final float l = Edge.LEFT.getCoordinate() + w;
final float t = Edge.TOP.getCoordinate() + w;
final float r = Edge.RIGHT.getCoordinate() - w;
final float b = Edge.BOTTOM.getCoordinate() - w;
// Top left
canvas.drawLine(l - mCornerOffset, t - mCornerExtension, l - mCornerOffset, t + mCornerLength, mCornerPaint);
canvas.drawLine(l, t - mCornerOffset, l + mCornerLength, t - mCornerOffset, mCornerPaint);
// Top right
canvas.drawLine(r + mCornerOffset, t - mCornerExtension, r + mCornerOffset, t + mCornerLength, mCornerPaint);
canvas.drawLine(r, t - mCornerOffset, r - mCornerLength, t - mCornerOffset, mCornerPaint);
// Bottom left
canvas.drawLine(l - mCornerOffset, b + mCornerExtension, l - mCornerOffset, b - mCornerLength, mCornerPaint);
canvas.drawLine(l, b + mCornerOffset, l + mCornerLength, b + mCornerOffset, mCornerPaint);
// Bottom left
canvas.drawLine(r + mCornerOffset, b + mCornerExtension, r + mCornerOffset, b - mCornerLength, mCornerPaint);
canvas.drawLine(r, b + mCornerOffset, r - mCornerLength, b + mCornerOffset, mCornerPaint);
}
/**
* Handles a {#link android.view.MotionEvent#ACTION_DOWN} event.
*
* #param x the x-coordinate of the down action
* #param y the y-coordinate of the down action
*/
private void onActionDown(float x, float y) {
final float left = Edge.LEFT.getCoordinate();
final float top = Edge.TOP.getCoordinate();
final float right = Edge.RIGHT.getCoordinate();
final float bottom = Edge.BOTTOM.getCoordinate();
mPressedHandle = HandleUtil.getPressedHandle(x, y, left, top, right, bottom, mHandleRadius);
if (mPressedHandle == null) {
return;
}
// Calculate the offset of the touch point from the precise location
// of the handle. Save these values in a member variable since we want
// to maintain this offset as we drag the handle.
mTouchOffset = HandleUtil.getOffset(mPressedHandle, x, y, left, top, right, bottom);
invalidate();
}
/**
* Handles a {#link android.view.MotionEvent#ACTION_UP} or
* {#link android.view.MotionEvent#ACTION_CANCEL} event.
*/
private void onActionUp() {
if (mPressedHandle == null) {
return;
}
mPressedHandle = null;
invalidate();
}
/**
* Handles a {#link android.view.MotionEvent#ACTION_MOVE} event.
*
* #param x the x-coordinate of the move event
* #param y the y-coordinate of the move event
*/
private void onActionMove(float x, float y) {
if (mPressedHandle == null) {
return;
}
// Adjust the coordinates for the finger position's offset (i.e. the
// distance from the initial touch to the precise handle location).
// We want to maintain the initial touch's distance to the pressed
// handle so that the crop window size does not "jump".
x += mTouchOffset.first;
y += mTouchOffset.second;
// Calculate the new crop window size/position.
if (mFixAspectRatio) {
mPressedHandle.updateCropWindow(x, y, mTargetAspectRatio, mBitmapRect, mSnapRadius);
} else {
mPressedHandle.updateCropWindow(x, y, mBitmapRect, mSnapRadius);
}
invalidate();
}
//endregion
}
You could just try using Picasso. It does all the scaling for you in the background after you put it into your Gradle build. Picasso
I'm trying to create a 360 video sphere (like the ones for cardboard) on Android. I have done this with a photo by rendering a sphere in OpenGL ES1.0 and than attaching a texture to it. Afterwards I can use the sensor values to rotate the sphere.
However, I can't figure out how to change the picture to a video. I've tried frame by frame rendering using texSubImage2D() but it's SUPER SLOW. My video is probably going to be about 4k density as I need a good quality even when only small portion of it is shown.
I've read some theoretical stuff about how this should be done (i.e. Frame Buffers, External Texture, Synchronization, etc.) but I couldn't find any example for these things, so some code would be EXTREMELY appreciated...
Here is how I render the Sphere, draw it and attach a texture to it (i.e. my Sphere class)...
import rapid.decoder.BitmapDecoder;
public class Sphere {
/** Buffer holding the vertices. */
private final List<FloatBuffer> mVertexBuffer = new ArrayList<FloatBuffer>();
/** The vertices for the sphere. */
private final List<float[]> mVertices = new ArrayList<float[]>();
/** Buffer holding the texture coordinates. */
private final List<FloatBuffer> mTextureBuffer = new ArrayList<FloatBuffer>();
/** Mapping texture coordinates for the vertices. */
private final List<float[]> mTexture = new ArrayList<float[]>();
/** The texture pointer. */
private final int[] mTextures = new int[1];
/** Total number of strips for the given depth. */
private final int mTotalNumStrips;
public Sphere(final int depth, final float radius) {
// Calculate basic values for the sphere.
this.mTotalNumStrips = Maths.power(2, depth - 1) * 5; //last 5 is related to properties of a icosahedron
final int numVerticesPerStrip = Maths.power(2, depth) * 3;
final double altitudeStepAngle = Maths.rad120 / Maths.power(2, depth);
final double azimuthStepAngle = Maths.rad360 / this.mTotalNumStrips;
double x, y, z, h, altitude, azimuth;
Log.e("mTotalNumStrips", ""+mTotalNumStrips);
Log.e("numVerticesPerStrip", ""+numVerticesPerStrip);
for (int stripNum = 0; stripNum < this.mTotalNumStrips; stripNum++) {
// Setup arrays to hold the points for this strip.
final float[] vertices = new float[numVerticesPerStrip * 3]; // x,y,z
final float[] texturePoints = new float[numVerticesPerStrip * 2]; // 2d texture
int vertexPos = 0;
int texturePos = 0;
// Calculate position of the first vertex in this strip.
altitude = Maths.rad90;
azimuth = stripNum * azimuthStepAngle;
// Draw the rest of this strip.
for (int vertexNum = 0; vertexNum < numVerticesPerStrip; vertexNum += 2) {
// First point - Vertex.
y = radius * Math.sin(altitude);
h = radius * Math.cos(altitude);
z = h * Math.sin(azimuth);
x = h * Math.cos(azimuth);
vertices[vertexPos++] = (float) x;
vertices[vertexPos++] = (float) y;
vertices[vertexPos++] = (float) z;
// First point - Texture.
texturePoints[texturePos++] = (float) (1 + azimuth / Maths.rad360);
texturePoints[texturePos++] = (float) (1 - (altitude + Maths.rad90) / Maths.rad180);
// Second point - Vertex.
altitude -= altitudeStepAngle;
azimuth -= azimuthStepAngle / 2.0;
y = radius * Math.sin(altitude);
h = radius * Math.cos(altitude);
z = h * Math.sin(azimuth);
x = h * Math.cos(azimuth);
vertices[vertexPos++] = (float) x;
vertices[vertexPos++] = (float) y;
vertices[vertexPos++] = (float) z;
// Second point - Texture.
texturePoints[texturePos++] = (float) (1 + azimuth / Maths.rad360);
texturePoints[texturePos++] = (float) (1 - (altitude + Maths.rad90) / Maths.rad180);
azimuth += azimuthStepAngle;
}
this.mVertices.add(vertices);
this.mTexture.add(texturePoints);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(numVerticesPerStrip * 3 * Float.SIZE);
byteBuffer.order(ByteOrder.nativeOrder());
FloatBuffer fb = byteBuffer.asFloatBuffer();
fb.put(this.mVertices.get(stripNum));
fb.position(0);
this.mVertexBuffer.add(fb);
// Setup texture.
byteBuffer = ByteBuffer.allocateDirect(numVerticesPerStrip * 2 * Float.SIZE);
byteBuffer.order(ByteOrder.nativeOrder());
fb = byteBuffer.asFloatBuffer();
fb.put(this.mTexture.get(stripNum));
fb.position(0);
this.mTextureBuffer.add(fb);
}
}
public void loadGLTexture(final GL10 gl, final Context context, final int texture) {
Bitmap bitmap = BitmapDecoder.from(context.getResources(), texture)
.scale(4048, 2024)
.decode();
// Generate one texture pointer, and bind it to the texture array.
gl.glGenTextures(1, this.mTextures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, this.mTextures[0]);
// Create nearest filtered texture.
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
// Use Android GLUtils to specify a two-dimensional texture image from our bitmap.
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// Tide up.
bitmap.recycle();
}
/**
* The draw method for the square with the GL context.
*
* #param gl Graphics handle.
*/
public void draw(final GL10 gl) {
// bind the previously generated texture.
gl.glBindTexture(GL10.GL_TEXTURE_2D, this.mTextures[0]);
// Point to our buffers.
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// Set the face rotation, clockwise in this case.
gl.glFrontFace(GL10.GL_CW);
// Point to our vertex buffer.
for (int i = 0; i < this.mTotalNumStrips; i++) {
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, this.mVertexBuffer.get(i));
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, this.mTextureBuffer.get(i));
// Draw the vertices as triangle strip.
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, this.mVertices.get(i).length / 3);
}
// Disable the client state before leaving.
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
}
And this is my renderer...
#Override
public void onDrawFrame(final GL10 gl) {
zvector = new float[] {0,0,1,0};
resultvector = new float[] {0,0,1,0};
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
float radiansX = (float) Math.toRadians(gyro_angle[1]);
float radiansY = (float) Math.toRadians(-gyro_angle[0]);
float radiansZ = (float) Math.toRadians(-gyro_angle[2]);
// Finds the Sin and Cosin for the half angle.
float sinX =(float) Math.sin(radiansX * 0.5);
float cosX =(float) Math.cos(radiansX * 0.5);
float sinY =(float) Math.sin(radiansY * 0.5);
float cosY =(float) Math.cos(radiansY * 0.5);
float sinZ =(float) Math.sin(radiansZ * 0.5);
float cosZ =(float) Math.cos(radiansZ * 0.5);
// Formula to construct a new Quaternion based on direction and angle.
quatX[0] = cosX;
quatX[1] = 1 * sinX;
quatX[2] = 0 * sinX;
quatX[3] = 0 * sinX;
quatY[0] = cosY;
quatY[1] = 0 * sinY;
quatY[2] = 1 * sinY;
quatY[3] = 0 * sinY;
quatZ[0] = cosZ;
quatZ[1] = 0 * sinZ;
quatZ[2] = 0 * sinZ;
quatZ[3] = 1 * sinZ;
quat1 = multiplyQuat(quatX, quatY);
quat2 = multiplyQuat(quat1, quatZ);
mMatrix = getMatrixfromQuat(quat1);
gl.glLoadMatrixf(mMatrix, 0);
this.mSphere.draw(gl);
}
#Override
public void onSurfaceChanged(final GL10 gl, final int width, final int height) {
final float aspectRatio = (float) width / (float) (height == 0 ? 1 : height);
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 45.0f, aspectRatio, 0.1f, 100.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
#Override
public void onSurfaceCreated(final GL10 gl, final EGLConfig config) {
this.mSphere.loadGLTexture(gl, this.mContext, R.drawable.pic360);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
gl.glClearDepthf(1.0f);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glDepthFunc(GL10.GL_LEQUAL);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
}
//CONSTRUCTER
public GlRenderer(final Context context) {
this.mContext = context;
this.mSphere = new Sphere(5, 2);
sensorManager = (SensorManager) this.mContext.getSystemService(this.mContext.SENSOR_SERVICE);
sensorGyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
sensorAccelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorMagneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
valuesAccelerometer = new float[3];
valuesMagneticField = new float[3];
matrixR = new float[9];
matrixI = new float[9];
matrixValues = new float[3];
sensorManager.registerListener(this, sensorGyroscope, SensorManager.SENSOR_DELAY_FASTEST);
sensorManager.registerListener(this, sensorAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
sensorManager.registerListener(this, sensorMagneticField, SensorManager.SENSOR_DELAY_FASTEST);
}
//HERE GOES SOME CURRENTLY IRRELEVANT STUFF ABOUT THE SENSORS AND QUATERNIONS
I had some this type of video texturing problem. First time I used ffmpeg for video decoding but the performance was so poor (just like you- Extracting frame by frame). For improving performance I used android default mediaplayer. You can use surface texture to create an opengl surface (sphere, cylinder, cube etc...) and then set the surface in the media player
Surface surface = new Surface(mSurface);//mSurface is your surface texture
mMediaPlayer.setSurface(surface);
mMediaPlayer.setScreenOnWhilePlaying(true);
This is just a technique. I did this for some commercial enclosed project, so I cant share the code. I hope I'll published a free code in github soon.
I have the following Java class I've written for a LibGdx OpenGL project.
The camera keeps the aspect ratio of the screen no matter how you resize it by letterboxing either the top and bottom, or the sides. So far, so good.
The issue comes when I try to obtain the mouse x, y coordinates of a click, and the letterbox is involved for that axis.
First here is the class:
public class Camera {
private static float viewportWidth;
private static float viewportHeight;
private static float aspectRatio;
private static float barSize;
/**
* Creates an orthographic camera where the "play area" has the given viewport size. The viewport will be scaled to maintain the aspect ratio.
*
* #param virtualWidth the width of the game screen in virtual pixels.
* #param virtualHeight the height of the game screen in virtual pixels.
* #return the new camera.
*
*/
public static OrthographicCamera createCamera(float virtualWidth, float virtualHeight) {
aspectRatio = virtualWidth / virtualHeight;
float physicalWidth = Gdx.graphics.getWidth();
float physicalHeight = Gdx.graphics.getHeight();
if (physicalWidth / physicalHeight >= aspectRatio) {
// Letterbox left and right.
viewportHeight = virtualHeight;
viewportWidth = viewportHeight * physicalWidth / physicalHeight;
barSize = ????;
}
else {
// Letterbox above and below.
viewportWidth = virtualWidth;
viewportHeight = viewportWidth * physicalHeight / physicalWidth;
barSize = ????;
}
OrthographicCamera cam = new OrthographicCamera(viewportWidth , viewportHeight);
cam.position.set(virtualWidth / 2, virtualHeight / 2, 0);
cam.rotate(180, 1, 0, 0);
cam.update();
Gdx.app.log("BTLog", "barSize:"+barSize);
return cam;
}
public static float getViewportWidth() {
return viewportWidth;
}
public static float getViewportHeight() {
return viewportHeight;
}
}
LibGdx supplies me the x and y coordinates when an even happens, and I need to translate these raw coordinates into the scale of my camera (the virtual height and width).
When the screen is stretched (no letterboxing at all), it's pretty easy to obtain the x and y coordinates by using:
xRelative = (int) (x / (float)Gdx.graphics.getWidth() * Camera.getViewportWidth());
yRelative = (int) (y / (float)Gdx.graphics.getHeight() * Camera.getViewportHeight());
The problem is when the letterboxes come into play, it throws off the coordinate for that axis. I know I need to take into account the width of the letterboxing, but i'm having a hell of a time figuring how to calculate it.
Above where I have "barSize = ????;" my first instinct was to do this:
barSize = physicalHeight - viewportHeight; // to use height for example
Once I get the barSize, i'm fairly sure I can use this to get the right numbers (using the y axis for example):
yRelative = (int) (y / (float)Gdx.graphics.getHeight() * Camera.getViewportHeight() - Camera.getBarSize());
But the numbers don't match up. Any suggestions would be a really appreciated!
Ray ray = camera.getPickRay(x, y);
System.out.println(ray.origin.x);
System.out.println(ray.origin.y);