Find polygon in bitmap image - android

I have a Bitmap inside a View.
Here's my code for drawing it:
#Override protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFCCCCCC);
int w = mBitmap.getWidth();
int h = mBitmap.getHeight();
float[] mVerts = {
0, 0,
w * 0.8f, 0,
0, h,
w, h * 0.7f
};
canvas.drawBitmapMesh(mBitmap, 1, 1, mVerts, 0, null, 0, null);
}
and it looks like this:
Now, the question is:
If I have an image with some shape and transparent background how can I find a polygon, which is covering the area and will look like this:
And then how should I add all these points to the bitmap mesh so I can let user move them and manipulate an image like in the first example?

My rough idea is to go to every pixel and check if the color is transparent or not.
We can traverse vertically to every horizontal line. In any horizontal line, we can first find the leftmost boundary point of focus image and break the loop when we find it. Now in same horizontal line, we can find the rightmost boundary point of your focus image. You can add all these boundary pixels (x,y) coordinates into your ArrayList mVerts.
Something like this -
for(int i=0;i<bitmap.getHeigth();i++){
for(int j=0;j<bitmap.getWidth();j++){
int pixel = bitmap.getPixel(i,j);
if(pixel != Color.TRANSPARENT){
mVerts.add(i);
mVerts.add(j);
break;
}
}
for(int j=bitmap.getWidth()-1; j>=0 ;j--){
int pixel = bitmap.getPixel(i,j);
if(pixel != Color.TRANSPARENT){
mVerts.add(i);
mVerts.add(j);
break;
}
}
}
You can pass this ArrayList mVerts to your canvas.drawBitmapMesh() method to extract the focus image.
You can use following to get color of any pixel. Source for this - here
int colorCode = imageView.getDrawingCache().getPixel(x, y);

Related

Android imageView with checkeredBackground and different size images ontop

