In my android app.I want to make image Straightening edit feature using android-gpuimage library but GPUImageView doesn't give feature of getBitmap() or setMatrix() then how is it possible please let me know? here is the code to review :
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(isStraightenEffectEnabled){
float angle = (progress - 45);
float width = mGPUImageView.getWidth();
float height = mGPUImageView.getHeight();
if (width > height) {
width = mGPUImageView.getHeight();
height = mGPUImageView.getWidth();
}
float a = (float) Math.atan(height/width);
// the length from the center to the corner of the green
float len1 = (width / 2) / (float) Math.cos(a - Math.abs(Math.toRadians(angle)));
// the length from the center to the corner of the black
float len2 = (float) Math.sqrt(Math.pow(width/2,2) + Math.pow(height/2,2));
// compute the scaling factor
float scale = len2 / len1;
Matrix matrix = mGPUImageView.getMatrix();
if (mMatrix == null) {
mMatrix = new Matrix(matrix);
}
matrix = new Matrix(mMatrix);
float newX = (mGPUImageView.getWidth() / 2) * (1 - scale);
float newY = (mGPUImageView.getHeight() / 2) * (1 - scale);
matrix.postScale(scale, scale);
matrix.postTranslate(newX, newY);
matrix.postRotate(angle, mGPUImageView.getWidth() / 2, mGPUImageView.getHeight() / 2);
Is it possible to get mGPUImageView.setMatrix(matrix);
NOW HERE getMatrix() is a method of GPUImageView but setMatrix() or getBitmap() is not method available with GPUImageView class. Any workarounds if possible ?
add getGPUImage in GPUImageView class
public GPUImage getGPUImage() {
return mGPUImage;
}
then get you can get bitmap like this:
mGPUImageView.getGPUImage().getBitmapWithFilterApplied();
You can also get bitmap like this:
Bitmap bitmap = mGPUImageView.capture();
You can get filtered bitmap renderer and Pixelbuffer. This might be helpful for you.
GPUImageLookupFilter amatorka = new GPUImageLookupFilter();
amatorka.setBitmap(BitmapFactory.decodeResource(getResources(), getResources().getIdentifier("fil_" + position, "drawable", getPackageName())));
GPUImageRenderer renderer = new GPUImageRenderer(amatorka);
renderer.setImageBitmap(bitmap, false);
PixelBuffer buffer = new PixelBuffer(80, 80);
buffer.setRenderer(renderer);
buffer.getBitmap();
Related
I'm trying to make a simple image editor. At the beginning I've thought that it'll be a good idea to simply save view state as Bitmap but, as it turned out, there is a wide range of screen resolutions and that leads to huge quality (and memory usage) fluctuations.
Now I'm trying to make a module that renders views state translated to desired resolution.
In the code below I'm trying to recreate current state of the views in canvas:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.id.test_1_1);
bitmap = Bitmap.createScaledBitmap(bitmap, parentView.getMeasuredWidth(), parentView.getMeasuredHeight(), true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
for (View rootView : addedViews) {
ImageView imageView = rootView.findViewById(R.id.sticker);
float[] viewPosition = new float[2];
transformToAncestor(viewPosition, parentView, imageView);
Bitmap originalBitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Matrix adjustMatrix = new Matrix();
adjustMatrix.postTranslate(viewPosition[0], viewPosition[1]);
adjustMatrix.postScale(
rootView.getScaleX(),
rootView.getScaleY(),
rootView.getWidth() / 2,
rootView.getHeight() / 2);
adjustMatrix.postRotate(rootView.getRotation(),
rootView.getWidth() / 2,
rootView.getHeight() / 2);
canvas.drawBitmap(originalBitmap, adjustMatrix, paint);
}
transformToAncestor function is from here.
public static void transformToAncestor(float[] point, final View ancestor, final View descendant) {
final float scrollX = descendant.getScrollX();
final float scrollY = descendant.getScrollY();
final float left = descendant.getLeft();
final float top = descendant.getTop();
final float px = descendant.getPivotX();
final float py = descendant.getPivotY();
final float tx = descendant.getTranslationX();
final float ty = descendant.getTranslationY();
final float sx = descendant.getScaleX();
final float sy = descendant.getScaleY();
point[0] = left + px + (point[0] - px) * sx + tx - scrollX;
point[1] = top + py + (point[1] - py) * sy + ty - scrollY;
ViewParent parent = descendant.getParent();
if (descendant != ancestor && parent != ancestor && parent instanceof View) {
transformToAncestor(point, ancestor, (View) parent);
}
}
(author wrote a note that his function does not support rotation, but there's not much rotation in my example so I don't think that important for now).
My problem is:
First image is generated via saving the parent view state. Second one is generated by translating views position, rotation and scale onto canvas.
As you can see, on the canvas, not scaled stickers are positioned properly, but scaled are incorrectly positioned.
How to position those scaled views properly?
I've managed to fix the issue myself.
It turned out my solution was nearly OK but I did not took into consideration that my manipulation of a matrix does change the arrangement of the original points, so my
rootView.getWidth() / 2,
rootView.getHeight() / 2
is no longer applicable as a center of the view after calling Matrix.postScale or Matrix.postRotation.
I wanted to:
apply scale with pivot on top left corner,
apply rotation with pivot on the center of the view.
Given the assumptions, here's the working code:
// setup variables for sizing and transformation
float position[] = new float[2];
transformToAncestor(position, rootView, imageView);
float desiredRotation = imageView.getRotation();
float sizeDeltaX = imageView.getMeasuredWidth() / (float) imageBitmap.getWidth();
float sizeDeltaY = imageView.getMeasuredHeight() / (float) imageBitmap.getHeight();
float desiredScaleX = imageView.getScaleX() * sizeDeltaX * scaleX;
float desiredScaleY = imageView.getScaleY() * sizeDeltaY * scaleY;
float imageViewWidth = imageView.getMeasuredWidth() * imageView.getScaleX();
float imageViewHeight = imageView.getMeasuredHeight() * imageView.getScaleY();
float rootViewWidth = rootView.getMeasuredWidth();
float rootViewHeight = rootView.getMeasuredHeight();
float percentXPos = position[0] / rootViewWidth;
float percentYPos = position[1] / rootViewHeight;
float percentXCenterPos = (position[0] + imageViewWidth/2)
/ rootViewWidth;
float percentYCenterPos = (position[1] + imageViewHeight/2)
/ rootViewHeight;
float desiredPositionX = background.getWidth() * percentXPos;
float desiredPositionY = background.getHeight() * percentYPos;
float desiredCenterX = background.getWidth() * percentXCenterPos;
float desiredCenterY = background.getHeight() * percentYCenterPos;
// apply above variables to matrix
Matrix matrix = new Matrix();
float[] points = new float[2];
matrix.postTranslate(
desiredPositionX,
desiredPositionY);
matrix.mapPoints(points);
matrix.postScale(
desiredScaleX,
desiredScaleY,
points[0],
points[1]);
matrix.postRotate(
desiredRotation,
desiredCenterX,
desiredCenterY);
// apply matrix to bitmap, then draw it on canvas
canvas.drawBitmap(imageBitmap, matrix, paint);
As you can see, the mapPoints method was the answer for my question - it simply returns points after tranformation.
I have an rotated textview and I want to drag and drop this view.
The problem is that the drag shadow has no rotation.
I found a solution for android in java but this does not work for me.
Maybe I translate the code wrong
How to drag a rotated DragShadow?
class CustomDragShdowBuilder : View.DragShadowBuilder
{
private View _view;
public CustomDragShdowBuilder(View view)
{
_view = view;
}
public override void OnDrawShadow(Canvas canvas)
{
double rotationRad = Math.ToRadians(_view.Rotation);
int w = (int)(_view.Width * _view.ScaleX);
int h = (int)(_view.Height * _view.ScaleY);
double s = Math.Abs(Math.Sin(rotationRad));
double c = Math.Abs(Math.Cos(rotationRad));
int width = (int)(w * c + h * s);
int height = (int)(w * s + h * c);
canvas.Scale(_view.ScaleX, _view.ScaleY, width / 2, height / 2);
canvas.Rotate(_view.Rotation, width / 2, height / 2);
canvas.Translate((width - _view.Width) / 2, (height - _view.Height) / 2);
base.OnDrawShadow(canvas);
}
public override void OnProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint)
{
shadowTouchPoint.Set(shadowSize.X / 2, shadowSize.Y / 2);
base.OnProvideShadowMetrics(shadowSize, shadowTouchPoint);
}
}
I found a solution for android in java but this does not work for me. Maybe I translate the code wrong
Yes, you are translating it wrong, you did change the codes of OnDrawShadow but you didn't pay attention to OnProvideShadowMetrics, which aims at changing the size of the canvas drawing area, so you need to pass the same width and height that has been calculated by codes:
Here is the modified version of DragShdowBuilder:
public class MyDragShadowBuilder : DragShadowBuilder
{
private int width, height;
// Defines the constructor for myDragShadowBuilder
public MyDragShadowBuilder(View v) : base(v)
{
}
// Defines a callback that sends the drag shadow dimensions and touch point back to the system.
public override void OnProvideShadowMetrics(Android.Graphics.Point outShadowSize, Android.Graphics.Point outShadowTouchPoint)
{
double rotationRad = Java.Lang.Math.ToRadians(View.Rotation);
int w = (int)(View.Width * View.ScaleX);
int h = (int)(View.Height * View.ScaleY);
double s = Java.Lang.Math.Abs(Java.Lang.Math.Sin(rotationRad));
double c = Java.Lang.Math.Abs(Java.Lang.Math.Cos(rotationRad));
//calculate the size of the canvas
//width = view's width*cos(rad)+height*sin(rad)
width = (int)(w * c + h * s);
//height = view's width*sin(rad)+height*cos(rad)
height = (int)(w * s + h * c);
outShadowSize.Set(width, height);
// Sets the touch point's position to be in the middle of the drag shadow
outShadowTouchPoint.Set(outShadowSize.X / 2, outShadowSize.Y / 2);
}
// Defines a callback that draws the drag shadow in a Canvas that the system constructs
// from the dimensions passed in onProvideShadowMetrics().
public override void OnDrawShadow(Canvas canvas)
{
canvas.Scale(View.ScaleX, View.ScaleY, width/2 , height/2);
//canvas.DrawColor(Android.Graphics.Color.White);
canvas.Rotate(View.Rotation,width/2, height / 2);
canvas.Translate((width - View.Width)/2, (height - View.Height) / 2);
base.OnDrawShadow(canvas);
}
}
And here is the complete sample:RotatedTextViewSample
I'm using the following code in my resize method to maintain aspect ratio across multiple screen sizes:
#Override
public void resize(int width, int height)
{
float aspectRatio = (float)width / (float)height;
float scale = 1f;
Vector2 crop = new Vector2(0, 0);
if (aspectRatio > globals.ASPECT_RATIO)
{
scale = (float)height / (float)globals.VIRTUAL_HEIGHT;
crop.x = (width - globals.VIRTUAL_WIDTH * scale) / 2f;
}
else if (aspectRatio < globals.ASPECT_RATIO)
{
scale = (float)width / (float)globals.VIRTUAL_WIDTH;
crop.y = (height - globals.VIRTUAL_HEIGHT * scale) / 2f;
}
else
{
scale = (float)width / (float)globals.VIRTUAL_WIDTH;
}
float w = (float)globals.VIRTUAL_WIDTH * scale;
float h = (float)globals.VIRTUAL_HEIGHT * scale;
viewport = new Rectangle(crop.x, crop.y, w, h);
}
VIRTUAL_WIDTH, VIRTUAL_HEIGHT and ASPECT_RATIO are set as follows:
public final int VIRTUAL_WIDTH = 800;
public final int VIRTUAL_HEIGHT = 480;
public final float ASPECT_RATIO = (float)VIRTUAL_WIDTH / (float)VIRTUAL_HEIGHT;
This works perfectly with regards to maintaining the correct ratio when the screen size changes. However, camera.uproject (which I call before all touch events) doesn't work properly - touch positions are not correct when the resize code changes the screen size to anything other than 800x480.
Here's how I setup my camera in my create() method:
camera = new OrthographicCamera(globals.VIRTUAL_WIDTH, globals.VIRTUAL_HEIGHT);
camera.setToOrtho(true, globals.VIRTUAL_WIDTH, globals.VIRTUAL_HEIGHT);
And this is the start of my render() method:
camera.update();
Gdx.graphics.getGL20().glViewport((int)viewport.x, (int)viewport.y,
(int)viewport.width, (int)viewport.height);
Gdx.graphics.getGL20().glClearColor(0, 0, 0, 1);
Gdx.graphics.getGL20().glClear(GL20.GL_COLOR_BUFFER_BIT);
If I ignore the resize code and set the glViewport method call to Gdx.graphics.getWidth() and Gdx.graphics.getHeight(), camera.unproject works, but I obviously lose the maintaining of the aspect ratio. Anyone have any ideas?
Here's how I perform the unproject on my touch events:
private Vector3 touchPos = new Vector3(0, 0, 0);
public boolean touchDown (int x, int y, int pointer, int newParam)
{
touchPos.x = x;
touchPos.y = y;
touchPos.z = 0;
camera.unproject(touchPos);
//touch event handling goes here...
}
UPDATE
I've made a little more progress with this by implementing a Stage object, but it's still not working perfectly.
Here's how I now setup the stage and camera in my create method:
stage = new Stage();
camera = new OrthographicCamera(globals.VIRTUAL_WIDTH, globals.VIRTUAL_HEIGHT);
camera.setToOrtho(true, globals.VIRTUAL_WIDTH, globals.VIRTUAL_HEIGHT);
stage.setCamera(camera);
Here's my resize code:
public void resize(int width, int height)
{
Vector2 size = Scaling.fit.apply(800, 480, width, height);
int viewportX = (int)(width - size.x) / 2;
int viewportY = (int)(height - size.y) / 2;
int viewportWidth = (int)size.x;
int viewportHeight = (int)size.y;
Gdx.gl.glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
stage.setViewport(800, 480, true, viewportX, viewportY, viewportWidth, viewportHeight);
}
Here's the start of my render code:
stage.getCamera().update();
Gdx.graphics.getGL20().glClearColor(0, 0, 0, 1);
Gdx.graphics.getGL20().glClear(GL20.GL_COLOR_BUFFER_BIT);
spriteBatch.setProjectionMatrix(stage.getCamera().combined);
And here's how I handle touch events:
private Vector3 touchPos = new Vector3(0, 0, 0);
public boolean touchDown (int x, int y, int pointer, int newParam)
{
touchPos.x = x;
touchPos.y = y;
touchPos.z = 0;
stage.getCamera().unproject(touchPos);
//touch event handling goes here...
}
This now works when the screen is at default size and when the screen is enlarged. However, when the screen size is reduced the touch points become more and more inaccurate the smaller the screen gets.
Managed to find the answer to this. I simply needed to use the camera.unproject method call which takes Vector3, viewportX, viewportY, viewportWidth and viewportHeight parameters. The required viewport params are those calculated by the resize code shown above.
To solve your issue there is already a implemented solution inside of the new Stage system. Please take a look at the wiki scene2d #viewport. To have a fixed aspec ratio you do not need to resize and fit in manually. Here is the example with blackbars from the wiki:
public void resize (int width, int height) {
Vector2 size = Scaling.fit.apply(800, 480, width, height);
int viewportX = (int)(width - size.x) / 2;
int viewportY = (int)(height - size.y) / 2;
int viewportWidth = (int)size.x;
int viewportHeight = (int)size.y;
Gdx.gl.glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
stage.setViewport(800, 480, true, viewportX, viewportY, viewportWidth, viewportHeight);
}
How to get ImageView's Height and Width after Zoom in Zoom out.
I am using the following code:
mRectSrc.left = (int)(panX * bitmapWidth - viewWidth / (zoomX * 2));
mRectSrc.top = (int)(panY * bitmapHeight - viewHeight / (zoomY * 2));
mRectSrc.right = (int)(mRectSrc.left + viewWidth / zoomX);
mRectSrc.bottom = (int)(mRectSrc.top + viewHeight / zoomY);
mRectDst.left = getLeft();
mRectDst.top = getTop();
mRectDst.right = getRight();
mRectDst.bottom = getBottom();
// Adjust source rectangle so that it fits within the source image.
if (mRectSrc.left < 0) {
mRectDst.left += -mRectSrc.left * zoomX;
mRectSrc.left = 0;
}
if (mRectSrc.right > bitmapWidth) {
mRectDst.right -= (mRectSrc.right - bitmapWidth) * zoomX;
mRectSrc.right = bitmapWidth;
}
if (mRectSrc.top < 0) {
mRectDst.top += -mRectSrc.top * zoomY;
mRectSrc.top = 0;
}
if (mRectSrc.bottom > bitmapHeight) {
mRectDst.bottom -= (mRectSrc.bottom - bitmapHeight) * zoomY;
mRectSrc.bottom = bitmapHeight;
}
canvas.drawBitmap(mBitmap, mRectSrc, mRectDst, mPaint);
So using This code i am getting zoom effect on image. How Can i Get Bitmap Height and width after zoom in and zoom out.
I tried looking for some inbuilt function for getting the size of the bitmap attached with the image view but couldn't find any. So, I made a workaround. Just store the initial height and width of the bitmap in some variable and then update these variable values with the scale factor.
This is how you can get the bitmap attached with the image(will always return the initial size):
Drawable d = imageView.getDrawable();
Bitmap bmp = ((BitmapDrawable)d).getBitmap();
float w = bmp.getWidth();
float h = bmp.getHeight();
Method to update the values:
public void updateSize(float scaleFactor)
{
w *= scaleFactor;
h *= scaleFactor;
}
You can call this method from where you are adding zoom to the image.
I am detecting a face in my app. I use two ImageView, the first for the face and the second for a mask. I need set the mask position over the calculated face position and then can move the mask with touch event. For the touch event I did use of this tutorial: http://blahti.wordpress.com/2011/01/17/moving-views-part-2/
The method for mask scalling can return the face position. I tried set the XY position to the mask Imageview but ever is showed in the position 0.0. over the face. Then I can move the mask ImageView over the face. "view" is my mask. Ideally I need initialize the mask imageview over the face position.
PD: Sorry my english is bad.
protected Bitmap draw(int mode) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.c10);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Point position = new Point();
xRatio = anchoCara * 1.0f / anchoCara;
yRatio = altoCara * 1.0f / altoCara;
Bitmap resizedBitmap = null;
float factor = 9.6f;//9.6
for (int i = 0; i < eyesMidPts.length; i++) {
if (eyesMidPts[i] != null) {
pOuterBullsEye.setStrokeWidth(eyesDistance[i] / 6);
float newWidth = eyesDistance[i] * factor;
float newHeight = eyesDistance[i] * factor;
float scaleWidth = (newWidth) / width;
float scaleHeight = (newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,
height, matrix, true);
position.set(
(int) ((eyesMidPts[i].x * xRatio) - (eyesDistance[i] * factor) ),//2
(int) ((eyesMidPts[i].y * yRatio) - (eyesDistance[i] * factor) ));
Log.e("Face", "positio x : " + position.x);
Log.e("Face", "positio y : " + position.y);
Log.e("Face", "mascara width : " + resizedBitmap.getWidth());
Log.e("Face", "mascara heigth : " + resizedBitmap.getHeight());
// view.setBackgroundDrawable(new BitmapDrawable(resizedBitmap));
// view.setAdjustViewBounds(true);
// Log.i("Face", "positio x : " + view.getScrollX());
// Log.i("Face", "positio y : " + view.getScrollY());
}
}
return resizedBitmap;
}
Finally I did using the next code:
#Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
faceDetect(MODO_VIEW);
Display m=getWindowManager().getDefaultDisplay();
anchoPant=m.getWidth();
Log.i("Face","separacion derecha: "+((anchoPant-cara.getWidth())/2)+"px" );
MyAbsoluteLayout.LayoutParams mp=new MyAbsoluteLayout.LayoutParams(MyAbsoluteLayout.LayoutParams.WRAP_CONTENT, MyAbsoluteLayout.LayoutParams.WRAP_CONTENT, (anchoPant-cara.getWidth())/2, 0);
cara.setLayoutParams(mp);
//view.setBackgroundDrawable(new BitmapDrawable(draw(MODO_VIEW)));
view.setImageBitmap(draw(MODO_VIEW));
MyAbsoluteLayout.LayoutParams lp=new MyAbsoluteLayout.LayoutParams(MyAbsoluteLayout.LayoutParams.WRAP_CONTENT,MyAbsoluteLayout.LayoutParams.WRAP_CONTENT,position.x+((anchoPant-cara.getWidth())/2),position.y);
view.setLayoutParams(lp);
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
In my question, the method can return the position, Then using MyAbsoluteLayout.LayoutParams over OnGlobalLayout() I can set XY Position without problems.