Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Java Development

Reply
New Developer
gzusphish
Posts: 21
Registered: ‎10-05-2009
My Device: Curve 8310

Diary of a noob 4 - Exploring EncodedImage

I need to display some scaled images and I noticed the EncodedImage class while looking at the Graphics class javadocs so I explored it: here are my findings.

EncodedImage is only partially effective at scaling images. It provides a deprecated method named setScale which, used in combination with getBitmap, allowed the user to scale a bitmap. The documentation for setScale references scaleImage32(int scaleX, int scaleY) as the method introduced to replace it. The new method works well for reducing bitmaps but doesn't do a very good job of upscaling them. It also chokes when you try to upscale a bitmap and don't use the Fixed32 class properly.

 

I highly suggest you thoroughly read the javadoc for Fixed32 before trying to figure out how to use scaledImage32. Fixed32 stores a decimal as a whole number with an implied decimal (i.e. 1.0 = 0x00010000, 1.5 = 0x00018000). This method is used because scaleImage32 requires an integer version of a decimal to upscale an image. Why they didn't just make the parameters for scaleImage32 double is a mystery to me. On a related note, you can probably use hexadecimal math to make the Fixed32 scaling factors make more sense and be proportional to the scaling percentage.

The sandbox class I've provided produces a lot of information. First it gives two visual demonstrations of scaling a simple image of the number 1 both up and down. Notice in the images generated by the sandbox class that reducing the image results in repetition in some of the smaller scales. When you get to some of the higher scale values resulting in smaller scaled versions of the image the scaling actually produces no further change. Next it performs the same operation but returns statistics on the scaled images instead. Note in this info that the scaling values don't seem to be directly proportional to the percentage scaled so you'll need to play around with them to find the right one. Finally, the class shows the value of most of the parameters with the exception of those which return objects such as getData.

Although the sandbox doesn't show it, the EncodedImage class supports multiple frames in an image. This would be useful for creating animations and for simplifying storing multiple images that are the same size. But, enough about that, lets look at some code...

 

 

import net.rim.device.api.math.Fixed32;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.EncodedImage;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.BitmapField;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.component.SeparatorField;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.container.MainScreen;

