#include "texture.h" #include // for SDL_GetError #include // for SDL_CreateRGBSurfaceWithFormatFrom #include // for SEEK_END, SEEK_SET #include // for fseek, fclose, fopen, fread, ftell, NULL #include // for malloc, free, exit #include // for basic_ostream, operator<<, cout, endl #include "gif.c" // for LoadGif, LoadPalette #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" // for stbi_failure_reason, stbi_image_free // Constructor Texture::Texture(SDL_Renderer *renderer, std::string path) : renderer_(renderer), path_(path) { // Inicializa surface_ = nullptr; texture_ = nullptr; width_ = 0; height_ = 0; paletteIndex_ = 0; palettes_.clear(); // Carga el fichero en la textura if (!path_.empty()) { // Obtiene la extensión const std::string extension = path_.substr(path_.find_last_of(".") + 1); // .png if (extension == "png") { loadFromFile(path_); } // .gif else if (extension == "gif") { surface_ = loadSurface(path_.c_str()); addPalette(path_.c_str()); setPaletteColor(0, 0, 0x00000000); createBlank(width_, height_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING); SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND); flipSurface(); } } } // Destructor Texture::~Texture() { unload(); } // Carga una imagen desde un fichero bool Texture::loadFromFile(std::string path) { const std::string file_name = path.substr(path.find_last_of("\\/") + 1); int req_format = STBI_rgb_alpha; int width, height, orig_format; unsigned char *data = stbi_load(path.c_str(), &width, &height, &orig_format, req_format); if (data == nullptr) { #ifdef VERBOSE std::cout << "Loading image failed: " << stbi_failure_reason() << std::endl; #endif exit(1); } else { #ifdef VERBOSE std::cout << "Image loaded: " << file_name.c_str() << std::endl; #endif } int depth, pitch; Uint32 pixel_format; if (req_format == STBI_rgb) { depth = 24; pitch = 3 * width; // 3 bytes por pixel * pixels por linea pixel_format = SDL_PIXELFORMAT_RGB24; } else { // STBI_rgb_alpha (RGBA) depth = 32; pitch = 4 * width; pixel_format = SDL_PIXELFORMAT_RGBA32; } // Limpia unload(); // La textura final SDL_Texture *newTexture = nullptr; // Carga la imagen desde una ruta específica SDL_Surface *loadedSurface = SDL_CreateRGBSurfaceWithFormatFrom((void *)data, width, height, depth, pitch, pixel_format); if (loadedSurface == nullptr) { #ifdef VERBOSE std::cout << "Unable to load image " << path.c_str() << std::endl; #endif } else { // Crea la textura desde los pixels de la surface newTexture = SDL_CreateTextureFromSurface(renderer_, loadedSurface); if (newTexture == nullptr) { #ifdef VERBOSE std::cout << "Unable to create texture from " << path.c_str() << "! SDL Error: " << SDL_GetError() << std::endl; #endif } else { // Obtiene las dimensiones de la imagen width_ = loadedSurface->w; height_ = loadedSurface->h; } // Elimina la textura cargada SDL_FreeSurface(loadedSurface); } // Return success stbi_image_free(data); texture_ = newTexture; return texture_ != nullptr; } // Crea una textura en blanco bool Texture::createBlank(int width, int height, SDL_PixelFormatEnum format, SDL_TextureAccess access) { // Crea una textura sin inicializar texture_ = SDL_CreateTexture(renderer_, format, access, width, height); if (!texture_) { #ifdef VERBOSE std::cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << std::endl; #endif } else { width_ = width; height_ = height; } return texture_ != nullptr; } // Libera la memoria de la textura void Texture::unload() { // Libera la textura if (texture_) { SDL_DestroyTexture(texture_); texture_ = nullptr; width_ = 0; height_ = 0; } // Libera la surface if (surface_) { deleteSurface(surface_); surface_ = nullptr; } } // Establece el color para la modulacion void Texture::setColor(Uint8 red, Uint8 green, Uint8 blue) { SDL_SetTextureColorMod(texture_, red, green, blue); } // Establece el blending void Texture::setBlendMode(SDL_BlendMode blending) { SDL_SetTextureBlendMode(texture_, blending); } // Establece el alpha para la modulación void Texture::setAlpha(Uint8 alpha) { SDL_SetTextureAlphaMod(texture_, alpha); } // Renderiza la textura en un punto específico void Texture::render(int x, int y, SDL_Rect *clip, float zoomW, float zoomH, double angle, SDL_Point *center, SDL_RendererFlip flip) { // Establece el destino de renderizado en la pantalla SDL_Rect renderQuad = {x, y, width_, height_}; // Obtiene las dimesiones del clip de renderizado if (clip != nullptr) { renderQuad.w = clip->w; renderQuad.h = clip->h; } renderQuad.w = renderQuad.w * zoomW; renderQuad.h = renderQuad.h * zoomH; // Renderiza a pantalla SDL_RenderCopyEx(renderer_, texture_, clip, &renderQuad, angle, center, flip); } // Establece la textura como objetivo de renderizado void Texture::setAsRenderTarget(SDL_Renderer *renderer) { SDL_SetRenderTarget(renderer, texture_); } // Obtiene el ancho de la imagen int Texture::getWidth() { return width_; } // Obtiene el alto de la imagen int Texture::getHeight() { return height_; } // Recarga la textura bool Texture::reLoad() { return loadFromFile(path_); } // Obtiene la textura SDL_Texture *Texture::getSDLTexture() { return texture_; } // Crea una nueva surface Surface Texture::newSurface(int w, int h) { Surface surf = (Surface)malloc(sizeof(surface_s)); surf->w = w; surf->h = h; surf->data = (Uint8 *)malloc(w * h); return surf; } // Elimina una surface void Texture::deleteSurface(Surface surface) { if (surface == nullptr) { return; } if (surface->data != nullptr) { free(surface->data); } free(surface); } // Crea una surface desde un fichero .gif Surface Texture::loadSurface(const char *file_name) { FILE *f = fopen(file_name, "rb"); if (!f) { return NULL; } fseek(f, 0, SEEK_END); long size = ftell(f); fseek(f, 0, SEEK_SET); Uint8 *buffer = (Uint8 *)malloc(size); fread(buffer, size, 1, f); fclose(f); Uint16 w, h; Uint8 *pixels = LoadGif(buffer, &w, &h); if (pixels == NULL) { return NULL; } Surface surface = (Surface)malloc(sizeof(surface_s)); surface->w = w; surface->h = h; surface->data = pixels; free(buffer); width_ = w; height_ = h; return surface; } // Vuelca la surface en la textura void Texture::flipSurface() { // Limpia la textura auto temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, texture_); SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); SDL_RenderClear(renderer_); SDL_SetRenderTarget(renderer_, temp); // Vuelca los datos Uint32 *pixels; int pitch; SDL_LockTexture(texture_, nullptr, (void **)&pixels, &pitch); for (int i = 0; i < width_ * height_; ++i) { pixels[i] = palettes_[paletteIndex_][surface_->data[i]]; } SDL_UnlockTexture(texture_); } // Establece un color de la paleta void Texture::setPaletteColor(int palette, int index, Uint32 color) { palettes_.at(palette)[index] = color; } // Carga una paleta desde un fichero std::vector Texture::loadPal(const char *file_name) { std::vector palette; FILE *f = fopen(file_name, "rb"); if (!f) { return palette; } fseek(f, 0, SEEK_END); long size = ftell(f); fseek(f, 0, SEEK_SET); Uint8 *buffer = (Uint8 *)malloc(size); fread(buffer, size, 1, f); fclose(f); Uint32 *pal = LoadPalette(buffer); if (pal == nullptr) { return palette; } free(buffer); for (int i = 0; i < 256; ++i) { palette.push_back((pal[i] << 8) + 255); } return palette; } // Añade una paleta a la lista void Texture::addPalette(std::string path) { palettes_.push_back(loadPal(path.c_str())); setPaletteColor((int)palettes_.size() - 1, 0, 0x00000000); } // Cambia la paleta de la textura void Texture::setPalette(int palette) { if (palette < (int)palettes_.size()) { paletteIndex_ = palette; flipSurface(); } } // Obtiene el renderizador SDL_Renderer *Texture::getRenderer() { return renderer_; }