android how to scale down a bitmap and keep its aspect ration - android-bitmap

using 3rd party library which returns a bitmap. in the app it would like to scale down the bitmap.
static public Bitmap getResizedBitmap(Bitmap bm, int newWidth, int newHeight) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height,
matrix, false);
return resizedBitmap;
}
===
Bitmap doScaleDownBitmap() {
Bitmap bitmap = libGetBitmap(); // got the bitmap from the lib
int width = bitmap.getWidth();
int height = bitmap.getHeight();
if (width > 320 || height > 160) {
bitmap = getResizedBitmap(bitmap, 320, 160);
}
System.out.println("+++ width;"+width+", height:"+height+ ", return bmp.w :"+bitmap.getWidth()+", bmp.h:"+bitmap.getHeight());
return bitmap;
}
the log for a test bitmap (348x96):
+++ width;348, height:96, return bmp.w :320, bmp.h:160
looks like the resized bitmap does not scale properly, shouldnt it be 320 x 88 to maintain the aspect ratio?
(it did from (348x96) ==> (320x160))
saw android sample
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
how to apply it if has the bitmap already?
or what is the correct way to scale down a bitmap?
EDIT:
this one could keep the aspect ration and one of the desired dimensions (either width or height) will be used for the generated bitmap. basically CENTER_FIT.
However it does not generate the bitmap with both desired width and height.
e.g. would like to have a new bitmap of (w:240 x h:120) from a src bitmap of (w:300 x h:600), it will map to (w:60 x h:120).
I guess it needs extra operation on top of this new bitmap if want the new bitmap has (w:240 x h:120).
is there a simpler way to do it?
public static Bitmap scaleBitmapAndKeepRation(Bitmap srcBmp, int dstWidth, int dstHeight) {
Matrix matrix = new Matrix();
matrix.setRectToRect(new RectF(0, 0, srcBmp.getWidth(), srcBmp.getHeight()),
new RectF(0, 0, dstWidth, dstHeight),
Matrix.ScaleToFit.CENTER);
Bitmap scaledBitmap = Bitmap.createBitmap(srcBmp, 0, 0, srcBmp.getWidth(), srcBmp.getHeight(), matrix, true);
return scaledBitmap;
}

When you Scale-Down the bitmap, if width and height are not divisible by scale, you should expect tiny change in ratio. if you don't want that, first crop the image to be divisible and then scale.
float scale=0.5f;
scaledBitmap=Bitmap.createScaledBitmap(bitmap,
(int)(bitmap.width()*scale),
(int)(bitmap.height()*scale),
true); //bilinear filtering

Found a way, I am sure there is better one
public static Bitmap updated_scaleBitmapAndKeepRation(Bitmap srcBitmap, int targetBmpWidth,
int targetBmpHeight) {
int width = srcBitmap.getWidth();
int height = srcBitmap.getHeight();
if (targetBmpHeight > 0 && targetBmpWidth > 0 && (width != targetBmpWidth || height != targetBmpHeight)) {
// create a canvas with the specified bitmap to draw into.
Bitmap scaledImage = Bitmap.createBitmap(targetBmpWidth, targetBmpHeight, Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(scaledImage);
// draw transparent background color
Paint paint = new Paint();
paint.setColor(Color.TRANSPARENT);
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);
// draw the source bitmap on canvas and scale the image with center_fit (the source image's larger side is mapped to the corresponding desired dimensions, and the other side scaled with aspect ration)
Matrix matrix = new Matrix();
matrix.setRectToRect(new RectF(0, 0, srcBitmap.getWidth(), srcBitmap.getHeight()),
new RectF(0, 0, targetBmpWidth, targetBmpHeight),
Matrix.ScaleToFit.CENTER);
canvas.drawBitmap(srcBitmap, matrix, null);
return scaledImage;
} else {
return srcBitmap;
}
}
The result screenshot:
The 1st image is the src (w:1680 x h:780),
2nd is from the scaleBitmapAndKeepRation() in the question part, which has scaled image but with dimensions (w:60 x h:120) not in desired dimensions (w: 240 x h:120),
3rd is the one does not keep the aspect ration, although has the dimension right.
4th is from the updated_scaleBitmapAndKeepRation() which has the desired dimensions and the image is center_fit and keep the aspect ratio.

Related

Windows Store Apps: measure many pixels a TextBlock width will be?