/**
* This class is intended to be subclassed and used for exploring BlackBerry
* development.
*
* @author Wyatt Williamson
*/
public class GenericSandBox extends UiApplication {
/**
* The usual constructor creating a mainscreen object and pushing it
* onto the screen stack. This method should be left unchanged.
*/
public GenericSandBox() {
SandboxScreen screen = new SandboxScreen();
pushScreen(screen);
} // end GenericSandBox()

/**
* The usual main method attaching the object to the event dispatcher.
* This method should be left unchanged.
*
* @param args
* Typical main arguments which, in this case, are unused.
*/
public static void main(final String args[]) {
GenericSandBox test = new GenericSandBox();
test.enterEventDispatcher();
} // end main()

/**
* The MainScreen class that's pushed onto the screen stack. This inner
* class is the class where all the changes need to be made.
*
* @author gzusphish
*/
private class SandboxScreen extends MainScreen {
/**
* Constructor responsible for building the application's screen.
* It is where all components and containers are instantiated and
* added to the default field manager and where the majority of the
* sandbox's logic resides. This is the only part that should be
* modified when using the sandbox.
*/
public SandboxScreen() {
/*
* This MUST be the first statement in this method. If you need
* to change the way the screen or its default
* VerticalFieldManager is constructed, add flags as arguments
* to the constructor: i.e. super(Manager.NO_VERTICAL_SCROLLBAR
* | Field.FIELD_VCENTER) tocenter content vertically but hide
* the vertical scrollbar.
 */
super();

// Declare and instantiate required items.
EncodedImage eImage;
eImage = EncodedImage.getEncodedImageResource("num_1.png");

HorizontalFieldManager line1 = new HorizontalFieldManager();
HorizontalFieldManager line2 = new HorizontalFieldManager();
add(new SeparatorField());
add(line1);
add(new SeparatorField());
add(line2);

int frameIndex = 0;
int scale;
int originalWidth = eImage.getWidth();
add(new LabelField("Original width = " + originalWidth));

// Scale from what I'm assuming is 130% through 100% in 5%
// increments
for (int i = 70; i <= 100; i += 5) {
scale = Fixed32.tenThouToFP(i * 100);

EncodedImage tmpEI = eImage.scaleImage32(scale, scale);
 Bitmap bitmap = tmpEI .getBitmap();
BitmapField bf = new BitmapField(bitmap, Field.FOCUSABLE);
line1.add(bf);
} // end for

// Scale from 100% to what I'm assuming is 10% in 5% increments
for (int i = 10; i <= 100; i += 5) {
scale = Fixed32.tenThouToFP(i * 1000);

EncodedImage = eImage.scaleImage32(scale, scale);
Bitmap bitmap = tmpEI.getBitmap();
BitmapField bf = new BitmapField(bitmap, Field.FOCUSABLE);
line2.add(bf);
} // end for

/* Compare scaling factor to effective percentage. */
add(new LabelField("Scaling Factor:New Width:Percent"));

// Increment scale from 7000 to 10000 at intervals of 500
for (int i = 70; i <= 100; i += 5) {
int factor = i * 100;
scale = Fixed32.tenThouToFP(factor);

EncodedImage tmpEI = eImage.scaleImage32(scale, scale);
int newWidth = tmpEI.getBitmap().getWidth();

double pct = (double)newWidth/(double)originalWidth*100;
String stemp = factor + ":" + newWidth + ":" + pct;

add(new LabelField(stemp, Field.FOCUSABLE));
} // end for

add(new SeparatorField());
add(new LabelField("Scaling Factor:New Width:Percent"));

// Increment scale from 10000 to 100000 at intervals of 5000
for (int i = 10; i <= 100; i += 5) {
int factor = i * 1000;
scale = Fixed32.tenThouToFP(factor);

EncodedImage tmpEI = eImage.scaleImage32(scale, scale);
int newWidth = tmpEI.getBitmap().getWidth();

double pct = (double)newWidth/(double)originalWidth*100;
String stemp = factor + ":" + newWidth + ":" + pct;

add(new LabelField(stemp, Field.FOCUSABLE));
} // end for

// Tour de método. Let's see what all this thing can do.
add(new LabelField("getBitmapType(int Index) = "
+ eImage.getBitmapType(frameIndex), Field.FOCUSABLE));
add(new LabelField("getDecodeMode() = "
+ eImage.getDecodeMode(),
Field.FOCUSABLE));
add(new LabelField("getFrameCount() = "
+ eImage.getFrameCount(),
Field.FOCUSABLE));
add(new LabelField("getFrameHeight(Index) = "
+ eImage.getFrameHeight(frameIndex), Field.FOCUSABLE));
add(new LabelField("getFrameMonochrome(Index) = "
+ eImage.getFrameMonochrome(frameIndex),
 Field.FOCUSABLE));
add(new LabelField("getFrameTransparency(Index) = "
+ eImage.getFrameTransparency(frameIndex),
Field.FOCUSABLE));
add(new LabelField("getFrameWidth(Index) = "
+ eImage.getFrameWidth(frameIndex), Field.FOCUSABLE));
add(new LabelField("getHeight() = " + eImage.getHeight(),
Field.FOCUSABLE));
add(new LabelField("getImageType() = " + eImage.getImageType(),
Field.FOCUSABLE));
add(new LabelField("getLength() = " + eImage.getLength(),
Field.FOCUSABLE));
add(new LabelField("getMIMEType() = " + eImage.getMIMEType(),
Field.FOCUSABLE));
add(new LabelField("getOffset() = " + eImage.getOffset(),
Field.FOCUSABLE));
add(new LabelField("getScale() = " + eImage.getScale(),
Field.FOCUSABLE));
add(new LabelField("getScaledFrameHeight(Index) = "
+ eImage.getScaledFrameHeight(frameIndex),
Field.FOCUSABLE));
add(new LabelField("getScaledFrameWidth(Index) = "
+ eImage.getScaledFrameWidth(frameIndex),
 Field.FOCUSABLE));
add(new LabelField("getScaledHeight() = "
+ eImage.getScaledHeight(), Field.FOCUSABLE));
add(new LabelField("getScaledHeight() = "
+ eImage.getScaledHeight(), Field.FOCUSABLE));
add(new LabelField("getScaledWidth() = "
  + eImage.getScaledWidth(),
Field.FOCUSABLE));
add(new LabelField("getScaledWidth(srcWidth) = "
+ eImage.getScaledWidth(), Field.FOCUSABLE));
add(new LabelField("getScaleX32() = " + eImage.getScaleX32(),
Field.FOCUSABLE));
add(new LabelField("getScaleY32() = " + eImage.getScaleY32(),
Field.FOCUSABLE));
add(new LabelField("getWidth() = " + eImage.getWidth(),
Field.FOCUSABLE));
add(new LabelField("hasTransparency() = "
+ eImage.hasTransparency(), Field.FOCUSABLE));
add(new LabelField("isMonochrome() = " + eImage.isMonochrome(),
Field.FOCUSABLE));
} // end SandboxScreen()
} // end SandboxScreen
} // end GenericSandBox

 

 

