# ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner # Copyright (c) 2008-2020 pyglet contributors # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # * Neither the name of pyglet nor the names of its # contributors may be used to endorse or promote products # derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- """Draw a NinePatch image. NinePatch is a format for storing how to cut up a 9-part resizable rectangular image within the image pixel data directly. For more information on the NinePatch format, see http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch. """ from pyglet.graphics import OrderedGroup from pyglet.gl import GL_BLEND, GL_ENABLE_BIT, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA from pyglet.gl import glBindTexture, glBlendFunc, glEnable, glPopAttrib, glPushAttrib class NinePatchException(Exception): pass class NinePatchGroup(OrderedGroup): def __init__(self, texture, order=0, parent=None): super().__init__(order, parent) self.texture = texture def set_state(self): glPushAttrib(GL_ENABLE_BIT) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glEnable(self.texture.target) glBindTexture(self.texture.target, self.texture.id) def unset_state(self): glPopAttrib() class _PixelData: def __init__(self, image): image_data = image.get_image_data() self.has_alpha = 'A' in image_data.format self.data = image_data.get_data("RGBA", image.width * 4) self.width = image.width self.height = image.height def is_black(self, x, y): p = (y * self.width + x) * 4 if self.has_alpha: if self.data[p + 3] == 0: return False # Fully transparent return self.data[p:p + 3] == b'\x00\x00\x00' class NinePatch: """A scalable 9-patch image. """ # Content area of the image, in pixels from the edge. _padding_top = 0 _padding_bottom = 0 _padding_right = 0 _padding_left = 0 # Resizable area of the image, in pixels from the closest edge _stretch_top = 0 _stretch_left = 0 _stretch_right = 0 _stretch_bottom = 0 def __init__(self, image): """Create NinePatch cuts of an image Arguments: image - an ImageData (Texture, TextureRegion, etc) texture - force cut ImageDatas to be Textures (or Regions) """ pixel_data = _PixelData(image) width = image.width height = pixel_data.height # Texture dimensions after removing the 9patch outline. self.width = width - 2 self.height = height - 2 # Find stretch area markers for x in range(1, width - 1): if pixel_data.is_black(x, height - 1): self._stretch_left = x break else: self._stretch_left = 1 for x in range(width - 2, 0, -1): if pixel_data.is_black(x, height - 1): self._stretch_right = width - x break else: self._stretch_right = 1 for y in range(1, height - 1): if pixel_data.is_black(0, y): self._stretch_bottom = y break else: self._stretch_bottom = 1 for y in range(height - 2, 0, -1): if pixel_data.is_black(0, y): self._stretch_top = height - y break else: self._stretch_top = 1 # Find content area markers, if any for x in range(1, width - 1): if pixel_data.is_black(x, 0): self._padding_left = x - 1 break for x in range(width - 2, 0, -1): if pixel_data.is_black(x, 0): self._padding_right = self.width - x break for y in range(1, height - 1): if pixel_data.is_black(width - 1, y): self._padding_bottom = y - 1 break for y in range(height - 2, 0, -1): if pixel_data.is_black(width - 1, y): self._padding_top = self.height - y break # Texture coordinates, in pixels u1 = 1 v1 = 1 u2 = self._stretch_left + 1 v2 = self._stretch_bottom + 1 u3 = width - self._stretch_right - 1 v3 = height - self._stretch_top - 1 u4 = width - 1 v4 = height - 1 # Texture coordinates as ratio of image size (0 to 1) u1, u2, u3, u4 = [s / width for s in (u1, u2, u3, u4)] v1, v2, v3, v4 = [s / height for s in (v1, v2, v3, v4)] # Scale texture coordinates to match the tex_coords pyglet gives us # (these aren't necessarily 0-1 as the texture may have been packed) (tu1, tv1, ___, ___, ___, ___, tu2, tv2, ___, ___, ___, ___) = image.get_texture().tex_coords u_scale = tu2 - tu1 u_bias = tu1 v_scale = tv2 - tv1 v_bias = tv1 u1, u2, u3, u4 = [u_bias + u_scale * s for s in (u1, u2, u3, u4)] v1, v2, v3, v4 = [v_bias + v_scale * s for s in (v1, v2, v3, v4)] # 2D texture coordinates, bottom-left to top-right self.tex_coords = (u1, v1, u2, v1, u3, v1, u4, v1, u1, v2, u2, v2, u3, v2, u4, v2, u1, v3, u2, v3, u3, v3, u4, v3, u1, v4, u2, v4, u3, v4, u4, v4) # Quad indices self.indices = [] for y in range(3): for x in range(3): self.indices.extend([x + y * 4, (x + 1) + y * 4, (x + 1) + (y + 1) * 4, x + (y + 1) * 4]) def get_vertices(self, x, y, width, height): """Get 16 2D vertices for the given image region""" x = int(x) y = int(y) if width < self.width + 2 or height < self.height + 2: raise NinePatchException("Requested size is smaller than the image.") width = int(width) height = int(height) x1 = x y1 = y x2 = x + self._stretch_left y2 = y + self._stretch_bottom x3 = x + width - self._stretch_right y3 = y + height - self._stretch_top x4 = x + width y4 = y + height # To match tex coords, vertices are bottom-left to top-right return (x1, y1, x2, y1, x3, y1, x4, y1, x1, y2, x2, y2, x3, y2, x4, y2, x1, y3, x2, y3, x3, y3, x4, y3, x1, y4, x2, y4, x3, y4, x4, y4)