Image maps of an arbitrary area

David Gavilan
Real Time Rendering
3 min readAug 14, 2021
Dumper web app

Introduction

Web developers may be familiar with the map and area tags to define clickable areas in an image. The area tag lets you define rectangular, circular, or polygonal areas. But wouldn’t it be easy if we just painted the areas in a separate image that defines the clickable sections? That’s what I made this tool for: Dumper (github).

It was common in the old 8-bits and 16-bits days to use indexed images this way to define areas. However, indexed images aren’t a thing anymore, and the common thing in browsers is that when the image gets loaded, it gets automatically converted to an RGB image. That means it’s not easy to get the index values with standard Javascript. So this Dumper web app I made simply lets you define a color palette, and looks for the actual RGB values in your images to give you the index back from the color palette. You can dump the values as hexadecimal numbers. The numbers are displayed with their actual color in the palette. The idea is that you copy and paste those values in your source code to embed image selections.

You can try the Dumper web app here: Dumper (web).

I made this tool last year because I wanted to make a map-based selection screen for my latest game, Sopilabitas (Word Wall ES), a Spanish word puzzle game. Here are a couple of those selection screens, as the user taps at different sections on the map:

Image maps in Swift

The way I use these maps in the game, which it’s programmed in Swift, is through these data types:

typealias UVCoord = (Float, Float)
struct ImageSegment {
let name: String
let centroid: UVCoord
}
struct ImageSegmentation {
static let empty = ImageSegmentation(width: 1, height: 1, segments: [], data: [0])
let width: Int
let height: Int
let segments: [ImageSegment]
let data: [Int]
func get(uv: UVCoord) -> Int {
let x = Clamp(Int(uv.0 * Float(width)), low: 0, high: width - 1)
let y = Clamp(Int((1 - uv.1) * Float(height)), low: 0, high: height - 1)
let i = y * width + x
return data[i]
}
}

So the data for each map looks like this:

let southAmerica = ImageSegmentation(
width: 24,
height: 52,
segments: [
ImageSegment(name: "Chile", centroid: (0.25, 0.307692)),
ImageSegment(name: "Argentina", centroid: (0.375, 0.288462)),
// ...
],
data: [0x1, 0x1, 0x0, 0x0, 0x0, 0x0,
//...
]
)

The get(uv) function in ImageSegmentation is used to convert the tapped coordinates to an index value from the map, and that way we know which country was tapped. It’s that simple.

Indices can also be used in other places, such as defining collision areas in games. It’s a very simple way to do collisions, but it works for 2D games. Perhaps you can find other usages.

I hope this is helpful to anyone making these kind of region-based interactive screens, or any other game or app.

Originally published at http://endavid.com.

--

--

David Gavilan
Real Time Rendering

Ph.D. Graphics Engineer at Metail. Worked on several graphics engines in the past (Fox Engine, Disney Infinity, mobile VR, server-side rendering).