Please use plain text.
Developer
peter_strange
Posts: 19,602
Registered: ‎07-14-2008
My Device: Not Specified

Re: Diary of a noob 4 - Exploring EncodedImage

Sorry I have not had time to review the code or comments in detail, but just wanted to say thank you for taking the time to do this.  Good job.

Please use plain text.
New Developer
gzusphish
Posts: 21
Registered: ‎10-05-2009
My Device: Curve 8310

Re: Diary of a noob 4 - Exploring EncodedImage

[ Edited ]

Thanks for the kudos Mr. Anderson . . . erm . . . Mr. Strange (sorry, lapsed into a Matrix moment there). The BlackBerry has you . . . GAA!

 

I just hope I did the tagging right. I don't know if I was supposed to or not, so I decided not to go overboard with them but I think I still put one too many. Is "scale" a good tag? It's what the example is doing but it may be a bit broad. I also left off Bitmap and BitmapField because the example contains them but that's not what the post is about.

 

The only thing left to do is figure out how to calculate the scaling value. Say, for instance, I wanted to scale five images to fit side-by-side across the screen. In my case, I need to fit twelve images across the screen since I'm making a timer display as follows HH:MM:smileyfrustrated:S:mmm and I'd like them to be as large as possible on the screen of any given model. I think the answer will have something to do with hex math, but I'm not sure.

Please use plain text.
New Developer
gzusphish
Posts: 21
Registered: ‎10-05-2009
My Device: Curve 8310

Re: Diary of a noob 4 - Exploring EncodedImage

Here's a composit screen shot of the output of the sandbox.

EncodedImage.png

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Here's the image - num_1.png - that was used for the example.

num_1.png

Please use plain text.
Developer
rcmaniac25
Posts: 1,804
Registered: ‎04-28-2009
My Device: Z10 (STL100-4)-10.2.1.3253, Z10 (STL100-3)-10.3.1.634 Dev OS, Z30 (STA100-5)-10.3.1.634 Dev OS, Passport (SQW100-1)-10.3.0.1154, PlayBook (16GB)-2.1.0.1917
My Carrier: Verizon

Re: Diary of a noob 4 - Exploring EncodedImage

[ Edited ]

Good job, I will respond to your post "backwards."

 

One idea is to add up all the default width's of the images that you want to display, then use that in comparison with screen width/height, whatever you are trying to fit it to. Then scale each appropriately based on that information. For example you have 2 images that are 30 and 20 pixels wide, the field/screen you are putting them in is 100 pixels wide. 30+20=50 so you scale it to 100 pixels (by multiplying by 2). 30*2=60, 20*2=40. There you have scaled them to fit the area.

 

Don't worry about tagging. You can do it for what you see fit (I myself can never seem to find what I am looking for when I do a search so I tag all my posts, this is evident if you look at the Top Taggers ranking).

 

When scaling the simple equation I remember reading was "scale = original size / new size", this can be translated to code as "int scale = Fixed32.div(Fixed32.toFP(original_size), Fixed32.toFP(new_size));" I don't know why they did it in that manner but that equation will get the job done.

 

BlackBerry's often don't have a FPUs and if you search around there is a post where one of the more professional developers on the forum compared the different data types and found that Float's and Double's are (I think it said) almost 2 times slower then processing Long's which are (I think it said) about 1.25-1.5 times slower then using an Integer. This is why Fixed32 is used because it uses Integer's to do all the processing and can operate extremely fast.

 

FInally if you are using 5.0 API you have a little more options with scaling. If you will be generating all images on device (as opposed to loading them from files) then Bitmap is the way to go, otherwise EncodedImage. Either way you can get it as a Bitmap which, not only have general scaling functions now but, have scaling functions that you can choose how you want the scaling (Box, Bilinear, or Lancozos). It also has a, what you might consider, better way of scaling where you pass in a image at the destination size-Bitmap.

 

Overall a very good post with a relatively large amount of information.

---Spends time in #blackberrydev on freenode (IRC)----
Three simple rules:
1. Please use the search bar before making new posts.
2. "Like" posts that you find helpful.
3. If a solution has been found for your post, mark it as solved.
--I code too much. Well, too bad.
Please use plain text.
New Developer
gzusphish
Posts: 21
Registered: ‎10-05-2009
My Device: Curve 8310

Re: Diary of a noob 4 - Exploring EncodedImage

[ Edited ]

