X-Git-Url: http://nitlanguage.org diff --git a/lib/gamnit/textures.nit b/lib/gamnit/textures.nit index 4bc95cb..4383c2b 100644 --- a/lib/gamnit/textures.nit +++ b/lib/gamnit/textures.nit @@ -20,7 +20,7 @@ import display # Texture composed of pixels, loaded from the assets folder by default # # Most textures should be created with `App` (as attributes) -# for the method `on_create` to load them. +# for the method `create_scene` to load them. # # ~~~ # import gamnit::flat @@ -29,9 +29,9 @@ import display # # Create the texture object, it will be loaded automatically # var texture = new Texture("path/in/assets.png") # -# redef fun on_create() +# redef fun create_scene() # do -# # Let `on_create` load the texture +# # Let `create_scene` load the texture # super # # # Use the texture @@ -41,7 +41,7 @@ import display # end # ~~~ # -# Otherwise, they can be loaded and error checked explicitly after `on_create`. +# Otherwise, they can be loaded and error checked explicitly after `create_scene`. # # ~~~nitish # var texture = new Texture("path/in/assets.png") @@ -80,9 +80,7 @@ abstract class Texture # Prepare a subtexture from this texture, from the given pixel offsets fun subtexture(left, top, width, height: Numeric): Subtexture do - # Setup the subtexture - var subtex = new Subtexture(root, self, left.to_f, top.to_f, width.to_f, height.to_f) - return subtex + return new AbsoluteSubtexture(self, left.to_f, top.to_f, width.to_f, height.to_f) end # Offset of the left border on `root` from 0.0 to 1.0 @@ -156,10 +154,9 @@ class CustomTexture # The argument `color` should be an array of up to 4 floats (RGBA). # If `color` has less than 4 items, the missing items are replaced by 1.0. # - # Require: `not loaded and x < width.to_i and y < height.to_i` + # Require: `x < width.to_i and y < height.to_i` fun []=(x, y: Int, color: Array[Float]) do - assert not loaded else print_error "{class_name}::[]= already loaded" assert x < width.to_i and y < height.to_i else print_error "{class_name}::[] out of bounds" # Simple conversion from [0.0..1.0] to [0..255] @@ -168,18 +165,16 @@ class CustomTexture var offset = 4*(x + y*width.to_i) for i in [0..4[ do cpixels[offset+i] = bytes[i] + + loaded = false end - # Overwrite all pixels with `color` + # Overwrite all pixels with `color`, return `self` # # The argument `color` should be an array of up to 4 floats (RGBA). # If `color` has less than 4 items, the missing items are replaced by 1.0. - # - # Require: `not loaded` - fun fill(color: Array[Float]) + fun fill(color: Array[Float]): SELF do - assert not loaded else print_error "{class_name}::fill already loaded" - # Simple conversion from [0.0..1.0] to [0..255] var bytes = [for c in color do (c*255.0).round.to_i.clamp(0, 255).to_bytes.last] while bytes.length < 4 do bytes.add 255u8 @@ -191,11 +186,21 @@ class CustomTexture i += 4 end end + + loaded = false + return self end redef fun load(force) do - if loaded then return + force = force or else false + if loaded and not force then return + + if force and glIsTexture(gl_texture) then + # Was already loaded, free the previous GL name + glDeleteTextures([gl_texture]) + end + gl_texture = -1 # Round down the desired dimension var width = width.to_i @@ -205,12 +210,11 @@ class CustomTexture load_from_pixels(cpixels.native_array, width, height, gl_RGBA) - cpixels.destroy loaded = true end end -# Texture with its own pixels +# Texture with its own pixel data class RootTexture super Texture @@ -223,14 +227,34 @@ class RootTexture init do all_root_textures.add self + # Should the pixels RGB values be premultiplied by their alpha value at loading? + # + # All gamnit textures must have premultiplied alpha, it provides a better + # alpha blending, avoids artifacts and allows for additive blending. + # + # When at `true`, the default, pixels RGB values are premultiplied + # at loading. Set to `false` if pixels RGB values are already + # premultiplied in the source data. + # + # This value must be set before calling `load`. + var premultiply_alpha = true is writable + private fun load_from_pixels(pixels: Pointer, width, height: Int, format: GLPixelFormat) do var max_texture_size = glGetIntegerv(gl_MAX_TEXTURE_SIZE, 0) - if width > max_texture_size or height > max_texture_size then - error = new Error("Texture {self} width or height is over the GL_MAX_TEXTURE_SIZE of {max_texture_size}") + if width > max_texture_size then + error = new Error("Texture width larger than gl_MAX_TEXTURE_SIZE ({max_texture_size}) in {self} at {width}") + return + else if height > max_texture_size then + error = new Error("Texture height larger than gl_MAX_TEXTURE_SIZE ({max_texture_size}) in {self} at {height}") return end + # Premultiply alpha? + if premultiply_alpha and format == gl_RGBA then + pixels.premultiply_alpha(width, height) + end + glPixelStorei(gl_UNPACK_ALIGNEMENT, 1) var tex = glGenTextures(1)[0] gl_texture = tex @@ -240,6 +264,9 @@ class RootTexture glHint(gl_GENERATE_MIPMAP_HINT, gl_NICEST) glGenerateMipmap(gl_TEXTURE_2D) + glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR_MIPMAP_LINEAR) + + glBindTexture(gl_TEXTURE_2D, 0) end private fun load_checker(size: Int) @@ -309,18 +336,25 @@ class TextureAsset end # Texture derived from another texture, does not own its pixels -class Subtexture +abstract class Subtexture super Texture - redef var root - # Parent texture, from which this texture was created var parent: Texture - # Left border of this texture compared to `parent` + redef fun root do return parent.root + + redef fun load(force) do root.load(force) +end + +# Subtexture created from pixel coordinates within `parent` +class AbsoluteSubtexture + super Subtexture + + # Left border of this texture relative to `parent` var left: Float - # Top border of this texture compared to `parent` + # Top border of this texture relative to `parent` var top: Float private fun set_wh(width, height: Float) @@ -329,14 +363,25 @@ class Subtexture self.height = height end - redef fun load(force) do root.load(force) - redef var offset_left = parent.offset_left + left / root.width is lazy redef var offset_top = parent.offset_top + top / root.height is lazy redef var offset_right = offset_left + width / root.width is lazy redef var offset_bottom = offset_top + height / root.height is lazy end +# Subtexture created from relative coordinates ([0..1]) out of the `root` texture +class RelativeSubtexture + super Subtexture + + redef var offset_left + redef var offset_top + redef var offset_right + redef var offset_bottom + + redef fun width do return root.width * (offset_right - offset_left) + redef fun height do return root.height * (offset_bottom - offset_top) +end + redef class Sys # All declared root textures var all_root_textures = new TextureSet @@ -349,3 +394,20 @@ class TextureSet # Load all texture of this set fun load_all do for t in self do t.load end + +redef class Pointer + # Multiply RGB values by their alpha value + private fun premultiply_alpha(width, height: Int) `{ + uint8_t *bytes = (uint8_t *)self; + int x, y, i = 0; + for(y = 0; y < height; y ++) { + for(x = 0; x < width; x ++) { + int a = bytes[i+3]; + bytes[i ] = bytes[i ] * a / 255; + bytes[i+1] = bytes[i+1] * a / 255; + bytes[i+2] = bytes[i+2] * a / 255; + i += 4; + } + } + `} +end