I am using BitmapRegionDecoder.decodeRegion(...) to crop image. But it does not work for android 2.3.1 (api level 10 ) and below. Please let me know is there any alternative way to achieve this. Even android-support-v4.jar library does not contain this class.
You can use Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height) to crop an image. It's been around since always, so provides an alternative for older APIs.
The documentation is pretty self-explanatory:
Returns an immutable bitmap from the specified subset of the source
bitmap. The new bitmap may be the same object as source, or a copy may
have been made. It is initialized with the same density as the
original bitmap.
Parameters
source The bitmap we are subsetting
x The x coordinate of the first pixel in source
y The y coordinate of the first pixel in source
width The number of pixels in each row
height The number of rows
Returns A copy of a subset of the source bitmap or the source bitmap
itself.
Related
I have no 2D graphics and gaming experience. I taught myself by hundreds of mistakes and tens of lost hours. I started with simple Dress Up game. I used Nexus 5x for development where a screen looked OK. When I finished one milestone I tried game on big Lenovo tablet and tiny Samsung Mini phone. It looked horribly.
An original vector PSD file looks perfect, PNG export does not have any issues either. But when I scale the picture it is bumpy. I know it is bitmap. But there is another picture I scale in other place and it looks fine (both pictures are 32 bit):
When I play some game from Google Play they never have scaling issues. How are they implemented? Do they use vectors? Is there some trick?
I put everything into assets folder and though it took 1 MB. I decompiled some apk and they do not have set of variants for each resolution. Though they scale pictures nicely.
Some source code snippets:
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.w = w;
this.h = h;
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}
protected void onDraw(Canvas canvas) {
if (baseBitmap != null) {
double scale = Math.min((double) w / (double) figure.getW(), (double) h / (double) figure.getH());
figure.setScaleRatio(scale);
int sw = (int) (scale * figure.getW());
int x = ((w - sw) >> 1);
figure.setX(x);
paintDressPart(canvas, figure, figure.getMain());
if (displayedParts != null) {
for (DressPart dressPart : figure.getParts()) {
if (displayedParts.contains(dressPart.getId())) {
paintDressPart(canvas, figure, dressPart);
}
}
}
}
}
private void paintDressPart(Canvas canvas, Figure figure, DressPart part) {
double scale = figure.getScaleRatio();
int sh = (int) (scale * part.getH());
int sw = (int) (scale * part.getW());
int sdx = (int) (scale * part.getDestX());
int sdy = (int) (scale * part.getDestY());
scaledRect.set(figure.getX() + sdx, sdy, figure.getX() + sw + sdx, sh + sdy);
Rect partRect = part.getRect();
canvas.drawBitmap(baseBitmap, partRect, scaledRect, canvasPaint);
}
And if you want to see it yourself I preparred complete project on GitHub so you can download it and run it yourself:
https://github.com/literakl/DressUp
Update
Downscaling sucks as well. But thanks to Paavnet's comment I realized that Paint matters. See difference on a picture:
I ran demo app on 3,2" AVD image. The picture 278x786 is scaled to 107x303.
October Update
I rewrote the app to use resource folder instead of assets. Android scales pictures and then I rescale it again. Though it looks better than when I do not use Android resources scaling. Resources scaled picture looks usually better than unscaled nodpi / assets picture.
I found that mdpi works best. I even had xxhdpi pictures and it looked worse than mdpi. I think that even on xxhdpi device! But it may be a trouble of the picture that it was not painted well. Android resource scaling may smooth edges on lines.
I faced same issue .. that scaling not that smooth .. best standard method
Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);
You can find more optimized size-convertor in web .. but actually best solution I ended with use :
1st. Use Vector SVG resource .. this is 100% working with perfect quality. just be sure that SVG elements are compatible with android ( see : https://developer.android.com/studio/write/vector-asset-studio.html )
pros:
best quality
efficient size
editable easily
cons:
not all elements are supported.
Or
2nd. provide high resolution image (one is enough in xxxhdpi )
pros:
good quality
cons:
might lead to performance issue
I hope that may help,'.
Do they use vectors?
SVG/Vector is a handy solution but vector art requires extremely high precision, making it unsuitable for many art styles.It's easy to use vector art with basic shapes but it can be hard to add a simple detail with SVG symbolic styles although it can added easily with paint with png,jpeg. You can watch performance pattern videos on SVG where googlers suggest SVG for icon designs.Most games don't use it for rich and complex images.
So i won't go for SVG specially where small detailing matter a lot in games.
How are they implemented? Is there some trick?
One way is just publish the app with a single regular assets support (MDPI,HDPI) and if the screen density required high resolution then show a dialog to the user, asking him if he wants to download the high-resolution assets.
You can have a higher density images and scale it down at run-time based on phone density requirement using DisplayMetrics.Ideally you should keep the same aspect ratio.Fitting a game made for a 4:3 display looks hideous on a 16:9, but fitting a 16:9 on to a 16:10, nobody might notice.read for more.
You can use this way to easily get the height and width of screen(considering you are using SurfaceView as best practice )
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenHeight = metrics.heightPixels;
int screenWidth = metrics.widthPixels;
To get new width-height on rotation :
surfaceChanged(SurfaceHolder holder, int format, int width, int height)
That method gives you the width/height of your surface, so you could now use:
Bitmap.createScaledBitmap (Bitmap src, int dstWidth, int dstHeight, boolean filter)
From the Bitmap class to re-size your images in proportion to the size of the surface you're drawing onto.You can look into some technique to find appropriate scaling factors.
OpenGL,kotlin can give you advantage for creating rich graphic app.With these you can control the memory and GPU performance which basically are the back-bone of most games plus you can take advantage of controlling the rendering more efficiently with GPU and native memory space plus there is lot more you can think of, it will take some time if you not familiar with these but once you get it they can do many magical tricks.
Webp will help you to reduce size of large images to a considerable difference.Should try webp. (suggestion , webp has a little issue for alpha channel of image on some devices but too good for image size compression)
Conclusion : Implements best practices using OpenGL, SurfaceView,NDK plus loading images efficiently using scaling factors docs link and should try this too.
you can also take a look into 9patch images where you can put constraints to just scale some portion of the image instead of whole.it's quite useful.
Update : As suggested , if you are going to use higher resolution image then also look into this and this to disable scaling or manually calculation scaling factor.Good Luck
Note: Guys feel free to edit this answer with valid info if you like.
There is a nice post made by the popular Google developer Romain Guy that shows how to use a rounded corners drawable (called "StreamDrawable" in his code ) on a view efficiently.
The sample itself works very well on my Galaxy S3 when in portrait mode, but I have a few issues with it:
if the screen is small (for example on qvga screens), the shown images get cropped.
if I have an input bitmap that is too small than how I wish to show it, the output image has its edges smeared. Even on the Galaxy S3, when you run the sample code and it's on landscape, it looks awful:
I'm still not sure about it (since I use a workaround of scaling the image for using the sample code), but it think that even this solution is a bit slow when being used in a listView. Maybe there is a renderscript solution for this?
It doesn't matter if I use setImageDrawable or setBackgroundDrawable. It must be something in the drawable itself.
I've tried to play with the variables and the bitmapShader, but nothing worked. Sadly TileMode doesn't have a value for just stretching the image, only tiling it in some way.
As a workaround I can create a new scaled bitmap, but it's just a workaround. Surely there is a better way which will also not use more memory than it should.
How do I fix those issues and use this great code?
I think that the solution that is presented on this website works well.
unlike other solutions, it doesn't cause memory leaks, even though it is based on Romain Guy's solution.
EDIT: now on the support library, you can also use RoundedBitmapDrawable (using RoundedBitmapDrawableFactory ) .
I had some size issues with this code, and I solved it.
Maybe this will help you, too:
1) in the constructor store the bitmap in a local variable (e.g. private Bitmap bmp;)
2) override two more methods:
#Override
public int getIntrinsicWidth() {
return bmp.getWidth();
}
#Override
public int getIntrinsicHeight() {
return bmp.getHeight();
}
Best regards,
DaRolla
There underlying problem is that the BitmapShader's TileMode doesn't have a scaling option. You'll note in the source that it's been set to Shader.TileMode.CLAMP, and the docs describe that as:
replicate the edge color if the shader draws outside of its original bounds
To work around this, there are three solutions:
Constrain the size of the view in which the drawable is used to the size of the bitmap.
Constrain the drawing region; for instance, change:
int width = bounds.width() - mMargin;
int height = bounds.height() - mMargin;
mRect.set(mMargin, mMargin, width, height);
To:
int width = Math.min(mBitmap.getWidth(), bounds.width()) - mMargin;
int height = Math.min(mBitmap.getHeight(), bounds.height()) - mMargin;
mRect.set(mMargin, mMargin, width, height);
Scale the bitmap to the size of the drawable. I've moved creating the shader into onBoundsChange() and have opted to create a new bitmap from here:
bitmap = Bitmap.createScaledBitmap(mBitmap, width, height, true);
mBitmapShader = new BitmapShader(bitmap,
Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Note that this a potentially slow operation and will be running on the main thread. You might want to carefully consider how you want to implement it before you go for this last solution.
I want to know how to cut an image in jigsaw form in android at run time. What i actually want is user can enter any image and then made puzzle of it.
Any API or method for doing this in android ?
Thanks in Advance.
You can use this method in Bitmap to cut it into a new BitMap of specified width and height pixels
starting from specified pixel location of x and y
public static Bitmap createBitmap (Bitmap source,
int x,
int y, int width, int height)
Assuming your puzzle piece is square, you can use createBitmap() function.
Returns an immutable bitmap from the specified subset of the source
bitmap. The new bitmap may be the same object as source, or a copy may
have been made. It is initialized with the same density as the
original bitmap.
I have a imageview that I want to change the color based on a user choice
From examples on the internet I see the only way to really do this is by going through and modifying each pixel... however it seems to be EXTREMELY slow
if I add this into my code, it takes long enough that it prompts the user to force close or wait
for(int i =0 ; i < mBitmap.getHeight(); ++i)
{
for(int g = 0; g < mBitmap.getWidth(); ++g)
{
}
}
What is the best way to change the color of the image?
The Image is a small image 320x100 and is mostly transparent with a small image in the inside, the small image I want to change the color of
The Problem lies in using getPixel(x,y). Grabbing each pixel one by one is a very slow process. Instead use getPixels
void getPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height)
Returns in pixels[] a copy of the data in the bitmap.
It'll return you an array of integers with the pixel values (and operate on that array and then use setPixels) and it will be faster (although requires more memory)
For a small image this method will do. Stride is equal to the image width.
mBitmap.getPixels( pixels, 0 , mBitmap.getWidth(), 0, 0 , mBitmap.getWidth(), mBitmap.getHeight());
In order of complexity:
Check the samples in API demos for usage of ColorFilter and ColorMatrix. But since you described it as a image within an image that you are trying to modify, this may not apply.
Put your processing code on its own thread to avoid the Application Not Responding issue. Look into AsyncTask. You may need to have a wait animation running while its processing.
Consider OpenGL ES 1.x. Use the image as a texture and overlay a color with alpha to get the effect. Although this would have better performance, the complexity of adding UI elements would need to be taken into account (i.e. build your own).
Say I have a somewhat large (i.e. not fit in most phones' memory) bitmap on disk. I want to draw only parts of it on the screen in a way that isn't scaled (i.e. inSampleSize == 1)
Is there a way to load/draw just the part I want given a Rect specifying the area without loading the entire bitmap content?
I'm quite confident this is possible since you can load a really large bitmap file into an ImageView without problems so there must be some sort of a built-in way to handle large bitmaps... and after a few attempts, I've found a solution:
Instead of loading the entire bitmap and manually draw it yourself, load it as a Drawable instead:
InputStream mapInput = getResources().openRawResource(
R.drawable.transit_map);
_map = Drawable.createFromStream(mapInput, "transit_map");
_map.setBounds(0, 0, _mapDimension.width(), _mapDimension.height());
I'm using a resource file but since you can use Drawable.createFromStream to load image from any InputStream, it should works with arbitrary bitmap.
Then, use the Drawable.draw method to draw it onto the desired canvas like so:
int left = -(int) contentOffset.x;
int top = -(int) contentOffset.y;
int right = (int) (zoom * _mapDimension.width() - contentOffset.x);
int bottom = (int) (zoom * _mapDimension.height() - contentOffset.y);
_map.setBounds(left, top, right, bottom);
_map.draw(canvas);
As in the above case, You can also scale and translate the bitmap as well by manipulating the drawable's bounds and only the relevant parts of the bitmap will be loaded and drawn onto the Canvas.
The result is a pinch-zoomable view from just one single 200KB bitmap file. I've also tested this with a 22MB PNG file and it still works without any OutOfMemoryError including when screen orientation changes.
Now it's very relevant: BitmapRegionDecoder.
Note: available since Android SDK 10
It can easily be done by using RapidDecoder.
import rapid.decoder.BitmapDecoder;
Rect bounds = new Rect(10, 20, 30, 40);
Bitmap bitmap = BitmapDecoder.from("your-file.png")
.region(bounds)
.decode();
imageView.setImageBitmap(bitmap);
It supports down to Android 2.2 (API Level 8).
Generally speaking, that isn't possible, particularly since most image formats are compressed, so you don't even know which bytes to read until you've extracted the uncompressed form.
Break your image up into small tiles and load just the tiles you need to cover the region you want to display at runtime. To avoid jittery scrolling, you might also want to preload tiles that are just out of sight (the ones that border the visible tiles) on a background thread.