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

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;
}

Related

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

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.

Adjusting image contrast in FIB/SEM image - without affecting the text bar at the bottom of the image

FIB/SEM images have a text bar at the bottom of the image.
When imported into GMS, any contrast, gamma, .. adjustment also affects the text bar.
Is it possible to break up the image and have the data processing affect only the actual image - not the text bar?
The best you can do here is to break the actual image array into 2 separate images and then have the text-bar section displayed as a separate imageDisplay which you can add onto the imageDisplay of the data. You can shift/scale them with respect to each other, and you can also lock the added display so that it can not be shifted by mouse anymore. The following example should do what you need:
void CropAndMerge(Image img, number h){
number sx = img.ImageGetDimensionSize(0)
number sy = img.ImageGetDimensionSize(1)
image data := img.slice2(0,0,0,0,sx,1,1,sy-h,1).ImageClone() // note ":=", we sub-slice keeping tags and all
image anno = img.slice2(0,sy-h,0,0,sx,1,1,h,1) // note "=", we just want the data copy
imageDisplay disp
// Simple way to get imageDisplay. First show, then grab
// data.ShowImage()
// disp = data.ImageGetImageDisplay(0)
// Better alternative: No need to show
imageDocument doc = NewImageDocument( img.ImageGetName() )
doc.ImageDocumentAddImage( data )
// doc.ImageDocumentAddImage( anno ) // Use this to add 'side ordered' in case of page-view type. However, I'd rather not use page-mode.
disp = data.ImageGetImageDisplay(0)
disp.ImageDisplaySetColorTableByName( "Black Body Extended" ) // Just to show you can act on the display before actually showing it that way
// Add Annotation area as annotation on imageDisplay (all are components)
imageDisplay annoDisp = NewImageDisplay( anno, "best" )
disp.ComponentAddChildAtEnd( annoDisp )
// move out of the way
// ComponentPositionAroundPoint: Moves the annotation so the 'rel_x' horizontal point in the bounding rect is at 'new_x' (if bool horz is true), and for y accordinglye
number doVert = 1
number rel_y = 1.0 // bottom (relative coordinate!)
number new_y = sy // becomes bottom (absolute position)
annoDisp.ComponentPositionAroundPoint( 0,new_y,0,rel_y,0,doVert)
// make sure nobody messes with the annotation area
annoDisp.ComponentSetSelectable(0)
doc.ImageDocumentShow()
}
number sx = 1024
number sy = 1024
number h = 300
image in := realimage("Imported",4,sx,sy)
in = (icol%100 + iradius*sin(irow/iheight*5*Pi() + itheta )**2)
in.slice2(0,sy-h,0,0,sx,1,1,h,1) = (icol+irow)%50>45?max(in)+100:0
//in.showimage()
CropAndMerge(in.imageClone(),h)

Align UI lements correctly in SkiaSharp (UWP)

I am trying to align a few buttons in a circular manner using SkiaSharp. My code looks like this
<skia:SKXamlCanvas x:Name="test" PaintSurface="Test_OnPaintSurface" />
Code behind
private void Test_OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
// the canvas and properties
var canvas = e.Surface.Canvas;
// get the screen density for scaling
var display = DisplayInformation.GetForCurrentView();
var scale = display.LogicalDpi / 96.0f;
var scaledSize = new SKSize(e.Info.Width / scale, e.Info.Height / scale);
// handle the device screen density
canvas.Scale(scale);
// make sure the canvas is blank
canvas.Clear(SKColors.Transparent);
// draw some text
var paintSmallCircle = new SKPaint
{
Color = SKColors.CornflowerBlue,
IsAntialias = true,
Style = SKPaintStyle.Fill,
TextAlign = SKTextAlign.Center,
TextSize = 24
};
var paintCircle = new SKPaint
{
Color = SKColors.LightGray,
IsAntialias = true,
Style = SKPaintStyle.Fill,
TextAlign = SKTextAlign.Center,
TextSize = 24
};
var coord = new SKPoint(
scaledSize.Width / 2,
(scaledSize.Height) / 2);
canvas.DrawCircle(coord, 120, paintCircle);
int r = 100;
int angle = 90;
for (int i = 0; i < 12; i++)
{
double x1 = scaledSize.Width / 2 + r * Math.Cos(Math.PI * angle / 180.0) ;
double y1 = scaledSize.Height / 2 - r * Math.Sin(Math.PI * angle / 180.0) ;
var coord1 = new SKPoint((float) x1, (float)y1);
canvas.DrawCircle(coord1, 10, paintSmallCircle);
Button btn = new Button { Content = i, Height = 25, Width = 25, };
btn.SetValue(SKXamlCanvas.LeftProperty, coord1.X);
btn.SetValue(SKXamlCanvas.TopProperty, coord1.Y);
test.Children.Add(btn);
angle = angle - 30;
}
}
With this code, I am able to draw Blue circles correctly, but the button alignment comes wrong. How can I solve this issue?
right now my output looks like this
As you can see blue small circles are aligned correctly, but not buttons.
The expected behavior is that buttons come in the same place where blue circles are rendered
The point is you place the Button's Left & Top property.
When you use canvas.DrawCircle(coord1, 10, paintSmallCircle); to draw a Circle, the center point is coord1.
And you draw the Buttons Left & Top at the center point of the Circle.
So you can draw Button using
btn.SetValue(SKXamlCanvas.LeftProperty, coord1.X - 25 /2);
btn.SetValue(SKXamlCanvas.TopProperty, coord1.Y - 25 / 2);
25 is the Height and Width of your Button.
See the result.

