Color Banding Android Solution - android

What is possible solution to banded images in Android Activity or in OpenGl.
Look at the answer below.
Hope it Helps

Color Banding Solved ooooooooooyyyyyyyeaaaaaaaaaa
I solved color banding in two phases
1) * when we use the BitmapFactory to decode resources it decodes the resource in RGB565 which shows color banding, instead of using ARGB_8888, so i used BitmapFactory.Options for setting the decode options to ARGB_8888
second problem was whenever i scaled the bitmap it again got banded
2) This was the tough part and took a lot of searching and finally worked
* the method Bitmap.createScaledBitmap for scaling bitmaps also reduced the images to RGB565 format after scaling i got banded images(the old method for solving this was using at least one transparent pixel in a png but no other format like jpg or bmp worked)so here i created a method CreateScaledBitmap to scale the bitmap with the original bitmaps configurations in the resulting scale bitmap(actually i copied the method from a post by logicnet.dk and translated in java)
BitmapFactory.Options myOptions = new BitmapFactory.Options();
myOptions.inDither = true;
myOptions.inScaled = false;
myOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//important
//myOptions.inDither = false;
myOptions.inPurgeable = true;
Bitmap tempImage =
BitmapFactory.decodeResource(getResources(),R.drawable.defaultart, myOptions);//important
//this is important part new scale method created by someone else
tempImage = CreateScaledBitmap(tempImage,300,300,false);
ImageView v = (ImageView)findViewById(R.id.imageView1);
v.setImageBitmap(tempImage);
// the function
public static Bitmap CreateScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)
{
Matrix m = new Matrix();
m.setScale(dstWidth / (float)src.getWidth(), dstHeight / (float)src.getHeight());
Bitmap result = Bitmap.createBitmap(dstWidth, dstHeight, src.getConfig());
Canvas canvas = new Canvas(result);
//using (var canvas = new Canvas(result))
{
Paint paint = new Paint();
paint.setFilterBitmap(filter);
canvas.drawBitmap(src, m, paint);
}
return result;
}
Please correct me if i am wrong.
Also comment if it worked for you.
I am so happy i solved it, Hope it works for you All.
For OpenGl you simply bind the bitmap created after applying upper functions

Related

Android SurfaceView Bitmap quality is worse than ImageView in XML

When I draw a bitmap on a SurfaceView by a draw method the image(background in this case) is blurry and bad quality, even though it is not scaled up. When I use the same image as background in an XML layout it looks way better, sharper.
This is the code I use to load a bitmap:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inPreferQualityOverSpeed = true;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
bmpLoader = BitmapFactory.decodeResource(getResources(), R.drawable.image);
bmpImage= Bitmap.createScaledBitmap(bmpLoader,width, height,true);
if (bmpLoader!=null){
bmpLoader.recycle();
bmpLoader = null;
}
And also I use a special paint for painting bitmaps:
bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);
bitmapPaint.setDither(true);
bitmapPaint.setFlags(Paint.DITHER_FLAG);
bitmapPaint.setFilterBitmap(true);
Thanks in advance for any suggestions.
In case someone has the same problem:
You have to call and set this pixel format
yoursurfaceviewinstance.getHolder().setFormat(PixelFormat.RGBA_8888);
before you set your content view to this surface view.
In case someone has the same problem:
You have to call and set this pixel format
yoursurfaceviewinstance.getHolder().setFormat(PixelFormat.RGBA_8888);
before you set your content view to this surface view.

Draw over Asset png efficiently and cache

