DrawText in canvas (SkiaSharp) : Text does not display - xamarin.forms

My problem is: text does not display on canvas
SKPaint _paint = new SKPaint();
using(Stream stream = _assembly.GetManifestResourceStream(fontPath))
{
_paint.Typeface = SKTypeface.FromStream(stream);
}
canvas.DrawText ("12345",X,Y,_paint);
If I don't use "using block" or "dispose", it works like a charm.
But if I do, before it reaches canvas.DrawText, _paint does not null but all values in FontMetrics, FontSpacing or TextSize equal 0.
Then there is no text on canvas.
I'm so confused, could you give me some advice, please ?
Thank you.

The reason this is happening is that you are disposing the stream and then trying to use it. SKTypeface takes ownership of the stream and you must not close it yourself. Ass soon as the typeface is no longer in use, it may decide to close the stream.
The correct way to use this is to open the stream, pass it to SKTypeface and then just worry about the typeface:
SKPaint _paint = new SKPaint();
Stream stream = _assembly.GetManifestResourceStream(fontPath);
_paint.Typeface = SKTypeface.FromStream(stream);
canvas.DrawText ("12345", X, Y,_paint);
https://developer.xamarin.com/api/member/SkiaSharp.SKTypeface.FromStream/p/System.IO.Stream/System.Int32/
Returns a new typeface given a stream. Ownership of the stream is transferred, so the caller must not reference it again.

Related

IDWriteGdiInterop::CreateBitmapRenderTarget failing

I have an application which renders glyphs to a printer device context. Earlier, it used GDI and it worked fine.
But now, I'm trying to use Directwrite instead.
I've changed the existing ExtTextOut call and used IDWriteTextLayout::Draw function.
I need to pass an object implementing IDWriteTextRenderer interface in the draw function.
I've implemented the DrawGlyphRun callback in IDWriteTextRenderer interface using a call to IDWriteBitmapRenderTarget:: DrawGlyphRun. So, I need to create a IDWriteBitmapRenderTarget object.
I have the following code :
IDWriteFactory* pDWriteFactory = NULL;
IDWriteGdiInterop* pGdiInterop = NULL;
IDWriteTextLayout* pTextLayout = NULL;
IDWriteBitmapRenderTarget* pBitmapRenderTarget = NULL;
IDWriteRenderingParams* pRenderingParams = NULL;
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&pDWriteFactory));
if (!SUCCEEDED(hr))
pDWriteFactory = 0;
else
{
hr = pDWriteFactory->GetGdiInterop(&pGdiInterop);
if (!SUCCEEDED(hr))
pGdiInterop = 0;
}
hr = pGdiInterop->CreateBitmapRenderTarget((HDC)hDC, someWidth, someHeight, &pBitmapRenderTarget);
The device context hDC is being passed by a function in another dll.
Result: hr=E_FAIL
Maybe I'm missing out on something very basic, but I have little knowledge of device contexts and I'm new to DirectWrite. I need to know why the same HDC structure which is compatible with a ExtTextOut call is not being compatible with DirectWrite.
Also, since the HDC field is optional, if I pass NULL, it succeeds but nothing gets rendered by the draw function. What does passing NULL signify ?
First make sure hDC is a valid memory device context, memory part is important. NULL argument means that target creates its own context compatible with screen/desktop. In any case nothing will be rendered automatically, you need to blit yourself from HDC returned by GetMemoryDC to your context, in other words target draws to this memory DC only, and what you do next is up to you.

How to find video resolution and aspect ratio of QMediaPlayer video?

