Android BlurView issues with SKCanvasView - xamarin

I am attempting to create a view for Android whose background appears to blur the view's content for which it is on top. This is nothing new and has been done before. I based my implementation on what was done here: Dimezis/BlurView.
The approach uses the pre-draw event from the view tree observed to draw a view to an internal canvas. The canvas is backed by a bitmap. A blur is applied to the bitmap before it is drawn to the canvas passed to BlurView's draw method. This approach works well for all standard views/controls and is a common method used to achieve the blur view effect.
However, it does not handle Skia-based drawings that may be on the view that is blurred.
Skia, via SKCanvasView, is used heavily for controls within the App so this is kind of a deal breaker if I cannot find a solution. The issue is very odd. Anything that is drawn on the canvas view appears to be scaled and translated when drawn to the internal canvas.
Screen-Shot - blur label v. blur SKCanvasView
The screen-shot show the difference in results from blurring a label with text v. blurring a red circle drawn on an SKCanvasView.
For reference, I've posted a sample project on GitHub. It can be found here: jaredballen/BlurView
I'd really appreciate any input that can be shared.

When you invoke _rootView?.Draw(_internalCanvas); the circle SKCanvasView is internally rendering itsself using _internalCanvas size (same as _blurView size) resulting in demonstrated behavour. My guess this could be solved by making _internalCanvas of the same size as the _rootView, rendering root view as blurred, then clipping and translating the result inside the smaller _blurView.

Related

What could prevent an NSView.layer to renderInContext:?

I've been working hard, searching the internet on that problem for 3 days and I'm now running out of ressource.
Currently porting an iOS app to MacOS (deployment 10.11). The problem:
I have a view hierarchy as below:
NSScrollview
documentView
grouping view
tiling view one
array of NSImageView (each one being a tile)
tiling view two
array of NSImageView (each one being a tile)
The two tiling views are overlaying completely, depending of UI one may be hidden or the second one has opacity set below 1.0 to blend the two tiled view.
Because of opacity requirement, as well as performance, views are CAlayer backed. This is done from IB where the to NSScrollview is checked for Core Animation. Thereoff, all the view tree is (implicitly) layer backed.
Works as expected, scroll, magnify, etc..
Then I need to make an image out of the document view to generate an SCNMaterial content (3D view).
On iOS the documentView renderInContext works as expected, and allows an image to be created.
On Appkit the context stay transparent, so is the image, a valid object while as if clearColor.
If the documentView.canDrawSubviewsIntoLayer is set at creation, the view tree renders OK. This can't be the solution since it prevents opacity setting to work.
Even when one tiling view is hidden (no opacity compositing) rendering fails.
I read that some kinds of views are not rendered. I don't use them. There are no filters, no masks, beside a default masksToBounds setting on all the view tree. I don't know why and where it is set. I tried to unset it on all the views at creation with no success. It is set again somehow, on the grouping view below the documentView. This may be the problem but why this property is out of my control ?
Alternative way to get view tree rendering, bitmapImageRepForCachingDisplayInRect: / cacheDisplayInRect:toBitmapImageRep: works the same : ok with canDrawSubviewsIntoLayer, KO otherwise. Apple code examples to make a texture out of a view are using either one of the two methods.
There are plenty of posts, mainly on SO, complaining about CALayer renderInContext and code to custom render a layer tree. Nevertheless, most are quite old and now there must be a simple standard way to achieve it.
Edit: among other attempts, I tried to set each view wantsLayer, with no success.
Well, as often when you post a request for help, you finally find the answer by yourself… Here it is:
Given the view tree as listed in the question, I achieved to render it by setting canDrawSubviewsIntoLayer on each tiling view. This way, the opacity compositing between layers is working, AND the views are rendered.
I post this as an answer, because it solves the problem.
As of WHY this works, here is my guess, but this is not a authorised answer: Each NSImageView tile, subviews of tiling view, have their origin set to their frame. This is not a transform on the layer but a position of the frame in the coordinates of the tiling view. This is a difference with the iOS code version where the tiles are positioned by a transform. I'm going to test further, and see if using a transform rather that a frame origin makes a difference to renderInContext.
Edit: after more testing it appears that the iOS version has a transform AND an offset to the tile.
So the only clue is that the layer system get lost when rendering in context on OSX when some subview have an offset ??
Summary:
The key point to the solution is to find the right layer where to set canDrawSubviewsIntoLayer

Unable to Move the Canvas or Change Any of the Transform Values