I’m using thquinn’s DraggableGridView and load ~60 images into it. This all works fine. I had all the images needed in my assets, but want to create them at runtime since first of only the Text on the images change which seems redundant and I can reduce the appsize and the second reason is that I need to sometimes change the icons over the Air where adding wouldn’t be the problem but deleting from assets isn’t possible and would use unnecessary space. That briefly to explain my motives here.
So I’ve used the method from this Post to draw text over my Asset png and then convert it into a Bitmap to be able to use them in the LRUcache. This works with a few images but as soon as I try and display all needed Images I get an OutOfMemory error. The Base Images are 200x200 px which I think should also be scaled to the need size depending on screensize and density.
First of, this method doesn’t seem efficient because I create a Bitmap canvas then make a LayerdDrawable which I make into a Bitmap (for caching) again. Not sure, but it just feels like I’m creating to much temp images which clutter up the memory.
And then I’m using a BitmapDrawable which is depreciated. How would this method look without the BitmapDrawable??
Am I going about this the right way in general and How would I make this method efficiently so I don’t get the OOM error?!?
BTW. When I don’t use LRUcache and just return the LayerdDrawable for the GridView the images load fine but I get the Exception after a couple of Orientation changes!
This is the method as I have it atm:
private Bitmap createIcon(Drawable backgroundImage, String text,
int width, int height) {
String key = text.toLowerCase();
Bitmap cachedBitmap = getBitmapFromMemCache(key);
if (cachedBitmap != null){
Log.d("TRACE", "is cached");
return cachedBitmap;
}
else{
Bitmap canvasBitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
Canvas imageCanvas = new Canvas(canvasBitmap);
Typeface font = Typeface.createFromAsset(getActivity().getAssets(), "myriadpro.ttf");
Paint imagePaint = new Paint();
imagePaint.setTextAlign(Paint.Align.CENTER);
imagePaint.setTextSize(26);//
imagePaint.setTypeface(font);
imagePaint.setAntiAlias(true);
imagePaint.setColor(Color.parseColor("#562b12"));
backgroundImage.draw(imageCanvas);
imageCanvas.drawText(text, (width / 2)+4, (height / 2)-8, imagePaint);
LayerDrawable layerDrawable = new LayerDrawable(
new Drawable[]{backgroundImage, new BitmapDrawable(canvasBitmap)});
int w = layerDrawable.getIntrinsicWidth();
int h = layerDrawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
layerDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
layerDrawable.draw(canvas);
addBitmapToMemoryCache(key,bitmap);
return bitmap;
}
}
Update:
I have tried with another method now, which seems better because it’s not using BitmapDrawable. But I still get OOM error. Also it generally doesn’t seem to realy use the cached images, when I change orientation only 1 or 2 images come from the cache.
I also failed to metion before the this is inside a Fragment. Not sure if it matters. But in portrait mode i have only this Fragment and in Landscape there can be another one if the width allows it.
public Bitmap drawTextToBitmap(Context mContext, int resourceId, String mText) {
try {
int memory = (int) (Runtime.getRuntime().freeMemory() / 1024);
Log.d("TRACE", "memory " + memory);
Log.d("TRACE", mText);
String key = mText.toLowerCase();
Bitmap cachedBitmap = getBitmapFromMemCache(key);
if (cachedBitmap != null){
Log.d("TRACE", "is cached");
return cachedBitmap;
}
else{
Resources resources = mContext.getResources();
float scale = resources.getDisplayMetrics().density;
Bitmap bitmap = BitmapFactory.decodeResource(resources, resourceId);
android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();
// set default bitmap config if none
if(bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
bitmap = bitmap.copy(bitmapConfig, true); // OOE error happens here
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.rgb(110,110, 110));
paint.setTextSize((int) (25 * scale));
Rect bounds = new Rect();
paint.getTextBounds(mText, 0, mText.length(), bounds);
int x = (bitmap.getWidth() - bounds.width())/6;
int y = (bitmap.getHeight() + bounds.height())/5;
canvas.drawText(mText, x * scale, y * scale, paint);
addBitmapToMemoryCache(key,bitmap);
return bitmap;
}
} catch (Exception e) {
// TODO: handle exception
return null;
}
}
I worked on an app which needed to constantly hold 3-4 very large images in memory, and I was struggling with a problem very similar to yours. I loaded a bitmap from a byte array, then I needed to copy it to make it mutable, so that I could draw on it. This copy would cause there to be 1 too many bitmaps in memory, and then cause an OOM crash.
Eventually I found a way to load it once, and make it mutable at the same time:
Options options = new Options();
options.inMutable = true;
BitmapFactory.decodeResource(getResources(), id, options);
Use this, instead of copying the bitmap to make it mutable. I'm not sure if you really need the ARGB_8888 configuration, but if you don't, this should at least improve your memory efficiency.
Note: This will only work with Android 3.0 and above. For versions that need to run on 2.x and above, you can use a little reflection magic to see if the "inMutable" field exists in runtime. While this won't help on pre-3.0 devices, it will still provide a good improvement for most devices (and I've also noticed that devices running 2.x tend to have more memory flexibility).
Here's the code:
Options options = new Options();
options.inPurgeable = true;
options.inInputShareable = true;
Bitmap mutableBitmap = null;
try
{
Options.class.getField("inMutable").set(options, Boolean.TRUE);
Bitmap decodedBitmap = BitmapFactory.decodeResource(getResources(), id, options);
mutableBitmap = decodedBytes;
}
catch (NoSuchFieldException noFieldException)
{
Bitmap decodedBitmap = BitmapFactory.decodeResource(getResources(), id, options);
mutableBitmap = decodedBitmap .copy(decodedBitmap .getConfig(), true);
decodedBitmap .recycle();
}

