Android Issue with 'createBitmap' and Sub-pixel Data - android

I'm currently developing a small Android application to familiarize myself with the API. However, I have come across a problem regarding sub-pixel data while using createBitmap. I currently have these blocks of code. (NOTE: Image being read in is a 128x128 RGB565 jpeg):
public class MainMenu extends View {
private int vWidth; // Width of our view
private int vHeight; // Height of our view
private Bitmap imgButtons;
private Rect rect;
private MenuItem testButton;
private MenuItem testButton2;
public MainMenu(Context context) {
super(context);
// Get our view dimensions
vWidth = getWidth();
vHeight = getHeight();
// Load our menu buttons' image
BitmapFactory.Options imgOptions = new BitmapFactory.Options();
imgButtons = BitmapFactory.decodeResource(getResources(), R.drawable.mainmenubuttons, imgOptions);
rect = new Rect(100, 400, 228, 528);
// Create our menu buttons and load their specific images
testButton = new MenuItem(100, 50);
testButton2 = new MenuItem(100, 200);
testButton.LoadImage(imgButtons, 128, 64, 0, 0);
testButton2.LoadImage(imgButtons, 128, 64, 0, 64);
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawBitmap(imgButtons, null, rect, null);
testButton.Draw(canvas);
testButton2.Draw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() != MotionEvent.ACTION_DOWN)
super.onTouchEvent(event);
return true;
}
}
class MenuItem {
private int xPos; // Center x-coordinate
private int yPos; // Center y-coordinate
private int width; // Button width
private int height; // Button height
private Rect rect;
private Bitmap buttonImage;
public MenuItem(int withXPos, int withYPos) {
xPos = withXPos;
yPos = withYPos;
}
public void LoadImage(Bitmap image, int imageWidth, int imageHeight, int xOffset, int yOffset) {
width = imageWidth;
height = imageHeight;
buttonImage = Bitmap.createBitmap(image, xOffset, yOffset, width, height);
rect = new Rect(xPos - (width >> 1), yPos - (height >> 1),
xPos + (width >> 1), yPos + (height >> 1));
}
public void Draw(Canvas canvas) {
canvas.drawBitmap(buttonImage, null, rect, null);
}
}
Below is an image displaying the issue:
I lied. I'm not allowed to post images yet, so here is a link to the image:
createBitmap issues
So, why is that I can display the original image correctly, but cannot correctly display images created from its sub-pixel data? I have run the application using versions 2.1 through 3.0, and 3.0 has no issues displaying the image correctly. Is it just an issue with devices before version 3.0, or am I missing something? I found an article on stacked describing issues between solid images used(RBG565), and images that contain alpha(ARGB8888). Where, my method wouldn't work if the image was of type ARGB8888, and if so I needed to physically handle the pixel data and copy the sub-pixel data myself and create my own pixel buffer for storing that data. I tried this on a whim and it lead me down to the same issue. I just figured why not try it? I have also tried other variations of Bitmap.createBitmap to no avail. At this point I'm a little stumped and figured I'd ask the community.

Related

Load a Bitmap with Fresco into DraweeView

