Bug 17148 - rasterImage shows incorrect image orientation (MacOSX)
Summary: rasterImage shows incorrect image orientation (MacOSX)
Status: ASSIGNED
Alias: None
Product: R
Classification: Unclassified
Component: Graphics (show other bugs)
Version: R-devel (trunk)
Hardware: x86_64/x64/amd64 (64-bit) OS X Yosemite
: P5 normal
Assignee: R-core
URL:
Depends on:
Blocks:
 
Reported: 2016-09-14 07:03 UTC by Adrian Baddeley
Modified: 2017-02-02 18:41 UTC (History)
3 users (show)

See Also:


Attachments
Proposed patch (1.46 KB, patch)
2017-01-30 16:41 UTC, Mikko Korpela
Details | Diff
Test script, rotation of rasterImage() (532 bytes, text/plain)
2017-01-30 16:45 UTC, Mikko Korpela
Details
From rotate4.R, png(type="quartz"), unpatched (26.51 KB, image/png)
2017-01-30 16:46 UTC, Mikko Korpela
Details
Alternative patch (1.41 KB, patch)
2017-01-30 16:47 UTC, Mikko Korpela
Details | Diff
From rotate4.R, png(type="quartz"), alt patch (26.00 KB, image/png)
2017-01-30 16:48 UTC, Mikko Korpela
Details
From rotate4.R, png(type="quartz"), proposed patch (26.60 KB, image/png)
2017-01-30 16:49 UTC, Mikko Korpela
Details
From rotate4.R, png(type="cairo") (17.15 KB, image/png)
2017-01-30 16:50 UTC, Mikko Korpela
Details
From rotate4.R, png(type="Xlib") (9.07 KB, image/png)
2017-01-30 16:51 UTC, Mikko Korpela
Details
From rotate4.R, png(type="windows") (17.76 KB, image/png)
2017-01-30 16:51 UTC, Mikko Korpela
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Adrian Baddeley 2016-09-14 07:03:21 UTC
rasterImage() and image.default(useRaster=TRUE, add=TRUE) display the image in the wrong spatial orientation when the pre-existing user coordinates are reversed, e.g. when xlim[1] > xlim[2]. Occurs only on MacOSX, with the default Quartz device and with other Quartz-dependent devices such as jpeg. 

Working example code below: source this code, start one of the offending devices, and execute tryall(). 

The display should show 4 panels, with a top row in which the letter R is in its usual orientation, and a bottom row in which the letter R is mirror-reflected. The bug is shown in the bottom right panel where the R is not reflected.

M <- c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
   1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
   1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0,
   0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1,
   1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
   1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0,
   0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1,
   1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
   1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
   0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
   1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0,
   0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1,
   1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1,
   1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

M <- matrix(M, 16, 21)

tryone <- function(flip=TRUE, raz=TRUE) {
  xl <- yl <- c(0,1)
  if(flip) xl <- rev(xl)
  plot(0,0,xlim=xl ,ylim=yl, type="n", xlab="", ylab="",
      main=paste("flip =", flip, ", useRaster =", raz))
  xx <- seq(0,1,length=16)
  yy <- seq(0,1,length=21)
  image(xx, yy, M, add=TRUE, useRaster=raz)
}

tryall <- function() {
  opa <- par(mfrow=c(2,2))
  tryone(FALSE, FALSE)
  tryone(FALSE, TRUE)
  tryone(TRUE, FALSE)
  tryone(TRUE, TRUE)
  par(opa)
  invisible(NULL)
}

tryall()
Comment 1 Mikko Korpela 2017-01-30 16:41:31 UTC
Created attachment 2222 [details]
Proposed patch

The attached patch "devQuartz-3.diff" affects the Quartz-specific part of the underlying function rasterImage() and should fix this issue. The following code extends the original example by flipping both axes. The unpatched Quartz device always shows the same image orientation, but the patched version flips the image as expected. This was tested on OS X 10.7.5, R-devel r72054.

tryone_xy <- function(flipY=TRUE, flipX=TRUE, raz=TRUE) {
  xl <- yl <- c(0,1)
  if(flipY) yl <- rev(yl)
  if(flipX) xl <- rev(xl)
  plot(0,0,xlim=xl ,ylim=yl, type="n", xlab="", ylab="",
      main=paste("flipY =", flipY, ", flipX =", flipX, ", useRaster =", raz))
  xx <- seq(0,1,length=16)
  yy <- seq(0,1,length=21)
  image(xx, yy, M, add=TRUE, useRaster=raz)
}