Createjs, how to use the mouse to draw a rectangle in the stage

sample
I want to use the mouse to draw a rectangle on the stage,but the rect can't change size.
this is code:
var planetText,selectBox=new createjs.Shape(),x, y, width, height;
stage.on('mousedown',function(event){
x = event.stageX;y = event.stageY;
selectBox.graphics.beginFill('#ffffff').drawRect(x, y, 100, 100);
selectBox.set({alpha:0.5});
planetText = new createjs.Text(x+','+y +','+0+','+0, "16px Arial", "#ffffff");
planetText.set({textAlign:'center',textBaseline:'middle',x:x,y:y});
//console.log(selectBox);
stage.addChild(selectBox);
stage.addChild(planetText);
stage.update();
});
stage.addEventListener('pressmove',function (event){
//console.log(event);
width = event.stageX - x;
height = event.stageY - y;
planetText.text = x + ',' + y +','+width+','+height;
//selectBox.set({w:width,h:height});
createjs.Tween.get(selectBox).to({width:width,height:height},100,createjs.Ease.bounceIn());
stage.update();
});
stage.on('pressup',function(event){
//console.log(self.selectBox);
createjs.Tween.get(selectBox).to({alpha:0},300,createjs.Ease.bounceIn());
stage.removeChild(selectBox);
stage.update();
});
how to fixed it , thx~
Have a look at this question/answer:
Change Color of Shape Mid Tween
Basically, there is no width/height of display objects in EaselJS, but you can change the actual drawing command any time. You can also change the scaleX/scaleY, and set the stroke to ignoreScale:
shape.graphics.setStrokeStyle(1, null, null, null, true); // 5th param
Further reading:
http://blog.createjs.com/new-command-approach-to-easeljs-graphics/
http://blog.createjs.com/update-width-height-in-easeljs/

Raphael -- How to fit the paper size to the browser's window size?

I want to change the paper(objects base) size of Raphael to fit the window resizing. [ using Firefox_13.0, Raphael_2.1.0, WindowsXP ]
If it is available, I would like to fit full-screen-mode.
==================================================
(steps)
I created the paper : paper = Raphael(0, 50, 800, 600); // initial width and height are 800 and 600.
I placed objects on the paper.
The window size of browser is checked by windowW = window.innerWidth and winnowH = window.innerHeight (on Firefox).
Scaling value is calculated by sv = windowW/800;
And scaling the paper by paper.scale(sv, sv);
==================================================
(the script)
window.onload = function () {
paper = Raphael(0, 50, 800, 600);
var background = paper.rect(0, 0, 800, 600).attr({fill:'#669999'});
// placing the objects
var circle = ...;
var rect = ...;
var ellipse = ...;
winowW = window.innerWidth;
winowH = window.innerHeight;
sv = winowW/800.
paper.scale(sv, sv);
}
==================================================
(result)
Though circle.scale(sv), rect.scale(sv, sv) and ellipse.scale(sv, sv) are valid, paper.scale(sv, sv) and background.scale(sv, sv) are not.
Why this case is happen ? I can get the window size by window.onresize = function() {...} on real-time. If there are better methods, please tell me.
Thanks,
I've succeeded by following two points:
1) "paper" itself is not manipulative object. I think we should look it as billboard.
2) use st = paper.set() and put the objects(circle, rect, ...) in it. And use st.scale(sv, sv, 0, 0);
* third and fourth parameter (0, 0) are very impotent.
(caution)
Serial resizing operation is not good for the function "scale()". Because each of resizing coefficient is piled as the
power of a number. So when one have done 1.1 times resizing operation 5 times, the scale will be 1.1^5.
Use setViewBox()
It should do the work
http://raphaeljs.com/reference.html#Paper.setViewBox