Tk Source Code

View Ticket
Login
Ticket UUID: 4c7d935e9736a1046a1da3ddd0d9039eb232c943
Title: No ttk::checkbutton in ttk "alt" theme on osx
Type: Bug Version: 8.6.11
Submitter: anonymous Created on: 2021-03-16 01:51:19
Subsystem: (unused) Assigned To: marc_culler
Priority: 5 Medium Severity: Minor
Status: Closed Last Modified: 2021-05-02 19:13:01
Resolution: Fixed Closed By: chrstphrchvz
    Closed on: 2021-05-02 19:13:01
Description:
the checkbox itself doesn't exist when using the alt theme on osx 10.14.6 in tk8.6.11. It is still visible in tk 8.6.10.

example in wish:
ttk::style theme use alt 
frame .c 
ttk::checkbutton .c.one -text One -variable one -onvalue 1; set one 1 
ttk::checkbutton .c.two -text Two -variable two -onvalue 1; set two 0 
grid .c -column 0 -row 0 
grid .c.one -column 0 -row 0 
grid .c.two -column 1 -row 0
User Comments: chrstphrchvz added on 2021-05-02 19:13:01:

A Windows TkpPutRGBAImage() implementation does not seem as trivial or superior as I thought it possibly was. After looking at what Win32 GDI offers, I only came across using AlphaBlend() (in place of BitBlt()), but it only accepts premultiplied BGRA (MSBFirst byte order). Tk photo format on Windows currently uses non-premultiplied RGBA (LSBFirst byte order). So in addition to premultiplying, TkpPutRGBAImage() would also have to perform byte reordering to support LSBFirst ZPixmaps (as would TkPutImage()/ImageGetPixel()/PutPixel()) unless Tk photo were to finally use “native image ordering” on Windows as some comments suggest. I would also guess the current approach in BlendComplexAlpha() provides slightly better color accuracy than premultiplying (which discards precision in an intermediate result), though I don’t know whether Tk considers this a significant advantage.


marc_culler (claiming to be Marc Culler) added on 2021-05-01 22:29:18:
I do think it would be great to have TkpPutRGBAImage implementations for
Windows and linux.  Compositing images really is a job for the graphics card,
and a native implementation would surely do it that way.

chrstphrchvz added on 2021-05-01 22:24:36:

Pardon my use of TkpPutAlphaImage()/TkpPutImageAlpha() where I meant to put TkpPutRGBAImage() in my last comment.


marc_culler (claiming to be Marc Culler) added on 2021-05-01 21:53:40:
Thanks for the suggestions, Christopher.  I followed them and merged the
branch.  So I will close the ticket now.

chrstphrchvz added on 2021-05-01 20:41:54:

Thanks for working on this, Marc. The bug fix branch appears to work.

Nitpick: should uint32 be uint32_t?

I think it would be worth adding a brief comment documenting the purpose of TkpPutAlphaImage(): that it is equivalent to TkPutImage() except that it allows reusing XImage in a manner that the XImage structure itself cannot indicate, namely drawing a 32-bpp ZPixmap containing alpha channel values in place of padding bytes.

The comment for Aqua TkPutImage() and XPutImage() should have its mention of TkImgPhotoDisplay() as the sole example removed, so as to reflect the fact these functions are used elsewhere in core Tk and by extensions. (TkImgPhotoDisplay() could instead be mentioned as one example for TkpPutImageAlpha().)

I may be able to provide an implementation of TkpPutAlphaImage() for Windows.

TkpPutImageAlpha() could be useful on X11, where it either uses the existing compositing algorithm, or becomes a compatibility wrapper for XRender.

I agree that in some places where XPutmage() is preceded by XGetImage(), it is merely to be able draw on top of or blend with what was already drawn, so being able to draw with alpha would allow removing those XGetImage() calls.


marc_culler (claiming to be Marc Culler) added on 2021-04-30 21:58:17:
I think that the bug-4c7d935e97 branch fixes this in a satisfactory way.

In tkMacOSXPort.h the constant TK_CAN_RENDER_RGBA is #defined and a function
TkpPutRGBAImage is declared with the same signature as TkPutImage.

In tkImgPhInstance.c if TK_CAN_RENDER_RGBA is #defined then, instead of
calling TkGetImage, doing the blending in C, and calling TkPutImage, the
function TkpPutRGBAImage is called to render the image directly.

The behavior of TkPutImage and XPutImage have been reverted so that the
ximage is assumed to be RGB, with the 3 channels packed into 4 bytes, and
the 4th byte is ignored rather than being treated as an alpha value.

Any other platform which can render RGBA-format XImages may modify its
platform-specific XXXPort.h file in the same way and implement its own
version of TkpPutRGBAImage.

We could consider exporting TkpPutRGBAImage as a stub function (for platforms
on which it exists) so that external packages could also use it.

In the bugfix branch the Alt theme now has checkbuttons.

marc_culler (claiming to be Marc Culler) added on 2021-04-30 12:32:08:
Hi Christoper.  I would like to understand the scope of this issue.  Can
you please indicate the major situations that you know about where
there are arbitrary padding values which get misinterpreted as alpha
values?

