1 /** 2 * Copyright: Enalye 3 * License: Zlib 4 * Authors: Enalye 5 */ 6 module atelier.render.texture; 7 8 import std..string; 9 import std.exception; 10 import std.algorithm.comparison : clamp; 11 12 import bindbc.sdl, bindbc.sdl.image; 13 14 import atelier.core; 15 import atelier.render.window, atelier.render.drawable; 16 17 /// Returns the SDL blend flag. 18 package SDL_BlendMode getSDLBlend(Blend blend) { 19 final switch (blend) with (Blend) { 20 case alpha: 21 return SDL_BLENDMODE_BLEND; 22 case additive: 23 return SDL_BLENDMODE_ADD; 24 case modular: 25 return SDL_BLENDMODE_MOD; 26 case none: 27 return SDL_BLENDMODE_NONE; 28 } 29 } 30 31 /// Base rendering class. 32 final class Texture : Drawable { 33 private { 34 bool _isLoaded = false, _ownData, _isSmooth; 35 SDL_Texture* _texture = null; 36 SDL_Surface* _surface = null; 37 uint _width, _height; 38 Color _color = Color.white; 39 float _alpha = 1f; 40 Blend _blend = Blend.alpha; 41 } 42 43 @property { 44 /// loaded ? 45 bool isLoaded() const { 46 return _isLoaded; 47 } 48 /// Width in texels. 49 uint width() const { 50 return _width; 51 } 52 /// Height in texels. 53 uint height() const { 54 return _height; 55 } 56 57 /// Color added to the canvas. 58 Color color() const { 59 return _color; 60 } 61 /// Ditto 62 Color color(Color color_) { 63 _color = color_; 64 auto sdlColor = _color.toSDL(); 65 SDL_SetTextureColorMod(_texture, sdlColor.r, sdlColor.g, sdlColor.b); 66 return _color; 67 } 68 69 /// Alpha 70 float alpha() const { 71 return _alpha; 72 } 73 /// Ditto 74 float alpha(float alpha_) { 75 _alpha = alpha_; 76 SDL_SetTextureAlphaMod(_texture, cast(ubyte)(clamp(_alpha, 0f, 1f) * 255f)); 77 return _alpha; 78 } 79 80 /// Blending algorithm. 81 Blend blend() const { 82 return _blend; 83 } 84 /// Ditto 85 Blend blend(Blend blend_) { 86 _blend = blend_; 87 SDL_SetTextureBlendMode(_texture, getSDLBlend(_blend)); 88 return _blend; 89 } 90 } 91 92 /// Ctor 93 this(const Texture texture) { 94 _isLoaded = texture._isLoaded; 95 _texture = cast(SDL_Texture*) texture._texture; 96 _width = texture._width; 97 _height = texture._height; 98 _isSmooth = texture._isSmooth; 99 _blend = texture._blend; 100 _color = texture._color; 101 _alpha = texture._alpha; 102 _ownData = false; 103 } 104 105 /// Ctor 106 this(SDL_Surface* surface, bool preload_ = false, bool isSmooth_ = false) { 107 _isSmooth = isSmooth_; 108 if (preload_) { 109 _surface = surface; 110 _width = _surface.w; 111 _height = _surface.h; 112 } 113 else 114 load(surface); 115 } 116 117 /// Ctor 118 this(string path, bool preload_ = false, bool isSmooth_ = false) { 119 _isSmooth = isSmooth_; 120 if (preload_) { 121 _surface = IMG_Load(toStringz(path)); 122 _width = _surface.w; 123 _height = _surface.h; 124 _ownData = true; 125 } 126 else 127 load(path); 128 } 129 130 ~this() { 131 unload(); 132 } 133 134 /// Call it if you set the preload flag on ctor. 135 void postload() { 136 enforce(null != _surface, "Invalid surface."); 137 enforce(null != _sdlRenderer, "The renderer does not exist."); 138 139 if (null != _texture) 140 SDL_DestroyTexture(_texture); 141 if (_isSmooth) 142 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); 143 _texture = SDL_CreateTextureFromSurface(_sdlRenderer, _surface); 144 enforce(null != _texture, "Error occurred while converting a surface to a texture format."); 145 if (_isSmooth) 146 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); 147 updateSettings(); 148 149 if (_ownData) 150 SDL_FreeSurface(_surface); 151 _surface = null; 152 _isLoaded = true; 153 } 154 155 package void load(SDL_Surface* surface) { 156 enforce(null != surface, "Invalid surface."); 157 enforce(null != _sdlRenderer, "The renderer does not exist."); 158 159 if (null != _texture) 160 SDL_DestroyTexture(_texture); 161 162 if (_isSmooth) 163 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); 164 _texture = SDL_CreateTextureFromSurface(_sdlRenderer, surface); 165 if (_isSmooth) 166 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); 167 168 enforce(null != _texture, "Error occurred while converting a surface to a texture format."); 169 updateSettings(); 170 171 _width = surface.w; 172 _height = surface.h; 173 174 _isLoaded = true; 175 _ownData = true; 176 } 177 178 /// Load from file 179 void load(string path) { 180 SDL_Surface* surface = IMG_Load(toStringz(path)); 181 182 enforce(null != surface, "Cannot load image file \'" ~ path ~ "\'."); 183 enforce(null != _sdlRenderer, "The renderer does not exist."); 184 185 if (_isSmooth) 186 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); 187 _texture = SDL_CreateTextureFromSurface(_sdlRenderer, surface); 188 if (_isSmooth) 189 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); 190 191 if (null == _texture) 192 throw new Exception( 193 "Error occurred while converting \'" ~ path ~ "\' to a texture format."); 194 updateSettings(); 195 196 _width = surface.w; 197 _height = surface.h; 198 SDL_FreeSurface(surface); 199 200 _isLoaded = true; 201 _ownData = true; 202 } 203 204 /// Free image data 205 void unload() { 206 if (!_ownData) 207 return; 208 if (null != _texture) 209 SDL_DestroyTexture(_texture); 210 _isLoaded = false; 211 } 212 213 private void updateSettings() { 214 auto sdlColor = _color.toSDL(); 215 SDL_SetTextureBlendMode(_texture, getSDLBlend(_blend)); 216 SDL_SetTextureColorMod(_texture, sdlColor.r, sdlColor.g, sdlColor.b); 217 SDL_SetTextureAlphaMod(_texture, cast(ubyte)(clamp(_alpha, 0f, 1f) * 255f)); 218 } 219 220 /// Render the whole texture here 221 void draw(Vec2f pos, Vec2f anchor = Vec2f.half) const { 222 assert(_isLoaded, "Cannot render the texture: Asset not loaded."); 223 pos -= anchor * Vec2f(_width, _height); 224 225 SDL_Rect destRect = {cast(uint) pos.x, cast(uint) pos.y, _width, _height}; 226 227 SDL_RenderCopy(cast(SDL_Renderer*) _sdlRenderer, 228 cast(SDL_Texture*) _texture, null, &destRect); 229 } 230 231 /// Render a section of the texture here 232 void draw(Vec2f pos, Vec4i srcRect, Vec2f anchor = Vec2f.half) const { 233 assert(_isLoaded, "Cannot render the texture: Asset not loaded."); 234 pos -= anchor * cast(Vec2f) srcRect.zw; 235 236 SDL_Rect srcSdlRect = srcRect.toSdlRect(); 237 SDL_Rect destSdlRect = { 238 cast(uint) pos.x, cast(uint) pos.y, srcSdlRect.w, srcSdlRect.h 239 }; 240 241 SDL_RenderCopy(cast(SDL_Renderer*) _sdlRenderer, 242 cast(SDL_Texture*) _texture, &srcSdlRect, &destSdlRect); 243 } 244 245 /// Render the whole texture here 246 void draw(Vec2f pos, Vec2f size, Vec2f anchor = Vec2f.half) const { 247 assert(_isLoaded, "Cannot render the texture: Asset not loaded."); 248 pos -= anchor * size; 249 250 SDL_Rect destSdlRect = { 251 cast(uint) pos.x, cast(uint) pos.y, cast(uint) size.x, cast(uint) size.y 252 }; 253 254 SDL_RenderCopy(cast(SDL_Renderer*) _sdlRenderer, 255 cast(SDL_Texture*) _texture, null, &destSdlRect); 256 } 257 258 /// Render a section of the texture here 259 void draw(Vec2f pos, Vec2f size, Vec4i srcRect, Vec2f anchor = Vec2f.half) const { 260 enforce(_isLoaded, "Cannot render the texture: Asset not loaded."); 261 pos -= anchor * size; 262 263 SDL_Rect srcSdlRect = srcRect.toSdlRect(); 264 SDL_Rect destSdlRect = { 265 cast(uint) pos.x, cast(uint) pos.y, cast(uint) size.x, cast(uint) size.y 266 }; 267 268 SDL_RenderCopy(cast(SDL_Renderer*) _sdlRenderer, 269 cast(SDL_Texture*) _texture, &srcSdlRect, &destSdlRect); 270 } 271 272 /// Render a section of the texture here 273 void draw(Vec2f pos, Vec2f size, Vec4i srcRect, float angle, 274 Flip flip = Flip.none, Vec2f anchor = Vec2f.half) const { 275 assert(_isLoaded, "Cannot render the texture: Asset not loaded."); 276 pos -= anchor * size; 277 278 SDL_Rect srcSdlRect = srcRect.toSdlRect(); 279 SDL_Rect destSdlRect = { 280 cast(uint) pos.x, cast(uint) pos.y, cast(uint) size.x, cast(uint) size.y 281 }; 282 283 const SDL_RendererFlip rendererFlip = getSDLFlip(flip); 284 SDL_RenderCopyEx(cast(SDL_Renderer*) _sdlRenderer, cast(SDL_Texture*) _texture, 285 &srcSdlRect, &destSdlRect, angle, null, rendererFlip); 286 } 287 }