Converting emf or wmf to BufferedImage --> bad quality

Hello,

we are using in our product aspose imaging only for viewing emf or wmf graphics.

Our older code produces high quality output and uses code like this, to write the content into a Graphics2D object:

this.bImage = this.metafile.createDefaultRendering();
this.metafile.playMetafile(g2d);

Since the version 20.x the method playMetafile(g2d) isn’t available anymore and so the code has changed to:

this.bImage = ImageExtensions.toJava(this.metaImage);                                                                        
 g2d.drawImage(this.bImage, 0, 0, this.metaImage.getWidth(), this.metaImage.getHeight(), null);

This seems to convert emf/wmf to png and after that, it will be written into g2d, what means a very bad quality:
grafik.png (70,2 KB)

Formerly we had much better quality:
grafik.png (152,0 KB)

So: How can we solve this problem?

Hello, @bpetermann ,
In case you use an old Aspose.Imaging version, you are unable to get the latest fixes and features. You are either up to use an older than 20.x version, which fits your requirements, or upgrading to the latest one.

I have reviewed the pictures you attached and found no significant difference in quality except the image size. To obtain the best quality on vector to PNG conversion I recommend setting SmoothingMode.AntiAlias:

public static void EmfToPng(string path)
{
    using (var image = Image.Load(path))
    {
        var antiAlias = SmoothingMode.AntiAlias;
        image.Save(path + ".png", new PngOptions
        {
            VectorRasterizationOptions = new EmfRasterizationOptions
            {
                PageSize = image.Size,
                SmoothingMode = antiAlias, // use anti alias smoothing
            }
        });
    }
}

Hope this helps!

Hello Denis,

sorry that there is a misunderstanding.
The two attached pictures are screenshots from our application, in which we are showing zoomed layouts.
You can see, that with the current version of aspose-imaging 24.4, where we can’t use

this.metafile.playMetafile(g2d);

the quality (first screenshot) is really bad (you can’t read the writing), because

ImageExtensions.toJava(this.metaImage);   

converts the emf/wmf into a png.

So we don’t want convert some emf/wmf to another picture format, but only showing it in our application in best quality.

@bpetermann ,
Having BufferedImage and converting EMF/WMF to PNG is actually the same thing, as you turn a vector into a bitmap. The quality downgrade you are telling about can be resolved by using SmoothingMode.AntiAlias (look the sent code example above). Have you tried using smoothing?

Hello Denis,

I’ll give it a try, thanks.

Hi, @bpetermann.
To get more control over the quality you can use the following code:

Image metafile = Image.load("any.wmf");
BufferedImage  memImage = convert(metafile, 10.0f); // you can estimate the scale factor for WMF by your own.

static BufferedImage convert(Image image, float wmfScaleFactor)
{
    if (image instanceof WmfImage)
    {
        WmfRasterizationOptions wmfRasterOptions = new WmfRasterizationOptions();
        wmfRasterOptions.setPageWidth(image.getWidth() * wmfScaleFactor); // the final width
        wmfRasterOptions.setPageHeight(image.getHeight() * wmfScaleFactor); // the final height
        wmfRasterOptions.setSmoothingMode(SmoothingMode.HighQuality); // the quality
        // Export to the raster image
        PngOptions pngOptions = new PngOptions();
        pngOptions.setVectorRasterizationOptions(wmfRasterOptions);
        com.aspose.imaging.system.io.MemoryStream mem = new com.aspose.imaging.system.io.MemoryStream(16000);
        image.save(mem.toOutputStream(), pngOptions);
        mem.setPosition(0);
        // convert the raster image into BufferedImage
        Image tmpImage = Image.load(mem.toInputStream());
        try
        {
            return ImageExtensions.toJava(tmpImage);
        }
        finally
        {
            tmpImage.close();
            mem.close();
            pngOptions.close();
            wmfRasterOptions.close();
        }
    }
    else
    {
        return ImageExtensions.toJava(image);
    }
}

As a new feature, we can implement new ImageExtensions.toJava method with VectorRasterizationOptions as a parameter.

Hello,

thanks for your answers, but it doesn’t solves our problems (we’re zooming the wmf/emf after loading it) and so I tried to going back to version 19.12/19.10.
The problem here is:
image.png (39,6 KB)
→ watermark which shows, that the license isn’t valid.

So it’s not possible to use a current license (bought in the meedle of last year) with an older version from aspose-imaging?

Hello, @bpetermann
A license must work with all older versions. I’ll check it today.
Could we provide your wmf file?

Here the wmf from the example, as zip:
A-Bau Ebene 0.zip (103,5 KB)

@bpetermann
Thank you, I will try it

Tested now our current license against 19.12 and 19.10. Ther result is, that the license is not accepted:

class com.aspose.imaging.coreexceptions.FrameworkException: Failed to set license. Details: Signature length not correct: got 256 but was expecting 128