Is there a way to measure many pixels will a TextBlock width will occupy?
let's say I have a string with length of 10 characters.
and I have a text block (without an assigned width value), I'll set the string to the Text property of that textblock.
Is there a way to measure the actual width of the textblock before adding it to the layout?
Until any control is added to the VisualTree, it has a Width and Height of zero.
You have two options, the first is this:
var newTextBlock = new TextBlock() { Opacity = 0.001, Text = "Test" };
RoutedEventHandler handler = null;
handler = (s, e) =>
{
newTextBlock.Loaded -= handler;
Panel.Children.Remove(newTextBlock);
};
newTextBlock.Loaded += handler;
Panel.Children.Add(newTextBlock);
And in the handler make all the operations you need, because now you have ActualWidth and ActualHeight.
The other one is using Win2D, you can do the following:
var test = new CanvasTextFormat() { WordWrapping = CanvasWordWrapping.WholeWord };
session.DrawText(largeloremipsum, new Rect(0, 0, 480, 0), Colors.Black, test);
var size = new CanvasTextLayout(canvasControl, text, test, 480, float.MaxValue);
But this is a more complex way, you can define the TextFormat for the Size, Font, etc.
Well, the only way I got is to create a new -in memory- text block- then assign it the same text, style and attributes as my target TextBlock, I assumed that my text block has a fixed height, to calculate it's desired width to fit the text, I made the following function:
private double CalculateDesiredWidth(string text, TextBlock targetText)
{
int MIN_TEXT_WIDTH=200;
//create a new text block to calculate how much space will the string occupy
TextBlock txt = new TextBlock();
txt.Text = text;
txt.FontSize = 16;
txt.Margin = targetText.Margin;
Size desiredSize = new Size(0, targetText.ActualHeight);
Rect desiredRect = new Rect(new Point(0, 0), desiredSize);
//measure the desired size
txt.Measure(desiredSize);
txt.Arrange(desiredRect);
//if the desired width is small, use the minimum default width
if (txt.ActualWidth <= MIN_TEXT_WIDTH)
return MIN_TEXT_WIDTH;
//calculate the desired area
double desiredArea = txt.ActualWidth * txt.ActualHeight;
//calculate the the width required to fit the desired area to my text box
double width = desiredArea / (targetText.Height - txt.Margin.Top - txt.Margin.Bottom);
return width;
}

How to make a simple screenshot method using LWJGL?

So basically I was messing about with LWJGL for a while now, and I came to a sudden stop with with annoyances surrounding glReadPixels().
And why it will only read from left-bottom -> top-right.
So I am here to answer my own question since I figured all this stuff out, And I am hoping my discoveries might be of some use to someone else.
As a side-note I am using:
glOrtho(0, WIDTH, 0 , HEIGHT, 1, -1);
So here it is my screen-capture code which can be implemented in any LWJGL application C:
//=========================getScreenImage==================================//
private void screenShot(){
//Creating an rbg array of total pixels
int[] pixels = new int[WIDTH * HEIGHT];
int bindex;
// allocate space for RBG pixels
ByteBuffer fb = ByteBuffer.allocateDirect(WIDTH * HEIGHT * 3);
// grab a copy of the current frame contents as RGB
glReadPixels(0, 0, WIDTH, HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, fb);
BufferedImage imageIn = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB);
// convert RGB data in ByteBuffer to integer array
for (int i=0; i < pixels.length; i++) {
bindex = i * 3;
pixels[i] =
((fb.get(bindex) << 16)) +
((fb.get(bindex+1) << 8)) +
((fb.get(bindex+2) << 0));
}
//Allocate colored pixel to buffered Image
imageIn.setRGB(0, 0, WIDTH, HEIGHT, pixels, 0 , WIDTH);
//Creating the transformation direction (horizontal)
AffineTransform at = AffineTransform.getScaleInstance(1, -1);
at.translate(0, -imageIn.getHeight(null));
//Applying transformation
AffineTransformOp opRotated = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
BufferedImage imageOut = opRotated.filter(imageIn, null);
try {//Try to screate image, else show exception.
ImageIO.write(imageOut, format , fileLoc);
}
catch (Exception e) {
System.out.println("ScreenShot() exception: " +e);
}
}
I hope this has been useful.
For any questions or comments on the code, ask/suggest as you like. C:
Hugs,
Rose.
sorry for the late reply but this is for anybody still looking for a solution.
public static void saveScreenshot() throws Exception {
System.out.println("Saving screenshot!");
Rectangle screenRect = new Rectangle(Display.getX(), Display.getY(), Display.getWidth(), Display.getHeight());
BufferedImage capture = new Robot().createScreenCapture(screenRect);
ImageIO.write(capture, "png", new File("doc/saved/screenshot.png"));
}

How to draw a texture as background in gtk?

I want to add a texture as background in a gtk container, is it possible?
What I want is similar to the repeat-x repeat-y properties in css, but it's not supported in gtk yet, so, how to do it without any ugly hacks?. Another example is what nautilus have, where you can change the background.
thanks :)
pd:sorry 4 ma english
I did it this way:
private bool draw_background (Cairo.Context cr) {
int width = this.get_allocated_width ();
int height = this.get_allocated_height ();
cr.set_operator (Cairo.Operator.CLEAR);
cr.paint ();
cr.set_operator (Cairo.Operator.OVER);
var background_style = this.get_style_context ();
background_style.render_background (cr, 0, 0, width, height);
background_style.render_frame (cr, 0, 0, width, height);
var pat = new Cairo.Pattern.for_surface (new Cairo.ImageSurface.from_png (Build.PKGDATADIR + "/files/texture.png"));
pat.set_extend (Cairo.Extend.REPEAT);
cr.set_source (pat);
cr.paint_with_alpha (0.6);
return false;
}