I agree that TkPutImage is a core operation for drawing into windows.
You certainly need the ability to render images into a window.  And,
ideally, you would want to be able to specify the compositing operation
to be used when doing that.  Is that what XRender provides?  Should
there be a TkRenderImage as well as TkPutImage?  Perhaps TkPutImage
would just be the special case where alpha is set to 1, so the image
is drawn on top of what was already there?

chrstphrchvz added on 2021-04-30 06:53:53:

Although the functions are complementary in some sense, there seems to be plenty of use of XPutImage() (and TkPutImage()) by itself, such as the core Tk usage related to this ticket.

From what I can tell, XPutImage() on Aqua seems to work fine, except for how it started interpreting input as RGBA when there is still usage where the input is 32-bit padded RGB, causing arbitrary padding values to now be treated as alpha.


marc_culler (claiming to be Marc Culler) added on 2021-04-29 18:04:58:
As far as I know, the only purpose for calling XGetImage and XPutImage is to
implement home-grown compositing operations in a context which does not provide
them.  You cut a box out of the window with XGetImage, scribble on it with
your own compositing code and write the box back into the window.

This is connected to the issue that Apple is making it so difficult, maybe
even impossible, for us to implement those two functions in a way which works
during normal drawing in the drawRect method.  They are trying to tell us, in
their usual tactless way, that we should not be writing our own compositing
operations because they provide us with everything we need (and presumably
they do it in the graphics card, not with C arrays.)

chrstphrchvz added on 2021-04-28 06:23:17:

A kludge I can think of (adapted from a debugging strategy I use) would be for code that wants to use “alpha-aware” XPutImage() on Aqua to first set an environment variable (think of it as a global variable) before calling XPutImage(). Then the environment variable is checked wherever appropriate; in TkMacOSXCreateCGImageFromXImage(), bitmapInfo is set either using kCGAlphaLast if the environment variable is defined, or kCGAlphaNoneSkipLast if not. Then the code using XPutImage() unsets the environment variable.

Otherwise, for alpha-aware compositing on X11, to my knowledge the tool for the job is XRender. Would it be overkill to emulate its functionality on non-X11? If not, then that is what I think Tk should consider providing instead of nonstandard XPutImage().


chrstphrchvz added on 2021-03-17 08:40:57:

"Aqua X11 emulation functions" specifically—not discounting the possibility of introducing a new API that does what was wanted.


chrstphrchvz added on 2021-03-17 08:33:25:

I think this issue goes back to TkPutImage()/XPutImage() being designated as alpha-aware on Aqua, as taken advantage of in TkImgPhotoDisplay(). While doing so was relatively simple, I think it disregarded the fact that XImage and XPutImage() are cross-platform APIs borrowed from X11 and used by existing Tk programs and extensions, and not private to core Tk. An existing Tk program providing 32-bit padded RGB to XPutImage() would have to deal with padding bytes suddenly being treated as alpha values on Aqua. If there is no such thing as a cross-platform RGBA XImage, then unfortunately I don't think Aqua functions should pretend there is.


chrstphrchvz added on 2021-03-16 19:08:42:

Instances of "bits 32:25" in previous comment should instead say "bits 31:24".


chrstphrchvz added on 2021-03-16 19:07:21:

Assuming that the generic code is correct, then the problem appears to reside in Tk Aqua's ImagePutPixel() and/or TkMacOSXCreateCGImageFromXImage(). For a 32-bpp XImage, ImagePutPixel() writes the pixel value as-is to the XImage's data buffer, and I observe the pixel values that IndicatorElementDraw() calls it with only ever use the lower 24 bits; bits 32:25 are 0. But TkMacOSXCreateCGImageFromXImage() assumes the data for 32-bpp ZPixmaps has alpha channel, so indeed it is drawing a completely transparent image. Is alpha channel even defined for ZPixmaps? If not, then I wonder if TkMacOSXCreateCGImageFromXImage() should be setting bitmapInfo with kCGAlphaNoneSkipLast instead of kCGAlphaLast.

This issue actually predates [5b2bc6add1] in a way: up until then, bits 32:25 of the pixel values used to contain a magic number (0x69 = 105), which when interpreted by TkMacOSXCreateCGImageFromXImage() as the alpha channel value meant that the indicators were actually being drawn with only 105/255 = 41% opacity.


chrstphrchvz added on 2021-03-16 17:39:29:

Currently stepping through IndicatorElementDraw() (ttkDefaultTheme.c). Wondering whether the indicator image is being set to completely transparent (pixel alpha channel values of 0) somewhere…


chrstphrchvz added on 2021-03-16 15:27:48:

The checkbutton exists, it's just not being visibly drawn.

The issue was introduced by something done in [5b2bc6add1] (not buildable without picking changes in/closer to [4cae793200]). I still have not spotted the cause, there is quite a bit of churn I would need to sort through…


chrstphrchvz added on 2021-03-16 08:00:58:

I can reproduce this in the "Simple Ttk widgets" (ttkbut.tcl) widget demo. I will bisect to see if I can find what's causing this.