Is there any way to load a Bitmap with Fresco into a DraweeView?
Somewhat like you can do with Glide:
Glide.with(context)
.load(bitmap)
.into(imageView);
Fresco can do these operations for you. From the comments, I see that you want to handle the Bitmap in an efficient way, so you can let Fresco do that.
As to the cropping that you are doing, there are multiple ways to do that with Fresco:
Use a SCALE_TYPE if you don't need the bitmap: Fresco has more than ImageView, including focus cropping to a given point within the image and you can also implement your own scaling, see https://frescolib.org/docs/scaletypes.html
If you need the cropped bitmap, you can implement your cropping logic via a Postprocessor (and also cache the bitmap if needed), see https://frescolib.org/docs/modifying-image.html#advanced-changing-the-bitmaps-size
Scale type example:
#Override
public void getTransformImpl(
Matrix outTransform,
Rect parentRect,
int childWidth,
int childHeight,
float focusX,
float focusY,
float scaleX,
float scaleY) {
// Your computations, such as for example a fit bottom implementation:
float scale = Math.min(scaleX, scaleY);
float dx = parentRect.left;
float dy = parentRect.top + (parentRect.height() - childHeight * scale);
outTransform.setScale(scale, scale);
outTransform.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
}
Postprocessor example that can do scaling and blurring from https://github.com/facebook/fresco/blob/master/samples/showcase/src/main/java/com/facebook/fresco/samples/showcase/postprocessor/ScalingBlurPostprocessor.java:
public class ScalingBlurPostprocessor extends BasePostprocessor {
private final Paint mPaint = new Paint();
private final int mIterations;
private final int mBlurRadius;
/**
* A scale ration of 4 means that we reduce the total number of pixels to process by factor 16.
*/
private final int mScaleRatio;
public ScalingBlurPostprocessor(int iterations, int blurRadius, int scaleRatio) {
Preconditions.checkArgument(scaleRatio > 0);
mIterations = iterations;
mBlurRadius = blurRadius;
mScaleRatio = scaleRatio;
}
#Override
public CloseableReference<Bitmap> process(
Bitmap sourceBitmap, PlatformBitmapFactory bitmapFactory) {
final CloseableReference<Bitmap> bitmapRef =
bitmapFactory.createBitmap(
sourceBitmap.getWidth() / mScaleRatio, sourceBitmap.getHeight() / mScaleRatio);
try {
final Bitmap destBitmap = bitmapRef.get();
final Canvas canvas = new Canvas(destBitmap);
canvas.drawBitmap(
sourceBitmap,
null,
new Rect(0, 0, destBitmap.getWidth(), destBitmap.getHeight()),
mPaint);
NativeBlurFilter.iterativeBoxBlur(
destBitmap, mIterations, Math.max(1, mBlurRadius / mScaleRatio));
return CloseableReference.cloneOrNull(bitmapRef);
} finally {
CloseableReference.closeSafely(bitmapRef);
}
}
}

Android 6.0 incorrectly handles drawCircle method