How to use high-quality rendering for an Android resource bitmap loaded into an OpenGL texture?

I know very little about OpenGL so please be gentle.
The app needs to load a bitmap (from resources), resize it, and use it in an OpenGL texture. I have an implementation that works, but there was a bad banding issue on the Wildfire S. So I changed the implementation and fixed the banding issue (by switching to ARGB_8888) but that then broke the functionality on the Galaxy Nexus and the Nexus One.
I am seeing three visual presentations:
The bitmap (a smooth 24-bit gradient) shows correctly, with no banding.
The gradient shows, but with obvious banding
The texture shows as flat white, no bitmap (or issues in logcat)
Here are two versions of the method to load the bitmap, and notes on the results seen with each:
// White on Galaxy Nexus. White on Nexus One. Renders correct image (no banding) on Wildfire S
private Bitmap getBitmap1() {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.outWidth = getTextureSize();
options.outHeight = getTextureSize();
final Bitmap bmp;
bmp = BitmapFactory.decodeResource(getResources(), bitmapResourceId, options);
return bmp;
}
// Renders correctly (no banding) on Galaxy Nexus. Renders on Nexus One and Wildfire S but with obvious banding.
private Bitmap getBitmap2() {
int textureSize = getTextureSize();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.outWidth = getTextureSize();
options.outHeight = getTextureSize();
final Bitmap bmp;
bmp = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), bitmapResourceId, options), textureSize, textureSize, true);
return bmp;
}
getTextureSize() returns 1024.
How do I build a single method that shows the bitmap without banding on all devices, and without any devices show a big white box?
getBitmap1
outHeight and outWidth are used in conjunction with inJustDecodeBounds. You cannot use them to load a scaled bitmap. So the reason you are seeing a white texture is that the bitmap is not a power of two.
getBitmap2
you should keep a reference to the bitmap returned by decodeResource so that you can recycle it later.
also use options.inScaled = false;to load an unscaled version of the bitmap. Also take note that createScaledBitmap may change the depth of the bitmap to RGB_565 if the original bitmap contains no alpha channel (Source);
Questions:
is the original Bitmap Resource square? If not your scaling code will change the aspect ratio which could result in artifacts.
EDIT:
so how do you scale a bitmap and preserve the bit depths?
Easiest solution is to pass a bitmap with alpha channel into createScaledBitmap.
You can also scale yourself like so:
Bitmap newBitmap = Bitmap.createBitmap(1024, 1024, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
final int width = src.getWidth();
final int height = src.getHeight();
final float sx = 1024 / (float)width;
final float sy = 1024 / (float)height;
Matrix m = new Matrix();
m.setScale(sx, sy);
canvas.drawBitmap(src,m,null );
src.recycle();
ANOTHER EDIT:
take a look at this Question for pointers on how to deal with that.
OpenGL.org has this to say about that error:
GL_INVALID_VALUE​, 0x0501: Given when a value parameter is not a leval
value for that function. This is only given for local problems; if the
spec allows the value in certain circumstances, and other parameters
or state dictate those circumstances, then GL_INVALID_OPERATION is the
result instead.
Step one is to find the exact opengl call that is causing the problem. You'll have to do trial and error to see which line is throwing that error. If you set up the program flow like this:
glSomeCallA()
glGetError() //returns 0
glSomeCallB()
glGetError() //returns 0
glSomeCallC()
glGetError() //returns 0x501
Then you'll know that glSomeCallC was the operation that caused the error. If you look at the man page for that particular call, it will enumerate everything that could cause a specific error to occur.
In your case I'll guess that the error will be after glTexImage call just to save you some time, though I'm not positive.
If you look at the glTexImage man page, at the bottom it will list everything that can cause an invalid value error. My guess will be that your texture is larger than the GL_MAX_TEXTURE_SIZE. You can confirm this by checking glGetIntegerv(GL_MAX_TEXTURE_SIZE);
Color Banding Solved ooooooooooyyyyyyyeaaaaaaaaaa
I solved color banding in two phases
1) * when we use the BitmapFactory to decode resources it decodes the resource in RGB565 which shows color banding, instead of using ARGB_8888, so i used BitmapFactory.Options for setting the decode options to ARGB_8888
second problem was whenever i scaled the bitmap it again got banded
2) This was the tough part and took a lot of searching and finally worked
* the method Bitmap.createScaledBitmap for scaling bitmaps also reduced the images to RGB565 format after scaling i got banded images(the old method for solving this was using at least one transparent pixel in a png but no other format like jpg or bmp worked)so here i created a method CreateScaledBitmap to scale the bitmap with the original bitmaps configurations in the resulting scale bitmap(actually i copied the method from a post by logicnet.dk and translated in java)
BitmapFactory.Options myOptions = new BitmapFactory.Options();
myOptions.inDither = true;
myOptions.inScaled = false;
myOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//important
//myOptions.inDither = false;
myOptions.inPurgeable = true;
Bitmap tempImage =
BitmapFactory.decodeResource(getResources(),R.drawable.defaultart, myOptions);//important
//this is important part new scale method created by someone else
tempImage = CreateScaledBitmap(tempImage,300,300,false);
ImageView v = (ImageView)findViewById(R.id.imageView1);
v.setImageBitmap(tempImage);
// the function
public static Bitmap CreateScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)
{
Matrix m = new Matrix();
m.setScale(dstWidth / (float)src.getWidth(), dstHeight / (float)src.getHeight());
Bitmap result = Bitmap.createBitmap(dstWidth, dstHeight, src.getConfig());
Canvas canvas = new Canvas(result);
//using (var canvas = new Canvas(result))
{
Paint paint = new Paint();
paint.setFilterBitmap(filter);
canvas.drawBitmap(src, m, paint);
}
return result;
}
Please correct me if i am wrong.
Also comment if it worked for you.
I am so happy i solved it, Hope it works for you.