tryall_xy <- function() {
  opa <- par(mfrow=c(4,2), mar = c(2.1, 4.1, 2.1, 2.1))
  tryone_xy(FALSE, FALSE, FALSE)
  tryone_xy(FALSE, FALSE, TRUE)
  tryone_xy(FALSE, TRUE, FALSE)
  tryone_xy(FALSE, TRUE, TRUE)
  tryone_xy(TRUE, FALSE, FALSE)
  tryone_xy(TRUE, FALSE, TRUE)
  tryone_xy(TRUE, TRUE, FALSE)
  tryone_xy(TRUE, TRUE, TRUE)
  par(opa)
  invisible(NULL)
}

tryall_xy()
Comment 2 Mikko Korpela 2017-01-30 16:45:13 UTC
Created attachment 2223 [details]
Test script, rotation of rasterImage()

What happens when a rasterImage() is rotated with the 'angle' argument is somewhat inconsistent between different graphics devices, particularly when the order of 'xleft' and 'xright' or 'ybottom' and 'ytop' is reversed.

Using the test code in the attached file "rotate4.R", the result ("rotate-quartz-3.png") from the patched ("devQuartz-3.diff") 'png(type = "quartz")' device is essentially equal to that of the Cairo-based png device ("rotate-cairo.png"). The result from the unpatched Quartz device is "rotate-quartz-1.png".

The alternative patch "devQuartz-2.diff" works differently; see "rotate-quartz-2.png". For comparison, "rotate-Xlib.png" was produced with the 'png(type = "Xlib")' device and "rotate-windows.png" with 'png(type = "windows")', using the Windows version of R running on top of Wine (Linux).
Comment 3 Mikko Korpela 2017-01-30 16:46:44 UTC
Created attachment 2224 [details]
From rotate4.R, png(type="quartz"), unpatched
Comment 4 Mikko Korpela 2017-01-30 16:47:54 UTC
Created attachment 2225 [details]
Alternative patch
Comment 5 Mikko Korpela 2017-01-30 16:48:50 UTC
Created attachment 2226 [details]
From rotate4.R, png(type="quartz"), alt patch
Comment 6 Mikko Korpela 2017-01-30 16:49:39 UTC
Created attachment 2227 [details]
From rotate4.R, png(type="quartz"), proposed patch
Comment 7 Mikko Korpela 2017-01-30 16:50:39 UTC
Created attachment 2228 [details]
From rotate4.R, png(type="cairo")
Comment 8 Mikko Korpela 2017-01-30 16:51:17 UTC
Created attachment 2229 [details]
From rotate4.R, png(type="Xlib")
Comment 9 Mikko Korpela 2017-01-30 16:51:59 UTC
Created attachment 2230 [details]
From rotate4.R, png(type="windows")
Comment 10 Martin Maechler 2017-02-02 17:22:15 UTC
I'll _not_ be the one fixing these... but I agree
that your attached images from this simple 'rotate4.R' script
(= https://bugs.r-project.org/bugzilla/attachment.cgi?id=2223 )

look so diverse, that we do have bugs somewhere here.
Note that  pdf() gives the same as  "cairo" ( = X11(type="cairo") ) which is default on a modern Linux.. and which also looks identical to your rotate-cairo.

Really "Xlib" and "windows" are wrong, in different ways, too!

Here's a very slightly improved version of your rotate4.R - as a function with "labels" easier for experiments:

## by Mikko Korpela  https://bugs.r-project.org/bugzilla/attachment.cgi?id=2223
## made into a function by MM
raster4 <- function(angle = 20, interpolate = FALSE) {
    image <- cbind(c(0, 1, 0, 1),
                   c(1, 0, 1, 0))
    image <- as.raster(cbind(image, image))
    image[1, 1] <- "red"
    image[4, 1] <- "blue"

    op <- par(bg = "thistle", mar = rep(2.5, 4)); on.exit(par(op))

    plot(1:10, type = "n", main = names(dev.cur()))
    mtext(R.version.string, cex = 0.75)
    abline(h = c(2, 4, 7, 9), lty = 2)
    abline(v = c(2, 4, 7, 9), lty = 2)
    rasterImage(image, 2, 7, 4, 9, angle=angle, interpolate=interpolate)
    rasterImage(image, 2, 4, 4, 2, angle=angle, interpolate=interpolate)
    rasterImage(image, 9, 4, 7, 2, angle=angle, interpolate=interpolate)
    rasterImage(image, 9, 7, 7, 9, angle=angle, interpolate=interpolate)
    invisible(image)
}
Comment 11 Paul Murrell 2017-02-02 18:41:19 UTC
Thanks Martin.  This is on my todo list.