Hi,
I'm using RadDiagramView on big diagram and I need to export png images.
png encoding and 91 dpi is ok for my needs, so I'm calling ExportToImage method leaving default parameters as follows:
using
(var stream = File.Open($
"{filename}.png"
, FileMode.Create))
{
radDiagram.ExportToImage(stream);
}
On big diagrams, i'm in trouble because the quality of exported images is very low (seems blurred); I cannot understand why.
I have cloned your github xaml sdk examples and pushed it on my personal account on this url https://gitlab.com/webartoli/repro-lowquality-exporttoimage-radtreeview. You can find a repro starting Diagram/Autoscrolling project.
Repro changes:
- Positions button will move shapes on graph
- Export image will export png file of current diagram in current directory with random filename.
attached a picture with a comparison of the same shape on a small diagram (300 x 150px) and on a big diagram (6100 x 6100 px).
Am I doing something wrong ?
How can I manage properly all the options, in order to obtain the same quality regardless of the size of the diagram ?
Thanks
Claudio
6 Answers, 1 is accepted
You will have to create your own image exporter. The default one Telerik has implemented doesn't take the current zoom factor into account which is why your large diagram images are blurred. Here is some code to point you in the right direction. The first block of code is a generic function which will take any FrameworkElement you pass into it and re-render the element to the provided Width/Height settings.
public
void
CopyImagetoFile(FrameworkElement surface,
double
width,
double
height,
double
dpi = 96d)
{
var saveFile =
new
SaveFileDialog
{
InitialDirectory = Application.ExecutablePath,
Filter =
"PNG files (*.png)|*.png|All files (*.*)|*.*"
,
FilterIndex = 1,
RestoreDirectory =
true
};
if
(saveFile.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
var fullName = saveFile.FileName;
var adjustedWidth = surface.ActualWidth * (dpi / 96d);
var adjustedHeight = surface.ActualHeight * (dpi / 96d);
// Now scale the layout to fit the bitmap
var renderBitmap =
new
RenderTargetBitmap(
(
int
)adjustedWidth,
(
int
)adjustedHeight,
dpi,
dpi,
PixelFormats.Default);
var dv =
new
DrawingVisual();
using
(DrawingContext dc = dv.RenderOpen())
{
var vb =
new
BitmapCacheBrush(surface)
{
BitmapCache =
new
BitmapCache { SnapsToDevicePixels =
true
, EnableClearType =
true
}
};
dc.DrawRectangle(vb,
null
,
new
Rect(
new
Point(),
new
Size(width, height)));
}
renderBitmap.Render(dv);
using
(var outStream =
new
FileStream(fullName, FileMode.Create))
{
// Use png encoder for our data
var encoder =
new
PngBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(outStream);
}
}
}
You can also change the DPI setting but be careful as your memory usage will grow significantly.
The call to this function looks like this:
var zoom = LocalDiagram.Zoom;
LocalDiagram.AutoFit();
var localBounds = LocalDiagram.CalculateEnclosingBounds();
var newWidth = localBounds.Width * zoom;
var newHeight = localBounds.Height * zoom;
CopyImagetoFile(LocalDiagram, newWidth, newHeight, 96d);
LocalDiagram is your RadDiagram. We multiple the bounds of the all shapes on the diagram by the current zoom factor to get the appropriate size you need to re-render for. You will want to zoom into your diagram to the desired size you want the output to look and then execute the above from a command/click event.
If your diagrams are going to be very large (i.e. 10000px+) you should really take a look at exporting to PDF instead of PNG as it will be much more efficient memory wise and look better. The UIElement export example is a great place to start: https://github.com/telerik/xaml-sdk/tree/master/PdfProcessing/ExportUIElement
Hope this helps,
Shawn
We would suggest increasing the dpi parameter in ExportToImage method, however, as Shawn said, the memory will be increased so please have this in mind.
Regards,
Petar Mladenov
Telerik
I have already tried multiple combination of DPI parameter in ExportToImage method but the resolution remains the same, labels unreadable, but generate images are bigger (according to this answer ExportToImage returnImageSize & dpi parameters).
I'll try Shawn's solution that seems consider the problem with a different approach that make sense. @Shawn: Thanks for your help.
Thanks,
Claudio
I'll try Shawn's solution that seems consider the problem with a different approach.
[/quote]
Hello Petar and Shawn,
I have tired both of your solutions and i have updated the public repo based on telerik's examples.
Petar's
I have tried to encrease dpi value, encrease zoom level, and to explicit image size with the same blurred resut.
This my last snippet about your solution:
public
class
ExportToImageTelerikCommand : ICommand
{
public
bool
CanExecute(
object
parameter)
{
return
true
;
}
public
void
Execute(
object
parameter)
{
var radDiagram = parameter
as
RadDiagram;
if
(radDiagram ==
null
)
return
;
// Prepare image
var encoder =
new
PngBitmapEncoder();
var enclosingBounds = radDiagram.CalculateEnclosingBounds();
var size = enclosingBounds.Size;
var brush = Brushes.Transparent;
var margin =
default
(Thickness);
var dpi = 192D;
// Save Image
using
(var stream = File.Open(GetTempFilename(), FileMode.Create))
{
radDiagram.ExportToImage(stream, encoder, enclosingBounds, size, brush, margin, dpi);
}
}
public
event
EventHandler CanExecuteChanged;
private
static
string
GetTempFilename()
{
return
$
"{Path.GetFileNameWithoutExtension(Path.GetTempFileName())}.png"
;
}
}
Shawn's
I've tried to apply your solution to my code, there is a little fine tuning to do about positions (i need to export the entire diagram at 100% zoom level) but the exported image is of the expected quality.
public
class
ExportToImageShawnCommand : ICommand
{
public
bool
CanExecute(
object
parameter)
{
return
true
;
}
public
void
Execute(
object
parameter)
{
var radDiagram = parameter
as
RadDiagram;
if
(radDiagram ==
null
)
return
;
// Prepare image
var enclosingBounds = radDiagram.CalculateEnclosingBounds();
CopyImagetoFile(radDiagram, enclosingBounds, GetTempFilename());
}
public
event
EventHandler CanExecuteChanged;
private
static
string
GetTempFilename()
{
return
$
"{Path.GetFileNameWithoutExtension(Path.GetTempFileName())}.png"
;
}
private
void
CopyImagetoFile(FrameworkElement surface, Rect rectangle,
string
fullName,
double
dpi = 96d)
{
// Now scale the layout to fit the bitmap
var renderBitmap =
new
RenderTargetBitmap((
int
) rectangle.Width, (
int
) rectangle.Height, dpi, dpi, PixelFormats.Default);
var dv =
new
DrawingVisual();
using
(var dc = dv.RenderOpen())
{
var vb =
new
BitmapCacheBrush(surface)
{
BitmapCache =
new
BitmapCache
{
SnapsToDevicePixels =
true
,
EnableClearType =
true
}
};
dc.DrawRectangle(vb,
null
, rectangle);
}
renderBitmap.Render(dv);
using
(var outStream =
new
FileStream(fullName, FileMode.Create))
{
// Use png encoder for our data
var encoder =
new
PngBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(outStream);
}
}
}
In attachment you can find images, exported starting from example project.
With some fine tuning I'll can export images of great quality using Shawn's solution: but I obviously prefer to use Telerik's component features.
There is any chance to obtain a non blurred export using ExportToImage method (with any parameters value) ?
Is it possibile that I have hit a sort of bug ?
Hi Claudio,
Glad to hear the solution will work for you.We spent a lot of time trying to figure out how to make this work. Trying to increase/change the input settings (bounds, DPI, etc) didn't have any noticeable improvement of image quality or would lead to OutOfMemoryExceptions due to DPI settings being too high.
In trying to find a solution, we took Telerik's implementation of the BitmpaUtils.CreateWriteableBitmap code as a base and worked on it until we discovered changing from a VisualBrush to a BitmapCacheBrush gave us the desired results. While the BitmapCacheBrush has a performance penalty, since it has to cache the image to video memory, the results are vastly improved over the VB.
I don't know how hard it would be for Telerik to change this on their end or what the ramifications would be for other code that uses this function but I would love to see this implemented as well.
Shawn
Thank you for your suggestions. It's a matter of research, planning, impact and we currently cannot say whether the BitMapCacheBrush can be easily included in our Framework. However, we created a feature request and you can vote for it , this way increasing its development priority.
Regards,
Petar Mladenov
Telerik