How does the TextSize property on an SKPaint object relate to the 'standard' Xamarin Forms FontSize?
In the image you can see the difference between size 40 on a label and as painted. What would I need to do to make them the same size?
As #hankide mentioned, it has to do with the fact that the native OS has scaling for UI elements so the app "looks the same size" on different devices.
This is great for buttons and all that as the OS is drawing them. So if the button is bigger, the OS just scales up the text. However, with SkiaSharp, we have no idea what you are drawing so we can't do any scaling. If we were to scale, the image would become blurry or pixelated on the high resolution screens.
One way to get everything the same size is to do a global scale before drawing anything:
var scale = canvasWidth / viewWidth;
canvas.Scale(scale);
And this is often good enough, but sometimes you really want to draw items differently on a high resolution screen. An example would be a tiled background. Instead of stretching the image on a bigger canvas, you may want to just tile it - preserving the pixels.
In the case of this question, you can either scale the entire canvas before drawing, or you can just scale the text:
var paint = new SKPaint {
TextSize = 40 * scale
};
This way, the text size is increased, but the rest of the drawing is on a larger canvas.
I have an example on GitHub: https://github.com/mattleibow/SkiaSharpXamarinFormsDemo
This compares Xamarin.Forms, SkiaSharp and Native labels. (They should all be exactly the same size)
I think that the problem is in the way Xamarin.Forms handles font sizes. For example on Android, you could define the font size in pixels (px), scale-independent pixels (sp), inches (in), millimeters and density-independent pixels (dp/dip).
I can't remember how Xamarin.Forms handles the sizes (px,sp or dp) but the difference you see here is because of that. What you could do, is create an Effect that changes the font size handling on the native control and try to match the sizing provided by SkiaSharp.
Related
This is my set up. I have 2 layers with transparency (I don't know if transparency matters here). Layers are the same size, 5x7 inches. Each layer has their image (say I draw a square on it and a circle on the other).
I want to resize ONLY the square.
The problem is when I scale the square I end up either scaling both, the circle AND the square, equally and they retain their layer size, or BOTH layers are rezise and no longer 5x7 inches. I've tried 'Tools-Transform-Scale' and 'Image-Resize canvas or image', but I can't find the tool to just resize ONE of the images.
Any ideas what I'm doing wrong?
Thanks
What you want is the Scale tool, and it will resize only the active layer if it is in Scale: layer mode (you seem to have it in Scale: image mode)(*).
Otherwise, to clear up things:
Image > Canvas size changes the size of the canvas, but nothing is stretched/compressed, the layers retain their size or are extended with transparency or white.
Image > Scale image scales everything in the image (layers, channels, paths...)
(*) Also,if what you apply a transform such as Scale to an item that has the chainlink, the same transform will be applied to all other chainlinked items (other layers, but also paths).
I have a mapbox project in production where the street map the user customizes (location, zoom, and text) will ultimately be printed on a surface which has rather small dimensions (3.5" x 2.25" at 600dpi. keeping in mind that the zoom level affects the visibility of the different street types, The problem I am running into is this:
Since the canvas element renders at 72dpi, this means that in order to get an accurate representation of how the map will print, I actually have to make the map's div container real size # 72dpi (252px x 162px) which is of course quite small and far less detailed than the map will look when it's printed at 600dpi
In order to allow people to interact with the map at a reasonable size on the screen, the cheap solution is of course to scale up the canvas using css transforms: i.e. #mapContainer {transform: scale(2.5)}. However this results in a very pixelated map since, unlike svg vector graphics (as seen in the text and graphics overlays in the images below), the browser does not re-render the canvas when it scales up.
Unscaled canvas
Scaled Canvas
I have spent a lot of time searching for a solution to this problem, and at best it looks like I may have to utilize a method where I pull in mapbox data into tiling services like nextzen with data visualization libraries like D3.js but id like to make this one last ditch effort to see if there is any way to trick the browser into rendering this element in a higher size dpi without changing the map bounds or zoom.
I suspect the answer to this lies in a similar vein to this stack overflow question Higher DPI graphics with HTML5 canvas However when I attempt it, I get a null value for var ctx = canvas.getContext('2d') since the mapbox canvas is "webgl" not "2d"... looking into the "webgl" method of resizing a canvas for higher dpi here: https://www.khronos.org/webgl/wiki/HandlingHighDPI but I really am having a hard time understanding how exactly to redraw the canvas after the resize.
I have a viewer with a perspective camera. I know the size of the viewer and the pixel ratio. I have several sprites in my scene that use the .sizeAttenuation property to never change size.
With all of this, I want to be able to set the scale of the sprite instances to, for example, be 20px x 20px. Is that possible? Is there a known conversion from pixels to sprite scale?
What I am experiencing now is that the sprites will change size depending on the viewer size. I wish to know how to resize them when the viewer changes so they are consistently the same size.
thanks!
I use Core Text to draw text to an offscreen bitmap context using CTLineDraw(). The bitmap is then processed internally before it is drawn to my window.
The problem here is that bitmap contexts aren't scaled on Retina Macs. Thus, on a Retina Mac, the text is still drawn at 72dpi to the bitmap but it should be drawn in 144dpi of course, because the pixel density is twice as high. Thus, the text currently looks blurry because it is drawn at 72dpi to the offscreen bitmap and this bitmap is then scaled when it is drawn to the window.
What is the best way to make Core Text Retina-aware in this context? Should I simply pass a transformation matrix to CTFontCreateWithName() that contains the screen's backingScaleFactor in its scale coefficients? That does look a little hackish, though. That's why I'm asking for some feedback or a better idea...
I'm trying to upload images and have them fit into different sized boxes....To give you an idea of what the application does: People upload images and have them printed onto posters.
For example, we'd have a poster size of 8" x 10"(live area) and the full size is 9.5" x 11.5", since the minimum DPI is 100, we typically multiple the 8x10 by 100 = 800x1000.
Here's an image explaining that I have an original image(http://i.imgur.com/Uds9rcZ.jpg) and need it to fit accordingly to the different sizes.
I may need to clarify this a bit, so ask questions if needed.
Thanks.
Canvas's context.drawImage has a version which allows you to scale an image while you are drawing it to the canvas.
If you resize disproportionally (like you do in your example) some of your resized image will fall off the canvas. Then your kitty will look distorted (in your example: stretched vertically)
This sample code resizes proportionally by using only the width. This way your kitty is not stretched.
// calculate how much to scale the resulting image
var originalWidth=16;
var originalHeight=20;
var desiredWidth=20;
var scalingFactor = desiredWidth/originalWidth;
// scale the original size proportionally
var newWidth=originalWidth*scalingFactor;
var newHeight=originalHeight*scalingFactor;
// resize the canvas to fit the desired image size
// Note: canvas is a reference to your html canvas element
canvas.width=newWidth;
canvas.height=newHeight;
// Draw the image to the canvas
// This version of drawImage allows you to scale the original image
// while you are drawing it to the canvas.
context.drawImage(
originalImage,
0,0,originalWidth,originalHeight,
0,0,newWidth,newHeight);
I would like to suggest you use the easyCanvas library to do this. The reason for this is that the built-in drawImage method of context do not scale the image proportionally for you, and it involves a small dose of math to get it right, especially in cases where destination shape differs from original and you want it to cover the whole area.
I made a method in this library to handle situations such as these allowing you to draw the original image proportionally into any shape even if the shape doesn't correspond with the original.
See this demo for an live example.
In essence what you do is to draw your uploaded image into the canvas with this method:
ez.drawImageProp(image, x, y, width, height);
where width and height would be that of destination.
It also has offset parameters so you can move the image around within that shape where an offset of 0.5 is center, 0 is from left and 1 is from right:
ez.drawImageProp(image, x, y, width, height, offsetX, offsetY);
Assuming image is already available all you need to do is:
var ez = new easyCanvas('canvas'); /// provide ID of existing canvas
ez.drawImageProp(image, 0, 0, ez.width, ez.height);
Disclaimer: I am the author of easyCanvas. easyCanvas is open-source (GPL3.0 license).