I created this function on DELPHI XE5, it's work very well.
You create a single image with all your icons, you load this image in a TBitmap (IDE in my exemple), and you create a lot of small TRECTANGLE in the form.
During the onCreate, i call Mapping method to set the background of each Trectangle.
BUT IN DELPHI XE8, It's not working on ANDROID
This is the mapping function.
Procedure TForm1.Mapping(Const Obj:TRectangle;Const ofx,ofy:Integer);
Var
Arect : TRectF;
FBitmap : TBitmap;
Begin
Arect:=TRectF.Create(0,0,Obj.Width,Obj.Height);
Obj.Stroke.Kind := TBrushKind.None;
Obj.Fill.Kind := TBrushKind.Bitmap;
Obj.Fill.Bitmap.WrapMode := TWrapMode.TileOriginal;
//Create Crop Bitmap
FBitmap:=TBitmap.create( Round(Obj.Width), Round(Obj.Height)) ;
Try
FBitmap.Canvas.BeginScene();
FBitmap.Canvas.ClearRect(ARect,StringToAlphaColor('$00000000'));
FBitmap.Canvas.DrawBitmap(IDE,
TRectF.Create(ofx,ofy,ofx+Obj.Width,ofy+Obj.Height),
Arect, 100) ;
FBitmap.Canvas.EndScene;
//Assign Crop image to Rectangle object
Obj.Fill.Bitmap.Bitmap.Assign(FBitmap);
Finally
FBitmap.Free;
End;
End;
The picture (delphiXE.png) is deploying in "asset/internal", and opening in oncreate.
procedure TForm1.FormCreate(Sender: TObject);
Var Mfile:String;
begin
IDE := TBitmap.Create(ClientWidth,ClientHeight);
{$IFDEF ANDROID}
Mfile:=TPath.Combine(TPath.GetDocumentsPath, 'delphiXE.png');
{$ELSE}
Mfile:=TPath.Combine(ExtractFilePath(ParamStr(0)), 'delphiXE.png');
{$ENDIF}
If FileExists(MFile)
Then begin
IDE.LoadFromFile(MFile);
Mapping(Logo,0,0);
End
Else ShowMessage('NO '+MFile);
end;
In Windows, everythings works fine, but no in Android, but if i add a simple onclick event with Mapping(Logo,0,0); call, the mapping work, but i need to click first on the grey Trectangle to set it background
Any idea ?
UPDATED :
Using a Timer, the mapping function is working and the trectangle have the good picture, but if i switch to another application and go back to my application, the picture disappear and the Trectangle turn back to solid gray.
That mean that the internal paint of the TRectangle is not updated. Why ?
Thanks for you help, i find a solution and the origin of the problem.
The fact that the picture in the Trectangle was not persistent help me.
//Assign Crop image to Rectangle objects
Obj.Fill.Bitmap.Bitmap.Assign(FBitmap);
is no longer working like it worked on DELPHI XE5 on Android. It 's copy the bitmap but not on a persistent way. This is why it's working with onclick, but disappear when i switch application.
So, I changed this line by
// Force the size of TRectangle internal Bitmap
Obj.Fill.Bitmap.Bitmap.SetSize(FBitmap.Width,FBitmap.Height);
// Copy the crop bitmap in TRectangle Internal Bitmap
Obj.Fill.Bitmap.Bitmap.CopyFromBitmap(FBitmap);
and in my TEST PROJECT, it's working.
I hope this can help another developper.
Did you try to test the return of Canvas.BeginScene ? If it's fail, that mean that the graphic context system does not succeded to init. (yes, that work on XE5 and perhaps other, go figure, they perhaps change the init sequence somewhere)
In Creation phase, certainly not abnormal.
you should do this somewhere else.
Related
I created a multi-device firemonkey form which included one viewport3d ,one Cube and one camera.I want to make a calloutpanel exactly on the top of the cube in the form with camera projection.
In Firemonkey, we can use worldtoscreen() to convert absolute position properties to screen position. The resulting point is correct for the Windows platform but unfortunately in Android platform the point is not accurate.
Procedure TForm50.Cube1Render(Sender: TObject; Context: TContext3D);
var p:TPoint3D;
x,y:real;
Begin
p:=context.worldtoscreen(TProjection.Camera, Cube1.AbsolutePosition);
Label1.Text:=(p.x.tostring+' '+P.Y.ToString);
x:=p.X-(CalloutPanel1.Width /2);
y:=p.y-CalloutPanel1.Height;
CalloutPanel1.Position.X:=x;
CalloutPanel1.Position.y:=y;
End;
update
Base of this post WorldToScreen function C# my Cube1.AbsolutePosition must change base on size of monitor(android screen) to place CalloutPanel1 over cube1.
Who can apply this code on Delphi?
Since Android phones are released in many different screen resolutions and my user interface is "skinned" using multiple TImage components, I've hit a major development issue, I must scale each of my images relative to the device's screen resolution.
For some reason which I can not understand, under Android, TImage is interpolated using a really low quality scaler (possibly nearest-neighbor), resulting in a very low quality image display (this happens even when the screen scale is taken into consideration and the form's quality is set the high).
Due to this, it means I can either pre-scale and include multiple different resolutions of the same image, hoping that it will look 'close enough' (and bloating my app), or I can use a software algorithm to scale the images in real-time and then cache the result for later runs.
I choose the second option, using a software bicubic scaler, but the problem is that my app has so many image resources, it can take 18 seconds to load the first time on a high end mobile device.
I'm aware it may be possible to do what I need in hardware using OpenGL, but I haven't been able to find a clear/clean example of how this can be done in Delphi for Android. And even if it can be done without having to re-code the entire UI for OpenGL.
Is there something I'm missing design wise?
Is a fix from Embarcadero my only chance?
It took me a while to lock this down, but here is Android native code that will scale the image in high quality much faster than any pure software solution I could find and optimize:
uses Androidapi.JNI.Media, Androidapi.JNI.GraphicsContentViewText, Androidapi.JNIBridge, FMX.Surfaces, FMX.Helpers.Android;
procedure AndroidResizeBitmap(srcBitmap,dstBitmap : TBitmap);
var
sJBitmap : JBitmap;
ScaledBitmap : JBitmap;
sSurface : TBitmapSurface;
begin
sSurface := TBitmapSurface.Create;
sSurface.Assign(srcBitmap);
sJBitmap := TJBitmap.JavaClass.createBitmap(sSurface.Width, sSurface.Height,TJBitmap_Config.JavaClass.ARGB_8888);
SurfaceToJBitmap(sSurface, sJBitmap);
ScaledBitmap := TJBitmap.JavaClass.createScaledBitmap(sJBitmap, dstBitmap.Width, dstBitmap.Height, True);
sJBitmap := nil;
JBitmapToSurface(ScaledBitmap,sSurface);
ScaledBitmap := nil;
dstBitmap.Assign(sSurface);
sSurface.Free;
sSurface := nil;
end;
I work on delphi xe7 with firemonkey and testing on android.
When i work on a device that have a normal resolution of 1 (scene.scale=1) then component like TRoundRect, TPie, etc produce ugly result because they are not anti-aliased (or the anti-alias is not visible by humain eyes). if i work on high definition device (scene.scale=2 for exemple), then it's not really a big problem because high definition compensate the problem.
So first question, is their any way to make theses component produce anti-alias graphic ?
also how to draw directly on the canvas an anti-aliased disque (with an image in it) ?
Now when i work on bitmap and on canvas, i notice that the FillText produce anti-aliasing result. this is in someway good for what i want previously (for disque) but a little disaster for text because it's make them "blur".
it's even worse when i do first Mybitmap.canvas.FillText => produce a little of antialias and then later i do MyPaintBox.canvas.DrawBitmap(MyBitmap) it's will add AGAIN more anti-alias ! the text will be very blur at the end :( it's sound crazy for me that
doing canvas.drawBitmap without any distorsion in the srcRec and destRect not copy the exact pixel from bitmap to the canvas :(
so it's their a way to :
Call canvas.FillText without any anti-alias or to configure the level of anti-alias.
Call canvas.DrawBitmap without any anti-alias at all ! pixel perfect copy from bitmap to the canvas
thanks by advance for your help !
some solutions i found todays (and theirs problems) :
I found a way how to make all the visual controls (troundrect, etc) with anti-aliasing : set Tform.quality to highQuality !
But now i m facing another very strong problem that i can not understand :(
maybe a bug in delphi so if someone can look at it i will be very thanks to him ...
when you need a canvas for the form it's created via
constructor TCanvasGpu.CreateFromWindow(...AQuality: TCanvasQuality)
and here the quality is taken from the MyForm.quality
now the problem is with TBITMAP :( same as previous when we need the canvas for the bitmap it's created via
constructor TCanvasGpu.CreateFromBitmap(....AQuality: TCanvasQuality=TCanvasQuality.SystemDefault)
but here the problem their is NO PROPERTY at all in the bitmap to setup the quality of the canvas :(
so i try this solution :
aBitmap := Tbitmap.Create(w,h);
try
aCanvas := TCanvasManager.CreateFromBitmap(ABitmap, TCanvasQuality.HighQuality);
Try
aCanvas.BeginScene;
try
aCanvas.Fill.Kind := TbrushKind.solid;
acanvas.Fill.Color := $ff000000;
aCanvas.FillRect(TRectF.Create(0, 0, w, h), w / 2, h / 2, ALLcorners, 1);
finally
aCanvas.EndScene;
end;
Finally
aCanvas.Free;
End;
....
and i was thinking i will have now on my bitmap the same antialiasing effect of what i have when i draw directly on the canvas of the form ? absolutely not, nothing change and i still have ugly round without any anti-aliasing :(
what i miss ??
EDIT:
The only option i found for now to remove the antialias is to make the bitmap 2x more bigger and reduce it after by 2x! crazy :( but the algorithme of reduction remove the aliased ... but the cost of all of this is that the speed become slow, especially when we know that all the graphic function must be done in the main thread :(
Now more i think more i say to myself that it's crazy to have a graphic library that not support multi-thread ! i can not believe that it's a requirement of openGL and i think now more and more that it's a bug or bad conception in delphi :(
Speaking about the previous point, even if openGL really required that all graphic routines must be done in the main thread (but really i doubt), i don't understand why delphi not offer on android another TcanvasClass (other than TcanvasGPU) that support multithread ! more crazy is that when you work with TCanvasGPU on Tbitmap, the result will be in any case (as you can see in my previous post) different from what you will have working with TCanvasGPU on the visual component !
now i m looking for function to work directly on pixels grids (old school), that will make me possible to work in multi-thread. but unfortunately their is not to much compatible with firemonley/android :(
to finish this is the function i use to draw my bitmap. but as this function must be call in the main thread, it's slow down my application (especially the scroll) ... so if you have any idea to make this more fast or multithread i take :)
function DrawAdvertBitmap: Tbitmap;
var aBitmapAliased: Tbitmap;
begin
aBitmapAliased := Tbitmap.Create(trunc(FWidth * fScreenScale) * 2, trunc(FHeight * fScreenScale) * 2);
try
aBitmapAliased.Canvas.BeginScene;
try
aBitmapAliased.canvas.Clear(0);
aBitmapAliased.canvas.Fill.Color := $ff000000;
aBitmapAliased.Canvas.Fill.Kind := TbrushKind.Solid;
aBitmapAliased.Canvas.FillRect(...);
aBitmapAliased.Canvas.Fill.Color := $ff333844;
aBitmapAliased.Canvas.Font.Family := 'Roboto-Bold';
aBitmapAliased.Canvas.Font.Size := 12 * fScreenScale * 2;
aBitmapAliased.Canvas.Font.Style := [TFontStyle.fsBold];
aBitmapAliased.Canvas.FillText(...);
finally
aBitmapAliased.Canvas.EndScene;
end;
//reduce by 2 to make antialiased
result := Tbitmap.Create(trunc(FWidth * fScreenScale), trunc(FHeight * fScreenScale));
try
result.Canvas.BeginScene;
try
result.Canvas.Clear(0);
result.Canvas.DrawBitmap(aBitmapAliased,...);
finally
result.Canvas.EndScene;
end;
except
result.Free;
raise;
end;
result.BitmapScale := fScreenScale;
finally
aBitmapAliased.free;
end;
end;
If we talk about Firemonkey:
TImage has the property "DisableInterpolation: boolean"
TForm has the property "Quality: TCanvasQuality = (SystemDefault, HighPerformance, HighQuality)" — it is works in design-time
Important to know:
Anti-aliasing is very expensive operation, this is disabled by default on mobile platforms.
The same anti-aliasing should be supported by the device. To Support multisampling the OpenGL must have GL_EXT_multisampled_render_to_texture property. If the device hardware does not support multisampling, the AA will not be.
If you are seeing weird anti-aliasing on some controls, I would recommend that you check that the controls do not have fractional position and size values, make sure to use trunc/round on the position or the GPU canvas uses some low-quality interpolation effect on top of any anti-aliasing.
If you plan to do per-pixel manual adjustments, I recommend that you use optimized color-conversion code (scanline <> TAlphaColor) as the built-in code is designed for clear code and not performance:
https://github.com/bLightZP/OptimizedDelphiFMXScanline
As for drawing anti-aliased circles with pictures in them, I found the easiest way is to generate an anti-aliased opacity map and apply it to a TImage's TBitmap. You can find code for generating an anti-aliased opacity map here:
https://github.com/bLightZP/AntiAliasedCircle
1. Use FMXNativeDraw by OneChen (Aone), - it's very simple, you even do not need to change your standard code for Canvas - need to add only 2 lines (IFDEF). It also supports TPath.
It works on 4 platoforms - Win, Mac, Android, iOS. But you need it only on mobile platforms, because antialiasing works on Win and Mac.
I use FMXNativeDraw in my mobile projects and it works very well!
Read this article first with google translate about FMXNativeDraw: http://www.cnblogs.com/onechen/p/6350096.html
Download:
https://github.com/OneChen/FMXNativeDraw
Btw if you're using PaintBox Canvas - work as usual.
If you need to draw on TBitmap Canvas - prepare Bitmap first:
if Scene <> nil then
lScale := Scene.GetSceneScale
else
lScale := 1;
fBitmap.BitmapScale := lScale;
fBitmap.SetSize(Ceil(Width * lScale), Ceil(Height * lScale) );
2. (optional)
Also use ZMaterialComponents, that is a wrapper for FMXNativeDraw (circle, rectangle, line etc). You can place these components on a FMX Form as standard TCircle and others.
https://github.com/rzaripov1990/ZMaterialComponents
I have some code that works fine in iOs, but which results in completely messed up images when on Android. I have found a partial workaround (not call some code), but it hints something is terrible wrong:
// some bitmap object buffer for mainthread only
R.BitmapRef := FPersistentBitmapBuffer;
// this TImage now contains the original wrongly sized bitmap
ImageBackground.Bitmap.Assign(R.BitmapRef);
// calculated somewhere
TmpNewWidth := 500;
TmpNewHeight := 500;
// draw the bitmap resized to wanted size
R.BitmapRef.Width := Round(TmpNewWidth);
R.BitmapRef.Height := Round(TmpNewHeight);
R.BitmapRef.Canvas.BeginScene();
R.BitmapRef.Canvas.DrawBitmap(ImageBackground.Bitmap, RectF(0,0,ImageBackground.Bitmap.Width,ImageBackground.Bitmap.Height), RectF(0,0,TmpNewWidth,TmpNewHeight), 1);
R.BitmapRef.Canvas.EndScene();
// assign it back to the image
ImageBackground.Bitmap.Assign(R.BitmapRef);
// THIS code causes the image shown in TImageBackground to look completely garbled ... which would indicate something is shareing memory/reference somewhere somehow... There is more odd behavior like debugger unhooking (it seems) if mouse in Delphi debugger hovers over ImageBackground.Bitmap - no error is reported
R.BitmapRef.Clear(TAlphaColorRec.White);
As can be seen, it the last line that messes it up. In some tests it has seemed to be enough to remove he line, but not in others. This is my best lead/description/example of the problem.
Here is an example of how a garbled image looks like. Since they look garbled the same way each time I run the app, I suspect it must be somehow relate to the image, but there is not any visual similarity.
My question is what could be wrong? I am testing the Delphi XE7 trial, so I can not access the source. It worked flawlessly on iOS using XE4 and XE7, but with Android something is going on. I am thinking it could possibly be some bitmap data that is sharing a reference... Does anyone have any ideas on how to test this theory / possible workarounds?
This looks plainly wrong. I'd suggest that you fill a bugreport at http://quality.embarcadero.com
Try using CopyFromBitmap instead of the "Assign". This will create a unique copy of the image. You'll also get a new unique image if you call MyBitmap.Map(TMapAccess.Write, MyBitmapData); followed by MyBitmap.UnMap(MyBitmapData);.
I started doing an app for android in Delphi XE5, and encountered some troubles. First one was, when creating new profile for device in design time. I created new profile for my HTC One (M7), which has 4,7" screen at 1080x1920 resolution. When created profile with such data, I only got the top left part of picture after I ran it on the device itself. So, since then I'm running my app, so I can test at least the code I'm doing, in mode with default profile " 5,1" WVGA Android Phone (480dp x 800dp: mdpi) ", since at this settings, I see about 75% of the design-time form size... Anyone has any idea why this is happening, and if that's XE5's problem, or the app's itself when ran on the phone?
Update: I figured from the sample projects of RAD studio, that if I use Samsung Galaxy S4 template, which also has 1080x1920 resolution, it's the correct form for my HTC One as well. Just can't see it's settings, since stock profiles can't be edited, but surely isn't set as 1080x1920, because the form itself looks smaller than the one I created.
I know now I can use this settings, but just don't understand why the difference.
Anyway, the main question now is, how to set up the screen settings so that they apply and change in dependence of the device and device's screen resolution...?
Thanks.
UPDATE:
Just got confirmed from a friend that on his Nexus tablet he sees the whole picture, meaning the form's size just as is set in delphi.
http://en.wikipedia.org/wiki/Nexus_7_(2012_version)
Wtf?
This is a real problem for most mobile phones. Most devices have a screenscale. ios has it, most modern Android devices have it. What you have to do is to scale a bitmap to the screen by multiplying its dimensions by the screen scale. Example from my own code:
function TMandel_View.get_screenscale: Single;
var
ScreenService: IFMXScreenService;
begin
Result := 1;
if TPlatformServices.Current.SupportsPlatformService (IFMXScreenService, IInterface(ScreenService)) then
begin
Result := ScreenService.GetScreenScale;
end; // if
end; // get_screenscale //
procedure TMandel_View.FormActivate (Sender: TObject);
var
screenscale: Single;
begin
if not FActivated then // Initialization should be called only once
begin
FActivated := True; // Disable initialization each time form is activated
screenscale := get_screenscale;
FImageBitmapWidth := FImage.Width * screenscale;
FImageBitmapHeight := FImage.Height * screenscale;
FImage.Bitmap.SetSize (Round (FImageBitmapWidth), Round (FImageBitmapHeight));
// etc...
end; // if
end; // FormActivate //
You might want to view the full sourcode but this is pretty much it.
A hacky way would be to store your design time width and height as const then use them in an OnCreate event with the device's actual screen width and height like
Offset.Height := Design.Height - Screen.Height;
Offset.Width := Design.Width - Screen.Width;
You then use these offsets to align all your labels,edits,ect .
What I discovered was that your graphic representing the phone has to be life sized. If your screen on the phone is 5", the screen on your graphic phone has to be 5" (same deal with width too of course). Download a graphic for your phone and match it's screen size to your phone and you're done. It should match very accurately between component placement and how the phone looks at run-time.