This seems too simple, I must be overlooking something?
How do I find the native video size or aspect ratio from a video file being displayed by a QMediaPlayer?
The video Resolution, PixelAspectRatio, etc., should be in the MetaData, but I wait for MetaData Update Signals, and wait for seconds after the video .play()s, but isMetaDataAvailable() always returns false, and .availableMetaData() and .metaData(QMediaMetaData::Resolution).toSize() always return empty.
There seems to be nowhere else to get the video resolution information, or am I missing something?
I can open the video, play the video at full screen, etc.
You can use QVideoWidget instance as video output for QMediaPlayer and retrieve native size of video from QVideoWidget::sizeHint.
QSize MyVideoPlayer::getVideoNativeSize(const QString& videoFilePath)
{
m_mediaPlayer = new QMediaPlayer(0, QMediaPlayer::VideoSurface);
m_mediaPlayer->setVideoOutput(m_videoWidget);
m_mediaPlayer->setMedia(QUrl::fromLocalFile(videoFilePath));
connect(m_mediaPlayer, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
this, SLOT(OnMediaStatusChanged(QMediaPlayer::MediaStatus)));
m_isStoppingVideo = false;
QEventLoop loop;
m_mediaPlayer->play();
while (!m_isStoppingVideo)
{
loop.processEvents();
}
disconnect(m_mediaPlayer, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
this, SLOT(OnMediaStatusChanged(QMediaPlayer::MediaStatus)));
m_mediaPlayer->stop();
return m_videoWidget->sizeHint();
}
void MyVideoPlayer::OnMediaStatusChanged(QMediaPlayer::MediaStatus mediaStatus)
{
if (mediaStatus == QMediaPlayer::BufferedMedia)
{
m_isStoppingVideo = true;
}
}
For finding the resolution without metadata, you can take a look at this question from the Qt Forums for a possible solution:
http://forum.qt.io/topic/31278/solved-get-resolution-of-a-video-file-40-qmediaplayer-41/2
I solved my problem by waiting until the user plays the video and as
soon as they do so i get the QGraphicsVideoItems class property:
nativeSize.
I also solved this problem with QGraphicsVideoItems nativeSize property. But the tricky thing is that nativeSize becomes valid only after some time since you start playing video. The trick is to connect to special QGraphicsVideoItem::nativeSizeChanged(const QSizeF &size) signal that is emitted in case of real nativeSize obtainment.

Mango Application Tile - remove back

This is a simple question, and a seemingly simple task but I can't find any info on how to accomplish what I need to do.
I have an application whose main tile (when pinned) sometimes needs to be the default, single sided tile and sometimes needs to have information displayed on the back of the tile. I can add the BackBackgroundImage, BackContent and BackTitle successfully from the ScheduledActionService, but I can't remove them when they are no longer required. This isn't a secondary tile so I can't remove it and re-create and re-add it.
Does anyone know if it is possible to revert a double-sided tile back to single-sided via code, and if so, how can I achieve that behaviour, please?
EDIT
The settings that get applied from the StandardTileData object are additive - if you only specify a title, for example, all other elements remain the same and only the title is updated. I have attempted to set the three parameters that appear on the back of the tile to null and had partial success. The effect is that the background image, title text and content text are all removed, but the tile still flips over to show a completely empty reverse side.
EDIT AGAIN
So, looking at the documentation, the tile back behaves differently to the front. Setting the back content or backtitle to string.Empty will remove those. All good there. However, it does say that "If set to an empty URI, the BackBackgroundImage will not be displayed.". How do I go about creating an empty Uri? I tried new Uri(string,Empty) but that throws an exception about trying to create an empty Uri - which is what I'm trying to do.
OK, I think I've got it, and it appears to be related to a change in the way tile data is handled...
Previously, setting a value to an empty string would have now effect in the tile. For eaxmple, setting title = string.Empty would leave the existing title in place. Now, though, it will blank out the title. That's good - it means I can remove BackTitle and BackContent string easily. We're half way there.
Now, to get rid of the BackBackgroundImage, the documentation states "If set to an empty URI, the BackBackgroundImage will not be displayed." - all good, except you can't create an empty Uri in any simple way. The one way I've made it work is to set it to a Uri value that doesn't exist, eg
BackBackgroundImage = new Uri("obviouslyMadeUpLocation", UriKind.Relative);
I would have expected that to throw an exception when you try to apply it to the tile, but it doesn't - it just clears the background image.
So that's it. All I appear to need to do is to call the following to unset these properties and put my tile back as it was.
private void ResetMyMainTile()
{
ShellTile tile = ShellTile.ActiveTiles.First();
StandardTileData data = new StandardTileData
{
BackBackgroundImage = new Uri("IDontExist",UriKind.Relative),
BackContent = string.Empty,
BackTitle = string.Empty
};
tile.Update(data);
}
This one works for me.
new Uri("Background.png", UriKind.RelativeOrAbsolute);
ShellTile TileToFind = ShellTile.ActiveTiles.First();
if (TileToFind != null)
{
StandardTileData NewTileData = new StandardTileData
{
Title ="Status",
BackgroundImage = new Uri("Background.png", UriKind.RelativeOrAbsolute),
Count = 0,
BackTitle = "",
BackBackgroundImage = new Uri("doesntexist.png", UriKind.RelativeOrAbsolute),
BackContent = ""
};
TileToFind.Update(NewTileData);
}
Try setting the whole tile (all details) again to everything as was before / is now but without the background details.
Update
Does this not work?:
ShellTile tile = ShellTile.ActiveTiles.First();
tile.Update(null);
or
tile.update(new StandardTileData());

load large image from database and return it to the client side

