How to control/scale image size in Codename One? - codenameone

I used the (new) GUI Builder and inserted an image (by way of adding a Label). However, it appears too big. Is there anyway I can scale and control the size? (I saw something which points to cloudinary but that seems too complicated. I just want to simply scale down the image.)

There are several ways to resize images in Codename One and I will mention few below:
1.
Use MultiImages in the GUI Builder. With this multiple sizes of images are generated from one image based on the sizes you specified. In your GUI Builder, Click Images -> Add Multi Images -> Select your image -> Check Preserve Aspect Ratio -> Increase the % that represents the percentage of the screen width you want the image to occupy. Set any DPI you don't require to 0.
2.
Use ScaledImageLabel or ScaledImageButton, it will resize the image the fill available space the component is occupying.
3.
Scale the image itself in code (This is not efficient, though):
public static Image getImageFromTheme(String name) {
try {
Resources resFile = Resources.openLayered("/theme");
Image image = resFile.getImage(name);
return image;
} catch (IOException ioe) {
//Log.p("Image " + name + " not found: " + ioe);
}
return null;
}
Image resizedImage = getImageFromTheme("myImage").scaledWidth(Math.round(Display.getInstance().getDisplayWidth() / 10)); //change value as necessary
4.
Mutate the image (Create an image from another image).

Related

Codename One: how to get the width in millimeters of a component?

I created an app to calculate the sizes of a multi-image, in order to use them in the Codename One designer.
As you can see in the screenshot below, my app has a slider and I get the width (in millimeters) of it. The problem is that the value of the selected width is incorrect. I tested the app on two Android devices and the measured lengths are different from the ones reported by the app.
You can see the full source code, however the relevant code is the following:
Label value = new Label("Move the cursor on the slider...");
Style thumbStyle = new Style();
thumbStyle.setFont(Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_LARGE), true);
Slider slider = new Slider();
slider.setMaxValue(1000);
slider.setMinValue(0);
slider.setProgress(20); // Set the starting value
slider.setThumbImage(FontImage.create("|", thumbStyle));
slider.setEditable(true); // to it works as a slider instead of a progress bar
slider.addActionListener(e -> {
Integer valueSelected = slider.getProgress();
Integer sliderWidth = slider.getWidth();
Double inch = sliderWidth.doubleValue() / (slider.getMaxValue() - slider.getMinValue()) * valueSelected / 100.0;
Integer millimeters = Double.valueOf(inch * 25.4).intValue();
value.setText("Value selected: " + millimeters.toString() + " mm");
});
Thank you very much for any help
Millimeter measures aren't accurate. A device can return different values or ratios for the convert method than the value it returns for the density flag.
Unfortunately, Googles test suite to certify a device as "good" doesn't actually cover these things. There isn't much we can do about that.

Share image painted on a larger graphics than the screen itself

The code bellow will paint he component on the image graphics - but it will translate it - instead of fitting it to the size of the graphics.
int ratio = 2;
Image screenshotImage = Image.createImage(getWidth()* ratio,getHeight()* ratio);
c.revalidate();
c.setVisible(true);
c.paintComponent(screenshotImage.getGraphics(), true);
I cannot use the the image and just scale it because some of the content will be truncated.
Can the component paint itself on image graphics with specified size.
Many thanks
Use:
c.setX(0);
c.setY(0);
c.setWidth(img.getWidth());
c.setHeight(img.getHeight());
if(c instanceof Container) {
c.setShouldLayout(true);
c.layoutContainer();
}
c.remove();
c.paintComponent(myImage.getGraphics(), true);
// add c back to it's parent container and revalidate
If the component is currently on a form you would want to undo all of that by calling revalidate on the parent form afterwards.

Why is the width of my placeholder not abode in URLImage (Codename One)

