Imageview scaling method "centercrop" as code - android

Im trying to find out what android is doing when it scales an Image, specifically the "centercrop" type. So to find an answer I searched the ImageView sourcecode and find it here.
So what I tried is this code :
public Bitmap buildBluredBoxBackground () {
int [] screenSize = Utilities.getScreenSize(mainActivityContext); //screensize[0] = x and [1] is y
Matrix mDrawMatrix = new Matrix();
Bitmap bitmap = ((BitmapDrawable)fullscreenViewHolder.imageViewArt.getDrawable()).getBitmap();
float scale;
float dx = 0, dy = 0;
if (bitmap.getWidth() * screenSize[1] > screenSize[0] * bitmap.getHeight()) {
scale = (float) screenSize[1] / (float) bitmap.getHeight();
dx = (screenSize[0] - bitmap.getWidth() * scale) * 0.5f;
} else {
scale = (float) screenSize[0] / (float) bitmap.getWidth();
dy = (screenSize[1] - bitmap.getHeight() * scale) * 0.5f;
}
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
result = Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),mDrawMatrix,true);
... //Some processing work
return result;
}
But it is not giving me the same result. What am I doing wrong ?
Heres an example:
Original Picture
Orginal ImageView Centercrop
Tried Code
Edit:
XML of the ImageView
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/imageViewFullscreenArt"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/imageViewFullscreenArtBluredBox"/>
</FrameLayout>
So my ImageView is fullscreened. Thats why Im using the screenSize to process it.
Code how I'm applying it
Bitmap bluredBoxBackground = buildBluredBoxBackground();
imageViewBluredBox.setImageDrawable(new BitmapDrawable(getResources(),bluredBoxBackground));
Detailed Description:
Im just trying to get the same effect as ImageView.setScaleType(ScaleType.CENTER_CROP). So my code should do the same like the original setScaleType method. Why do I need it as code ? Because in my situation I can't get the drawingcache of my ImageView but I have to process & edit it somehow.

I adapted from teh source.
It will work with you change the return to Matrix how you apply.
public Matrix buildBluredBoxBackground () {
int dwidth = imageView.getDrawable().getIntrinsicWidth();
int dheight = imageView.getDrawable().getIntrinsicHeight();
int vwidth = imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight();
int vheight = imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom();
Matrix mDrawMatrix = imageView.getImageMatrix();
Bitmap bMap = imageView.getDrawingCache();
float scale;
float dx = 0, dy = 0;
if (dwidth * vheight > vwidth * dheight) {
scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * 0.5f;
} else {
scale = (float) vwidth / (float) dwidth;
dy = (vheight - dheight * scale) * 0.5f;
}
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
return mDrawMatrix;
}
And then use:
Matrix bluredBoxBackground = buildBluredBoxBackground();
imageViewBluredBox.setImageMatrix(bluredBoxBackground));
imageViewBluredBox.invalidate();

Related

TextureView TranslationX & Y not expected behaviour on API 23