I am using Unity 5 and I started to make a menu scene. When I made the canvas, all of values under the Rect Transform component are locked and it says "some values driven by Canvas." The only thing I can change is the z position when using the gizmo in the editor. I can't reset the position or anything. Why is this happening?
This means that the canvas's canvas component has it's render mode set to Screen space - overlay. This forces it to be the size of the screen. Change it to World Space and it will allow you to resize it and move it around.
Changing the Render mode is not an ideal solution; neither is Overlay mode the reason why this is happening at all. World Space is just a render mode that changes the way your whole UI behaves and would mean a whole different set up and a whole lot more work just to get a child UI object to move independently.
Here is the description of what World Space is for from the Unity site:
In this render mode, the Canvas will behave as any other object in the
scene. The size of the Canvas can be set manually using its Rect
Transform, and UI elements will render in front of or behind other
objects in the scene based on 3D placement. This is useful for UIs
that are meant to be a part of the world. This is also known as a
“diegetic interface”.
The Rect Transform usually gets locked because it is a child of another Canvas Object which controls its Transforms. The way to fix this is to overwrite it by adding a “Layout Element” component to it. From there you can configure it to work the way you like and it can have transforms independent of the Parent UI Object.
For full details, see this Unity support page: https://support.unity3d.com/hc/en-us/articles/115000179163-How-to-overwrite-Width-and-Height-values-that-are-driven-by-a-Layout-Group-in-runtime-
Canvas is depend on game tab in your window panel.
Adjust panel by use of close tab or resize panel or doc game panel.
It will help you make default 800 X 600 canvas.

Trouble with Auto-Layout

Xcode 5
I'm trying to learn the auto-layout system. Thought I would start with something simple, but I'm already getting stumped :-)
Scene: Main View -> ImageView -> View
I want to support rotation such that the Image rotates and centers on the screen, using Aspect-Fit content.
I want the smaller view to maintain it's relative position to the top edge of the UIimage view. It does't seem to understand the aspect-fit, and it aligns the sub view along the top of the main view, not the fitted image.
I think it has something to do with the fact that the small view is a sibling of the Image, and not a sub-view. I can only seem to create constraints to the superview.
.
You haven't started with something simple!
An aspect fitted image view doesn't actually change its size under auto layout depending on the image, it fits the image into the bounds that the constraints have determined, leaving the rest of its frame blank. If you set a border or background colour on the image view you will see this.
To achieve the effect you're after you would need to do the aspect fitting calculation yourself and modify the sizing constraints on the image view appropriately.

Drawing on CATiledLayer or CALayer with pdf in iOS

i have a scrollview which contains a pdfpage rendered with CATiledLayer, i want to draw stuff onto the pdf page so i created a overlay layer, i need the graphic to look vectorized so i decided to use CATiledlayer for the overlay layer. Only problem is that it is very slow to draw (I'm using beizerpath to draw), then i tried to optimize it by creating the overlay layer with the visible height and width when zooming in and out, so i don't need to create the overlay for the whole content bound. But still no luck , i want to try CALayer but the draw path just becomes blurry and pixelated, so i'm not sure how i can improve on this. I also tried drawinrect but for some reason it doesn't seem to work.
I suggest not using bezierpath to draw annotations since it requires you to redraw the whole path every time the pen moves. It would be better if you draw only the current line segment using CGContextAddQuadCurveToPoint.
At touchMoved, get the current point and 2 prev points
Using those points, get the area where the line should be drawn
draw at that area in drawRect using setNeedsDisplayAtRect
inside drawRect, go to the end of your path and add your new line using CGContextAddQuadCurveToPoint

"CoreAnimation: surface is too large"

I'm creating a custom (layer-hosting) document view, which is contained within a scroll view. The root layer has two sub layers of the same size--one for the view's content, and one for anything that needs to hover over the main content. I set the frame to 2500x2500 and added a number of cells to the content layer, which was fine. On adding a translucent clone of one of the cell's layers to the overlay layer, the whole view clears briefly, and I get a log message 'core animation: surface 2502x2502 is too large'. This happens between adding the new layer and the next cycle of the event loop, so I guess when core animation renders the new layer.
I knew that a layer's content size is related to opengl texture size, but didn't think its frame mattered. I'm not drawing anything to these layers, not setting any style properties, and remove offscreen sub layers. All I'm really using them for is to handle the geometry of the document view. Is this an appropriate use of CA layers? If not, are there better ways of handling a large core animation-based document view?
Edit:
I've had this problem again, caused by an implicit animation on adding sublayers to the large parent. So in addition to what is suggested below, that's one to check if you run into this.
I would check to make sure that you're not setting any properties on your 2500x2500 layers which could require offscreen rendering. (This causes the layer to try and create a full-size buffer off-screen and render its contents into that buffer, rather than just rendering the contents to the screen directly.)
For example, setting an opacity, masksToBounds, mask, shouldRasterize, etc, could cause offscreen-rendering. You can see if offscreen-rendering is happening with the Core Animation instrument. (There's a checkbox to highlight offscreen-rendered areas.)

Resources