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();
}
Related
If I try to render a square with the same scaling for x and y without resizing the window everything is fine. But after resizing the window there is no longer a square rendered. Instead, you can see a rectangle, even though I am recalculating the projection matrix and passing the matrix to the shader every time the window's width or height changes.
With a different width and height, the scaling of a square is incorrect. Changing the size of the window in my code without resizing the window does not change the scaling.
I have no idea where the error is. Maybe I missed something.
Coordinates:
float[] positions = new float[] {0, 1, 0, 0, 1, 1, 1, 0};
Calculating the projection matrix:
public static void createProjectionMatrix() {
Vector2f windowSize = DisplayManager.getWindowSize();
float aspectRatio = windowSize.x / windowSize.y;
float halfWidth = 1.0f;
float halfHeight = halfWidth / aspectRatio;
float left = -halfWidth;
float right = halfWidth;
float bottom = -halfHeight;
float top = halfHeight;
float far = -1f;
float near = 1f;
Matrix4f matrix = new Matrix4f();
matrix.setIdentity();
matrix.m00 = 2f / (right - left);
matrix.m11 = 2f / (top - bottom);
matrix.m22 = -2f / (far - near);
matrix.m32 = (far + near) / (far - near);
matrix.m30 = (right + left) / (right - left);
matrix.m31 = (top + bottom) / (top - bottom);
projectionMatrix = matrix;
}
Calculation the transformation (Vector2f worldScale is not used):
private Vector2f getDisplayCoords(Vector2f percentage) {
Vector2f v = DisplayManager.getWindowSize();
return new Vector2f(v.x * percentage.x, v.y * percentage.y);
}
public void loadTransformationMatricies() {
Vector2f displaySize = getDisplayCoords(size);
Vector2f displayPos = getDisplayCoords(position);
Vector2f display = DisplayManager.getWindowSize();
Vector2f worldPos = Mouse.getWorldPos(displayPos);
Vector2f worldScale = new Vector2f(displaySize.x / (display.x / 2.0f), displaySize.y / (display.y / 2));
float x, y;
x = worldPos.x;
y = worldPos.y - CORNER_SCALE;
//y is calculated correct. Moving the mouse to the same y value as calculated shows that everything is correctly calculated
System.out.println(y + " | " + Mouse.getWorldPos().y);
transforms[0] = Maths.getTransformationMatrix(new Vector2f(x, y), new Vector2f(CORNER_SCALE, CORNER_SCALE));
}
Check size:
GLFW.glfwSetWindowSizeCallback(WINDOW, new GLFWWindowSizeCallback() {
#Override
public void invoke(long arg0, int arg1, int arg2) {
resized = true;
}
});
Rendering (would normally render more objects):
#Override
protected void render() {
shader.start();
if(DisplayManager.isResized()) {
shader.loadProjectionMatrix(MasterRenderer.getProjectionMatrix());
}
bindModel(Loader.getQuad(), new int[] {0});
for(GUI gui : guis) {
if(DisplayManager.isResized()) {
gui.loadTransformationMatricies();
}
bindTexture(gui.getTexture().getTopLeftCorner(), GL13.GL_TEXTURE0);
Matrix4f[] transforms = gui.getTransformations();
for(int i = 0; i < 1; i++) {
shader.loadTransformationMatrix(transforms[i]);
drawSTRIP(Loader.getQuad());
}
}
unbind(new int[] {0});
shader.stop();
}
Vertexshader:
#version 400 core
in vec2 position;
uniform mat4 transformationMatrix;
uniform mat4 projectionMatrix;
out vec2 textureCoords;
void main (void){
gl_Position = projectionMatrix * transformationMatrix * vec4(position, 0, 1.0);
textureCoords = vec2((position.x+1.0)/2.0, 1 - (position.y+1.0)/2.0);
}
You can find the whole code on Github if you need more information but I only want to know how to fix this specific problem.
https://github.com/StackOverflowEx/GameEngine2D
After the size of the window has changed, the viewport rectangle has to be adjusted to the new size:
Use glViewport to set the viewport rectangle.
Add a method updateViewPort to the class DisplayManager
public static void updateViewPort() {
IntBuffer pWidth = stack.mallocInt(1);
IntBuffer pHeight = stack.mallocInt(1);
GLFW.glfwGetFramebufferSize(WINDOW, pWidth, pHeight);
GL11.glViewport(0, 0, pWidth.get(0), pHeight.get(0));
}
Call the DisplayManager.updateViewPort in the methodprepare in the class MasterRenderer:
private void prepare() {
if(DisplayManager.isResized()) {
DisplayManager.updateViewPort();
createProjectionMatrix();
}
Camera.calcViewMatrix();
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
GL11.glClearColor(1, 0, 0, 1);
}
See also Java Code Examples for org.lwjgl.glfw.GLFW.glfwGetFramebufferSize().
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 am looking for an animation for a view flipper.
How would i go about animating so that the view basically goes back to front when a button is pressed. where there is a map view on one side and a listview on the other.
i have the above working except for the animation . could someone help or explain android animations to me
edit added here
here is some code for animation for rotation, how could i implment this in xml
public class Rotate3dAnimation extends Animation {
private final float mFromDegrees;
private final float mToDegrees;
private final float mCenterX;
private final float mCenterY;
private final float mDepthZ;
private final boolean mReverse;
private Camera mCamera;
/**
* Creates a new 3D rotation on the Y axis. The rotation is defined by its
* start angle and its end angle. Both angles are in degrees. The rotation
* is performed around a center point on the 2D space, definied by a pair
* of X and Y coordinates, called centerX and centerY. When the animation
* starts, a translation on the Z axis (depth) is performed. The length
* of the translation can be specified, as well as whether the translation
* should be reversed in time.
*
* #param fromDegrees the start angle of the 3D rotation
* #param toDegrees the end angle of the 3D rotation
* #param centerX the X center of the 3D rotation
* #param centerY the Y center of the 3D rotation
* #param reverse true if the translation should be reversed, false otherwise
*/
public Rotate3dAnimation(float fromDegrees, float toDegrees,
float centerX, float centerY, float depthZ, boolean reverse) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mCenterX = centerX;
mCenterY = centerY;
mDepthZ = depthZ;
mReverse = reverse;
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
if (mReverse) {
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
} else {
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
}
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
It depends on what you want to do but you can lookup wiEngine for Android SDK, at the transitions example... take a look at the code thought the documentation is in chinese, you can simply read the code.
Also take a look at Chet Haase blog for some tips, http://graphics-geek.blogspot.com/ he has a bunch of stuff related to animation. Targeting API level12 that would take just about one line of code using the method objectToAnimate.rotateYBy(360).setDuration(250f)
I'm trying to have an image scale to a certain size depending on the horizontal size sent to an update function, but the following code doesnt seem to size the image correctly.
EDIT: The code:
public class GlassesView extends View {
private Paint paint;
private BitmapFactory.Options options;
private Bitmap bitmapOrg;
private Bitmap target;
private Bitmap bitmapRev;
private Bitmap resizedBitmap;
private int currY;
public int glassesX;
public int glassesY;
public float glassesSizeX;
public float glassesSizeY;
private boolean drawGlasses;
private boolean glassesMirrored;
public GlassesView(Context context) {
super(context);
paint = new Paint();
paint.setDither(false);
paint.setAntiAlias(false);
options = new BitmapFactory.Options();
options.inDither = false;
options.inScaled = false;
bitmapOrg = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(),
R.drawable.micro_glasses, options), 32, 5, false);
bitmapRev = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(),
R.drawable.glasses_reverse, options), 32, 5, false);
drawGlasses = false;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(target, 0, 0, paint);
boolean moving = currY < glassesY;
if (moving) {
currY++;
}
if (drawGlasses) {
int newWidth = resizedBitmap.getWidth();
int newHeight = resizedBitmap.getHeight();
Paint bluey = new Paint();
bluey.setColor(Color.argb(64, 0, 0, 255));
canvas.drawRect(new Rect(glassesX, currY, glassesX + newWidth,
currY + newHeight), bluey);
canvas.drawBitmap(resizedBitmap, glassesX, currY, paint);
}
if (moving) {
invalidate();
}
}
public void drawGlasses(int x1, int x2, int y, boolean mirror) {
drawGlasses = true;
glassesMirrored = mirror;
if (!mirror) {
glassesSizeX = (float) (x2 - x1) / (float) (25 - 16);
glassesSizeY = glassesSizeX;
glassesY = y - (int)(1*glassesSizeX);
glassesX = (int) (x1 - (glassesSizeX * 16));
} else {
glassesSizeX = (float) (x1 - x2) / (float) (25 - 16);
glassesSizeY = glassesSizeX;
glassesY = y - (int)(1*glassesSizeX);
glassesX = (int) (x1 - (glassesSizeX * 16));
}
currY = -1;
if (!glassesMirrored) {
resizedBitmap = Bitmap.createScaledBitmap(bitmapOrg,
(int) (bitmapOrg.getWidth() * glassesSizeX),
(int) (bitmapOrg.getHeight() * glassesSizeY), false);
} else {
resizedBitmap = Bitmap.createScaledBitmap(bitmapRev,
(int) (bitmapRev.getWidth() * glassesSizeX),
(int) (bitmapRev.getHeight() * glassesSizeY), false);
}
}
public void setTargetPic(Bitmap targetPic) {
target = targetPic;
}
}
The result. (The blue rectangle being the bounding box of the image's intended size)
Which part am I going wrong at?
EDIT 2:
Here are the glasses:
EDIT 3:
Out of curiousity, I ran it on my actual phone, and got a much different result, the image was stretched passed the intended blue box.
EDIT 4:
I tried running the app on a few emulators to see if it was an Android version incompatibility thing, but they all seemed to work perfectly. The scaling issue only occurs on my phone (Vibrant, rooted, CM7) and my cousin's (Droid, also rooted). These are the only physical devices I have tested on, but they both seem to have the same issue.
I'd really appreciate if someone could help me out here, this is a huge roadblock in my project and no other forums or message groups are responding.
EDIT 5:
I should mention that in update 4, the code changed a bit, which fixed the problem in the emulators as I stated, but doesn't work on physical devices. Changes are updated in the code above. *desperate for help* :P
EDIT 6:
Yet another update, I tested the same code on my old G1, and it works perfectly as expected. I have absolutely no clue now.
have you been using the /res/drawable-nodpi/ directory to store your images?
Apparently if you use the /drawable-ldpi/, /drawable-mdpi/ or /drawable-hdpi/ folders, Android will apply scaling to the image when you load it depending on the device. The reason your G1 may work is that it may not require any scaling depending on the folder you used.
Also, your IMGUR links are broken... also your code doesn't seem correct... you call DrawGlasses with no arguments.
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0, glassesWidth,
glassesHeight, matrix, false);
Change the last parameter from false to true.
you can have a try.
The follow is i used zoom in method by scale:
private void small() {
int bmpWidth=bmp.getWidth();
int bmpHeight=bmp.getHeight();
Log.i(TAG, "bmpWidth = " + bmpWidth + ", bmpHeight = " + bmpHeight);
/* 设置图片缩小的比例 */
double scale=0.8;
/* 计算出这次要缩小的比例 */
scaleWidth=(float) (scaleWidth*scale);
scaleHeight=(float) (scaleHeight*scale);
/* 产生reSize后的Bitmap对象 */
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizeBmp = Bitmap.createBitmap(bmp,0,0,bmpWidth,
bmpHeight,matrix,true);
Found what was wrong. The image has to be in a folder called "drawable-nodpi", otherwise it will be scaled depending on DPI.