Thanks for mentioning the versioning as that's one of the things I wanted to include. The scaleImage32 method only goes back to version 4.2.0 whereas setScale and getBitmap go all the way back to 3.6.0. The Fixed32 class was avaliable back in 4.0.0 so it should be okay to use with scaleImage32 so long as you're targeting users with V4.2.0 and onward. As rc implied, if you want to use the latest and greatest, the Bitmap class has lots of bells and whistles that make scaling images easier and will produce better results because of the inclusion of what I believe are called interpolation algorithms (ow, that almost hurt to say).

 

Also, I figured out the exact method of calculating the scaling factor which should be passed to Fixed32.tenThouToFP if you're using that method. It's kind of backward because if you want to reduce an image you have to provide a scaling factor higher than 10000, whereas if you want to increase an image you have to provide a scaling factor lower than 10000.

 

If you want to scale by a whole percentage then you have to modify the percentage before you can do so. When scaling an image larger you have to subtract one hundred from the percentage; for instance, to scale an image by 150% you actually use 50 instead and, likewise, 30 for 130. The percentage must add one hundred to the percentage when scaling smaller; so use 150 to scale to 50% and 190 to scale to 90%. Once you have the modified percentage, divide one million by that percentage [i.e. 1000000/175 will produce the factor to scale the image to 75% of its original size and 1000000/37 will produce the factor to scale the image to 137% of its original size].

 

If you want to scale an image using a fractional percentage, as rc mentioned, divide the original size by the new size to get the fractional percentage and then modify it similar to using a whole percentage before using it to derive the scaling factor. When increasing or decreasing an image by scaling you subtract 1 and add 1 respectively to the fractional percentage. To scale an image to 150% (1.5) of its original size, subtract 1 from the fractional percentage (i.e. 1.5 - 1 = .5). To scale an image to 75% (.75) of its original size, add 1 to the fractional percentage (i.e. .75 + 1 = 1.75). Using the modified fractional percentage, divide ten thousand by that fractional percentage [i.e. (10000 / ( (30 / 60) + 1) ) willproduce the factor to scale by 50% as will 10000 / 1.5].

Please use plain text.
Developer
rcmaniac25
Posts: 1,804
Registered: ‎04-28-2009
My Device: Z10 (STL100-4)-10.2.1.3253, Z10 (STL100-3)-10.3.1.634 Dev OS, Z30 (STA100-5)-10.3.1.634 Dev OS, Passport (SQW100-1)-10.3.0.1154, PlayBook (16GB)-2.1.0.1917
My Carrier: Verizon

Re: Diary of a noob 4 - Exploring EncodedImage

More good information, I used toFP because I don't know of any images that are 100.5 pixels wide. :smileyhappy:

---Spends time in #blackberrydev on freenode (IRC)----
Three simple rules:
1. Please use the search bar before making new posts.
2. "Like" posts that you find helpful.
3. If a solution has been found for your post, mark it as solved.
--I code too much. Well, too bad.
Please use plain text.
Developer
Atraxis
Posts: 27
Registered: ‎09-04-2009
My Device: Not Specified

Re: Diary of a noob 4 - Exploring EncodedImage

you could pass your field manager dynamic with and height, and then you can calculate?

 

height:

return Math.max(getFont().getHeight(), image.getHeight())

 

width:

int width = getFont().getAdvance(label)

width += image.getWidth()

return width;

 

So you have dynamic values and can write a scaling algo that does not depend on the device type.. i hope i'm right ;-)

Please use plain text.
New Developer
gzusphish
Posts: 21
Registered: ‎10-05-2009
My Device: Curve 8310

Re: Diary of a noob 4 - Exploring EncodedImage

Sorry bout the images. I uploaded them when I made the post and they haven't been approved yet. I'll probably use Flickr from now on.

Please use plain text.
New Developer
gzusphish
Posts: 21
Registered: ‎10-05-2009
My Device: Curve 8310

Re: Diary of a noob 4 - Exploring EncodedImage

[ Edited ]

 


rcmaniac25 wrote:

More good information, I used toFP because I don't know of any images that are 100.5 pixels wide. :smileyhappy:


 

I used tenThouToFP because I couldn't - or was too lazy to - figure out how to use toFP. Also, I didn't think about ending up with half pixels. Maybe I'll do a post on Fixed32 next.

 

I'll probably use the method you mentioned in an earlier reply as it seems to fit the bill and is a lot more simple and elegant than anything I've thought of.

 

 

int scale = Fixed32.div(Fixed32.toFP(original_size), Fixed32.toFP(new_size));

 

 

 

Please use plain text.