## ----include = FALSE----------------------------------------------------------
knitr::opts_chunk$set(
  collapse = FALSE,
  comment = "#>"
)

## ----echo=FALSE---------------------------------------------------------------
jpeg_markers <-  data.frame(
  stringsAsFactors = FALSE,
                 Hex = c("FFC0","FFC1","FFC2",
                         "FFC3","FFC4","FFC5","FFC6","FFC7","FFC8","FFC9",
                         "FFCA","FFCB","FFCC","FFCD","FFCE","FFCF","FFD0",
                         "FFD1","FFD2","FFD3","FFD4","FFD5","FFD6","FFD7",
                         "FFD8","FFD9","FFDA","FFDB","FFDC","FFDD","FFDE",
                         "FFDF","FFE0","FFE1","FFE2","FFE3","FFE4","FFE5",
                         "FFE6","FFE7","FFE8","FFE9","FFEA","FFEB",
                         "FFEC","FFED","FFEE","FFEF","FFF0","FFF1","FFF2",
                         "FFF3","FFF4","FFF5","FFF6","FFF7","FFF8","FFF9",
                         "FFFA","FFFB","FFFC","FFFD","FFFE"),
              Marker = c("SOF0","SOF1","SOF2",
                         "SOF3","DHT","SOF5","SOF6","SOF7","JPG","SOF9",
                         "SOF10","SOF11","DAC","SOF13","SOF14","SOF15","RST0",
                         "RST1","RST2","RST3","RST4","RST5","RST6","RST7",
                         "SOI","EOI","SOS","DQT","DNL","DRI","DHP",
                         "EXP","APP0","APP1","APP2","APP3","APP4","APP5",
                         "APP6","APP7","APP8","APP9","APP10","APP11","APP12",
                         "APP13","APP14","APP15","JPG0","JPG1","JPG2",
                         "JPG3","JPG4","JPG5","JPG6","JPG7 SOF48","JPG8 LSE",
                         "JPG9","JPG10","JPG11","JPG12","JPG13","COM"),
                Name = c("Start of Frame 0",
                         "Start of Frame 1","Start of Frame 2","Start of Frame 3",
                         "Define Huffman Table","Start of Frame 5",
                         "Start of Frame 6","Start of Frame 7","JPEG Extensions",
                         "Start of Frame 9","Start of Frame 10","Start of Frame 11",
                         "Define Arithmetic Coding","Start of Frame 13",
                         "Start of Frame 14","Start of Frame 15","Restart Marker 0",
                         "Restart Marker 1","Restart Marker 2",
                         "Restart Marker 3","Restart Marker 4","Restart Marker 5",
                         "Restart Marker 6","Restart Marker 7","Start of Image",
                         "End of Image","Start of Scan","Define Quantization Table",
                         "Define Number of Lines","Define Restart Interval",
                         "Define Hierarchical Progression",
                         "Expand Reference Component","Application Segment 0","Application Segment 1",
                         "Application Segment 2","Application Segment 3",
                         "Application Segment 4","Application Segment 5",
                         "Application Segment 6","Application Segment 7",
                         "Application Segment 8","Application Segment 9",
                         "Application Segment 10 PhoTags","Application Segment 11",
                         "Application Segment 12","Application Segment 13",
                         "Application Segment 14","Application Segment 15","JPEG Extension 0",
                         "JPEG Extension 1","JPEG Extension 2",
                         "JPEG Extension 3","JPEG Extension 4","JPEG Extension 5",
                         "JPEG Extension 6","JPEG Extension 7 JPEG-LS",
                         "JPEG Extension 8 JPEG-LS Extension","JPEG Extension 9",
                         "JPEG Extension 10","JPEG Extension 11","JPEG Extension 12",
                         "JPEG Extension 13","Comment"),
         Description = c("Baseline DCT",
                         "Extended Sequential DCT","Progressive DCT",
                         "Lossless (sequential)",NA,"Differential sequential DCT",
                         "Differential progressive DCT","Differential lossless (sequential)",
                         NA,"Extended sequential DCT, Arithmetic coding",
                         "Progressive DCT, Arithmetic coding",
                         "Lossless (sequential), Arithmetic coding",NA,
                         "Differential sequential DCT, Arithmetic coding",
                         "Differential progressive DCT, Arithmetic coding",
                         "Differential lossless (sequential), Arithmetic coding",NA,NA,NA,NA,NA,NA,NA,NA,NA,
                         NA,NA,NA,"(Not common)",NA,"(Not common)",
                         "(Not common)",
                         "JFIF – JFIF JPEG image AVI1 – Motion JPEG (MJPG)",
                         "EXIF Metadata, TIFF IFD format, JPEG Thumbnail (160×120) Adobe XMP","ICC color profile, FlashPix",
                         "(Not common) JPS Tag for Stereoscopic JPEG images",
                         "(Not common)","(Not common)",
                         "(Not common) NITF Lossles profile","(Not common)","(Not common)","(Not common)",
                         "(Not common) ActiveObject (multimedia messages / captions)",
                         "(Not common) HELIOS JPEG Resources (OPI Postscript)",
                         "Picture Info (older digicams), Photoshop Save for Web: Ducky","Photoshop Save As: IRB, 8BIM, IPTC",
                         "(Not common)","(Not common)","(Not common)",
                         "(Not common)","(Not common)","(Not common)",
                         "(Not common)","(Not common)","(Not common)","Lossless JPEG",
                         "Lossless JPEG Extension Parameters","(Not common)",
                         "(Not common)","(Not common)","(Not common)",
                         "(Not common)",NA)
  )
  