Background
I have an ImageView which is used to display previews of a file.
I would like to have the ImageView with a checkerboard background, so that when a file with transparency is rendered on top (such as PNG and SVG files) the checkerboard shows through on the transparent parts.
I have found lots of questions on StackOverflow on how to create the checkered background and this question is not entirely specific to that.
I am currently doing it in code. I create a 2 by 2 bitmap (top left/bottom right are one colour, top right, bottom left are the other colour) where the size of each box is specified. Then i create the main bitmap by drawing this small bitmap repeatedly.
int checkeredBackgroundSquareSize= 16;
private static Bitmap getCheckeredBitmap(int size) {
size = (size > 0) ? size : DEFAULT_SQUARE_SIZE;
int colorOne = ContentApplication.appCtx().getColor(R.color.checkerboard_background_color_one);
int colorTwo = ContentApplication.appCtx().getColor(R.color.checkerboard_background_color_two);
// width/height is twice the size of the individual squares
Bitmap squareBitmap = Bitmap.createBitmap(size*2, size*2, Bitmap.Config.ARGB_8888);
Paint bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bitmapPaint.setStyle(Paint.Style.FILL);
Canvas canvas = new Canvas(squareBitmap);
// draw 2 rectangles on 2 rows
// top left and bottom right are the first colour
// top right and bottom left are the second colour
// set colour for top left/bottom right squares
bitmapPaint.setColor(colorOne);
// Square 1 : top left
Rect rect = new Rect(0, 0, size, size);
canvas.drawRect(rect, bitmapPaint);
// Square 2 : bottom right
rect.offset(size, size);
canvas.drawRect(rect, bitmapPaint);
// change colour for top right/bottom left squares
bitmapPaint.setColor(colorTwo);
// Square 3 : top right
rect.offset(-size, 0);
canvas.drawRect(rect, bitmapPaint);
// Square 4: bottom left
rect.offset(size, -size);
canvas.drawRect(rect, bitmapPaint);
return squareBitmap;
}
I then create a Bitmap the size of my preview image, and use the checkered background bitmap to repeatedly draw on the canvas before the preview image is added on top.
// Create a Bitmap to render our SVG to
Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
// Create a Canvas to use for rendering
Canvas canvas = new Canvas(bitmap);
// If we don't specify a viewport box, then AndroidSVG will use the bounds of the Canvas
// as the viewport. So a scale of 1.0 corresponds to that size
canvas.scale(scaling,scaling );
// create the checkered background, indicating transparency
Bitmap square = getCheckeredBitmap(checkeredBackgroundSquareSize);
BitmapShader shader = new BitmapShader(square, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
Paint paint = new Paint();
paint.setShader(shader);
// in your draw method
canvas.drawRect(0, 0, bitmapWidth, bitmapHeight, paint);
The issue
My previews can be different sizes, for example 100x100, 6000x2000 etc As i am creating the initial bitmap on these sizes, the final image for the files all render looking like the squares on the checkered background are different sizes.
I need to have the checkerboard look exactly the same regardless of the overlaid image's size.
Is there a way to set a background image for an ImageView to be an image. I can only see how to set it to a drawable and I can not see how to define a checkboard as an xml drawable.
Mike M gave the answer that solved my issue. See Mike's comments to the first post
Create your own Drawable that renders the checkerboard
public class CheckerboardDrawable extends Drawable {
// I inadvertently ran the example image with Paint.ANTI_ALIAS_FLAG, but
// we actually don't want that 'cause we're looking for crisp, clean lines.
private final Paint paint = new Paint();
private int checkeredBackgroundSquareSize = 16;
private int colorOne = Color.LTGRAY;
private int colorTwo = Color.WHITE;
#Override
public void draw(#NonNull Canvas canvas) {
final Rect bounds = getBounds();
final int squareSize = checkeredBackgroundSquareSize;
final int columns = bounds.width() / squareSize + 1;
final int rows = bounds.height() / squareSize + 1;
canvas.translate(bounds.left, bounds.top);
for (int c = 0; c < columns; c++) {
for (int r = 0; r < rows; r++) {
paint.setColor((c + r) % 2 == 0 ? colorOne : colorTwo);
final int x = c * squareSize;
final int y = r * squareSize;
canvas.drawRect(x, y, x + squareSize, y + squareSize, paint);
}
}
canvas.translate(-bounds.left, -bounds.top);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
#Override
public void setAlpha(int alpha) {}
#Override
public void setColorFilter(#Nullable ColorFilter colorFilter) {}
}
Then add the drawable to the ImageView
final ImageView imageView = findViewById(R.id.image);
imageView.setBackground(new CheckerboardDrawable());

Speeding up bitmap drawing on android

I have been battling with trying to draw a bitmap and then highlighting a region on it with a rectangle. Originally, I was drawing a bitmap with alpha black in paint to make image darker and then on top drawing original bitmap in a region creating effect of highlight. I discovered that largest slowdown was because of alpha in Paint. So I have reworked the code and ended up with following in my draw thread:
private synchronized void drawSquare(int xStart, int yStart, int xEnd, int yEnd) {
Canvas c = holder.lockCanvas();
if(c != null) {
// Draw the background picture on top with some changed alpha channel to blend
Paint paint = new Paint();
paint.setAntiAlias(true);
if(bg != null && cWidth > 0 && cHeight > 0) {
c.clipRect(xStart, yStart, xEnd, yEnd, Region.Op.DIFFERENCE);
c.drawBitmap(bg, gTransform, blackSqr); // Draw derker background
c.clipRect(xStart, yStart, xEnd, yEnd, Region.Op.REPLACE);
c.drawBitmap(bg, gTransform, paint); ///draw original in selection
c.clipRect(0, 0, cWidth, cHeight,Region.Op.REPLACE);
}
Matrix RTcorner = new Matrix();
RTcorner.setRotate(90);
RTcorner.postTranslate(xEnd + 13, yStart - 13);
Matrix RBcorner = new Matrix();
RBcorner.setRotate(180);
RBcorner.postTranslate(xEnd + 13, yEnd + 13);
Matrix LBcorner = new Matrix();
LBcorner.setRotate(270);
LBcorner.postTranslate(xStart - 13, yEnd + 13);
// Draw the fancy bounding box
c.drawRect(xStart, yStart, xEnd, yEnd, linePaintB);
// Draw corners for the fancy box
c.drawBitmap(corner, xStart - 13, yStart - 13, new Paint());
c.drawBitmap(corner, RBcorner, new Paint());
c.drawBitmap(corner, LBcorner, new Paint());
c.drawBitmap(corner, RTcorner, new Paint());
}
holder.unlockCanvasAndPost(c);
}
So this clips out my selection area, I draw with paint that has this code to make it darker.
blackSqr.setColorFilter(new LightingColorFilter(Color.rgb(100,100,100),0));
And in the area inside the clip I draw my original bitmap. It works. But I am not happy with response time. After profiling Bitmap is what takes the longest. I have scaled the bitmap to the size of the screen already so it's drawing 300x800-ish image. The biggest resource hog seems to be the Lighting effect. Because when I turn it off I get decent response time.
So I was wondering if I have missed anything to improve how quickly bitmap is drawn, maybe caching? Or am I just stuck with this because I want darker image and actually should rethink the "highlighting/selection" altogether? Why is is so expensive to draw a bitmap with alpha colour in 2D image?
if I understand what you want, you want a rectangle (with rounded corners) to highlight a part from another image.
if it is that, then I would use an image with the square wit draw9patch and use it as a floating view over the image view
RelativeLaoyut (Image container)
+- ImageView (your actual image)
+- view (it has the square as a background, and you only have to move it to the area you want to highlight)
I'm sorry, I'm not good explaining myself.
For anyone that is interested, perhaps facing similar problem. This solution applies to my particular situation, but I have a separate background bitmap with darkened pixels manually set using:
for(int i = 0; i < cWidth; i++){
for(int j = 0; j < cHeight; j++){
int c = bg2.getPixel(i, j);
float mult = 0.15f;
int r = (int) (Color.red(c) * mult);
int g = (int) (Color.green(c) * mult);
int b = (int) (Color.blue(c) * mult);
bg2.setPixel(i, j, Color.rgb(r, g, b));
}
}
Then use the bg2 to draw main part and the original (not darkened) for the clip rectangle of the selection. There is a bit of overhead for creating and maintaining the second bitmap but the draw speed and response time is quick and smooth in comparison to bitmaps with alpha.

Water reflection effect on bitmap

I trying to achieve water reflection effect on bitmap. As I saw some apps called water reflection. I know how to do the reflection of the image but the wave on the image is what making me confused on how it is done.
see this image for example
I did many apps on bitmap manipulation but this is quite hard to achieve.
So any idea on where to start. Just an idea to start can be helpful.
For any one needed, I tried some simple tricks to get as closer as water reflection effect. It is not great but it looks fine to me.
I used two methods
Bitmap reflection method (give bitmap as a parameter)
public static Bitmap Reflection(Bitmap imageBitmap) {
int width = imageBitmap.getWidth();
int height = imageBitmap.getHeight();
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
Bitmap reflectionImage = Bitmap.createBitmap(imageBitmap, 0,
0, width, height , matrix, false);
Bitmap newbit=Bitmap.createScaledBitmap(reflectionImage, reflectionImage.getWidth()/8, reflectionImage.getHeight()/8, true);
Bitmap newbit1=Bitmap.createScaledBitmap(newbit, newbit.getWidth()*8, newbit.getHeight()*8, true);
Bitmap scalednew=Bitmap.createScaledBitmap(newbit1, width, height-(height/4), true);
Bitmap newscaledone=overlay(scalednew);
reflectionImage=newscaledone;
Bitmap reflectedBitmap = Bitmap.createBitmap(width,
(height + height), Config.ARGB_8888);
Canvas canvas = new Canvas(reflectedBitmap);
canvas.drawBitmap(imageBitmap, 0, 0, null);
Paint defaultPaint = new Paint();
canvas.drawRect(0, height, width, height, defaultPaint);
canvas.drawBitmap(reflectionImage, 0, height , null);
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
canvas.drawRect(0, height, width, reflectedBitmap.getHeight()
, paint);
return reflectedBitmap;
}
Bitmap overlay method. I am taking a wave bitmap with some opacity to overlay on the reflected image. So that it may look like water.
Bitmap wavebitmap=BitmapFactory.decodeResource(getResources(), R.drawable.waves1);
private static Bitmap overlay( Bitmap bmp2) {
Bitmap bmp1=WaterReflectionMainActivity.wavebitmap;
Bitmap bmp1new =Bitmap.createScaledBitmap(bmp1, bmp2.getWidth(), bmp2.getHeight(), true);
Bitmap bmOverlay = Bitmap.createBitmap(bmp1new.getWidth(), bmp1new.getHeight(), bmp1new.getConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bmp2, new Matrix(), null);
canvas.drawBitmap(bmp1new, new Matrix(), null);
return bmOverlay;
}
Well this is my version of water effect, I know this looks shit.
So if anyone still got some better effect please share your code .
thank you
Tutorial related to this: http://www.xaraxone.com/webxealot/workbook34/page_4.htm
Also have a read at this question: Add water effect on bitmap android.
Have a read at both of them, i hope you will get an idea from this
You may also want to look through these: 1, 2, 3
This is just an idea but basically, what you need is to apply a deformation on the bottom part of the image, meaning that for each pixel on the bottom half, you compute a position to get it's color from the top picture.
Here's a pseudo code to give you a hint :
for (int x = 0; x < width; x++) {
for (int y = 0; y < img.height; y++) {
// Compute a position on the original image
// tweak the values heres to get the effect you want
sourceX = x + (int) (cos(10000.0 / y) * 20);
sourceY = img.height - y - 1 +(int)( sin(y* 0.5) * 20);
// Maybe check the range of sourceX and source Y
int color = img.getColor(sourceX, sourceY)
outptut.setColor(x, y + img.height, color);
}
}
you can achieve this by masking may this code will help you
http://www.seeques.com/22527681/how-can-do-this-effect-in-android-may-be-android-bitmap-masking-effect.html
EDIT
also see this for reference
http://code.google.com/p/android-ripple-demo/source/browse/#svn%2Ftrunk%2Fsrc%2Fcom%2Fkesalin%2FRippleDemo
https://github.com/esteewhy/whater
http://code.google.com/p/waterrippleeffect/source/browse/trunk/src/com/example/android/watereffect/WaterEffectView.java?r=3
android noise effect on bitmap

Android Bitmap Transformation Math

I need to combine 2 images into one. Basically all i need to do is to overlay one of them on top of the other in the center of the image. This needs to work on all major android devices.
I have tried a number of things, but here is my code snippet as of right now (and yes I know it's messed up, we need to figure out delx and dely):
/* Rotate our original photo */
// final float scale = getResources().getDisplayMetrics().density;
canvas.drawBitmap(bmp, 0f, 0f, null);
final float overlay_scale_factor = .5f;
final int overlaywidth = (int)(overlay.getWidth() * overlay_scale_factor);
final int overlayheight = (int)(overlay.getHeight() * overlay_scale_factor);
final int delx = overlaywidth;
final int dely = overlayheight;
Matrix mat = new Matrix();
mat.postRotate(270);
mat.postScale(overlay_scale_factor, overlay_scale_factor);
//mat.postTranslate(-delx, -dely);
canvas.drawBitmap(overlay, mat, null);
/* Bottom image 'composite' is now a composite of the two. */
Any help is appreciated. I know this is just math, but I'm not good at this kind of stuff.
The first image, 'bmp' is 100% the size of the canvas.
The second image, 'overlay' is the overlay that needs to be centered after it's rotated 270 degrees.
Totally untested, but I'd expect something like this to work:
// Set the origin (0,0) in the middle of the view
canvas.translate(width/2, height/2);
// Draw the first bitmap so it is centered at (0,0)
canvas.drawBitmap(bmp, -bmp.getWidth()/2, -bmp.getHeight()/2, null);
// Rotate & scale
canvas.save();
canvas.rotate(270);
canvas.scale(.5f);
// Draw the overlay
canvas.drawBitmap(overlay, -overlay.getWidth()/2, -overlay.getHeight()/2, null);
canvas.restore();

Compass with bitmap is going everywhere in the screen

i want my compass to spin like this
but my result is that:
the compass is going everywhere in my screen...
where is my problem please?this is my compass.java code:
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GRAY);
int w = canvas.getWidth();
int h = canvas.getHeight();
int cw = w / 2;
int ch = h / 2;
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.compass);
canvas.translate(cw, ch);
if (mValues != null) {
canvas.rotate(-mValues[0]);
}
int cx = (mWidth - myBitmap.getWidth()) / 2;
int cy = (mHeight - myBitmap.getHeight()) / 2;
canvas.drawBitmap(myBitmap, cx, cy, null);
}
p.s.: i m sorry for the bad pictures but i really dont know how to explain my problem in english!Thanks
Since you have already translated to the center of the canvas, you may only need to offset the compass with its half width/height to center it. Try:
int cx = -myBitmap.getWidth() / 2;
int cy = -myBitmap.getHeight() / 2;
canvas.drawBitmap(myBitmap, cx, cy, null);
Also to get a good hang of transformations (translate, rotate), read The OpenGL Red book chapter 3, specifically the part Thinking about Transformations. While this is about OpenGL, you can use the knowledge for non-OpenGL transforms too.
EDIT:
Think in turtle logic. Your first translation takes your pencil to the center of your canvas. The rotation rotates your pencil. So now you could draw the compass exactly where the pencil is (no offsets), except that drawing the compass image is done starting from its top-left corner instead of its center. Therefore you need a last translation of (-compassWidth/2, -compassHeight/2). Note that this translation already occurs on the rotated x & y axes. Also note that you may pass 0/0 for cx/cy in drawBitmap if you manually apply that translation to the canvas itself.
So the full sequence is: translate to canvas center, rotate, translate negated to image center.
Don't decode the Bitmap in onDraw - do it when the view is created and reuse the Bitmap.
Make a Matrix and matrix.postRotate(mValues[0], half_width_of_bitmap, half_height_of_bitmap); and matrix.postTranslate(cw, ch);
Draw the bitmap with canvas.drawBitmap(bitmap, matrix, null);

Categories

Resources