How to resize bitmap image to be <200 KB and meet Tile restrictions (WinRT)

I am developing a routine to scale some bitmap images to be part of tile notifications for my Window-8 app
The tile images must be <200KB and less than 1024x1024 px in dimension. I am able to use a scaling routine to resize the source image as necessary to fit the 1024x1024 px dimension limitation.
How can I alter the source image to guarantee the size restriction will be met?
My first attempt was to continue to scale down the image until it clears the size threshold, and use isTooBig = destFileStream.Size > MaxBytes to determine the size. But, the code below results in an infinite loop. How can I reliably measure the size of the destination file?
bool isTooBig = true;
int count = 0;
while (isTooBig)
{
// create a stream from the file and decode the image
using (var sourceFileStream = await sourceFile.OpenAsync(Windows.Storage.FileAccessMode.Read))
using (var destFileStream = await destFile.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(sourceFileStream);
BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(destFileStream, decoder);
double h = decoder.OrientedPixelHeight;
double w = decoder.OrientedPixelWidth;
if (h > baselinesize || w > baselinesize)
{
uint scaledHeight, scaledWidth;
if (h >= w)
{
scaledHeight = (uint)baselinesize;
scaledWidth = (uint)((double)baselinesize * (w / h));
}
else
{
scaledWidth = (uint)baselinesize;
scaledHeight = (uint)((double)baselinesize * (h / w));
}
//Scale the bitmap to fit
enc.BitmapTransform.ScaledHeight = scaledHeight;
enc.BitmapTransform.ScaledWidth = scaledWidth;
}
// write out to the stream
await enc.FlushAsync();
await destFileStream.FlushAsync();
isTooBig = destFileStream.Size > MaxBytes;
baselinesize *= .90d * ((double)MaxBytes / (double)destFileStream.Size);
}
}
Can you not calculate it using width x height x colourDepth (where colourDepth is in bytes, so 32bit=4bytes). Presumably you're maintaining aspect ratio so you just need to scale down width/height until you find it less than 200KB.
This assumes the output is an a bitmap and therefore uncompressed.
Considering that tile size either 150x150 for square tiles or 310x150 for wide tiles you should be able to shrink image down to the appropriate size and with jpeg compression you are pretty much guaranteed to be under 200k. Set compression quality around 80. It will give you good compression ratio while keeping decent image quality.

Android - Trying to gradually fill a circle bottom to top

I'm trying to fill a round circle (transparent other than the outline of the circle) in an ImageView.
I have the code working:
public void setPercentage(int p) {
if (this.percentage != p ) {
this.percentage = p;
this.invalidate();
}
}
#Override public void onDraw(Canvas canvas) {
Canvas tempCanvas;
Paint paint;
Bitmap bmCircle = null;
if (this.getWidth() == 0 || this.getHeight() == 0 )
return ; // nothing to do
mergedLayersBitmap = Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.ARGB_8888);
tempCanvas = new Canvas(mergedLayersBitmap);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setFilterBitmap(false);
bmCircle = drawCircle(this.getWidth(), this.getHeight());
tempCanvas.drawBitmap(bmCircle, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
tempCanvas.clipRect(0,0, this.getWidth(), (int) FloatMath.floor(this.getHeight() - this.getHeight() * ( percentage/100)));
tempCanvas.drawColor(0xFF660000, PorterDuff.Mode.CLEAR);
canvas.drawBitmap(mergedLayersBitmap, null, new RectF(0,0, this.getWidth(), this.getHeight()), new Paint());
canvas.drawBitmap(mergedLayersBitmap, 0, 0, new Paint());
}
static Bitmap drawCircle(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(drawColor);
c.drawOval(new RectF(0, 0, w, h), p);
return bm;
}
It kind of works. However, I have two issues: I run out of memory quickly and the GC goes crazy. How can I utilize the least amount of memory for this operation?
I know I Shouldn't be instantiating objects in onDraw, however I'm not sure where to draw then. Thank you.
pseudo would look something like this.
for each pixel inside CircleBitmap {
if (pixel.y is < Yboundary && pixelIsInCircle(pixel.x, pixel.y)) {
CircleBitmap .setPixel(x, y, Color.rgb(45, 127, 0));
}
}
that may be slow, but it would work, and the smaller the circle the faster it would go.
just know the basics, bitmap width and height, for example 256x256, the circles radius, and to make things easy make the circle centered at 128,128. then as you go pixel by pixel, check the pixels X and Y to see if it falls inside the circle, and below the Y limit line.
then just use:
CircleBitmap .setPixel(x, y, Color.rgb(45, 127, 0));
edit: to speed things up, don't even bother looking at the pixels above the Y limit.
in case if you want to see another solution (perhaps cleaner), look at this link, filling a circle gradually from bottom to top android