From c5c28fd811377b15977dd0b64d64810b9c4f97f2 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 14 Oct 2020 18:01:19 -0700 Subject: [PATCH] Add wx.Image.ConvertToRegion allowing to construct a region from an image without needing to make a bitmap first. --- demo/ShapedWindow.py | 17 ++++++---- etg/image.py | 79 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 6 deletions(-) diff --git a/demo/ShapedWindow.py b/demo/ShapedWindow.py index fe2701dc..0ce8aa3b 100644 --- a/demo/ShapedWindow.py +++ b/demo/ShapedWindow.py @@ -26,14 +26,17 @@ class TestFrame(wx.Frame): self.Bind(wx.EVT_RIGHT_UP, self.OnExit) self.Bind(wx.EVT_PAINT, self.OnPaint) - self.bmp = images.Vippi.GetBitmap() + img = images.Vippi.GetImage() + if img.HasAlpha(): + img.ConvertAlphaToMask() + self.img = img + self.bmp = wx.Bitmap(img) + w, h = self.bmp.GetWidth(), self.bmp.GetHeight() self.SetClientSize( (w, h) ) - if wx.Platform != "__WXMAC__": - # wxMac clips the tooltip to the window shape, YUCK!!! - self.SetToolTip("Right-click to close the window\n" - "Double-click the image to set/unset the window shape") + self.SetToolTip("Right-click to close the window\n" + "Double-click the image to set/unset the window shape") if wx.Platform == "__WXGTK__": # wxGTK requires that the window be created before you can @@ -50,9 +53,11 @@ class TestFrame(wx.Frame): def SetWindowShape(self, *evt): # Use the bitmap's mask to determine the region - r = wx.Region(self.bmp) + #r = wx.Region(self.bmp) + r = self.img.ConvertToRegion() self.hasShape = self.SetShape(r) + def OnDoubleClick(self, evt): if self.hasShape: self.SetShape(wx.Region()) diff --git a/etg/image.py b/etg/image.py index ff2c974b..3a0eb052 100644 --- a/etg/image.py +++ b/etg/image.py @@ -496,6 +496,85 @@ def run(): c.addProperty('Type GetType SetType') + c.addCppMethod('wxRegion*', 'ConvertToRegion', + '(int R=-1, int G=-1, int B=-1, int tolerance=0)', + doc="""\ + Create a :class:`wx.Region` where the transparent areas match the given RGB values. + + If the RGB values are not given, then the image's mask colour + components will be used instead. If a non-zero tolerance is given + then the pixels that fall into the range of (R,G,B) to + (R+tolerance, G+tolerance, B+tolerance) will be considered to be + transparent. + + If there are no pixels matching the transparent colours then the + region returned will match the image's full dimensions. + + :param int `R`: The red component of the transparent colour. + :param int `G`: The red component of the transparent colour. + :param int `B`: The red component of the transparent colour. + :param int `tolerance`: Broadens the range of colours that will + be considered transparent. + :returns: a :class:`wx.Region` object. + """, + body="""\ + wxRegion* region = new wxRegion(); + unsigned char hiR, hiG, hiB; + + if (R == -1) { R = self->GetMaskRed(); } + if (G == -1) { G = self->GetMaskGreen(); } + if (B == -1) { B = self->GetMaskBlue(); } + + // Make sure nothing out of range was passed + R &= 0xFF; + G &= 0xFF; + B &= 0xFF; + + hiR = (unsigned char)wxMin(0xFF, R + tolerance); + hiG = (unsigned char)wxMin(0xFF, G + tolerance); + hiB = (unsigned char)wxMin(0xFF, B + tolerance); + + // Loop through the image row by row, pixel by pixel, building up + // rectangles to add to the region. + int width = self->GetWidth(); + int height = self->GetHeight(); + + for (int y=0; y < height; y++) + { + wxRect rect; + rect.y = y; + rect.height = 1; + + for (int x=0; x < width; x++) + { + // search for a continuous range of non-transparent pixels + int x0 = x; + while ( x < width) + { + unsigned char red = self->GetRed(x,y); + unsigned char grn = self->GetGreen(x,y); + unsigned char blu = self->GetBlue(x,y); + if (( red >= R && red <= hiR) && + ( grn >= G && grn <= hiG) && + ( blu >= B && blu <= hiB)) // It's transparent + break; + x++; + } + + // Add the run of non-transparent pixels (if any) to the region + if (x > x0) { + rect.x = x0; + rect.width = x - x0; + region->Union(rect); + } + } + } + if (region->IsEmpty()) + region->Union(0, 0, width, height); + return region; + """) + + # For compatibility: module.addPyFunction('EmptyImage', '(width=0, height=0, clear=True)', deprecated="Use :class:`Image` instead.",