Currently I am making a camera player with a textureview to render my camera. Because the preview can have any dimension I have created some custom code to alter the textureview when OnSurfaceTextureUpdated is called:
void updateTextureMatrix(int width, int height) {
Display display = WindowManager.DefaultDisplay;
var isPortrait = (display.Rotation == SurfaceOrientation.Rotation0 || display.Rotation == SurfaceOrientation.Rotation180);
int previewWidth = orgPreviewWidth;
int previewHeight = orgPreviewHeight;
if(isPortrait) {
previewWidth = orgPreviewHeight;
previewHeight = orgPreviewWidth;
}
// determine which part to crop
float widthRatio = (float)width / previewWidth;
float heightRatio = (float)height / previewHeight;
float scaleX;
float scaleY;
if(widthRatio > heightRatio) {
// height must be cropped
scaleX = 1;
scaleY = widthRatio * ((float)previewHeight / height);
} else {
// width must be cropped
scaleX = heightRatio * ((float)previewWidth / width);
scaleY = 1;
}
Android.Graphics.Matrix matrix = new Android.Graphics.Matrix();
matrix.SetScale(scaleX, scaleY);
_textureView.SetTransform(matrix);
float scaledWidth = width * scaleX;
float scaledHeight = height * scaleY;
float dx = (width - scaledWidth) * 0.5f;
float dy = (height - scaledHeight) * 0.5f;
_textureView.TranslationX = dx;
_textureView.TranslationY = dy;
}
The scaling & calculation of dx & dy work perfectly fine on older android devices but the devices I have at my disposal with API level 23 throw unexpected behaviour.
The galaxy S3 displays it correctly:
But on the S7:
The phone cuts off a lot of the image, despite positioning it correctly. This makes me believe the bottom part is not being rendered where on older devices it is. Can anyone confirm this and point me in the correct position to fix this issue?
After long testing I figured out the issue was due to the SetTransform method. I was setting my scale using the matrix but this somehow rendered my texture & ignored the TranslationX & TranslationY. Removing the matrix & replacing it by
float scaledWidth = width * scaleX;
float scaledHeight = height * scaleY;
float dx = (width - scaledWidth) * 0.5f;
float dy = (height - scaledHeight) * 0.5f;
_textureView.ScaleX = scaleX;
_textureView.ScaleY = scaleY;
_textureView.TranslationX = dx;
_textureView.TranslationY = dy;
Fixed my issue of rendering wrongly on certain android devices.

android-gpuimage - image straightening with GPUImageView

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();

Set maximum width to load images with Picasso

I have a fixed header in my activity that I need to load images dynamically into it. I need this images to fill this header entirely, keep the width and height proportion.
If I load the image before, I get OutOfMemoryException. If I use Picasso's fit() method there will be blank spaces in the area. And if I use Transformation as some people in StackOverflow have suggested, I also get OutOfMemoryException.
How can I solve this?
Header XML:
<FrameLayout
android:id="#+id/headerContainer"
android:layout_width="match_parent"
android:layout_height="140dp"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:background="#ffd5d5d5">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imageView4"
android:layout_gravity="center"
android:src="#drawable/background_camera" />
<ViewFlipper
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/viewFlipper"
android:layout_gravity="center" >
</ViewFlipper>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Adicionar foto do estacionamento"
android:id="#+id/textAddPhotos"
android:layout_gravity="center"
android:textColor="#ff757575"
android:padding="10dp" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:id="#+id/addPhotosArea"></RelativeLayout>
</FrameLayout>
The images are added to the ViewFlipper programmatically using the following code:
final ImageView imageView = new ImageView(SuggestionActivity.this);
imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
Picasso.with(SuggestionActivity.this).load(selectedImageUri)
.fit().centerInside()
.into(imageView);
mViewFlipper.addView(imageView);
UPDATE
I figured out that Picasso is miscalculating the size when I load an image with exif orientation = 90ยบ.
I'm trying to change its source, but the logic is pretty confusing.
I think the problem is in this function of the BitmapHunter.java file:
static Bitmap transformResult(Request data, Bitmap result, int exifRotation) {
int inWidth = result.getWidth();
int inHeight = result.getHeight();
int drawX = 0;
int drawY = 0;
int drawWidth = inWidth;
int drawHeight = inHeight;
Matrix matrix = new Matrix();
if (data.needsMatrixTransform()) {
int targetWidth = data.targetWidth;
int targetHeight = data.targetHeight;
float targetRotation = data.rotationDegrees;
if (targetRotation != 0) {
if (data.hasRotationPivot) {
matrix.setRotate(targetRotation, data.rotationPivotX, data.rotationPivotY);
} else {
matrix.setRotate(targetRotation);
}
}
if (data.centerCrop) {
float widthRatio = targetWidth / (float) inWidth;
float heightRatio = targetHeight / (float) inHeight;
float scale;
if (widthRatio > heightRatio) {
scale = widthRatio;
int newSize = (int) Math.ceil(inHeight * (heightRatio / widthRatio));
drawY = (inHeight - newSize) / 2;
drawHeight = newSize;
} else {
scale = heightRatio;
int newSize = (int) Math.ceil(inWidth * (widthRatio / heightRatio));
drawX = (inWidth - newSize) / 2;
drawWidth = newSize;
}
matrix.preScale(scale, scale);
} else if (data.centerInside) {
float widthRatio = targetWidth / (float) inWidth;
float heightRatio = targetHeight / (float) inHeight;
float scale = widthRatio < heightRatio ? widthRatio : heightRatio;
matrix.preScale(scale, scale);
} else if (targetWidth != 0 && targetHeight != 0 //
&& (targetWidth != inWidth || targetHeight != inHeight)) {
// If an explicit target size has been specified and they do not match the results bounds,
// pre-scale the existing matrix appropriately.
float sx = targetWidth / (float) inWidth;
float sy = targetHeight / (float) inHeight;
matrix.preScale(sx, sy);
}
}
if (exifRotation != 0) {
matrix.preRotate(exifRotation);
}
Bitmap newResult =
Bitmap.createBitmap(result, drawX, drawY, drawWidth, drawHeight, matrix, true);
if (newResult != result) {
result.recycle();
result = newResult;
}
return result;
}
Use .fit().centerCrop(), it should do what you're looking for.
The ImageView must be added to the ViewFlipper before loading the image. This is so that Picasso knows the height/width of the ImageView, which it needs to scale the image properly to the size of the ImageView.
Change it to:
final ImageView imageView = new ImageView(SuggestionActivity.this);
imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
mViewFlipper.addView(imageView);
Picasso.with(SuggestionActivity.this).load(selectedImageUri)
.fit().centerInside()
.into(imageView);

get zoomed image bitmap height and width in android

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.

Get the displayed size of an image inside an ImageView

The code is simple:
<ImageView android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#drawable/cat"/>
Notice the ImageView used fill_parent for width and height.
The image cat is a small image and it will be zoomed in to fit the ImageView, and keep the width/height ratio at the same time.
My question is how to get the displayed size of the image? I tried:
imageView.getDrawable().getIntrinsicHeight()
But which it the original height of the image cat.
I tried:
imageView.getDrawable().getBounds()
But which returns Rect(0,0,0,0).
the following will work:
ih=imageView.getMeasuredHeight();//height of imageView
iw=imageView.getMeasuredWidth();//width of imageView
iH=imageView.getDrawable().getIntrinsicHeight();//original height of underlying image
iW=imageView.getDrawable().getIntrinsicWidth();//original width of underlying image
if (ih/iH<=iw/iW) iw=iW*ih/iH;//rescaled width of image within ImageView
else ih= iH*iw/iW;//rescaled height of image within ImageView
(iw x ih) now represents the actual rescaled (width x height) for the image within the view (in other words the displayed size of the image)
EDIT: I think a nicer way to write the above answer (and one that works with ints) :
final int actualHeight, actualWidth;
final int imageViewHeight = imageView.getHeight(), imageViewWidth = imageView.getWidth();
final int bitmapHeight = ..., bitmapWidth = ...;
if (imageViewHeight * bitmapWidth <= imageViewWidth * bitmapHeight) {
actualWidth = bitmapWidth * imageViewHeight / bitmapHeight;
actualHeight = imageViewHeight;
} else {
actualHeight = bitmapHeight * imageViewWidth / bitmapWidth;
actualWidth = imageViewWidth;
}
return new Point(actualWidth,actualHeight);
Here is a helper function to get the bounds of image in an imageView.
/**
* Helper method to get the bounds of image inside the imageView.
*
* #param imageView the imageView.
* #return bounding rectangle of the image.
*/
public static RectF getImageBounds(ImageView imageView) {
RectF bounds = new RectF();
Drawable drawable = imageView.getDrawable();
if (drawable != null) {
imageView.getImageMatrix().mapRect(bounds, new RectF(drawable.getBounds()));
}
return bounds;
}
I guess a lot of people are coming from this example https://developer.android.com/training/animation/zoom.html and don't want to use android:scaleType="centerCrop" (maybe because the ImageView is in a constraint layout and you want to see the small picture uncroped) don't you worry, I got your back!
Just replace the entire block beginning with
// Adjust the start bounds to be the same aspect ratio as the final
// bounds using the "center crop" technique.
with the following
//adjust for scaled image to constraint
int realheight = ResourcesCompat.getDrawable(getResources(),imageResId,null).getIntrinsicHeight();
int realwidth = ResourcesCompat.getDrawable(getResources(),imageResId,null).getIntrinsicWidth();
// Adjust the start bounds to be the same aspect ratio as the final
// bounds using ueen's adjusteddimensions technique. This prevents undesirable
// stretching during the animation. Also calculate the start scaling
// factor (the end scaling factor is always 1.0).
float startScale;
if ((float) finalBounds.width() / finalBounds.height()
> (float) startBounds.width() / startBounds.height()) {
// Extend start bounds horizontally
// after check whether height or width needs adjusting
if ((float) startBounds.width() / startBounds.height() < (float) realwidth / realheight) {
int adjustedheight = realheight*startBounds.width()/realwidth;
int adjustedoffset = (startBounds.height()-adjustedheight) / 2;
startScale = (float) adjustedheight / finalBounds.height();
float startWidth = startScale * finalBounds.width();
float deltaWidth = (startWidth - startBounds.width()) / 2;
startBounds.left -= deltaWidth;
startBounds.right += deltaWidth;
startBounds.offset(+0, +adjustedoffset);
} else {
int adjustedwidth = realwidth*startBounds.height()/realheight;
int adjustedoffset = (startBounds.width()-adjustedwidth) / 2;
startScale = (float) startBounds.height() / finalBounds.height();
float startWidth = startScale * finalBounds.width();
float deltaWidth = (startWidth - adjustedwidth) / 2;
startBounds.left -= deltaWidth;
startBounds.right += deltaWidth;
startBounds.offset(+adjustedoffset, +0);
}
} else {
// Extend start bounds vertically
// after check whether height or width needs adjusting
if ((float) startBounds.width() / startBounds.height() > (float) realwidth / realheight) {
int adjustedwidth = realwidth*startBounds.height()/realheight;
int adjustedoffset = (startBounds.width()-adjustedwidth) / 2;
startScale = (float) adjustedwidth / finalBounds.width();
float startHeight = startScale * finalBounds.height();
float deltaHeight = (startHeight - startBounds.height()) / 2;
startBounds.top -= deltaHeight;
startBounds.bottom += deltaHeight;
startBounds.offset(+adjustedoffset, +0);
} else {
int adjustedheight = realheight*startBounds.width()/realwidth;
int adjustedoffset = (startBounds.height()-adjustedheight) / 2;
startScale = (float) startBounds.width() / finalBounds.width();
float startHeight = startScale * finalBounds.height();
float deltaHeight = (startHeight - adjustedheight) / 2;
startBounds.top -= deltaHeight;
startBounds.bottom += deltaHeight;
startBounds.offset(+0, +adjustedoffset);
}
}
works like a charme,
you're welcome :)
Further explanation: as usual we check wheter the picture is taller than wide (expanded the height of the picture should match the height of expandedImageView) or vice versa. Then we check if the picture in the original (smaller) ImageView (thumbView) is matching the width or the heigth, so we can adjust for the space.
This way we achieve a smooth scaling animation while not croping the picture in the thumbView, no matter it's dimension (as they may change from device to device when using constarints) or that of the picture.
use
// For getting imageview height
imgObj.getMeasuredHeight()
// For getting imageview width
imgObj.getMeasuredWidth();
//For getting image height inside ImageView
imgObj.getDrawable().getIntrinsicHeight();
//For getting image width inside ImageView
imgObj.getDrawable().getIntrinsicWidth();

Categories

Resources