com.aspose.imaging.License.setLicense(Unknown Source)

New license with 1151 bytes, the old one with 959 bytes.

So, how can I resolve this problem?

Hi, @bpetermann
About licenses
Since the version Aspose.Imaging for Java 21.4 we started to use the other SHA algorithm for the license signature. That is why the older library can not work with licenses created with SHA256. Moreover, all the new licenses have the SHA256 signature and cannot used in the libraries released before April 2021. If you have a license with SHA1 signature you use only libraries with version below 21.4.

About rendering

For your case, I think you can use aspose-imaging 24.4 but you need to change your code that implements rendering a metafile onto the Graphics2D.
I will share a code showing how to save the quality using aspose-imaging from 24.4 to 24.6.

Hi, @bpetermann
Sorry for delaying. :frowning:
Please, look at this project to understand how it is possible to scale the vector images without losing quality.
MetafileScaling.7z (8,2 КБ)

Hi,

thanks for your code example.
I’ve implemented into our application and it seems to work.

One question about the class MemoryStream (there isn’t any javadoc for it):

com.aspose.imaging.system.io.MemoryStream mem = new com.aspose.imaging.system.io.MemoryStream(
16000);
What means here “16000” exactly?

Hi, @bpetermann
Oh, no. :frowning: Sorry for that. It is not good to use MemoryStream. It is a class like ByteArrayInputStream/ByteArrayOutputStream.

What means here “16000” exactly?

It means that the internal storage will be allocated with a size of 16000 bytes.
Instead of that internal class you can use ByteArrayOutputStream for saving, and ByteArrayInputStream for loading.

Ok, I understand.
Now the problem is:
For bigger zoomed vector images:

PngOptions pngOptions = new PngOptions();
pngOptions.setVectorRasterizationOptions(vectorOptions);
ByteArrayOutputStream out = new ByteArrayOutputStream();
image.save(out, pngOptions);

“hangs”, which means perhaps, that there isn’t enough memory available.
So, what can I do here?

@bpetermann
Yes, it could be. To resolve the memory issue we have two options

  1. Save the rasterized image into a file
PngOptions pngOptions = new PngOptions();
pngOptions.setVectorRasterizationOptions(vectorOptions);
image.save("tempfile.png", pngOptions);
  1. We can limit the memory used inside Aspose.Imaging using BufferHint + storing in file
PngOptions pngOptions = new PngOptions();
pngOptions.setBufferHint(500); // memory limit in MB
pngOptions.setVectorRasterizationOptions(vectorOptions);
image.save("tempfile.png", pngOptions);

In case the image is huge, there are some tricks using

RasterImage.loadPartialArgb32Pixels(Rectangle rectangle, 
    IPartialArgb32PixelLoader partialPixelLoader)

More information RasterImage | Aspose.Imaging for Java API Reference

Hello,

tried now a lot of different options (also in using jpeg unstead of png), but

image.save(out, pngOptions);

hangs (over hours without any error), if we are using a large wmf/emf.
For example try to use the uploaded wmf (from June 10) and scale it up with the factor 20.

Also in compare to the older solution:

this.bImage = this.metafile.createDefaultRendering();
this.metafile.playMetafile(g2d);

the whole thing is much slower, so we can’t use in this way for our application.

So my questions:

  • do you can find out, where image.save(out, pngOptions) will hang?
  • how can we make the image.save method faster, without loosing quality (also

this.bImage = ImageExtensions.toJava(this.metaImage);
is very slow for bigger images)?

  • isn’t there a chance to get a license, which is usable with a version < 20.1?

RasterImage.loadPartialArgb32Pixels(Rectangle rectangle,
IPartialArgb32PixelLoader partialPixelLoader)
→ I don’t understand how use it for the image.save method?

Thank’s for your patience.

Hi, @bpetermann
So sorry to hear it.

hangs (over hours without any error), if we are using a large wmf/emf.
For example try to use the uploaded wmf (from June 10) and scale it up with the factor 20.

I will try it.

the whole thing is much slower, so we can’t use in this way for our application.

Yes, this could be. The problem is that the old version was completely removed from our product for many reasons, and we can’t use it again. The new version is more powerful and produces more qualified results but is still slower than the old one although we try to improve and optimize it.

Answering your questions

do you can find out, where image.save(out, pngOptions) will hang?

It is an unpredictable behavior, and we will find out the reason.

how can we make the image.save method faster, without loosing quality (also

We are working on this issue.

isn’t there a chance to get a license, which is usable with a version < 20.1?

Unfortunately, we can’t make the old versions of the library support the new version of the license by many reasons.

RasterImage.loadPartialArgb32Pixels
I don’t understand how to use it for the image.save method?

I will prepare an example for you soon.

@bpetermann
We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): IMAGINGJAVA-8774

You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.