My app features a form with 2 images. The first one is downloaded and rounded via an ImageAdapter on the fly and the second one is from the theme. They are applied to a Button and laid next to each other as shown below :
In my Form
The placeholder in my form is defined as :
int imageWidth = (int) (Display.getInstance().getDisplayWidth() / 2.5);
EncodedImage placeholder = EncodedImage.createFromImage(Image.createImage(
imageWidth,
imageWidth,
ParametresGeneraux.accentColor), true);
The image I apply on the button is created like this :
reportImage = URLImage.createToStorage(placeholder,
Report.getFilename(chosenReport.getPhotoPath()),
chosenReport.getPhotoPath(),
ParametresGeneraux.RESIZE_SCALE_WITH_ROUND_MASK
);
Then I do just afterwards
System.err.println("The placeholder in FORM has a width of " + placeholder.getWidth()); // 432
I get a width of 432 for the placeholder from within the FORM.
However from the ImageAdapter
public final static URLImage.ImageAdapter RESIZE_SCALE_WITH_ROUND_MASK = new URLImage.ImageAdapter() {
#Override
public EncodedImage adaptImage(EncodedImage downloadedImage, EncodedImage placeholderImage) {
System.err.println("The placeholder in the ImageAdapter has a width of " + placeholderImage.getWidth());
// ...
}
});
The placeholder width I get from within the ImageAdapter is different with a value of 200.
Where should I look at for a mistake, since the placeholder widths should be the same in both methods, shouldn't they ?
Edit 2017-02-23 : Important note
Before showing this form, the involved image has already been downloaded a first time in another form with a different size. And actually the message from the ImageAdapter (see System.err()) is not triggered. I overflew this because many images are loaded. See my answer for the continuation.
Any hint appreciated,
Regards
Eventually it appeared that the
System.err.println("The placeholder in the ImageAdapter has a width of " + placeholderImage.getWidth());
displayed in the console was not from the ImageAdapter call in this form but from the previous form which actually downloaded and showed the same image a first time.
So the second time I call URLImage.createToStorage() with the same internal name (see Report.getFilename(chosenReport.getPhotoPath()) in my question) as the file has already been downloaded and is present in cache, it is not downloaded again as written in the doc :
Notice that the image that will be saved is the scaled image, which means you will have very little overhead in downloading images that are the wrong size although you will get some artifacts.
Finally if I changed the internal name, and then even though the Image gets downloaded it scales perfectly :

Codename One rounded image from an internet source

