New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Project: Adding Alpha Masks to the Quartz Graphics Device #43
Comments
Amusingly the simple diff below seems to be enough to get it to work, at least on my version of macOS. I checked the docs for CGContextClipToMask, and the documentation does clearly state that the mask image should be in grayscale without an alpha channel, so even if this works it is not "correct". I guess it is simply luck and/or undocumented that clipping to an RGBA mask image also just works. Tomorrow I will take a stab at converting the resulting RGBA image to a grayscale context/image and passing that to Index: src/library/grDevices/src/devQuartz.c
===================================================================
--- src/library/grDevices/src/devQuartz.c (revision 84998)
+++ src/library/grDevices/src/devQuartz.c (working copy)
@@ -1030,7 +1030,7 @@
QMaskRef quartz_mask = malloc(sizeof(QMaskRef));
if (!quartz_mask) error(_("Failed to create Quartz mask"));
- cs = CGColorSpaceCreateDeviceGray();
+ cs = CGColorSpaceCreateDeviceRGB();
/* Create bitmap grahics context
* drawing is redirected to this context */
@@ -1040,7 +1040,7 @@
8,
0,
cs,
- kCGImageAlphaNone);
+ kCGImageAlphaPremultipliedLast);
quartz_mask->context = quartz_bitmap;
xd->masks[index] = quartz_mask;
@@ -2715,10 +2715,6 @@
if (isNull(mask)) {
/* Set NO mask */
index = -1;
- } else if (R_GE_maskType(mask) == R_GE_alphaMask) {
- warning(_("Ignored alpha mask (not supported on this device)"));
- /* Set NO mask */
- index = -1;
} else {
if (isNull(ref)) {
/* Create a new mask */ |
Nice discovery! Presumably that simple patch would break luminance masks though? And on that note, if you can get something going that agrees with the documentation, it will be important to check that your patch does not break existing behaviour. See #74 for a discussion of some of the checks that would be useful, in particular the 'gdiff' testing. There are some tests for masks in Just looking at those mask tests prompts a couple more thoughts:
Thanks for taking a look at this! |
Cool. Nice output! So this is still taking advantage of the undocumented behaviour, right? Yes, it would be ideal to avoid that, possibly by drawing the mask onto an RGBA image and then constructing a greyscale image from the alpha channel of the RGBA image. I think effort in that direction would be more useful than 'gdiff'ing the current "naughty" solution. I would rather have an untested legal solution than a tested illegal one, if that makes sense. Also, if you get time, please check that the output from Thanks! |
The latest version of the patch is below. In this version, when an alpha mask is used a bitmap with an alpha-only channel is created. When a luminance mask is used instead, the bitmap is a grayscale bitmap with no alpha channel (as before). When luminance masking, things proceed as in the current R-devel. When alpha masking, a second bitmap is created that's grayscale with no alpha channel, as required for
We got caught up in building R on some other machines today, so we never got around to testing things for this patch thoroughly. Perhaps if one of us has time in the coming weeks they could run through the output of Index: src/library/grDevices/src/devQuartz.c
===================================================================
--- src/library/grDevices/src/devQuartz.c (revision 85035)
+++ src/library/grDevices/src/devQuartz.c (working copy)
@@ -1032,6 +1032,12 @@
cs = CGColorSpaceCreateDeviceGray();
+ /* For alpha masks, create a bitmap with only an alpha channel */
+ uint32_t bitmapInfo = kCGImageAlphaNone;
+ if (R_GE_maskType(mask) == R_GE_alphaMask) {
+ bitmapInfo = kCGImageAlphaOnly;
+ }
+
/* Create bitmap grahics context
* drawing is redirected to this context */
quartz_bitmap = CGBitmapContextCreate(NULL,
@@ -1040,7 +1046,7 @@
8,
0,
cs,
- kCGImageAlphaNone);
+ bitmapInfo);
quartz_mask->context = quartz_bitmap;
xd->masks[index] = quartz_mask;
@@ -1055,6 +1061,31 @@
eval(R_fcall, R_GlobalEnv);
UNPROTECT(1);
+ /* When working with an alpha mask, convert into a grayscale bitmap */
+ if (R_GE_maskType(mask) == R_GE_alphaMask) {
+ CGContextRef alpha_bitmap = quartz_bitmap;
+
+ /* Create a new grayscale bitmap with no alpha channel */
+ int stride = CGBitmapContextGetBytesPerRow(alpha_bitmap);
+ quartz_bitmap = CGBitmapContextCreate(NULL,
+ (size_t) devWidth,
+ (size_t) devHeight,
+ 8,
+ stride,
+ cs,
+ kCGImageAlphaNone);
+ quartz_mask->context = quartz_bitmap;
+
+ void *alpha_data = CGBitmapContextGetData(alpha_bitmap);
+ void *gray_data = CGBitmapContextGetData(quartz_bitmap);
+
+ /* Copy the alpha channel data into the grayscale bitmap */
+ memcpy(gray_data, alpha_data, stride * devHeight);
+
+ /* We're finished with the alpha channel bitmap now */
+ CGContextRelease(alpha_bitmap);
+ }
+
/* Create image from bitmap context */
CGImageRef maskImage;
maskImage = CGBitmapContextCreateImage(quartz_bitmap);
@@ -2715,10 +2746,6 @@
if (isNull(mask)) {
/* Set NO mask */
index = -1;
- } else if (R_GE_maskType(mask) == R_GE_alphaMask) {
- warning(_("Ignored alpha mask (not supported on this device)"));
- /* Set NO mask */
- index = -1;
} else {
if (isNull(ref)) {
/* Create a new mask */
@@ -2938,8 +2965,9 @@
SET_VECTOR_ELT(capabilities, R_GE_capability_clippingPaths, clippingPaths);
UNPROTECT(1);
- PROTECT(masks = allocVector(INTSXP, 1));
+ PROTECT(masks = allocVector(INTSXP, 2));
INTEGER(masks)[0] = R_GE_luminanceMask;
+ INTEGER(masks)[1] = R_GE_alphaMask;
SET_VECTOR_ELT(capabilities, R_GE_capability_masks, masks);
UNPROTECT(1);
|
I worked on some tests for this that may be helpful for some:
|
Thanks very much! This looks like it makes sense. Will try to do some more testing to confirm. |
Discussed in #2
Originally posted by giscus[bot] June 1, 2023
R Project Sprint 2023 - Adding Alpha Masks to the Quartz Graphics Device
https://contributor.r-project.org/r-project-sprint-2023/projects/quartz-alpha-mask/
The text was updated successfully, but these errors were encountered: