Android ImageView with PixelArt - android

I want to dispaly pixelart in an ImageView but it keeps getting blurry.
I tried it like explained in this post (and some others) and I tried it with the function createScaledBitmap like you can see in the code. But it still gets blurry.
What else can I do to show my PixelArt correctly?
var bitmap = Bitmap.createScaledBitmap(fu, 120, 120, false)
var drawable = BitmapDrawable(context.resources, bitmap)
drawable.setAntiAlias(false)
drawable.setFilterBitmap(false)
myImageView.setImageDrawable(drawable )
(The Imageview is inside a RecyclerView but I don't think that that is important for my problem.)

You're scaling the image up before disabling the filtering, so it still looks filtered. It also wastes memory to enlarge a small sprite into a large bitmap.
You don't need to mess with manual scaling like that. You can set the drawable on you ImageView (either assign it in your XML layout or call setImageResource on it). Then set isFilterBitmap to false on its drawable.
myImageView.setImageResource(R.drawable.mySprite) // if not already set in XML
myImageView.drawable?.isFilterBitmap = false

Related

Using ClipDrawable to hide a part of and ImageView

I am trying to hide a part of an image so that the user does not see it. Initially I copied the Bitmap pixels on another Bitmap, without copying only the pixels that I needed and making the second bitmap the correct size at creation. That worked, but I have many large images and that results in OOMs unfortunately. So instead of doing that I thought on using a ClipDrawable to draw the image, and making the pixels that I don't need invisible.
The code is as follows
ClipDrawable clipDrawable = new ClipDrawable(new BitmapDrawable(resources, bitmap), gravity, orientation);
clipDrawable.setLevel(level);
// Cannot use as the imageview source. Must use background or else we don't get anything on the screen.
picture.setBackground(clipDrawable);
// This is super important. Do not modify this! Without it you will not get the fullscreen image working and the ImageView will be deleted
// from the parent layout.
picture.setImageResource(android.R.color.transparent);
The idea is that I calculate the level based on the image size so that I hide the pixels that I don't need. And it's working. Except I don't understand why I need to use
picture.setBackground(clipDrawable);
picture.setImageResource(android.R.color.transparent);
instead of the more normal:
picture.setImageDrawable(clipDrawable);
If I do the second more normal example then I don't get anything in the ImageView, but if I set it as a background and put a transparent image over it, then it works. Since I want to further manipulate the ImageView using a zooming class that needs the picture set as the src and not as background, I cannot have both, either I get the ClipDrawable showing or I get to have zoom on the image.
Any help would be appreciated!
picture.setImageDrawable(new
ClipDrawable(new BitmapDrawable(resources, bitmap), gravity, orientation
));
ClipDrawable clipDrawable = (ClipDrawable) picture.getDrawable();
clipDrawable.setLevel(level);

Android Memory Leak - setBackgroundDrawable - with images

When a user clicks a button, i keep switching the background of a layout like this in Activity code:
...
mylayout.setBackgroundDrawable(getResources().getDrawable(R.drawable.img1));//On First click
mylayout.setBackgroundDrawable(getResources().getDrawable(R.drawable.img2));//On Second Click
mylayout.setBackgroundDrawable(getResources().getDrawable(R.drawable.img10));//On 10th Click
...
mylayout.setBackgroundDrawable(getResources().getDrawable(R.drawable.img1));//On 11th Click 1st image again and so on.
I have 10 images which i keep rotating.
Soon, it causes OutOfMemory exception. What am i doing wrong?
If it matters in my manifest file i have:
android:minSdkVersion="11"
android:targetSdkVersion="17" />
EDIT 1
Average size of the image is: 50K
Average dimension of the image is: 600x450
All images img1, img2 etc.. are jpeg images
Solution Update
Reducing image dimensions to 300x200 resolved the issue. The memory requirements went down significantly by this single change.
It might be happening because your image resources are in drawable folder; which is equivalent to drawable-mdpi. And your device might be something other than mdpi.
So, either provide images for all the screed densities you are gonna support. Or, put images in drawable-nodpi, so your images will not be resized. Hence no OutOfMemoryException.
Referred from here
Use Bitmap.recycle() if you can change your Drawable with Bitmap.
Hope This will help
Each bitmap with dimension 600x450 has size of 600*450*4 = 1 080 000 bytes (1 MB) in RAM.
You should load so large bitmaps in another way:
Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.img1);
myLayout.setBackgroundDrawable(new BitmapDrawable(resources, bitmap));
And before changing background of the view you have to do
Drawable background = myLayout.getBackground();
if (background != null && background instanceof BitmapDrawable) {
myLayout.setBackgroundDrawable(null);
BitmapDrawable bd = (BitmapDrawable) background;
bd.getBitmap().recycle();
}
I dont think changing background layout so frequently is a good idead. I suggest that you could use RelativeLayout with an ImageView as first item. Then everytime you need to change your background, you can change your ImageView with some cached library such as: Universal Image Loader

CircularImageView with a solid color

The CircularImageView, https://github.com/Pkmmte/CircularImageView, works great when setting an image bitmap like so...
circularImageView.setImageBitmap(bitmap);
However, there are times where I just want to set a solid color instead of a bitmap. If I do something like this,
circularImageView.setBackgroundResource(R.color.blue);
The color of the view is set but the image is never made circular, so it fill the entire rectangular view. I'm assuming the getDrawable() is returning null so it can't actually manipulate the view. Anyone ran into this problem or any suggestions on what to do?
Edit:
I can do this but it seems a bit flimsy:
Bitmap image = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
image.eraseColor(android.graphics.Color.GREEN);
circularImageView.setImageBitmap(image);
You should call circularImageView.setImageResource(R.color.blue) instead.
The code you wrote sets the background of the view, not the content image. Looking at the code on github, this view will only clip the content image to the circle--it has no effect on the background at all.
Give a try for ColorDrawable;
int decode = Integer.decode("FF6666");
ColorDrawable colorDrawable = new ColorDrawable(decode);

android background image slows down app

I am using a viewpager to swipe amongst fragments in my app. I define the background in the XML, so
android:background="#drawable/bg_final"
If I use a simple background color, my app works very smooth. If I use it with this background image, fps decreases and my app becomes only passable. It is not slow, just not so smooth, however on weaker devices it could work laggy. Is it a bad way to apply a background image? The whole image is a 480x800 png with the size of 14.7kB. What might be the problem?
(the backgrounds of the fragments are transparent, the viewpager is in a main.xml which has its background with this image)
There are lots of answers out there that point to pre-scaling your bitmap. This is very imporant and you should do it if you can, however after trying several different things, the biggest performance gain I experienced was to create a view and override the onDraw method.
The class could be as simple as this:
public class DTImageView extends View {
public Bitmap imageBitmap;
public DTImageView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(imageBitmap != null)
canvas.drawBitmap(imageBitmap, 0, 0, null);
}
}
Invoke the code using a prescaled bitmap:
DTImageView bgImageView = new DTImageView(context);
bgImageView.imageBitmap = Bitmap.createScaledBitmap(bitmap,width,height,true);
At this point you can add this view to the view hierarchy where it belongs. In my case, my view hierarchy had a bgImage, a middle ground where all the other functionality happened, and fgImage.
Use the android sdk tool Hierarchy Viewer to profile the app and see how fast each view is taking to draw. My app spent 30-40ms drawing the bgImage and fgImage. Pre-scaling and overriding onDraw reduced that 30-40ms to about 1.5ms.
Here is a screenshot of the Draw performance for one of my views before and after:
BEFORE:
AFTER:
I'm a bit late, but this just bit me, and when running the GPU debugger, I noticed that the system scaled up my image! I had a 1920x1080 image, and it showed up as a 3780x6720 texture!
Ideally, you should have textures for each density in the respective folder (res/drawable-mdpi, res/drawable-hdpi, etc).
However, if you think your one texture is fine, put it into res/drawable-nodpi, not res/drawable. If you put it into res/drawable-nodpi, the system won't scale it up based on the dpi.
In my case, it changed a simple UI from 10fps on a Pixel XL to the full frame rate of 60fps.
I had a background image, not big in size, but with weird dimensions - therefore the stretching and bad performance. I made a method with parameters Context, a View and a drawable ID(int) that will match the device screen size. Use this in e.g a Fragments onCreateView to set the background.
public void setBackground(Context context, View view, int drawableId){
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), drawableId);
int width = Resources.getSystem().getDisplayMetrics().widthPixels;
int height = Resources.getSystem().getDisplayMetrics().heightPixels;
bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
BitmapDrawable bitmapDrawable = new BitmapDrawable(context.getResources(), bitmap);
view.setBackground(bitmapDrawable);
}
The reason for this could be that the image is being stretched, and Android has performance issues with stretching background images.
Instead, you should either replace the background image with either a background color or see this answer to have the image repeat instead of stretch.
I had the same problem and I resolved it by using this method:
Add your image to the mipmap folder which is located under the res directory. It will add the image according to it's density.
Create add only one background image with size 1080x1920 and put it in drawable-nodpi folder and that solved this problem

Moving views inside a layout/view in Android

I have more than one question, but I'll start with the more important and problematic one:
I have a FrameLayout with a ImageView inside it. I need to get the size of the "usable area" of the screen my activity is ocupping, so I set the onSizeChanged on my View (I extended the ImageView class). Everything worked fine here. Now I have a 1000x1000 image that I want to show on the screen, but without scaling. I want it to be clipped, really. If I set the ImageView dimensions using viewObject.setLayoutParams(new Gallery.LayoutParams(1000, 1000)); I get the image being showed correctly, but then the onSizeChanged event returns me always the "1000 x 1000" custom size rather than the real screen size value.
Any ideas of how can I show an image with its real size (no scale!) and still get the view to report the screen available space? I can change the layout as needed as well, of course.
. Amplexos.
Are you asking to get the dimensions of the ImageView? If so then you can get that using getLocalVisibleRect. Here's roughly how it's done:
ImageView yourImageView;
public void onCreate(...){
setContentView(...)
yourImageView = (ImageView) findViewById(...);
(...)
}
getImageViewSize(){
Rect imageViewSize = new Rect();
yourImageView.getLocalVisibleRect(imageViewSize);
// imageViewSize now has all the values you need
Log.d("Your log tag", "ImageView width = " + (imageViewSize.right -
imageViewSize.left));
}
There is however a catch. You have to make sure that you don't try to get the size of the view until after view is finished being laid out on the screen. In other words, if you try to get its size in onCreate, its size will be 0. You have to get it afterwards, for example at the same time as you resize your image, assuming that's done with a button. (If you're using a SurfaceHolder you can also call it during the surfaceCreated callback, but I doubt you're using one of those...)

Categories

Resources