Replace already drawn bitmap on SK Canvas at particular point - xamarin.forms

I wanted to update the existing bitmap drawn >#particular coords> on SK Canvas, while calling InvalidateSurface am able to draw but the previous bitmap is not clearing. New Image is beneath the older one! Canvas.Clear(0) didn't clear all previous bitmaps.
Needed help in getting over this issue.
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
canvas.Clear(0) // Dont seems be clearing old Bitmaps
if (condition)
{
using (Stream streami = assembly.GetManifestResourceStream(resourceID))
{
resourceBitmap = SKBitmap.Decode(streami);
}
canvas.DrawBitmap(resourceBitmap, listPoint);
}
else
{
using (Stream streami = assembly.GetManifestResourceStream(secondID))
{
secondBitmap = SKBitmap.Decode(streami);
}
canvas.DrawBitmap(secondBitmap, listPoint); // Drawn beneath the older
}
}
Now the if condition changed to true outside the code blocks with new value again calling canvasView.InvalidateSurface();
Doest not clear previous

Related

QrCoder Asp.Net - How to remove noise?

Right now I'm creating my QR-Code using QrCoder from Asp.Net. You can see my code below:
SvgQrCode:
public void UpdateText(string value)
{
using (var qrGenerator = new QRCodeGenerator())
{
using (var qrCodeData = qrGenerator.CreateQrCode(value, QRCodeGenerator.ECCLevel.Q))
{
using (var qrCode = new QRCode(qrCodeData))
{
using (var bitmap = qrCode.GetGraphic(1, Color.Black, Color.White, false))
{
Image.FromData(bitmap);
}
Text = value;
} } } }
Xml:
<Image Source="{Binding Element.ImageSource}"
RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased"
Width="{Binding Element.Width}"
Height="{Binding Element.Height}"
Stretch="Uniform">
What happens:
The generated QR-Code has some light noise that you can see here in the screenshot on the left side(left side Gray8, right side BlackWhite):
What I tried:
I changed in the method Decode the Pixelformats from Gray8 to BlackWhite. The result is the screenshot above (qr code on the right side).
internal static BitmapSource Decode(string value, int? pixelWidth, BitmapCacheOption cacheOption = BitmapCacheOption.OnLoad)
{
// ..some code..
var grey = new FormatConvertedBitmap(bitmap, PixelFormats.Gray8, BitmapPalettes.Gray256, 1.0);
return grey;
}
Another thing that I tried is changing qrCode.GetGraphic(1) to something higher like qrCode.GetGraphic(10), which increases the pixels per module. But this is not a clean way to fix the problem, because the noise is just getting realy small(so you can hardly see it anymore) and the pixel per module are getting increased.
My Problem: I'm using the method decode for qr codes, bar codes and images. So If I would add an image it would be black and white. Of course I can use an if-statement and check what if its an image or or qr/bar-code. But why is PixelFormats.Gray8 creating noise? Why is it not clean?
I found the problem. I overlooked those lines and didnt realize that .jpeg was choosen as format:
public void FromData(Bitmap bitmap)
{
using (var ms = new MemoryStream())
{
bitmap.Save(ms, ImageFormat.Jpeg);
//...

Applying a background for the control in an Effect in XamarinForms

I'm trying to apply a background for what ever the control the effect is attached to.
First i made the effect to apply a background for the controls that have either a Fill or Background properties in UWP.
But the problem is a lot of controls renderes to FrameworkElement in UWP. And FrameworkElement doesn't have a Background property .
In the VisualElementRenderer Xamarin forms adresses this issue be applying a background layer behind the content but this is an effect.
Is there is any way to apply a background for whatever the effect is attached to?
I'm appplying an AcrylicBrush So it must be assigned to the Control directly.
The code i written before:
try
{
Control.GetType().GetProperty("Background").SetValue(Control, brush);
}
catch
{
Control.GetType().GetProperty("Fill").SetValue(Control, brush);
}
I'm appplying an AcrylicBrush So it must be assigned to the Control directly.
You could use compositor to apply Acrylic to SpriteVisual. We use Win2D to make GaussianBlurEffect for UIElement before we have Acrylic API. Get Compositor of UIElement, and use this Compositor to CreateSpriteVisual. then set GaussianBlurEffect to hostSprite.Brush like the following.
Compositor _compositor;
SpriteVisual _hostSprite;
private void applyAcrylicAccent(Panel panel)
{
_compositor = ElementCompositionPreview.GetElementVisual(panel).Compositor;
_hostSprite = _compositor.CreateSpriteVisual();
_hostSprite.Size = new Vector2((float)panel.ActualWidth, (float)panel.ActualHeight);
var backdrop = _compositor.CreateHostBackdropBrush();
// Use a Win2D blur affect applied to a CompositionBackdropBrush.
var graphicsEffect = new GaussianBlurEffect
{
Name = "Blur",
BlurAmount = 100f,
Source = new CompositionEffectSourceParameter("backdrop")
};
var effectFactory = Window.Current.Compositor.CreateEffectFactory(graphicsEffect, new[] { "Blur.BlurAmount" });
var effectBrush = effectFactory.CreateBrush();
effectBrush.SetSourceParameter("backdrop", backdrop);
_hostSprite.Brush = effectBrush;
ElementCompositionPreview.SetElementChildVisual(panel, _hostSprite);
}
And calling it with applyAcrylicAccent(RootLayout). You also will need to handle the SizeChanged event :
private void LayoutRoot_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (_hostSprite != null)
_hostSprite.Size = e.NewSize.ToVector2();
}

Adobe Air Mobile + Camera - Retaining image

I have a page that displays data about an object. At the top of the page is room for an icon, showing a picture of that object. Tapping this icon brings up a new page that allows the user to take a new picture, and save it as a temporary new picture for the object (not put in database, but should persist for the session)
Initial page:
private var source:Object = new Object();
protected function onInitialize():void {
source = navigator.poppedViewReturnedObject;
}
When setting source for the image later...
if (source != null) {
pic.source = source.object;
}
else {
pic.source = "no_picture_available_image.png";
}
2nd Page (User can take picture, and view new picture):
[Bindable]
private var imageSource:Object = null;
<s:Image id="pic" width="90%" height="75%" horizontalCenter="0" source="{imageSource}" />
After taking picture...
protected function mediaPromiseLoaded(evt:Event):void {
var loaderInfo:LoaderInfo = evt.target as LoaderInfo;
imageSource = loaderInfo.loader;
}
This does show the picture just taken correctly on this page.
To get back to old page, i use navigator.popView, and use:
override public function createReturnObject():Object {
return imageSource;
}
Unfortunately, it doesn't work. The imageSource isn't null when it is read from navigator.poppedViewReturnedObject, but no image is shown.
Does the LoaderInfo not persist after popping the view? Are the camera pics not automatically saved? I can't find answers to any of these questions, and I can't debug using the phone in my current environment.
After thinking about this a bit, don't return LoaderInfo.loader as the poppedViewReturnedObject. If I remember correctly, a DisplayObject can only be set as the source of one Image. Instead, return LoaderInfo.loader.content.bitmapData. That BitmapData should be the raw data used to display the image. This data can be used repeatedly to create images and can be set as the source of an Image.
Problem turned out to be in my first page's image declaration - I didn't set a width. Seemingly, the object being displayed couldn't handle not having a specified width.
Note that passing back the loader did work fine.

How to keep a list from scrolling on dataProvider refresh/update/change?

I have a simple list and a background refresh protocol.
When the list is scrolled down, the refresh scrolls it back to the top. I want to stop this.
I have tried catching the COLLECTION_CHANGE event and
validateNow(); // try to get the component to reset to the new data
list.ensureIndexIsVisible(previousIndex); // actually, I search for the previous data id in the IList, but that's not important
This fails because the list resets itself after the change (in DataGroup.commitProperties).
I hate to use a Timer, ENTER_FRAME, or callLater(), but I cannot seem to figure out a way.
The only other alternatives I can see is sub-classing the List so it can catch the dataProviderChanged event the DataGroup in the skin is throwing.
Any ideas?
Actually MUCH better solution to this is to extend DataGroup. You need to override this.
All the solutions here create a flicker as the scrollbar gets resetted to 0 and the it's set back to the previous value. That looks wrong. This solution works without any flicker and the best of all, you just change DataGroup to FixedDataGroup in your code and it works, no other changes in code are needed ;).
Enjoy guys.
public class FixedDataGroup extends spark.components.DataGroup
{
private var _dataProviderChanged:Boolean;
private var _lastScrollPosition:Number = 0;
public function FixedDataGroup()
{
super();
}
override public function set dataProvider(value:IList):void
{
if ( this.dataProvider != null && value != this.dataProvider )
{
dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE, onDataProviderChanged);
}
super.dataProvider = value;
if ( value != null )
{
value.addEventListener(CollectionEvent.COLLECTION_CHANGE, onDataProviderChanged);
}
}
override protected function commitProperties():void
{
var lastScrollPosition:Number = _lastScrollPosition;
super.commitProperties();
if ( _dataProviderChanged )
{
verticalScrollPosition = lastScrollPosition;
}
}
private function onDataProviderChanged(e:CollectionEvent):void
{
_dataProviderChanged = true;
invalidateProperties();
}
override public function set verticalScrollPosition(value:Number):void
{
super.verticalScrollPosition = value;
_lastScrollPosition = value;
}
}
I ll try to explain my approach...If you are still unsure let me know and I ll give you the source code as well.
1) Create a variable to store the current scroll position of the viewport.
2) Add Event listener for Event.CHANGE and MouseEvent.MOUSE_WHEEL on the scroller and update the variable created in step 1 with the current scroll position;
3) Add a event listener on your viewport for FlexEvent.UpdateComplete and set the scroll position to the variable stored.
In a nutshell, what we are doing is to have the scroll position stored in variable every time user interacts with it and when our viewport is updated (due to dataprovider change) we just set the scroll position we have stored previously in the variable.
I have faced this problem before and solved it by using a data proxy pattern with a matcher. Write a matcher for your collection that supports your list by updating only changed objects and by updating only attributes for existing objects. The goal is to avoid creation of new objects when your data source refreshes.
When you have new data for the list (after a refresh), loop through your list of new data objects, copying attributes from these objects into the objects in the collection supporting your list. Typically you will match the objects based on id. Any objects in the new list that did not exist in the old one get added. Your scroll position will normally not change and any selections are usually kept.
Here is an example.
for each(newObject:Object in newArrayValues){
var found:Boolean = false;
for each(oldObject:Object in oldArrayValues){
if(oldObject.id == newObject.id){
found = true;
oldObject.myAttribute = newObject.myAttribute;
oldObject.myAttribute2 = newObject.myAttribute2;
}
}
if(!found){
oldArrayValues.addItem(newObject);
}
}
My solution for this problem was targeting a specific situation, but it has the advantage of being very simple so perhaps you can draw something that fits your needs from it. Since I don't know exactly what issue you're trying to solve I'll give you a description of mine:
I had a List that was progressively loading data from the server. When the user scrolled down and the next batch of items would be added to the dataprovider, the scrollposition would jump back to the start.
The solution for this was as simple as stopping the propagation of the COLLECTION_CHANGE event so that the List wouldn't catch it.
myDataProvider.addEventListener(
CollectionEvent.COLLECTION_CHANGE, preventRefresh
);
private function preventRefresh(event:CollectionEvent):void {
event.stopImmediatePropagation();
}
You have to know that this effectively prevents a redraw of the List component, hence any added items would not be shown. This was not an issue for me since the items would be added at the end of the List (outside the viewport) and when the user would scroll, the List would automatically be redrawn and the new items would be displayed. Perhaps in your situation you can force the redraw if need be.
When all items had been loaded I could then remove the event listener and return to the normal behavior of the List component.

problem in itemClick

if (theData.hasOwnProperty("#id1")) {
var myObj:Hello = new Hello();
textArea.visible = false;
panel.addChild(myObj);
} else if (theData.hasOwnProperty("#id2")) {
textArea.visible = false;
var vijay:MCQ = new MCQ();
panel.addChild(vijay);
}
When i click on the next item, the previous window is still visible. How can i destroy myObj. I am not able to do it through removeChild.
If panel only ever contains one object, you could use the following before adding the new one:
panel.removeAllChildren();
If there are a known number of "static" children in panel, you could conditionally remove the additional ones:
while (panel.numChildren > EXPECTED) {
panel.removeChildAt(panel.numChildren - 1);
}
The best option would be to hold a reference to the object you added so that you can remove it explicitly using removeChild(). If these alternatives won't work, perhaps you could explain your constraints.

Resources