In my app I need to draw circles using bitmap and the drawCircle() method.
Everything was working fine and exactly as it should up until Android 6.0.
It still draws circles on all the previous versions, but draws rectangles when I use the app on 6.0. But if I change it to be filled, it draws a circle both in api 22 and api 23.
Anyone has the same problem or any idea why this happens?
Here is the source code and a screenshot (app running on API 23 on the left, and API 22 on the right). same app on different api's
public final class Circle1View extends View {
private float xCenter, yCenter;
private Bitmap grid = null;
public Circle1View (Context context) {
super(context);
init();
}
private void init() {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
#Override
protected void onDraw(Canvas canvas) {
int w = getWidth();
int h = getHeight();
xCenter = w / 2;
yCenter = h / 2;
drawBitmaps(w, h);
canvas.translate(xCenter, yCenter);
canvas.scale(xCenter, yCenter);
canvas.drawBitmap(grid, null, new RectF(-1, -1, 1, 1), null);
}
private void drawBitmaps(int w, int h) {
grid = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas();
canvas.translate(xCenter, yCenter);
canvas.scale(xCenter, yCenter);
Paint gridPaint = new Paint();
gridPaint.setStrokeWidth(0.01f);
// Works with FILL
// gridPaint.setStyle(Paint.Style.FILL);
gridPaint.setStyle(Paint.Style.STROKE);
canvas.setBitmap(grid);
canvas.drawCircle(0, 0, 0.5f, gridPaint);
}
}
I think it has something to do with the scaling and translation you do. Imagine the circle that is drawn is so small, it only takes 4 pixels. When enlarging this back to the full size, you are left with 4 straight lines between these pixels.
When I change the stroke width to 0.04f, the issue is gone. I would suggest you simplify your code by drawing on the supplied Canvas directly:
#Override
protected void onDraw(Canvas canvas) {
int w = getWidth();
int h = getHeight();
xCenter = w / 2;
yCenter = h / 2;
Paint gridPaint = new Paint();
gridPaint.setStrokeWidth(1f);
gridPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(xCenter, yCenter, w/4, gridPaint);
}
As for your question about the difference between API levels: Marshmallow introduced changes for drawBitmap(). You can have a look at the respective source code for Lollipop and Marshmallow.

android placing bitmap in middle

I am designing a drawing app where user can import image from gallery and then scale up or down to fit the screen width or height. so that user can draw onto the imported picture, using the below code.
I am extending View, named DrawView. The DrawView is same as screenwidth, but its height is less than screenheight because there are some buttons above it, placing the DrawView to the bottom of the Screen under the functioning buttons, and so I declared DrawViewHeight.
See below for examples for dimension and results of variables.
Question:
The bitmap can be properly loaded and scaled to fit to the DrawView.
However, it is located at the top of the DrawView. I would like to place it in the middle of the screen, so i added the following code but still FAILS.
bitmapCanvas.drawBitmap(bitmap, x_adjustment, y_adjustment, null);
How could it be further modified such that the imported image (decoded and copied as bitmap while importing) is placed center of DrawView, with blank space (eg. white) above and below and left and right of the loaded scaled bitmap image?
Note: Those surrounding space around the image are not to be drawn onto by user.
Codes:
Declarations:
private Bitmap bitmap; // drawing area for display or saving
private Canvas bitmapCanvas; // used to draw on bitmap
OnSizeChanged:
public void onSizeChanged(int w, int h, int oldW, int oldH)
{
super.onSizeChanged(w, h, oldW, oldH);
DrawViewWidth = w;
DrawViewHeight = h;
bitmap = Bitmap.createBitmap(getWidth(), DrawViewHeight, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
bitmap.eraseColor(Color.WHITE); // erase the BitMap with white
}
OnDraw:
#Override
protected void onDraw(Canvas canvas) // called each time this View is drawn
{
canvas.drawBitmap(bitmap, 0, 0, paintScreen);
}
Load Pictures:
public void load_pic(final String picturePath)
{
// get screen dimension first
WindowManager wm = (WindowManager) context_new.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
final int screenWidth = display.getWidth();
final int screenHeight = display.getHeight();
//get importing bitmap dimension
Options op = new Options();
op.inJustDecodeBounds = true;
Bitmap pic_to_be_imported = BitmapFactory.decodeFile(picturePath, op);
final int x_pic = op.outWidth;
final int y_pic = op.outHeight;
// scaling
final int IMAGE_MAX_SIZE= (int) Math.max(DrawViewWidth, DrawViewHeight);
int scale = 1;
if (op.outHeight > IMAGE_MAX_SIZE || op.outWidth > IMAGE_MAX_SIZE) {
scale = (int)Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(op.outHeight, op.outWidth)) / Math.log(0.5))); }
final BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
// Start loading image to the DrawView
if ((x_pic > DrawViewWidth) || (y_pic > DrawViewHeight))
{
AlertDialog.Builder onBackBuilder = new AlertDialog.Builder(context_new);
onBackBuilder.setPositiveButton(R.string.buttontext_create_load_pic_stretch, new DialogInterface.OnClickListener()
{
//skipped
}
onBackBuilder.setNegativeButton(R.string.buttontext_create_load_pic_keep_scale, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
float xratio = (float) x_pic / (float) screenWidth;
float yratio = (float) y_pic / (float) DrawViewHeight;
int adjusted_x = 0;
int adjusted_y = 0;
if (xratio >= yratio) {adjusted_x = screenWidth; adjusted_y = (int) (y_pic / xratio);}
if (xratio < yratio) {adjusted_y = DrawViewHeight; adjusted_x = (int) (x_pic / yratio);}
bitmap = (BitmapFactory.decodeFile(picturePath, o2));
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
bitmap = Bitmap.createScaledBitmap(bitmap, adjusted_x, adjusted_y, true);
int x_adjustment = (screenWidth - adjusted_x) /2;
int y_adjustment = (DrawViewHeight -adjusted_y) /2;
bitmapCanvas.drawBitmap(bitmap, x_adjustment, y_adjustment, null);
// How to modify to put to center of DrawView????
invalidate();
}
});
AlertDialog alert = onBackBuilder.create();
alert.show();
}
Examples of dimension and results of variables:
Screen : 480W * 800H
DrawView : 480W * 590H
Original image : 3264W * 2448H
Scaled image : adjusted_x=480 (meet screenwidth), adjusted_y=360
x_adjustment : 0
y-adjustment : 115
Layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.pearmak.drawing.DrawView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center_vertical|center_horizontal|center"
android:background="#drawable/drawview_border" />
</RelativeLayout>
// You can try this :
int width = containerBitmap.getWidth();
int height = containerBitmap.getHeight();
float centerX = (width - centeredBitmap.getWidth()) * 0.5f;
float centerY = (height- centeredBitmap.getHeight()) * 0.5f;
mCanvas.drawBitmap(centeredBitmap, centerX, centerY, paint);
You can use it to draw a bitmap at the center of another bitmap.
In your onDraw() you specify (0,0) as the bitmap (x,y) which will draw it at the top left corner of your DrawView so your x_adjustment, y_adjustment effect is lost once onDraw is called.
You should do your bitmap location adjustment code inside onDraw not inside onClick
#Override
protected void onDraw(Canvas canvas) // called each time this View is drawn
{
canvas.drawBitmap(bitmap, x_adjustment, y_adjustment, null);
}
The LayoutParameters of your imageview or the imageview's parent view need to specify centering within the screen
also, post your XML layout if there is one, as well as where you make the bitmap part of an imageview in your code
As iTech has noted, to get your Bitmap drawn in the center of the DrawView, you need to draw it in the correct location inside your onDraw method.
Create two additional fields:
int x_adjust, y_adjust;
In your onClick (when loading the bitmap), calculate the correct center, and set x_adjust and y_adjust to the relevant values. Then make sure you use these values in the onDraw method (rather than 0,0).
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawBitmap(bitmap, x_adjust, y_adjust, null);
}
Note: Make sure to recalculate x_adjust and y_adjust in the onSizeChanged method also. Otherwise the bitmap will no longer be centered after you rotate the device.