jpeg_markers$Hex <- tolower(jpeg_markers$Hex)

head(jpeg_markers, 15) |> 
  knitr::kable()

## -----------------------------------------------------------------------------
library(ctypesio)

jpeg_file <- system.file("img", "Rlogo.jpg", package="jpeg")
jpeg <- jpeg::readJPEG(jpeg_file)
plot(as.raster(jpeg))

## -----------------------------------------------------------------------------
dim(jpeg)
dat <- readBin(jpeg_file, raw(), n = file.size(jpeg_file)) 
head(dat, 100)

## -----------------------------------------------------------------------------
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Open a connection, and tag the connection such that 
# values are read in **big endian** by default.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
con <- file(jpeg_file, 'rb') |>
  set_endian('big')

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Read the first 2 bytes as HEX
# For regular JPEG files, this should be the "Start of Image (SOI)" marker
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
soi <- read_hex(con, n = 1, size = 2) # ffd8: SOI
stopifnot(soi == 'ffd8')

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Keep reading markers and the chunk data until we reach
# the 'Start of Scan' marker (ffda)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
marker <- read_hex(con, n = 1, size = 2)

while(length(marker) > 0 && nchar(marker) > 0) {
  
  # The relevant row from the 'jpeg_markers' data.frame
  info <- subset(jpeg_markers, jpeg_markers$Hex == marker)
  
  # It's possible there may be custom markers which aren't included
  # in my list of markers
  if (nrow(info) == 0) {
    cat("Unknown marker: ", marker, "\n")
    marker <- read_hex(con, n = 1, size = 2)
    next
  }
  
  # Read the length of data in this chunk and output the chunk info
  len <- read_uint16(con)
  msg <- sprintf("%s [%5i] [%s] [%s] [%s]\n", marker, len, info$Marker, info$Name, info$Description)
  cat(msg)
  
  # Check if we've reached the Start of Scan marker
  if (marker == 'ffda') {
    cat("Compressed image data until end of file\n")
    break
  }
  
  # Read the chunk data 
  # In JPEG the length of each chunk includes the 2 bytes which specify 
  # the chunk length, so read len-2 bytes from the current position
  chunk_data <- read_uint8(con, n = len - 2)
  
  # Process chunk data here
  
  
  # Read the next marker and continue
  marker <- read_hex(con, n = 1, size = 2)
}

close(con)

