Parsing a favicon with {minitypes}

minitypes

minitypes is a package of type conversion tools for R atomic vectors.

This package is built upon R’s type conversion tools and aims to:

  • have a consistent interface for converting between types
  • add handling for types not handled by R explicitly e.g. uint8

It’s available from github.

Worked Example - Parsing an ICO favicon

  • Read in a favicon file (ICO) as raw bytes (ICO values are represented in little-endian byte order)
  • Manually parse the ICO header (see e.g. ICO format)
  • Parse out the pixels and plot

Read the favicon as raw bytes

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# cran.r-project favicon!
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# https://cran.r-project.org/favicon.ico
ico_filename <- "../../static/img/minitypes/favicon.ico"
ico          <- readBin(ico_filename, 'raw', n = file.size(ico_filename))

head(ico, 50)
 [1] 00 00 01 00 02 00 10 10 00 00 01 00 20 00 68 04 00 00 26 00 00 00 20 20 00
[26] 00 01 00 20 00 a8 10 00 00 8e 04 00 00 28 00 00 00 10 00 00 00 20 00 00 00

Read the header to find where the iamge data starts

# Reserved - must always be zero
raw_to_uint16(ico[1:2], endian = 'little')  
[1] 0

# image type. 1 = ICO, 2 = CUR
raw_to_uint16(ico[3:4], endian = 'little')  
[1] 1

# number of images in the file
raw_to_uint16(ico[5:6], endian = 'little')  
[1] 2

# width, height
raw_to_uint8(ico[7:8], endian = 'little') 
[1] 16 16

# number of color planes
raw_to_uint16(ico[11:12], endian = 'little') 
[1] 1

# bits per pixel
raw_to_uint16(ico[13:14], endian = 'little') 
[1] 32

# number of bytes of image data
raw_to_int32(ico[15:18], endian = 'little') 
[1] 1128

# Image data offset
raw_to_int32(ico[19:22], endian = 'little') 
[1] 38

Read one channel of the image data

# read 4bytes per pixel for 16x16 pixels.
# Skip to the image data offset (+38), and jump over the BMP data header(?) (+44)
image <- raw_to_int32(ico[38 + 44 - 1 + seq_len(16*16*4)])

# Reshape to be a 16x16 matrix, keeping only the lowest byte.
image <- (matrix(image %% 256, 16, 16, byrow=TRUE))[16:1,]

# plot as raster
rim <- raster(image)
plot(rim, col = grey(seq(1, 0, length.out = 16)), ann = FALSE)