Scale & rotate Bitmap using Matrix in Android

I'm trying to scale and rotate in single operation before creting the final bitmap but the preRotate, postConcat doesn't seem to work.
Bitmap bmp = ... original image ...
Matrix m = new Matrix()
m.setScale(x, y);
m.preRotate(degrees, (float) width / 2, (float) height / 2);
Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
It only applies the scale and not rotation.
The answer was given, but to make things more clear to anyone reading this:
1) if you wish to perform ONE transformation in your bitmap, you CAN use SET (setRotate, setScale etc).
But note that any call to a "set" method OVERRIDES other transformations. It's like a new matrix. That's why OP's rotation was not working. These calls are not performed line by line. It's like they are scheduled to be done at runtime by the GPU when the new bitmap is being drawn. It's like when resolving your matrix, GPU rotated it, but then, created a scaled new one, ignoring previous matrix.
2) if you wish to perform more then one transformation, then you MUST use "pre" or "post" methods.
And what is the difference between a postRotate and a preRotate, for example? Well, this matrix math stuff is not my strength, but what I know is that the graphic cards make these transformations using matrix multiplication. It seems to be way more efficient. And as far as I remember from school, when multiplicating matrices the order IS important. A X B != B X A. So, scale a matrix and then rotate it is different from rotate and then scale it.
BUUUUT, as far as the final result in the screen is the same, we high level programmers usually do not need to know these differences. The GPU does.
Well, in that rare cases when you are performing really complicated matrix operations, and results are not what you expected or the performance is terrible and you need to deeply understand these methods to fix your code, well, then android documentation can not be of much help anyway. Instead, a good Linear Algebra book would be your best friend. ;)
This is the code
public class Bitmaptest extends Activity {
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
LinearLayout linLayout = new LinearLayout(this);
// load the origial BitMap (500 x 500 px)
Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(),
R.drawable.android);
int width = bitmapOrg.getWidth();
int height = bitmapOrg.getHeight();
int newWidth = 200;
int newHeight = 200;
// calculate the scale - in this case = 0.4f
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// createa matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// rotate the Bitmap
matrix.postRotate(45);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0,
newWidth, newHeight, matrix, true);
// make a Drawable from Bitmap to allow to set the BitMap
// to the ImageView, ImageButton or what ever
BitmapDrawable bmd = new BitmapDrawable(resizedBitmap);
ImageView imageView = new ImageView(this);
// set the Drawable on the ImageView
imageView.setImageDrawable(bmd);
// center the Image
imageView.setScaleType(ScaleType.CENTER);
// add ImageView to the Layout
linLayout.addView(imageView,
new LinearLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT
)
);
// set LinearLayout as ContentView
setContentView(linLayout);
}
}
If you face the issue of OutOfMemory with above answers, than use below:
Bitmap MyFinalBitmap = Bitmap.createBitmap(CurrentBitmap, 0, 0,CurrentBitmap.getWidth()/2, CurrentBitmap.getHeight()/2,matrix, true);
Canvas holds a matrix stack und you can use it with the methods:
Canvas.save()
Doc:
/**
* Saves the current matrix and clip onto a private stack.
*
* Subsequent calls to translate,scale,rotate,skew,concat or clipRect,
* clipPath will all operate as usual, but when the balancing call to
* restore() is made, those calls will be forgotten, and the settings that
* existed before the save() will be reinstated.
*
* #return The value to pass to restoreToCount() to balance this save()
*/
Canvas.restore()
Doc:
/**
* This call balances a previous call to save(), and is used to remove all
* modifications to the matrix/clip state since the last save call. It is
* an error to call restore() more times than save() was called.
*/
example:
A custom View(Android) which looks like a rotary knob(e.g. potentiometer)
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewX = getWidth(); //views width
viewY = getHeight(); //views height
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); //a must call for every custom view
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
double tempAngel = 3.6 * barValue;
int deltaX = bitmap.getWidth() / 2;
int deltaY = bitmap.getHeight() / 2;
...
canvas.save();
canvas.translate(viewX / 2, viewY / 2); //translate drawing point to center
canvas.rotate((float) tempAngel); //rotate matrix
canvas.save(); //save matrix. your drawing point is still at (viewX / 2, viewY / 2)
canvas.translate(deltaX * -1, deltaY * -1); //translate drawing point a bit up and left to draw the bitmap in the middle
canvas.drawBitmap(bitmap, 0,0, bitmapPaint); // draw bitmap to the tranlated point at 0,0
canvas.restore(); //must calls...
canvas.restore();
}
All of the previous answer assume that this change to the bitmap is being made in a view. However in my case I was making the change to be saved out. Figured I would answer it for those in a similar boat.
There are two ways to do translation. Below dx is the translation in the X axis, and dy is the translation in the Y axis. The other variables should be self explanatory.
1 - Translation within the image (without rotation)
val newBitmap = Bitmap.createBitmap(originalBitmap, dx, dy, newWidth, newHeight, matrix, false)
2 - Complex matrix
matrix.postTranslate(dx, dy)
val newBitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(newBitmap)
canvas.drawBitmap(originalBitmap, matrix, null)
Matrix rotateMatrix = new Matrix();
rotateMatrix.postRotate(rotation);
rotatedBitmap = Bitmap.createBitmap(loadedImage, 0, 0,loadedImage.getWidth(), loadedImage.getHeight(),rotateMatrix, false);
Refer to the following code, seems to work. In your code you are defining Matrix as m but referring to it as matrix
public class FourthActivity extends Activity {
private static final int WIDTH = 50;
private static final int HEIGHT = 50;
private static final int STRIDE = 64;
private static int[] createColors() {
int[] colors = new int[STRIDE * HEIGHT];
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
int r = x * 255 / (WIDTH - 1);
int g = y * 255 / (HEIGHT - 1);
int b = 255 - Math.min(r, g);
int a = Math.max(r, g);
colors[y * STRIDE + x] = (a << 24) | (r << 16) | (g << 8) | b;
}
}
return colors;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
final ImageView view1 = (ImageView) findViewById(R.id.imageView1);
int[] colors = createColors();
final Bitmap bmp1 = Bitmap.createBitmap(colors, 0, STRIDE, WIDTH, HEIGHT,
Bitmap.Config.ARGB_8888);
view1.setImageBitmap(bmp1);
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
Matrix matrix = new Matrix();
matrix.setScale(2, 2);
matrix.preRotate(45, (float) WIDTH / 2, (float) HEIGHT / 2);
Bitmap bmp2 = Bitmap.createBitmap(bmp1, 0, 0,
bmp1.getWidth(), bmp1.getHeight(), matrix, true);
ImageView view2 = (ImageView) findViewById(R.id.imageView2);
view2.setImageBitmap(bmp2);
}
});
}
}
Use matrix to scale area of original bitmap to 50% and compress bitmap until it's size < 200k
Compress bitmap to a specific byte size in Android

