BlackBerry 10 Cascade blog

A blog for developpers

Building an Image Pixel by Pixel

Today I had to make a list representing a map legend with different colors and titles. The list was dynamically send as a JSON from the server. My first tougth was to add some ActionItems to the page but how do I represent color in an ActionItem. Looking at the API we can find that an ActionItem contains a title, an image and a triggered signal. It seems to be enough for what I want to do. I can’t have an image for every single hex color code, so I my idea was to create an Image of a colored square from scratch. Good luck finding any documentation on this… I mean other than that blog post :P. So there it is! In this post you will find out how I managed to create an image from nothing else than a hex color code and from an other basic Image.

Creating an image from scratch

By reading the Image’s documentation of BlackBerry, there is a way to create an Image from scratch giving a color to each pixel of a table.

1
2
3
4
5
6
7
8
9
10
int data[8][8];
for (int y=0;y<8;y++) {
    for (int x=0;x<8;x++) {
        data[x][y] = rand();
    }
}
Image m_image = bb::ImageData::fromPixels((
        const unsigned char*)data,
        bb::PixelFormat::RGBX,
        8, 8, 8*4);

In this case they are creating a random colored square and the color is defined by rand(). Alright but my JSON says #CC6633. How do I choose the color of a pixel? Here’s how color basically works.

When you have a color like #CC6633, it’s the same thing as saying you have a RGB color of (CC,66,33) with hexadecimal values. By taking CC6633 and converting it to unsigned int, we get the decimal value we are searching for telling the pixel color. Here’s a little fonction that can convert your hex value to a int.

1
2
3
4
5
6
int Class::colorStringToInt(const QString color) const {
  QString pColor = color;
  bool ok;
  uint pColorInt = pColor.toUInt(&ok, 16);
  return pColorInt;
}

By using the color value and the Image code we can now create an Image of a square with the given color. But by using this code you will notice something strange. The color is not right! In fact, and I don’t know why but #CC6633 will look like #3366CC. It seem that the code is inversing R and B of RGB giving BGR. By changing the original color from #CC6633 to #3366CC you will get the expected color. I’m still searching for an explanation for that. I should poke BlackBerry with that question.

Now that we obtain the result with our legend, we need some custom pin on the map representing these element from the map legend. Should be simple to create an Image with the right color but this time using a Marker. Oh wait! A marker need a uri to an image, can’t give an Image object :(. Alright then! We can still make an Image, save it in the device and give the uri to the Marker.

Creating an Image based on an other PNG

A map pin can’t be just a square in needs to look like a pin. I do have a white pin icon but I need it colored. Here what I do.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
QDir app("app/native/assets/images/");
QString relpath = app.absoluteFilePath("pin.png");
QImageReader image_reader(relpath);

QImage pin = image_reader.read();

bb::ImageData imageData(bb::PixelFormat::RGBA_Premultiplied, pin.width(), pin.height());

int imgData[imageData.height()][imageData.width()];
for (int y = 0; y < imageData.height(); y++) {
  for (int x = 0; x < imageData.width(); x++) {
      if(qAlpha(pin.pixel(x, y)) == 255){
          imgData[y][x] = colorStringToInt()
      }
  }
}

First, I open the Image. I also tried to begin by creating an Image but the ImageData extraction was a pain. I decided to use a QImage giving me a chance to get the information of each pixel with RGBA value. I used the alpha value to detect a basic shape of the pin that I colored. I can now save the Image and give the path to the Marker.

1
2
bb::ImageData pinImage = bb::ImageData::fromPixels((const unsigned char*)imgData, bb::PixelFormat::RGBA_Premultiplied, imageData.width(), imageData.height(), imageData.width()*4);
ImageConverter::encode("image/png", pinImage, 100);

Looking at the result I found that the pin is the right color but everything else is black!

To resolve this problem we need to change the pixel type that we use for ImageData creation and change the way we find the int value of the color. For a transparent pixel you will find that the value is 16777215.

1
2
3
4
5
6
7
int Class::colorStringToInt(const QString color) const {
  QString pColor = color;
  pColor = "FF" + pColor.remove(0,1);
  bool ok;
  uint pColorInt = pColor.toUInt(&ok, 16);
  return pColorInt;
}

The “FF” is the value for an alpha of 255. Convert the alpha value to hexa and replace the “FF” by this value to have a variable alpha.

Here we go! Now with the basics of decoding and encoding an image it’s a good start to make more sophisticated things.

Things that still need work:

  • Why does RGB is BGR?
  • By usign the color tones there is a way to modify the basic color and get an image with more definition.