Converting emf or wmf to BufferedImage --> bad quality

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.

I see, the the issue status from IMAGINGJAVA-8774 os resolved.
Means this, that the next version of apsose.imaging will include the bugfix for it?

@bpetermann
Hi!
Yes, some optimization has been implemented for such a situation. They will be available in the next version 24.8 at the beginning of August.
Talking about your specific case I recommend using the new API for rendering. Please, take a look at an example.

import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
import com.aspose.imaging.Image;
import com.aspose.imaging.awt.GraphicsRenderer;

BufferedImage image = new BufferedImage(300, 300, BufferedImage.TYPE_INT_ARGB);

try (Image wmf = Image.load("some.wmf")) // any image file
{
    final Graphics2D graphics = image.createGraphics();
    try (GraphicsRenderer renderer = new GraphicsRenderer(wmf, Color.getWhite(), SmoothingMode.HighQuality, TextRenderingHint.ClearTypeGridFit))
    {
        renderer.render(graphics);
        // or with scaling
        // renderer.render(graphics, 10f);
    }
}

The best way is to initialize GraphicsRenderer only once when you load a new image and then just use GraphicsRenderer.render(Graphics2D, scale) in the paint method of your Swing component.