Hi:
IN my application,I have some images saved in the db,so I create a ImgDownLoad.aspx to retrive the image and retun them,since the image in the db may very large(some of them is more than 20M),so I generate some thumbnails ,this is the code:
page_load(){
string id=Requset.QueryString["id"];
string imgtype=Requset.Querystring["itype"];
if(imgType=="small")
{
//request the thumbnail
string small_loaction=getSmallLocationById(id);
if(!File.exists(small_location)
{
byte[] img_stream =getStreamFromDb(id);
Image img=Image.frameStream(new MemsorStream(img_steam));//here,I often get the out of memory error,but I am not sure when it will happen.
generateSmallImage(img,location)
}
Response.TransferFile(small_location);
}
else if(imgType=="large"){
byte[] img_stream =getStreamFromDb(id);
new MemorySteam(img_stream).writeTo(Response.outputstream);
}
}
Anything wrong?
ALso,since I do not know the image format,so I can not add the
Response.contenttype="image/xxx";
What confusing me most is that I will meet the out of memory error,so I change the code:
try{
byte[] img_stream =getStreamFromDb(id);
Image img=Image.frameStream(new MemsorStream(img_steam));//here,I often get the out of memory error,but I am not sure when it will happen.
generateSmallImage(img,location)
}
catche(exceptin e){
//the small image can not generated,just return the whole image
new MemorySteam(img_stream).writeTo(Response.outputstream);
return;
}
In this case,I will avoid the out of memory problem,but some large image can not downloaded sometime.
So I wonder if there are any ways to handle the large image stream?
Take a large image for exmaple:
resolution:12590x4000
size:26M.
In fact,I have opened a large image(almost 24M) with the mspaint,and then save the image again,I found that it size is much smaller than at first. So is it possible to resize the image in the server side? Or other good manners to hanle my problem?
Firstly, you're not disposing of the Image and Stream type instances that you create - given subsequent calls, over time, this is bound to cause issues; particularly with images around the 20meg mark!
Also, why create the thumbnails every call? Create once and cache, or flush to disk: either way, serve a 'one you made earlier' rather than do this processing over and over.
I would recommend, however, you try an minimise the size (in bytes) of the images. Some might argue that they shouldn't be in the database if over 1meg, but store them on disk and a file name in the database. I guess that's open for debate, browse if interested.
To your comment, I'd urge you not to allow other scopes take control of resources 'owned' by another; dispose of items in the scope that creates them (obviously sometimes some things need to stick around, but what is responsible for them should be clear). Here's a little rework of some of your code:
if (imgType == "small")
{
string small_loaction = getSmallLocationById(id);
if(!File.exists(small_location)
{
byte[] imageBytes = getStreamFromDb(id);
using (var imageStream = new MemoryStream(imageBytes))
{
using (var image = Image.FromStream(imageStream))
{
generateSmallImage(image, small_location)
}
}
}
Response.TransferFile(small_location);
}
else if (imgType=="large")
{
byte[] imageBytes = getStreamFromDb(id);
Response.OutputStream.Write(imageBytes, 0, imageBytes.Length);
}

Why am I losing my databinding when printing to an XpsDocument?

Update!
Binding works. The issue is that the XpsDocumentWriter doesn't properly write the first page of the first document of a FixedDocumentSequence. This seems to be an issue encountered by lots of people doing this sort of thing (i.e., five developers worldwide). The solution is slightly odd. I include it as an answer.
Okay, its a bit more subtle than the question suggests.
I've got a series of FixedPages, each has its DataContext set individually. Each FixedPage also has one or more controls that are bound to the context.
If I add these FixedPages to a single FixedDocument and write this single FixedDocument to an XpsDocument, my binds are de-referenced (so to speak) and the correct values are presented in the XpsDocument.
If I add these FixedPages to individual FixedDocuments (each FP gets added to a new FD), then these FixedDocuments are added to a FixedDocumentSequence, and this sequence is then written to the XpsDocument, my binds are NOT de-referenced and my FixedPages appear blank.
Debugging tells me that I'm not losing my bindings or my binding context, so that's not the cause of this failure.
Here's some sample code to illustrate what's going on:
// This works
FixedPage fp = CreateFixedPageWithBinding();
fp.DataContext = CreateDataContext();
// Add my databound fixed page to a new fixed document
var fd = new FixedDocument();
var pc = new PageContent();
((IAddChild)pc).AddChild(fp);
fd.Pages.Add(pageContent);
// Create an xps document and write my fixed document to it
var p = Package.Open("c:\\output.xps", FileMode.CreateNew);
var doc = new XpsDocument(p);
var writer = XpsDocument.CreateXpsDocumentWriter(doc);
wri2.Write(fd);
p.Flush();
p.Close();
// This does NOT work
FixedPage fp = CreateFixedPageWithBinding();
fp.DataContext = CreateDataContext();
// Add my databound fixed page to a new fixed document
var fd = new FixedDocument();
var pc = new PageContent();
((IAddChild)pc).AddChild(fp);
fd.Pages.Add(pageContent);
// Create a fixed document sequence and add the fixed document to it
FixedDocumentSequence fds = CreateFixedDocumentSequence();
var dr = new DocumentReference();
dr.BeginInit();
dr.SetDocument(fd);
dr.EndInit();
(fds as IAddChild).AddChild(dr);
// Create an xps document and write the fixed document sequence to it
var p = Package.Open("c:\\output.xps", FileMode.CreateNew);
var doc = new XpsDocument(p);
var writer = XpsDocument.CreateXpsDocumentWriter(doc);
wri2.Write(fds);
p.Flush();
p.Close();
You can see that the only difference between the two is that I'm adding the fixed document to a fixed document sequence, which then gets written.
Obviously, whatever magic happens that causes the databinding to be evaluated and the bound values be inserted isn't happening when my fixed documents aren't being Written to the Xps Document. I need to be able to write more than one fixed document, and the Write method can only be called once, thus requiring I add the FixedDocuments to a FixedDocumentSequence which I then write. But I also need my damn databinding to work as well!
Any help in this situation would be appreciated. I know its not exactly the most common part of the framework; I'm just hoping that someone here has some operational experience with this (I'm looking at you, lurking MS employee).
The cause of this bug is that the FixedPage's layout isn't being updated prior to writing. This causes the first FixedPage in the first FixedDocument in the FixedDocumentSequence to be written incorrectly. This affects NO OTHER PAGES IN THE RESULTING DOCUMENT, which made this bug/edge case harder to nail down.
The following WORKS (rewritten version of the non-working example):
FixedPage fp = CreateFixedPageWithBinding();
fp.DataContext = CreateDataContext();
var fd = new FixedDocument();
/* PAY ATTENTION HERE */
// set the page size on our fixed document
fd.DocumentPaginator.PageSize =
new System.Windows.Size()
{
Width = DotsPerInch * PageWidth,
Height = DotsPerInch * PageHeight
};
// Update the layout of our FixedPage
var size = fd.DocumentPaginator.PageSize;
page.Measure(size);
page.Arrange(new Rect(new Point(), size));
page.UpdateLayout();
/* STOP PAYING ATTENTION HERE */
var pc = new PageContent();
((IAddChild)pc).AddChild(fp);
fd.Pages.Add(pageContent);
// Create a fixed document sequence and add the fixed document to it
FixedDocumentSequence fds = CreateFixedDocumentSequence();
var dr = new DocumentReference();
dr.BeginInit();
dr.SetDocument(fd);
dr.EndInit();
(fds as IAddChild).AddChild(dr);
// Create an xps document and write the fixed document sequence to it
var p = Package.Open("c:\\output.xps", FileMode.CreateNew);
var doc = new XpsDocument(p);
var writer = XpsDocument.CreateXpsDocumentWriter(doc);
wri2.Write(fds);
p.Flush();
p.Close();
I found this issue while trying to use XpsDocumentWriter to write to a PrintQueue. The following code prints the first page correctly.
//Prints correctly
FixedDocumentSequence Documents = new FixedDocumentSequence();
//some code to add DocumentReferences to FixedDocumentSequence
PrintDialog printDialog = new PrintDialog
{
PrintQueue = LocalPrintServer.GetDefaultPrintQueue()
};
printDialog.PrintTicket = printDialog.PrintQueue.DefaultPrintTicket;
if (printDialog.ShowDialog() == true)
{
Documents.PrintTicket = printDialog.PrintTicket;
XpsDocumentWriter writer = PrintQueue.CreateXpsDocumentWriter(printDialog.PrintQueue);
writer.Write(Documents, printDialog.PrintTicket);
printerName = printDialog.PrintQueue.FullName;
}
If you remove the printDialog.ShowDialog() and just attempt to silent print to the default printer, the first page prints incorrectly. However, in my scenario, I didn't need to use a FixedDocumentSequence so I swapped it out for just a single FixedDocument and silent printing worked. I tried updating the layout on the FixedPage without success. Weird how the first page prints fine if I show the print dialog though.
One reason that you lose a binding is that you throw an exception somewhere - unfortunately, this exception is silently swallowed and your binding just "stops working". Turn on First-chance exceptions and see if anything gets hit.

Resources