error with setPixels

i am trying to edit images. but i am getting errors with setPixels.
picw = pic.getWidth();
pich = pic.getHeight();
picsize = picw*pich;
int[] pix = new int [picsize];
pic.getPixels(pix, 0, picw, 0, 0, picw, pich);
pic.setPixels(pix,0,pic.getWidth(),0,0,pic.getWidth(),pic.getHeight());
but i am getting illegal state exception with setPixels
Caused by: java.lang.IllegalStateException
at android.graphics.Bitmap.setPixels(Bitmap.java:878)
at com.sandyapps.testapp.testapp.onCreate(testapp.java:66)
I think your Bitmap is not mutable (see setPixel()'s documentation).
If so, create a mutable copy of this Bitmap (using Bitmap.copy(Bitmap.Config config, boolean isMutable) as an example) and work on this one.
It's simple, just use the following command to change it to a mutable Bitmap:
myBitmap = myBitmap.copy( Bitmap.Config.ARGB_8888 , true);
Now the Bitmap myBitmap is replaced by the same Bitmap but this time is mutable
You can also choose another way of storing Pixels (ARGB_8888 etc..):
https://developer.android.com/reference/android/graphics/Bitmap.Config.html
Most probably your pic is immutable. By default, any bitmap created from drawable would be immutable.
If you need to modify an existing bitmap, you should do following:
// Create a bitmap of the same size
Bitmap newBmp = Bitmap.createBitmap(pic.getWidth(), pic.getHeight(), Config.ARGB);
// Create a canvas for new bitmap
Canvas c = new Canvas(newBmp);
// Draw your old bitmap on it.
c.drawBitmap(pic, 0, 0, new Paint());
I had the same problem. Use to fix it:
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inMutable = true;
Bitmap bitmap = BitmapFactory.decodeResource( getResources(), R.drawable.my_bitmap, opt );
I was facing this problem and finally fixed after long time.
public static void filterApply(Filter filter){
Bitmap bitmcopy = PhotoModel.getInstance().getPhotoCopyBitmap();
//custom scalling is important to apply filter otherwise it will not apply on image
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmcopy, bitmcopy.getWidth()-1, bitmcopy.getHeight()-1, false);
filter.processFilter(scaledBitmap);
filterImage.setImageBitmap(scaledBitmap);
}

Blending pixels from Two Bitmaps

I'm beating my head against a wall here, and I'm fairly certain I'm doing something stupid, so time to make my stupidity public.
I'm trying to take two images, blend them together into a third image using standard blending algorithms (Hardlight, softlight, overlay, multiply, etc).
Because Android does not have such blend properties build in, I've gone down the path of taking each pixel and combine them using an algorithm. However, the results are garbage. Below is the results of a simple multiply blend (images used, and expected result).
BASE:
BLEND:
EXPECTED RESULT:
GARBAGE RESULT:
Any help would be appreciated. Below is the code, which I've tried to strip out all the "junk", but some may have made it through. I'll clean it up if something isn't clear.
ImageView imageView = (ImageView) findViewById(R.id.ImageView01);
Bitmap base = BitmapFactory.decodeResource(getResources(), R.drawable.base);
Bitmap result = base.copy(Bitmap.Config.RGB_565, true);
Bitmap blend = BitmapFactory.decodeResource(getResources(), R.drawable.blend);
IntBuffer buffBase = IntBuffer.allocate(base.getWidth() * base.getHeight());
base.copyPixelsToBuffer(buffBase);
buffBase.rewind();
IntBuffer buffBlend = IntBuffer.allocate(blend.getWidth() * blend.getHeight());
blend.copyPixelsToBuffer(buffBlend);
buffBlend.rewind();
IntBuffer buffOut = IntBuffer.allocate(base.getWidth() * base.getHeight());
buffOut.rewind();
while (buffOut.position() < buffOut.limit()) {
int filterInt = buffBlend.get();
int srcInt = buffBase.get();
int redValueFilter = Color.red(filterInt);
int greenValueFilter = Color.green(filterInt);
int blueValueFilter = Color.blue(filterInt);
int redValueSrc = Color.red(srcInt);
int greenValueSrc = Color.green(srcInt);
int blueValueSrc = Color.blue(srcInt);
int redValueFinal = multiply(redValueFilter, redValueSrc);
int greenValueFinal = multiply(greenValueFilter, greenValueSrc);
int blueValueFinal = multiply(blueValueFilter, blueValueSrc);
int pixel = Color.argb(255, redValueFinal, greenValueFinal, blueValueFinal);
buffOut.put(pixel);
}
buffOut.rewind();
result.copyPixelsFromBuffer(buffOut);
BitmapDrawable drawable = new BitmapDrawable(getResources(), result);
imageView.setImageDrawable(drawable);
}
int multiply(int in1, int in2) {
return in1 * in2 / 255;
}
After reproducing, I think your issue has to do with manipulating the images in RGB565 mode. As discussed in this post, Bitmaps apparently need to be in ARGB8888 mode to manipulate properly. I first got the expected result on a multiply blend by doing the following:
Resources res = getResources();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap base = BitmapFactory.decodeResource(res, R.drawable.base, options);
Bitmap blend = BitmapFactory.decodeResource(res, R.drawable.blend, options);
// now base and blend are in ARGB8888 mode, which is what you want
Bitmap result = base.copy(Config.ARGB_8888, true);
// Continue with IntBuffers as before...
Converting the Bitmaps to ARGB8888 mode did seem to work for me, at least with the gradient test patterns. However, it you only need to do Screen or Multiply, you might try this as well:
// Same image creation/reading as above, then:
Paint p = new Paint();
p.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
p.setShader(new BitmapShader(blend, TileMode.CLAMP, TileMode.CLAMP));
Canvas c = new Canvas();
c.setBitmap(result);
c.drawBitmap(base, 0, 0, null);
c.drawRect(0, 0, base.getWidth(), base.getHeight(), p);
With that, you aren't doing the per-pixel calculations, but you are limited to the preset PorterDuff.Modes. In my quick (and dirty) testing, this was the only way I was able to get the blending to work on non-gradient images.
Simple overlay you can do this way (for simplicity it is supposed that bmp1 is equal or bigger than bmp2):
private Bitmap bitmapOverlay(Bitmap bmp1, Bitmap bmp2)
{
Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth(), bmp1.getHeight(), bmp1.getConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bmp1, 0, 0, null);
canvas.drawBitmap(bmp2, 0, 0, null);
return bmOverlay;
}
For more complex blending algorithms, maybe you can help yourself with some available Bitmap/Canvas functions.

Categories

Resources