I am working on Android app that has to put frame and Logo on Image at a Time, The Problem I am facing is Frame is Coming from first Fragment and the Logo from Second Fragment. I am Setting the Frame on Bitmap image on imageView as well as Logo.
The Issue I am facing is, as I successfully Successfully Add Frame on Bitmap image, and I try to also set Logo on Bitmap image it Remove the Frame and Set the Logo on Bitmap and vice versa..
What I really want is Frame and Logo are set on Bitmap at a time...
Here, where Logo Coming From First Fragment Adapter to main Activity via Method..
holder.iconslogo.setOnClickListener {
when (charItemlogo.itemsidlogo) {
1 -> {
var btmp= arrayList[0].iconslogo
(context as MakeStylishActivity).setLogos(btmp)
}
Here the Frame is Coming from Frame Fragment to Main Activity
holder.iconsframe.setOnClickListener {
when (charItemFrame.itemsidframe) {
1 -> {
var btmp= arrayList[0].iconsframe
(context as MakeStylishActivity).setFrames(btmp)
}}
This is Main Activity that is Setting the Logo and Frame to Bitmap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_make_stylish)
val byteArray = intent.getByteArrayExtra("pictures")
bmp = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)!!
img_bitmap1.setImageBitmap(bmp)
stringqrcontent= intent.getStringExtra("qrcontent")
bottom_nav_viewstyle.setOnNavigationItemSelectedListener {
when (it.itemId) {
R.id.action_default -> {
true
}
R.id.action_colors -> {
ShowFColorFragment()
true
}
R.id.action_logos -> {
ShowLogoFragment()
true
}
R.id.action_frames -> {
FunctionAddFrames();
true
}
R.id.action_patterns -> {
true
}
else -> false
}
}
}
fun setLogos(btmp: Bitmap?) {
//img_bitmap1.setImageBitmap(btmp)
PutLogoOnQRBitmaps(btmp, bmp!!)
}
fun setFrames(btmp: Bitmap?) {
// img_bitmap1.setImageBitmap(btmp)
//addWhiteBorder(bmp!!,10)
PutFrameImages(btmp, bmp!!)
}
//Combine Frame Behind QR Code
fun PutFrameImages(frame: Bitmap?, image: Bitmap): Bitmap? {
var cs: Bitmap? = null
var rs: Bitmap? = null
rs = Bitmap.createScaledBitmap(frame!!, image.width, image.height, true)
cs = Bitmap.createBitmap(rs.width, rs.height, Bitmap.Config.RGB_565)
val comboImage = Canvas(cs)
comboImage.drawBitmap(image, 0F, 0F, null)
comboImage.drawBitmap(rs, 0F, 0F, null)
if (rs != null) {
rs.recycle()
rs = null
}
// Runtime.getRuntime().gc()
img_bitmap1.setImageBitmap(cs!!)
return cs
}
//Put Logo on QR Code
fun PutLogoOnQRBitmaps(logo: Bitmap?, qrcode: Bitmap): Bitmap? {
val combined = Bitmap.createBitmap(qrcode.width, qrcode.height, qrcode.config)
val canvas = Canvas(combined)
val canvasWidth = canvas.width
val canvasHeight = canvas.height
canvas.drawBitmap(qrcode, Matrix(), null)
val resizeLogo = Bitmap.createScaledBitmap(logo!!, canvasWidth / 5, canvasHeight / 5, true)
val centreX = (canvasWidth - resizeLogo.width) / 2
val centreY = (canvasHeight - resizeLogo.height) / 2
canvas.drawBitmap(resizeLogo, centreX.toFloat(), centreY.toFloat(), null)
img_bitmap1.setImageBitmap(combined)
return combined
}}
I see a few things here that aren't considered a good idea, but the most important is that the size of an Intent has a limit (very small, that is) and is not designed to pass large amounts of data.
What I would do
Regardless of your simplistic Architecture (no usage of ViewModels, or correct separation of concerns, and a few other SOLID principles ignored here...), I would not pass the image via intent. Instead, I would save the image to the filesystem (temporarily), pass the "path" as a string to the next activity, and have said activity open the file and create the Bitmap from the filesystem.
This means you no longer need to worry about going overboard with the image size/intent size, and that your two activities decouple a little bit. You can now pass any path to a bitmap there and the other activity will pick it up, regardless of where it came from.
A second improvement would be to delegate all this (image storing, passing, retrieving, etc.) to a ViewModel + UseCase (and/or Repository), in which case you'd be further decoupling your code. For this, and much more, the starting point would be getting started with Android Jetpack; I recommend at least trying to leverage a ViewModel in your architecture.
You'd also want to be careful when creating bitmaps out of nowhere, you can easily run out of memory by doing what you're doing; you should take a look at Android's official documentation about handling large bitmaps.
Related
I have a map which has a GraphicsOverlay with various points. I have given the user the ability to select a subset of the points by drawing a polygon using the SketchEditor. How can I determine which points have been selected?
Here is a subset of the code to set up the map:
private GraphicsOverlay graphicsOverlayLow;
// Graphics overlay to host sketch graphics
private GraphicsOverlay _sketchOverlay;
var symbolLow = new SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Colors.Green, 10d);
graphicsOverlayLow = new GraphicsOverlay() { Renderer = new SimpleRenderer(symbolLow) };
foreach (var graphic in graphicListLow) // graphicListLow is a List of Points
graphicsOverlayLow.Graphics.Add(graphic);
MyMapView.GraphicsOverlays = new GraphicsOverlayCollection();
MyMapView.GraphicsOverlays.Add(graphicsOverlayLow);
_sketchOverlay = new GraphicsOverlay();
MyMapView.GraphicsOverlays.Add(_sketchOverlay);
I have two buttons, one for starting the drawing of the polygon and one to click when done (this follows the esri example for the SketchEditor). The code for starting is as follows:
private async void SelectButton_Click(object sender, RoutedEventArgs e)
{
try
{
// Let the user draw on the map view using the chosen sketch mode
SketchCreationMode creationMode = SketchCreationMode.Polygon;
Esri.ArcGISRuntime.Geometry.Geometry geometry = await MyMapView.SketchEditor.StartAsync(creationMode, true);
// Create and add a graphic from the geometry the user drew
Graphic graphic = CreateGraphic(geometry);
_sketchOverlay.Graphics.Add(graphic);
}
catch (TaskCanceledException)
{
// Ignore ... let the user cancel drawing
}
catch (Exception ex)
{
// Report exceptions
MessageBox.Show("Error drawing graphic shape: " + ex.Message);
}
}
private Graphic CreateGraphic(Esri.ArcGISRuntime.Geometry.Geometry geometry)
{
// Create a graphic to display the specified geometry
Symbol symbol = null;
switch (geometry.GeometryType)
{
// Symbolize with a fill symbol
case GeometryType.Envelope:
case GeometryType.Polygon:
{
symbol = new SimpleFillSymbol()
{
Color = Colors.Red,
Style = SimpleFillSymbolStyle.Solid,
};
break;
}
Here is the handler for the routine that is called when the user clicks the button signaling that they are done drawing the polygon. This is where I want to determine which points have been selected.
private void CompleteButton_Click(object sender, RoutedEventArgs e)
{
// Cancel execution of the sketch task if it is already active
if (MyMapView.SketchEditor.CancelCommand.CanExecute(null))
{
MyMapView.SketchEditor.CancelCommand.Execute(null);
}
}
Note that I am using the 100.4 SDK for WPF.
This can be accomplished by a spatial query. You will have to use the geometry returned by the sketch editor and use it to perform a spatial query on the layer(s) using geometry filter.
Esri.ArcGISRuntime.Geometry.Geometry geometry = await MyMapView.SketchEditor.StartAsync(creationMode, true);
var queryparameters = new QueryParameters()
{
Geometry = geometry,
SpatialRelationship = SpatialRelationship.Intersects
};
await layer.SelectFeaturesAsync(queryparameters, Esri.ArcGISRuntime.Mapping.SelectionMode.New);
You can use GeometryEngine.Intersects method to check when point graphics intersect, touch, cross the selection polygon.
https://community.esri.com/message/826699-re-determine-points-that-are-under-a-sketchoverlay-in-a-map?commentID=826699#comment-826699
When scrolling down, Groove moves the header up, outside of the viewable area just like a regular ListView header. When scrolling back up it moves the header back down into the viewable area right away, regardless of the current vertical scroll offset. The header seems to be part of the ListView content because the scrollbar includes the header.
How can this be implemented in a Windows 10 UWP app?
You can do this by utilizing the ListView's internal ScrollViewer's ViewChanged event.
First you got to obtain the internal ScrollViewer. This is the simplest version, but you might want to use one of the many VisualTreeHelper Extensions around to do it safer and easier:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var border = VisualTreeHelper.GetChild(MyListView, 0);
var scrollviewer = VisualTreeHelper.GetChild(border, 0) as ScrollViewer;
scrollviewer.ViewChanged += Scrollviewer_ViewChanged;
}
In the EventHandler, you can then change the visibility of your header depending on the scroll direction.
private void Scrollviewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
var sv = sender as ScrollViewer;
if (sv.VerticalOffset > _lastVerticalOffset)
{
MyHeader.Visibility = Visibility.Collapsed;
}
else
{
MyHeader.Visibility = Visibility.Visible;
}
}
This is the basic idea. You might wan't to add some smooth animations instead of just changing the visibility.
After looking around a bit and experimentation I can now answer my own question.
One can use an expression based composition animation to adjust the Y offset of the the header in relation to scrolling. The idea is based on this answer. I prepared a complete working example on GitHub.
The animation is prepared in the SizeChanged event of the ListView:
ScrollViewer scrollViewer = null;
private double previousVerticalScrollOffset = 0.0;
private CompositionPropertySet scrollProperties;
private CompositionPropertySet animationProperties;
SizeChanged += (sender, args) =>
{
if (scrollProperties == null)
scrollProperties = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scrollViewer);
var compositor = scrollProperties.Compositor;
if (animationProperties == null)
{
animationProperties = compositor.CreatePropertySet();
animationProperties.InsertScalar("OffsetY", 0.0f);
}
var expressionAnimation = compositor.CreateExpressionAnimation("animationProperties.OffsetY - ScrollingProperties.Translation.Y");
expressionAnimation.SetReferenceParameter("ScrollingProperties", scrollProperties);
expressionAnimation.SetReferenceParameter("animationProperties", animationProperties);
var headerVisual = ElementCompositionPreview.GetElementVisual((UIElement)Header);
headerVisual.StartAnimation("Offset.Y", expressionAnimation);
};
The OffsetY variable in the animationProperties will drive the animation of the OffsetY property of the header. The OffsetY variable is updated in the ViewChanged event of the ScrollViewer:
scrollViewer.ViewChanged += (sender, args) =>
{
float oldOffsetY = 0.0f;
animationProperties.TryGetScalar("OffsetY", out oldOffsetY);
var delta = scrollViewer.VerticalOffset - previousVerticalScrollOffset;
previousVerticalScrollOffset = scrollViewer.VerticalOffset;
var newOffsetY = oldOffsetY - (float)delta;
// Keep values within negativ header size and 0
FrameworkElement header = (FrameworkElement)Header;
newOffsetY = Math.Max((float)-header.ActualHeight, newOffsetY);
newOffsetY = Math.Min(0, newOffsetY);
if (oldOffsetY != newOffsetY)
animationProperties.InsertScalar("OffsetY", newOffsetY);
};
While this does animate correctly, the header is not stacked on top of the ListView items. Therefore the final piece to the puzzle is to decrease the ZIndex of the ItemsPanelTemplate of the ListView:
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Canvas.ZIndex="-1" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
Which gives this as a result:
I have two 3d buttons in my scene and when I gaze into any of the buttons it will invoke OnPointerEnter callback and saving the object the pointer gazed to.
Upon pressing Fire1 on the Gamepad I apply materials taken from Resources folder.
My problem started when I gazed into the second button, and pressing Fire1 button will awkwardly changed both buttons at the same time.
This is the script I attached to both of the buttons
using UnityEngine;
using UnityEngine.EventSystems;
using Vuforia;
using System.Collections;
public class TriggerMethods : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
Material _mat;
GameObject targetObject;
Renderer rend;
int i = 0;
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("Fire1"))
TukarMat();
}
public void OnPointerEnter(PointerEventData eventData)
{
targetObject = ExecuteEvents.GetEventHandler<IPointerEnterHandler>(eventData.pointerEnter);
}
public void OnPointerExit(PointerEventData eventData)
{
targetObject = null;
}
public void TukarMat()
{
Debug.Log("Value i = " + i);
if (i == 0)
{
ApplyTexture(i);
i++;
}
else if (i == 1)
{
ApplyTexture(i);
i++;
}
else if (i == 2)
{
ApplyTexture(i);
i = 0;
}
}
void ApplyTexture(int i)
{
rend = targetObject.GetComponent<Renderer>();
rend.enabled = true;
switch (i)
{
case 0:
_mat = Resources.Load("Balut", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
case 1:
_mat = Resources.Load("Khasiat", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
case 2:
_mat = Resources.Load("Alma", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
default:
break;
}
}
I sensed some logic error and tried making another class to only manage object the pointer gazed to but I was getting more confused.
Hope getting some helps
Thank you
TukarMat() is beeing called on both buttons when you press Fire1. If targetObject is really becoming null this should give an error on first button since it's trying to get component from a null object. Else, it'll change both as you said. Make sure OnPointerExit is beeing called.
Also, it seems you are changing the shared material.
The documentation suggests:
Modifying sharedMaterial will change the appearance of all objects using this material, and change material settings that are stored in the project too.
It is not recommended to modify materials returned by sharedMaterial. If you want to modify the material of a renderer use material instead.
So, try changing the material property instead of sharedMaterial since it'll change the material for that object only.
I have a myNode: scalafx.scene.Node that I'd like to turn into a bitmap so that I can save it to disk, compare it to other bitmaps, etc. I think the correct code to turn it into a scalafx.scene.image.WritableImage is
val writableImg = new Scene { root = new Group(myNode) }.snapshot(null)
and then I should be able to turn that into a java.awt.image.RenderedImage with
val renderedImg = SwingFXUtils.fromFXImage(writableImg, null)
The problem, as you've probably realized, is that I have to run the code to get wrImg on the ScalaFX thread. There's a question here that explains how to return a value, but I'm not having any luck translating that to Scala. I tried this:
lazy val writableImg: WritableImage = {
val wrImg = new FutureTask(new Callable[WritableImage]() {
override def call(): WritableImage = {
new Scene { root = new Group(myNode) }.snapshot(null)
}
})
Platform.runLater(wrImg)
wrImg.get()
}
but the code just hangs and never completes. Can anyone provide an idiomatic Scala version and/or tell me why the code never returns?
If you just want to save the image to disk you can simply do it on the same thread avoiding complication passing the image around. Something like this will work:
Platform.runLater {
val node = new Circle {
centerX = 200
centerY = 200
radius = 50
stroke = Color.BROWN
strokeWidth = 2
fill = Color.DARKKHAKI
}
val jfxImage = node.snapshot(new SnapshotParameters(), null)
val bufferedImage = SwingFXUtils.fromFXImage(jfxImage, null)
val file = new File("./snapshot.png")
println("Writing snapshot to: " + file.getAbsolutePath)
javax.imageio.ImageIO.write(bufferedImage, "png", file)
()
}
The empty () to have closure returning Unit, so ScalaFX Platform.runLater is happy.
Update:
If you want to have a value from Platform.runLater the approach you suggested in your question should be in general fine. However, you want to make sure that you do not block the FX Application Thread. If you call Platform.runLater on FX Application Thread you will lock out, so you may want to have something like this
def makeSnapshot() = myNode.snapshot(new SnapshotParameters(), null)
val writableImg = if (Platform.isFxApplicationThread) {
makeSnapshot()
} else {
val futureTask = new FutureTask(new Callable[WritableImage]() {
override def call(): WritableImage = makeSnapshot()
})
Platform.runLater(futureTask)
futureTask.get()
}
In fact it is a wild mix of technologies, but the answer to my question (I think) is closest to Direct3D 9. I am hooking to an arbitrary D3D9 applications, in most cases it is a game, and injecting my own code to mofify the behavior of the EndScene function. The backbuffer is copied into a surface which is set to point to a bitmap in a push source DirectShow filter. The filter samples the bitmaps at 25 fps and streams the video into an .avi file. There is a text overlay shown across the game's screnn telling the user about a hot key combination that is supposed to stop gameplay capture, but this overlay is not supposed to show up in the recoreded video. Everything works fast and beautiful except for one annoying fact. On a random occasion, a frame with the text overaly makes its way into the recoreded video. This is not a really desired artefact, the end user only wants to see his gameplay in the video and nothing else. I'd love to hear if anyone can share ideas of why this is happening. Here is the source code for the EndScene hook:
using System;
using SlimDX;
using SlimDX.Direct3D9;
using System.Diagnostics;
using DirectShowLib;
using System.Runtime.InteropServices;
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[System.Security.SuppressUnmanagedCodeSecurity]
[Guid("EA2829B9-F644-4341-B3CF-82FF92FD7C20")]
public interface IScene
{
unsafe int PassMemoryPtr(void* ptr, bool noheaders);
int SetBITMAPINFO([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]byte[] ptr, bool noheaders);
}
public class Class1
{
object _lockRenderTarget = new object();
public string StatusMess { get; set; }
Surface _renderTarget;
//points to image bytes
unsafe void* bytesptr;
//used to store headers AND image bytes
byte[] bytes;
IFilterGraph2 ifg2;
ICaptureGraphBuilder2 icgb2;
IBaseFilter push;
IBaseFilter compressor;
IScene scene;
IBaseFilter mux;
IFileSinkFilter sink;
IMediaControl media;
bool NeedRunGraphInit = true;
bool NeedRunGraphClean = true;
DataStream s;
DataRectangle dr;
unsafe int EndSceneHook(IntPtr devicePtr)
{
int hr;
using (Device device = Device.FromPointer(devicePtr))
{
try
{
lock (_lockRenderTarget)
{
bool TimeToGrabFrame = false;
//....
//logic based on elapsed milliseconds deciding if it is time to grab another frame
if (TimeToGrabFrame)
{
//First ensure we have a Surface to render target data into
//called only once
if (_renderTarget == null)
{
//Create offscreen surface to use as copy of render target data
using (SwapChain sc = device.GetSwapChain(0))
{
//Att: created in system memory, not in video memory
_renderTarget = Surface.CreateOffscreenPlain(device, sc.PresentParameters.BackBufferWidth, sc.PresentParameters.BackBufferHeight, sc.PresentParameters.BackBufferFormat, Pool.SystemMemory);
} //end using
} // end if
using (Surface backBuffer = device.GetBackBuffer(0, 0))
{
//The following line is where main action takes place:
//Direct3D 9 back buffer gets copied to Surface _renderTarget,
//which has been connected by references to DirectShow's
//bitmap capture filter
//Inside the filter ( code not shown in this listing) the bitmap is periodically
//scanned to create a streaming video.
device.GetRenderTargetData(backBuffer, _renderTarget);
if (NeedRunGraphInit) //ran only once
{
ifg2 = (IFilterGraph2)new FilterGraph();
icgb2 = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
icgb2.SetFiltergraph(ifg2);
push = (IBaseFilter) new PushSourceFilter();
scene = (IScene)push;
//this way we get bitmapfile and bitmapinfo headers
//ToStream is slow, but run it only once to get the headers
s = Surface.ToStream(_renderTarget, ImageFileFormat.Bmp);
bytes = new byte[s.Length];
s.Read(bytes, 0, (int)s.Length);
hr = scene.SetBITMAPINFO(bytes, false);
//we just supplied the header to the PushSource
//filter. Let's pass reference to
//just image bytes from LockRectangle
dr = _renderTarget.LockRectangle(LockFlags.None);
s = dr.Data;
Result r = _renderTarget.UnlockRectangle();
bytesptr = s.DataPointer.ToPointer();
hr = scene.PassMemoryPtr(bytesptr, true);
//continue building graph
ifg2.AddFilter(push, "MyPushSource");
icgb2.SetOutputFileName(MediaSubType.Avi, "C:\foo.avi", out mux, out sink);
icgb2.RenderStream(null, null, push, null, mux);
media = (IMediaControl)ifg2;
media.Run();
NeedRunGraphInit = false;
NeedRunGraphClean = true;
StatusMess = "now capturing, press shift-F11 to stop";
} //end if
} // end using backbuffer
} // end if Time to grab frame
} //end lock
} // end try
//It is usually thrown when the user makes game window inactive
//or it is thrown deliberately when time is up, or the user pressed F11 and
//it resulted in stopping a capture.
//If it is thrown for another reason, it is still a good
//idea to stop recording and free the graph
catch (Exception ex)
{
//..
//stop the DirectShow graph and cleanup
} // end catch
//draw overlay
using (SlimDX.Direct3D9.Font font = new SlimDX.Direct3D9.Font(device, new System.Drawing.Font("Times New Roman", 26.0f, FontStyle.Bold)))
{
font.DrawString(null, StatusMess, 20, 100, System.Drawing.Color.FromArgb(255, 255, 255, 255));
}
return device.EndScene().Code;
} // end using device
} //end EndSceneHook
As it happens sometimes, I finally found an answer to this question myself, if anyone is interested. It turned out that backbuffer in some Direct3D9 apps is not necessarily refreshed each time the hooked EndScene is called. Hence, occasionally the backbuffer with the text overlay from the previous EndScene hook call was passed to the DirectShow source filter responsible for collecting input frames. I started stamping each frame with a tiny 3 pixel overlay with known RGB values and checking if this dummy overlay was still present before passing the frame to the DirectShow filter. If the overlay was there, the previously cached frame was passed instead of the current one. This approach effectively removed the text overlay from the video recorded in the DirectShow graph.