I am trying to display a rounded image that I get straight from the Internet.
I used the code below to create a round mask, get the image from the Internet, then tried to either set the mask on the image or the label itself. None of these approaches worked. If I remove the mask, the image is displayed fine. If I keep the code to set the mask then all I see is an empty white circle.
I have the idea that if I apply the mask on the image itself, then it may not take effect because the image was not downloaded at the time the mask was applied.
But I don't seem to understand why calling setMask on the label is also not working.
// Create MASK
Image maskImage = Image.createImage(w, l);
Graphics g = maskImage.getGraphics();
g.setAntiAliased(true);
g.setColor(0x000000);
g.fillRect(0, 0, w, l);
g.setColor(0xffffff);
g.fillArc(0, 0, w, l, 0, 360);
Object mask = maskImage.createMask();
// GET IMAGE
com.cloudinary.Cloudinary cloudinary = new com.cloudinary.Cloudinary(ObjectUtils.asMap(
"cloud_name", "REMOVED",
"api_key", "REMOVED",
"api_secret", "REMOVED"));
// Disable private CDN URLs as this doesn't seem to work with free accounts
cloudinary.config.privateCdn = false;
Image placeholder = Image.createImage(150, 150);
EncodedImage encImage = EncodedImage.createFromImage(placeholder, false);
Image img2 = cloudinary.url()
.type("fetch") // Says we are fetching an image
.format("jpg") // We want it to be a jpg
.transformation(
new Transformation()
.radius("max").width(150).height(150).crop("thumb").gravity("faces").image(encImage, "http://upload.wikimedia.org/wikipedia/commons/4/46/Jennifer_Lawrence_at_the_83rd_Academy_Awards.jpg");
Label label = new Label(img2);
label.setMask(mask); // also tried to do img2.applyMask(mask); before passing img2
So I tried various things:
1) Removing the mask that was set through cloudinary - That did not work
2) applied the mask to the placeholder & encoded image (as expected these shouldnt affect the final version that is getting published)
3) This is what works! I am not sure if the issue is really with downloading the picture before or after applying the mask.. time can tell down the road
Label label = new Label();
img2.applyMask(mask); // If you remove this line , the image will no longer be displayed, I will only see a rounded white circle ! I am not sure what this is doing, it might be simply stalling the process until the image is downloaded? or maybe somehow calling repaint or revalidate
label.setIcon( img2.applyMask(mask));
Here is what worked for me if anyone else is having similar issues:
//CREATE MASK
Image maskImage = Image.createImage(w, l);
Graphics g = maskImage.getGraphics();
g.setAntiAliased(true);
g.setColor(0x000000);
g.fillRect(0, 0, w, l);
g.setColor(0xffffff);
g.fillArc(0, 0, w, l, 0, 360);
Object mask = maskImage.createMask();
//CONNECT TO CLOUDINARY
com.cloudinary.Cloudinary cloudinary = new com.cloudinary.Cloudinary(ObjectUtils.asMap(
"cloud_name", "REMOVED",
"api_key", "REMOVED",
"api_secret", "REMOVED"));
// Disable private CDN URLs as this doesn't seem to work with free accounts
cloudinary.config.privateCdn = false;
//CREATE IMAGE PLACEHOLDERS
Image placeholder = Image.createImage(w, l);
EncodedImage encImage = EncodedImage.createFromImage(placeholder, false);
//DOWNLOAD IMAGE
Image img2 = cloudinary.url()
.type("fetch") // Says we are fetching an image
.format("jpg") // We want it to be a jpg
.transformation(
new Transformation()
.crop("thumb").gravity("faces")
.image(encImage, url);
// Add the image to a label and place it on the form.
//GetCircleImage(img2);
Label label = new Label();
img2.applyMask(mask); // If you remove this line , the image will no longer be displayed, I will only see a rounded white circle ! I am not sure what this is doing, it might be simply stalling the process until the image is downloaded? or maybe somehow calling repaint or revalidate
label.setIcon( img2.applyMask(mask));
Shai, I seriously appreciate your time!! Thank you very much. Will have to dig more into it if it gives me any other problems later but it seems to consistently work for now.
The Cloudinary API returns a URLImage which doesn't work well with the Label.setMask() method because, technically, a URLImage is an animated image (it is a placeholder image until it finishes loading, and then "animates" to become the target image).
I have just released a new version of the cloudinary cn1lib which gives you a couple of options for working around this.
I have added two new image() methods. One that takes an ImageAdapter parameter that you can use to apply the mask to the image itself, before setting it as the icon for the label. Then you wouldn't use Label.setMask() at all.
See javadocs for this method here
The other method uses the new Async image loading APIs underneath to load the image asynchronously. The image you receive in the callback is a "real" image so you can use it with a mask normally.
See javadocs for this method here
We are looking at adding a soft warning to the Label.setMask() and setIcon() methods if you try to add an "animated" image and mask it so that it is more clear.
I think the making code you set to the label might be conflicting with the masking code you get from Cloudinary.

WPF image vector format export (XPS?)

Our tool allows export to PNG, which works very nicely.
Now, I would like to add export to some vector format. I tried XPS, but the results are not satisfying at all.
Take a look at a comparison http://www.jakubmaly.cz/xps-vs-png.png.
The picture on the left comes from an XPS export, the picture on the right from PNG export, the XPS picture is visibly blurred when opened in XPS Viewer and zoomed 100%.
Are there any settings that I am missing or why is it so?
Thanks,
Jakub.
A sample xps output can be found here: http://www.jakubmaly.cz/files/a.xps.
This is the code that does the XPS export:
if (!boundingRectangle.HasValue)
{
boundingRectangle = new Rect(0, 0, frameworkElement.ActualWidth, frameworkElement.ActualHeight);
}
// Save current canvas transorm
Transform transform = frameworkElement.LayoutTransform;
// Temporarily reset the layout transform before saving
frameworkElement.LayoutTransform = null;
// Get the size of the canvas
Size size = new Size(boundingRectangle.Value.Width, boundingRectangle.Value.Height);
// Measure and arrange elements
frameworkElement.Measure(size);
frameworkElement.Arrange(new Rect(size));
// Open new package
System.IO.Packaging.Package package = System.IO.Packaging.Package.Open(filename, FileMode.Create);
// Create new xps document based on the package opened
XpsDocument doc = new XpsDocument(package);
// Create an instance of XpsDocumentWriter for the document
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
// Write the canvas (as Visual) to the document
writer.Write(frameworkElement);
// Close document
doc.Close();
// Close package
package.Close();
// Restore previously saved layout
frameworkElement.LayoutTransform = transform;
Interesting (and annoying) issue - you may want to check out the lengthy answer from Jo0815 to Printing XpsDocument causes resampled images (96dpi?) - FixedDocument prints sharp, quoting a Microsoft support response - a couple of excerpts:
Some vector features from WPF cannot be emulated in our GDI code and
we resort to converting subsets of the scene to GDI bitmaps. These
bitmaps are the cause of the blurred zooming.
[...]
These bitmaps are the cause of the blurred zooming. The problem is
that the WPF is being rasterised to a bitmap at the -wrong resolution.
The print path is designed to rasterise unsupported features into a
bitmap, but it is supposed to do it at device resolution. Instead the
rasterisation is always being done at 96dpi. That's fine for a screen
but produces blurred output for a 600dpi printer. [emphasis mine]
Please note that the latter will apply for nowadays higher DPI screens as well of course, I've encountered blurring like this various times already - do you by chance use a high DPI monitor?
Now, apparently Microsoft is not entirely in control of the apparatus regarding this:
Additionally the problem only occurs when printing XPS and isn't a
problem when printing XAML directly. I'm pretty sure there is
documentation somewhere that says XPS will print at device resolution.
[...] It is something we
plan to improve in the next version of the product but not for Win 7.
The problem is that when printing XAML it will correctly render the
image at 600dpi, but when printing XPS it will still render the image
at 96dpi. Since XAML is converted to XPS before printing it seems
highly odd that one method of printing XPS produces different results
to another method of printing XPS. [emphasis mine]
[...]
There is no UI to configure the XPS Document Writer DPI. If you later
print a generated XPS document at a different DPI from the writers
internal default you may get poor results for bitmap content. With GDI
printers you can control the final DPI and your final desitination is
usally paper - no chance to reprint the document.
Conclusion
In conclusion, I'd still try to adjust PrintTicket.PageResolution Property within Néstor Sánchez' approach (+1), if your use case does allow this (though I remotely recall reading somewhere, that this doesn't have any effect as well); section Bitmap Resolution and Pixel Format in Using the XPS Rasterization Service confirms the issue he encountered with FixedDocument:
XPS rasterizer object for a fixed page must know the resolution at
which the page will be rendered. The XPSDrv filter specifies this
resolution, in dots per inch (DPI), as an input parameter [...] For example, if a display device has a resolution
of 600 DPI, and a fixed page describes a standard letter-size page, a
bitmap image of the entire page has the following dimensions [...]
Workaround
As a potential workaround you might want to explore alexandrud's solution for the related question How to convert a XPS file to an image in high quality (rather than blurry low resolution)?, which recommends using xps2img, a XPS (XML Paper Specification) document to set of images conversion utility. In particular it Allows to specify images size or DPI, which might help depending on the print path solution applied in turn.
Good luck!
I've had a similar problem. My image was very blurry when passed to XPS intermediated thru a FixedDocument.
The solution was to write the image directly to the XPS...
/// <summary>
/// Saves the supplied visual Source, within the specified Bounds, as XPS in the specified File-Name.
/// Returns error message or null when succeeded.
/// </summary>
public static string SaveVisualAsXPS(Visual Source, Size Bounds, string FileName)
{
string ErrorMessage = null;
try
{
using (var Container = Package.Open(FileName, FileMode.Create))
{
using (var TargetDocument = new XpsDocument(Container, CompressionOption.Maximum))
{
var Writer = XpsDocument.CreateXpsDocumentWriter(TargetDocument);
var Ticket = GetPrintTicketFromPrinter();
if (Ticket == null)
return "No printer is defined.";
Ticket.PageMediaSize = new PageMediaSize(Bounds.Width, Bounds.Height);
var SourceVisual = Source;
Writer.Write(SourceVisual, Ticket);
}
}
}
catch (Exception Problem)
{
ErrorMessage = "Cannot export document to XPS.\nProblem: " + Problem.Message;
}
return ErrorMessage;
}
Giving a print-ticket with the exact width and height avoids scaling (that was I wanted in my case).
Get the function from the example in:
http://msdn.microsoft.com/en-us/library/system.printing.printticket.aspx

Resources