Android: Scrollable (bitmap) screen

I am currently implementing a view in Android that involves using a larger than the screen size bitmap as a background and then having drawables drawn ontop of this. This is so as to simulate a "map" that can be scrolled horizontally aswell as vertically.
Which is done by using a canvas and then drawing to this the full "map" bitmap, then putting the other images on top as an overlay and then drawing only the viewable bit of this to screen.
Overriding the touch events to redraw the screen on a scroll/fling.
I'm sure this probably has a huge ammount of overhead (by creating a canvas of the full image whilst using(drawing) only a fifth of it) and could be done in a different way as to the explained, but I was just wondering what people would do in this situation, and perhaps examples?
If you need more info just let me know,
Thanks,
Simon
I cooked up an example of how to do this using the BitmapRegionDecoder API. The example has a large (6000,4000) image of the world that the user can scroll around in full resolution. The initial tag is quite small and easy to understand.
I write this class to an project I'm developing.
public class ScrollableImage extends View {
private Bitmap bmLargeImage; // bitmap large enough to be scrolled
private Rect displayRect = null; // rect we display to
private Rect scrollRect = null; // rect we scroll over our bitmap with
private int scrollRectX = 0; // current left location of scroll rect
private int scrollRectY = 0; // current top location of scroll rect
private float scrollByX = 0; // x amount to scroll by
private float scrollByY = 0; // y amount to scroll by
private int width, height;
private Paint background;
public ScrollableImage(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScrollableImage(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setSize(int width, int height) {
background = new Paint();
background.setColor(Color.WHITE);
this.width = width;
this.height = height;
// Destination rect for our main canvas draw. It never changes.
displayRect = new Rect(0, 0, width, height);
// Scroll rect: this will be used to 'scroll around' over the
// bitmap in memory. Initialize as above.
scrollRect = new Rect(0, 0, width, height);
// scrollRect = new Rect(0, 0, bmp.getWidth(), bmp.getHeight());
}
public void setImage(Bitmap bmp) {
if (bmLargeImage != null)
bmLargeImage.recycle();
bmLargeImage = bmp;
scrollRect = new Rect(0, 0, width, height);
scrollRectX = 0;
scrollRectY = 0;
scrollByX = 0;
scrollByY = 0;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return true; // done with this event so consume it
}
public void notifyScroll(float distX, float distY) {
scrollByX = distX; // move update x increment
scrollByY = distY; // move update y increment
}
#Override
protected void onDraw(Canvas canvas) {
if (bmLargeImage == null)
return;
if (scrollByX != 0 || scrollByY != 0) {
// Our move updates are calculated in ACTION_MOVE in the opposite direction
// from how we want to move the scroll rect. Think of this as
// dragging to
// the left being the same as sliding the scroll rect to the right.
int newScrollRectX = scrollRectX - (int) scrollByX;
int newScrollRectY = scrollRectY - (int) scrollByY;
scrollByX = 0;
scrollByY = 0;
// Don't scroll off the left or right edges of the bitmap.
if (newScrollRectX < 0)
newScrollRectX = 0;
else if (newScrollRectX > (bmLargeImage.getWidth() - width))
newScrollRectX = (bmLargeImage.getWidth() - width);
// Don't scroll off the top or bottom edges of the bitmap.
if (newScrollRectY < 0)
newScrollRectY = 0;
else if (newScrollRectY > (bmLargeImage.getHeight() - height))
newScrollRectY = (bmLargeImage.getHeight() - height);
scrollRect.set(newScrollRectX, newScrollRectY, newScrollRectX
+ width, newScrollRectY + height);
scrollRectX = newScrollRectX;
scrollRectY = newScrollRectY;
}
canvas.drawRect(displayRect, background);
// We have our updated scroll rect coordinates, set them and draw.
canvas.drawBitmap(bmLargeImage, scrollRect, displayRect, background);
}
}
And in the gesture listener I has this implementation of onScroll
Where img is your ScrollableImage instance.
Remember to use the setImage with your large image.
Edit: Also use setSize to set the size of your display.
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
img.notifyScroll(-distanceX, -distanceY);
img.invalidate();
return true;
}
I would divide the huge image into tiles and then draw the appropriate tiles depending on which part of the image has to be shown. Pretty much what Google Maps does. You can check http://openzoom.org/. There is nothing in Android but I think you can follow the same approach.

Categories

Resources