{"action":"created","issue":{"url":"https://api.github.com/repos/processing/processing/issues/3391","labels_url":"https://api.github.com/repos/processing/processing/issues/3391/labels{/name}","comments_url":"https://api.github.com/repos/processing/processing/issues/3391/comments","events_url":"https://api.github.com/repos/processing/processing/issues/3391/events","html_url":"https://github.com/processing/processing/issues/3391","id":88780027,"number":3391,"title":"Add support for pre-multiplied alpha","user":{"login":"neilcsmith-net","id":3975960,"avatar_url":"https://avatars.githubusercontent.com/u/3975960?v=3","gravatar_id":"","url":"https://api.github.com/users/neilcsmith-net","html_url":"https://github.com/neilcsmith-net","followers_url":"https://api.github.com/users/neilcsmith-net/followers","following_url":"https://api.github.com/users/neilcsmith-net/following{/other_user}","gists_url":"https://api.github.com/users/neilcsmith-net/gists{/gist_id}","starred_url":"https://api.github.com/users/neilcsmith-net/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/neilcsmith-net/subscriptions","organizations_url":"https://api.github.com/users/neilcsmith-net/orgs","repos_url":"https://api.github.com/users/neilcsmith-net/repos","events_url":"https://api.github.com/users/neilcsmith-net/events{/privacy}","received_events_url":"https://api.github.com/users/neilcsmith-net/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/processing/processing/labels/enhancement","name":"enhancement","color":"c7def8"}],"state":"open","locked":false,"assignee":{"login":"codeanticode","id":62246,"avatar_url":"https://avatars.githubusercontent.com/u/62246?v=3","gravatar_id":"","url":"https://api.github.com/users/codeanticode","html_url":"https://github.com/codeanticode","followers_url":"https://api.github.com/users/codeanticode/followers","following_url":"https://api.github.com/users/codeanticode/following{/other_user}","gists_url":"https://api.github.com/users/codeanticode/gists{/gist_id}","starred_url":"https://api.github.com/users/codeanticode/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/codeanticode/subscriptions","organizations_url":"https://api.github.com/users/codeanticode/orgs","repos_url":"https://api.github.com/users/codeanticode/repos","events_url":"https://api.github.com/users/codeanticode/events{/privacy}","received_events_url":"https://api.github.com/users/codeanticode/received_events","type":"User","site_admin":false},"milestone":null,"comments":8,"created_at":"2015-06-16T16:45:52Z","updated_at":"2015-07-02T08:24:17Z","closed_at":null,"body":"At the bottom of this pull request (https://github.com/processing/processing/pull/2601) @benfry mentioned opening an issue about implementing pre-multiplied alpha, which I don't think was ever done? I certainly dropped the ball on following up.  It would be great if an official image mode for this was added (I'm currently sub-classing the GL renderer to achieve this).  Otherwise multiple layering (as well as convolution, etc) don't work correctly.\r\n\r\nFor reference, a long and admittedly opinionated extract of an email sent to @JakubValtar last year with a suggestion for handling this internally in the OpenGL renderer - feel free to TL;DR, although the NVIDIA link is worth a read.\r\n\r\n------\r\n\r\nOK, you asked me how Processing might use pre-multiplied alpha without\r\nbreaking the API.  So to start, a possibly controversial statement -\r\nblending in Processing is completely broken.  It was one of the\r\nreasons I stopped using Processing the first time around (~2006) - I\r\nlike the API but in my opinion there are some issues with the\r\nimplementation.  Praxis LIVE v1 deliberately made use of aspects of\r\nthe Processing API, but re-implemented on top of a custom software and\r\nOpenGL renderer.  Fast forward to a year ago, and I decided to stop\r\ntrying to re-implement the API - the new Processing OpenGL is too good\r\nto miss!  So Praxis LIVE v2 now embeds Processing as a library -\r\nhowever, there's no way I'm breaking blending in the process - luckily\r\nit isn't that difficult to work around.\r\n\r\nThere are many sources around about pre-multipled alpha.  I found this\r\npage recently looking into GL blend modes\r\n(http://www.slideshare.net/Mark_Kilgard/blend-modes-for-opengl) - it's\r\nabout the new NVIDIA blend mode extensions - note that slide 16 talks\r\nabout the fact that blend modes assume pre-multiplied alpha.  It\r\nmentions one of the key issues (one that affects me) - drawing into\r\nsurfaces with alpha channels - sometimes you can get away with it if\r\nthe destination is opaque.  He also mentions the Porter-Duff model and\r\nhow it relies on pre-multiplied alpha - interesting that the default\r\nblending in the Java2D renderer works fine - because the JRE\r\nAlphaComposite  pre-multiplies, blends and un-premultiplies.\r\n\r\nIncidentally, that slide also touches on another issue -\r\npre-multiplied alpha is necessary for performing various filtering\r\neffects (eg. blur) on surfaces with alpha.  I haven't looked at the\r\nPImage filters but I imagine there might be an issue there (note the\r\nConvolveOp in the JRE multiplies, filters and unmultiplies).\r\n\r\nNow, all that multiplying and unmultiplying is a bit pointless! ;-)\r\nNot to mention it's not easily achievable with OpenGL.  So, how to use\r\npre-multiplied alpha without changing the API?\r\n\r\nThe important thing in my opinion is separating the external format of\r\nimages to the format they're stored internally.  Take managed images\r\nin the Java2D backend - cached textures are not stored in the format\r\nof the BufferedImage, but the optimum format for rendering.  Just\r\nbecause the format to the user is un-premultiplied ARGB doesn't mean\r\nthat the underlying storage has to be.  Processing could easily switch\r\nto storing the underlying pixel data, in all modes, as pre-multiplied.\r\nOr, simpler, though not fixing issues elsewhere, it could just store\r\npre-multiplied data in the OpenGL textures.\r\n\r\nThe key things to handle are times when the user is setting colours,\r\ntimes when the user is retrieving colours, and access to the pixel\r\narray.\r\n\r\nProcessing already does colour calculations to match a colour format\r\nset by the user and convert into internal data.  What I've done in\r\nPraxis LIVE's PGraphics sub-class is override the various colorCalc()\r\nmethods to multiply in the alpha.  That's pretty much all that I'm\r\nusing right now, but then I don't have 100% API coverage!  It would be\r\npossible to handle multiply and unpremultiply fairly easily in set()\r\nand get(), which leaves the pixel array - I'd suggest running a\r\nun-premultiply <> premultiply during loadPixels() and updatePixels(),\r\nwith an extra hint to leave it premultiplied and save any hit (of\r\ncourse RGB images wouldn't have any anyway).\r\n\r\nI'm not quite sure if there would be any implications to PShape that\r\nwould need looking at - I guess I'll find out when I come to\r\nintegrating support for them! ;-)\r\n\r\nThe one aspect of the API that would change would be GLSL - mind you\r\nI'd assert that's fixing it rather than breaking it - however, it's\r\nthe one place that might affect the behaviour of user code.\r\n\r\nIncidentally, if it's of interest - some alternate software blend\r\nmodes that I and another person on the Java Gaming forum worked on a\r\nfew years back - all using pre-multiplied alpha, obviously -\r\nhttp://www.java-gaming.org/topics/some-new-blendmodes-add-multiply-overlay-etc/24529/view.html\r\n"},"comment":{"url":"https://api.github.com/repos/processing/processing/issues/comments/117955142","html_url":"https://github.com/processing/processing/issues/3391#issuecomment-117955142","issue_url":"https://api.github.com/repos/processing/processing/issues/3391","id":117955142,"user":{"login":"tillnagel","id":908847,"avatar_url":"https://avatars.githubusercontent.com/u/908847?v=3","gravatar_id":"","url":"https://api.github.com/users/tillnagel","html_url":"https://github.com/tillnagel","followers_url":"https://api.github.com/users/tillnagel/followers","following_url":"https://api.github.com/users/tillnagel/following{/other_user}","gists_url":"https://api.github.com/users/tillnagel/gists{/gist_id}","starred_url":"https://api.github.com/users/tillnagel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tillnagel/subscriptions","organizations_url":"https://api.github.com/users/tillnagel/orgs","repos_url":"https://api.github.com/users/tillnagel/repos","events_url":"https://api.github.com/users/tillnagel/events{/privacy}","received_events_url":"https://api.github.com/users/tillnagel/received_events","type":"User","site_admin":false},"created_at":"2015-07-02T08:24:17Z","updated_at":"2015-07-02T08:24:17Z","body":"@neilcsmith-net Care to share your OpenGL renderer subclass, or some pointers on how to work-around this for now?"}}