I am developing an Instagram like application for learning image processing and android. But I am stuck, I have a problem implementing Grayscale Filter in my application. I am trying a simple approach for now to convert individual pixels in a Bitmap to Grayscale.
Here's the whole class I am writing to apply various filters to an Image:
package com.dosa2.photoeditor.ImageEffects;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
public class ImageEffects {
Bitmap bitmap;
int width, height;
public ImageEffects(Bitmap bitmap) {
this.bitmap = bitmap;
width = bitmap.getWidth();
height = bitmap.getHeight();
}
public Bitmap toGrayscale() {
Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(resultBitmap);
Paint p = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
p.setColorFilter(f);
c.drawBitmap(bitmap, 0, 0, p);
return resultBitmap;
}
public Bitmap toGrayscale2() {
Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
for(int i=0;i<height;i++) {
for (int j=0;i<width;j++) {
int c = bitmap.getPixel(i,j);
resultBitmap.setPixel(i, j, (Color.red(c)+Color.blue(c)+Color.green(c)/3));
}
}
return resultBitmap;
}
}
I have tried 2 methods to convert the Bitmap into Grayscale. The former seems to be working(but I am not able to understand it) and the latter is not.
Can anyone help me out? And do mention if there's an easier way to manipulate Images in Android.
The error (or at least one of them...) is in one of your for loops:
for (int j=0;i<width;j++)
should be
for (int j=0;j<width;j++)
to prevent an indefinite loop.
Your method "toGrayscale" is using the ColorMatrix class, which I think internally uses the RenderScript API to do the rendering (or at least GPU shaders). RenderScript is the Android's computing API (which is similar to OpenCL, in fact, is a layer that works on OpenCL), so you are not using only the CPU to do the color filtering, you are using even the GPU or other DSPs your device may have. The second method "toGrayscale2" is slower because you are using only the CPU to convert your Bitmap to grayscale (pixel by pixel) and you shouldn't use it. Check this presentation (it's very intersting) in order to understand a bit more how your first method works, the link points to the page 12 which is about color filtering, but you should view it entirely in order to understand better.
Related
What is the correct way to rotate bitmap every frame in main game loop?
What I have tried:
I created rotated bitmap every frame:
Bitmap image, tmp;
Matrix m;
...
public void mainGameLoop(Canvas c){
m.reset();
m.postRotate(angle);
tmp = Bitmap.createBitmap(image, 0, 0, width, height, m, true);
c.drawBitmap(tmp, 50, 50, null);
}
However, because Bitmap.createBitmap creates bitmap every frame Garbage Collector works so madly, it causes low FPS.
I have tried saving bitmap in array at all angles, and just taking bitmap which I want to. However, this needs a lot of RAM and app just couldn't launch at high screen resolutions.
If you have any ideas, please comment below.
Some think like this:
Bitmap image;
Matrix m;
.
.
.
public YourClass(){
m = new Matrix();
image = Bitmap.createBitmap(image, 0, 0, width, height,);
}
.
.
.
public void mainGameLoop(Canvas c){
m.setRotate(angle, imageCenterX, imageCenterY);
yourCanvas.drawBitmap(image, m, null);
}
I've been trying this since morning, yet I can't get it to work.
What I'm trying to do is create a somewhat like long shadow for the TextView, which is similar to the following:
http://www.iceflowstudios.com/v3/wp-content/uploads/2013/07/long_shadow_banner.jpg
http://web3canvas.com/wp-content/uploads/2013/07/lsd-ps-action-720x400.png
My solution so far was to create a lot of TextViews and cascade them under each other, but there are a lot of performance issues if I go with the current way.
Another solution is the usage of a custom font that has that similar allure, yet I cannot find any that matches the font I am currently using.
So I was wondering, is it possible to use: (I have to mention, the textviews are created dynamically)
TV.setShadowLayer(1f, 5f, 5f, Color.GREY);
To create several of them in a line (as a cascading layer), making the shadow seem smooth? Or do you guys suggest any other solutions?
Thanks in advance.
Try to play with raster images:
Detect bounds of text using Paint.getTextBounds() method
Create transparent Bitmap with such metrics (W + H) x H (you may use Bitmap.Config.ALPHA_8 to optimize memory usage)
Draw text on this Bitmap at 0x0 position
Copy first row of Bitmap into new one with original width, but with height of 1px
Iterate over the Y-axis of Bitmap (from top to bottom) and draw single-line Bitmap with the corresponding offset by X-axis (you will overdraw some transparent pixels)
Now you have the top-part of your shadow
Draw the bottom part using same technique, but choosing last row of this Bitmap
This algorithm may be optimized if you detect, that all pixels in last row have the same color (full shadow).
UPDATE 1
I achieved such result using this quick solution:
MainActivity.java
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle state) {
super.onCreate(state);
LongShadowTextView longShadow = new LongShadowTextView(this);
longShadow.setText("Hello World");
setContentView(longShadow);
}
}
LongShadowTextView.java
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.View;
public class LongShadowTextView extends View {
private Bitmap mBitmap;
private String mText;
public LongShadowTextView(Context context) {
super(context);
}
public void setText(String text) {
Paint paint = new Paint();
// TODO provide setters for these values
paint.setColor(Color.BLACK);
paint.setTextSize(142);
Rect rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);
Bitmap bitmap = Bitmap.createBitmap(rect.width() + rect.height(), rect.height(), Bitmap.Config.ALPHA_8);
Canvas canvas = new Canvas(bitmap);
canvas.drawText(text, 0, rect.height(), paint);
Rect src = new Rect();
RectF dst = new RectF();
int w = bitmap.getWidth();
int h = bitmap.getHeight();
src.left = 0;
src.right = w;
for (int i = 0; i < h; ++i) {
src.top = i;
src.bottom = i + 1;
dst.left = 1;
dst.top = i + 1;
dst.right = 1 + w;
dst.bottom = i + 2;
canvas.drawBitmap(bitmap, src, dst, null);
}
mText = text;
mBitmap = bitmap;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, null);
}
}
UPDATE 2
Here is final result which I achieved. Clone this demo from github.
I'm afraid your suggested approach of using setShadowLayer() wont work as this approach effectively draws a second TextPaint with blurring.
Superimposing several TextPaints on top of each other will essentially mean you need to offset it by 1px for each step, which is very graphically intensive and will have a very poor performance.
This is an excellent question and a real challenge!
The only solution that comes to mind is to handle each glyph independently, inspecting all path elements and extending a shadow between the furthest bottom-left and top-right point. This seems very complicated, and I don't know if there's any mechanics in the SDK that facilitates an approach like that.
Suggested reading:
This question tackles obtaining glyph paths from TTFs.
This answer illustrates how you can leverage using paths, although it concerns a JavaScript approach.
Small comment if someone would try to run setText() method. it is not working now.
You should call invalidate(); in setText(); method
public void setText(String value) {
boolean changed =
mText == null && value != null || mText != null && !mText.equals(value);
mText = value;
if (changed) {
refresh();
}
invalidate();
}
I'm developing an Android application and I would like to blend Bitmap with a filter like "Multiply" or "Difference" from Photoshop. Bitmap1 is translating horizontally and Bitmap2 vertically.
I had few ideas :
1) Create a method which do some calculation to find the intersection part of the two Bitmap (Requires to cross 2 matrix every frame, very heavy, seems impossible ?). Then crop the intersection on the 2 Bitmap, blend them, and finally draw it. Another idea would be to do a ANDing between the two Bitmap, maybe faster than the other method, does thi function already exist ?
2) OpenGL ES ? But I really don't understand how to use it.
3) But recently I have found this sample code which blend two Bitmap on a canvas and use a paint. The result is the one I want BUT there is still no animation. So I've create a new Class (extends View) in addition to this activity, with the method onDraw(Canvas canvas).
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_compo);
Bitmap bm1 = BitmapFactory.decodeResource(getResources(), R.drawable.bm1);
Bitmap bm2 = BitmapFactory.decodeResource(getResources(), R.drawable.bm2);
blend(bm1,bm2);
}
private void blend(Bitmap bm1, Bitmap bm2){
DisplayMetrics metrics = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(metrics);
int h = metrics.heightPixels;
int w = metrics.widthPixels;
/// BLEND MODE ///
// Create result image
Bitmap.Config conf = Bitmap.Config.ARGB_8888; // see other conf types
Bitmap result = Bitmap.createBitmap(w, h, conf);
Canvas canvas = new Canvas();
canvas.setBitmap(result);
// Get proper display reference
BitmapDrawable drawable = new BitmapDrawable(getResources(), result);
ImageView imageView = (ImageView)findViewById(R.id.photo1);
imageView.setImageDrawable(drawable);
// Draw base
canvas.drawBitmap(bm2, 0, 0, null);
// Draw overlay
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
paint.setShader(new BitmapShader(bm1, TileMode.CLAMP, TileMode.CLAMP));
canvas.drawRect(700, 700, bm2.getWidth(), bm2.getHeight(), paint);
}
Here is the result with changing the value in 'canvas.drawRect()' :
Before translation
After translation
Do you have other solutions ? Or suggestions which would help to realize my filter on animate views ? (The perfect solutions would be to add something like android:blendMode="Multiply" in the XML, but it doesn't seem as easy)
Thank you in advance. J'.
I have created a small applicaiton (a game), that reuses the same set of images several times. SO i thought i should create a class that is responsible for loading all of the different images once, from which i can then access them statically from other classes. However, i believe this may have caused an issue with the Garbage Collector, which causes my App to lag whenever the GC is run. Here is What the ImgLoader class looks like:
public class ImgLoader extends View {
public static Bitmap tree1;
public ImgLoader(Context context) {
super(context);
loadImgs();
}
public void loadImgs() {
System.gc(); // Manually Call GC
// TREES
tree1 = BitmapFactory.decodeResource(getResources(), R.drawable.tree);
tree1 = getResizedBitmap(tree1, MainActivity.height / 2,
MainActivity.width / 10);
}
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// "RECREATE" THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height,
matrix, false);
return resizedBitmap;
}
public static Bitmap RotateBitmap(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(),
source.getHeight(), matrix, true);
}
}
Note, i only use one Bitmap for the question. Essentially, when i want to use an image in my application, i say:
object.image = ImgLoader.tree1;
What is an alternative to doing this, that is not so memory intensive?
Thank you!
This seems like very bad practice to me, especially because you are loading multiple Bitmaps and keep static references of them.
Since even small Bitmaps consume considerably large amounts of memory, you will run into OutOfMemoryErrors very soon.
A Bitmap 512x512 already consumes 1 Megabyte of RAM (in 32 Bit) color. You can load your Bitmaps in RGB_565 (16 Bit) to reduce memory consuption.
Why not just load the Bitmap from the Resources when you need it?
try getting them in InputStream format ... they will be in byte .. but personnaly,( and its really personnally ) i prefer to load every image when i need it instead of leaving them in a memory .. for future use , the number of images can increase , plus , as u mentionned and i noticed in your code , the images are store locally ( not doenloaded ) so it shldnt b a big problem to reload them .. but again its my own opinion
Don't think it has anything to do with the gc; you are loading images when you manually call the gc.
I believe it's the loading of images that is causing your app to lag. You could load the bitmap asynchronously.
Can anyone suggest a way to create a small solid colored bitmap image from a hex value?
Alternatively, you can use Bitmap.eraseColor() to set a solid color for your bitmap.
Example:
import android.graphics.Bitmap;
...
Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
image.eraseColor(android.graphics.Color.GREEN);
I think I may have the answer. Technically I believe it is much easier on Android than on a "pc". The last time I searched to create a bitmap (.bmp), I only found some Android functions and the BitmapFactory for non-android, which didn't work for me.
Please look at this site: http://developer.android.com/reference/android/graphics/Bitmap.html
This point could fit for you:
static Bitmap createBitmap(int[] colors, int offset, int stride, int width, int height, Bitmap.Config config)
Returns a immutable bitmap
with the specified width and height, with each pixel value set to the
corresponding value in the colors array.
Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas=new Canvas (image);
int HEX=0xFF888888;
canvas.drawColor (HEX);
Use the createBitmap().
Here is a link that will show you how: http://developer.android.com/reference/android/graphics/Bitmap.html