locked
UWP Setting Custom Icon in MapRenderer RRS feed

  • Question

  • User395103 posted

    I'm trying to draw text on a custom image and set it as a map icon. I've done this with Android and IoS; however, in UWP I've encountered some issues. The UWP renderer mapIcon.Image requires a uri, stream or file. I've tried several different ways. The following is attempting to return a stream:

    public async Task WriteTextOnImage(string uri, string text, Windows.UI.Color color) { Uri uriSource = new Uri(uri); using (CanvasBitmap bitmap = await CanvasBitmap.LoadAsync(CanvasDevice.GetSharedDevice(), uriSource).AsTask().ConfigureAwait(false)) using (CanvasRenderTarget target = new CanvasRenderTarget(CanvasDevice.GetSharedDevice(), (float)bitmap.Size.Width, (float)bitmap.Size.Height, bitmap.Dpi)) { using (var ds = target.CreateDrawingSession()) { ds.Clear(Colors.White); ds.DrawImage(bitmap); ds.DrawText(text, new System.Numerics.Vector2(150, 150), color); } byte[] arr = target.GetPixelBytes(); MemoryStream stream = new MemoryStream(arr); return stream.AsRandomAccessStream(); } }

    The image is not appearing. I'd appreciate any ideas on this issue.

    Wednesday, July 1, 2020 11:52 PM

Answers

  • User41045 posted

    @oregon39 Can you contain this in a small sample reproducing the said issue? I would love to look into it. Thanks! If needed, you can open a case with us mentioning me so that I look into it. https://aka.ms/xamarinsupport

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Thursday, July 16, 2020 9:55 AM

All replies

  • User41045 posted

    @oregon39 Can you contain this in a small sample reproducing the said issue? I would love to look into it. Thanks! If needed, you can open a case with us mentioning me so that I look into it. https://aka.ms/xamarinsupport

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Thursday, July 16, 2020 9:55 AM
  • User395103 posted

    @AnubhavRanjan Yes, basically all I need is to get an image from the Assets folder draw the id numbers on that image and set that image as the mapIcon image in UWP.

    This was achieved in the Android map renderer with the following code:

    public static BitmapDrawable WriteOnDrawable(Context context, int drawableId, String text, Color color) { Bitmap bitmap = BitmapFactory.DecodeResource(context.Resources, drawableId) .Copy(Bitmap.Config.Argb8888, true);

            Paint paint = new Paint();
            paint.SetStyle(Paint.Style.Fill);
            paint.Color = color;
            paint.TextSize = 50;
            paint.TextAlign = Paint.Align.Center;
            paint.AntiAlias = true;
            Canvas canvas = new Canvas(bitmap);
    
            canvas.DrawText(text, bitmap.Width / 2 //x position
                , bitmap.Height / 2 // y position
                , paint);
            return new BitmapDrawable(context.Resources, bitmap);
        }
    

    This was achieved in the IOS map render with the following code:

    public static UIImage DrawText(UIImage uiImage, string sText, UIColor textColor, int iFontSize) { nfloat fWidth = uiImage.Size.Width; nfloat fHeight = uiImage.Size.Height;

            CGColorSpace colorSpace = CGColorSpace.CreateDeviceRGB();
    
            using (CGBitmapContext ctx = new CGBitmapContext(IntPtr.Zero, (nint)fWidth, (nint)fHeight, 8, 4 * (nint)fWidth, CGColorSpace.CreateDeviceRGB(), CGImageAlphaInfo.PremultipliedFirst))
            {
                ctx.DrawImage(new CGRect(0, 0, (double)fWidth, (double)fHeight), uiImage.CGImage);
    
                ctx.SelectFont("Helvetica", iFontSize, CGTextEncoding.MacRoman);
    
                float start, end, textWidth;
    
                start = (float)ctx.TextPosition.X;
                ctx.SetTextDrawingMode(CGTextDrawingMode.Invisible);
                ctx.ShowText(sText);
                end = (float)ctx.TextPosition.X;
                textWidth = end - start;
    
                nfloat fRed;
                nfloat fGreen;
                nfloat fBlue;
                nfloat fAlpha;
                textColor.GetRGBA(out fRed, out fGreen, out fBlue, out fAlpha);
                ctx.SetFillColor(fRed, fGreen, fBlue, fAlpha);
                ctx.SetTextDrawingMode(CGTextDrawingMode.Fill);
                ctx.ShowTextAtPoint(10, 15, sText);
    
                return UIImage.FromImage(ctx.ToImage());
            }
        }
    

    The UWP code is in the initial post. I've tried it a number of ways (WriteableBitmap, Win2D etc), and it wouldn't work. In UWP, the image appeared possibly not to load from the assets folder, except when it was loaded and set directly without any editing using mapIcon.Image = RandomAccessStreamReference.CreateFromUri(uri). Either that or it wouldn't recognize the format. In UWP, we have to set the icon image with a RandomAccessStream, and it looks like System.Drawing is incompatible. When you say sample, do you mean a sample app?

    Monday, July 20, 2020 9:44 PM
  • User395103 posted

    @AnubhavRanjan resolved this issue with the following changes to the code:

    //Method to draw text on the custom map icon image public async Task WriteTextOnImage(string uri, string text, Xamarin.Forms.Color textColor) { Uri uriSource = new Uri(uri);

            StorageFile inputFile = await StorageFile.GetFileFromApplicationUriAsync(uriSource);
            BitmapDecoder imagedecoder;
            using (var imagestream = await inputFile.OpenAsync(FileAccessMode.Read))
            {
                imagedecoder = await BitmapDecoder.CreateAsync(imagestream);
            }
            CanvasDevice device = CanvasDevice.GetSharedDevice();
    
            // Need to set the New Canvas height, which should be bigger than the Image Size to write the Text
            CanvasRenderTarget renderTarget = new CanvasRenderTarget(device, imagedecoder.PixelWidth + 120, imagedecoder.PixelHeight + 50, 96);
            using (var ds = renderTarget.CreateDrawingSession())
            {
                ds.Clear(Colors.Transparent);
                CanvasBitmap image = await CanvasBitmap.LoadAsync(device, inputFile.Path, 96);
    
                // Play around here to adjust the Image position. I have divided it by 3 to get the mid portiona as the starting point for the Image
                ds.DrawImage(image, new System.Numerics.Vector2((imagedecoder.PixelWidth + 120)/3, 10));
    
                // You can add here some position which would place the Text next to the Image at the bottom
                ds.DrawText(text, new System.Numerics.Vector2(67, 20), Colors.Black);
            }
            InMemoryRandomAccessStream str = new InMemoryRandomAccessStream();
            await renderTarget.SaveAsync(str, CanvasBitmapFileFormat.Png);
            await str.FlushAsync();
            return str;
    

    }

    I added some minor adjustments to accommodate my particular requirements, changing the ds.Clear color to transparent to remove the white border and adjusting the DrawText position to overlay the text on the image.

                ds.Clear(Colors.Transparent);
                ...
                ds.DrawText(text, new System.Numerics.Vector2(67, 20), Colors.Black);
    

    Thanks Anubhav @AnubhavRanjan !

    Friday, August 14, 2020 11:37 PM
  • User41045 posted

    @oregon39 Glad to be of service :)

    Thursday, August 20, 2020 8:09 AM
  • User396349 posted

    Nice information.

    Thursday, August 20, 2020 2:42 PM