code:
#include "Core/Graphics/RenderImage.hpp"

#include "Core/Application.hpp"
#include "Core/Graphics/Texture.hpp"

#include <SDL3/SDL.h>

void RenderImage::createTexture() {
  if (m_renderImage[m_workBuffer]) {
    SDL_DestroyTexture(static_cast<SDL_Texture*>(m_renderImage[m_workBuffer]));
  }
  m_renderImage[m_workBuffer] = SDL_CreateTexture(App::APP->getWindow()->getNativeRenderer(), SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, m_pendingWidth, m_pendingHeight);
  SDL_SetTextureBlendMode(static_cast<SDL_Texture*>(m_renderImage[m_workBuffer]), SDL_BLENDMODE_BLEND);
  auto* renderer = App::APP->getWindow()->getNativeRenderer();
  auto* oldTarget = SDL_GetRenderTarget(renderer);
  SDL_SetRenderTarget(renderer, static_cast<SDL_Texture*>(m_renderImage[m_workBuffer]));
  SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
  SDL_RenderClear(renderer);
  SDL_RenderPresent(renderer);
  SDL_SetRenderTarget(renderer, oldTarget);
}
RenderImage::RenderImage(const int width, const int height)
: m_pendingWidth(width)
, m_pendingHeight(height) {
  for (int i = 0; i < 2; ++i) {
    m_width[i] = width;
    m_height[i] = height;
  }
}

void RenderImage::setSize(int width, int height) {
  m_pendingWidth = width;
  m_pendingHeight = height;
  m_bufferInvalid[0] = true;
  m_bufferInvalid[1] = true;
}

void RenderImage::setCurrentCompositionMode() const {
  auto* renderer = App::APP->getWindow()->getNativeRenderer();
  SDL_BlendMode blendMode = SDL_BLENDMODE_BLEND;
  switch (m_compositionMode) {
  case CompositionMode::SourceOver:
    blendMode =
        SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
    break;
  case CompositionMode::DestinationOver:
    blendMode =
        SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD);
    break;
  case CompositionMode::Clear:
    blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD);
    break;
  case CompositionMode::Source:
    blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD);
    break;
  case CompositionMode::Destination:
    blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD);
    break;
  case CompositionMode::SourceIn:
    blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD);
    break;
  case CompositionMode::DestinationIn:
    blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
    break;
  case CompositionMode::SourceOut:
    blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA, SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA, SDL_BLENDFACTOR_ZERO,
                                           SDL_BLENDOPERATION_ADD);
    break;
  case CompositionMode::DestinationOut:
    blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
                                           SDL_BLENDOPERATION_ADD);
    break;
  case CompositionMode::SourceAtop:
    blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
                                           SDL_BLENDOPERATION_ADD);
    break;
  case CompositionMode::DestinationAtop:
    blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA, SDL_BLENDFACTOR_SRC_ALPHA,
                                           SDL_BLENDOPERATION_ADD);
    break;
  case CompositionMode::Xor:
    blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA,
                                           SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
    break;
  case CompositionMode::Plus:
    blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD);
    break;
  }

  SDL_SetRenderDrawBlendMode(renderer, blendMode);
}

bool RenderImage::lock() {
  if (m_locked) {
    APP_WARN("Trying to lock an already locked image");
    return false;
  }

  auto* renderer = App::APP->getWindow()->getNativeRenderer();
  // First we attempt to create the texture
  if (m_bufferInvalid[m_workBuffer]) {
    createTexture();
    m_width[m_workBuffer] = m_pendingWidth;
    m_height[m_workBuffer] = m_pendingHeight;
    m_bufferInvalid[m_workBuffer] = false;
  }

  // If we don't succeed bail so we don't do anything weird
  if (!m_renderImage[m_workBuffer]) {
    return false;
  }

  m_locked = true;
  m_oldTarget = SDL_GetRenderTarget(renderer);
  SDL_SetRenderTarget(renderer, static_cast<SDL_Texture*>(m_renderImage[m_workBuffer]));
  SDL_GetRenderDrawBlendMode(renderer, reinterpret_cast<SDL_BlendMode*>(&m_oldBlend));
  return true;
}

void RenderImage::unlock() {
  if (!m_locked) {
    APP_WARN("Trying to unlocked an already unlocked image");
    return;
  }

  auto* renderer = App::APP->getWindow()->getNativeRenderer();
  if (SDL_GetRenderTarget(renderer) != m_renderImage[m_workBuffer]) {
    APP_ERROR("RenderImage::unlock() called when on non-matching render target!");
    return;
  }

  SDL_SetRenderDrawBlendMode(renderer, static_cast<SDL_BlendMode>(m_oldBlend));
  SDL_SetRenderTarget(renderer, static_cast<SDL_Texture*>(m_oldTarget));
  m_locked = false;
  std::swap(m_currentBuffer, m_workBuffer);
}

void RenderImage::eraseRect(const RectF& rect) const {
  if (!m_locked) {
    APP_WARN("Trying to erase on an unlocked image");
    return;
  }
  auto* renderer = App::APP->getWindow()->getNativeRenderer();
  setCurrentCompositionMode();

  const SDL_FRect renderRect = {rect.x(), rect.y(), rect.width(), rect.height()};
  SDL_RenderFillRect(renderer, &renderRect);
}

void RenderImage::fillRect(const RectF& rect, const Color& color) const {
  if (!m_locked) {
    APP_WARN("Trying to erase on an unlocked image");
    return;
  }
  auto* renderer = App::APP->getWindow()->getNativeRenderer();

  if (SDL_GetRenderTarget(renderer) != m_renderImage[m_workBuffer]) {
    APP_ERROR("RenderImage::eraseRect() called when on non-matching render target!");
    return;
  }
  setCurrentCompositionMode();
  Uint8 r, g, b, a;
  SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a);
  const SDL_FRect renderRect = {rect.x(), rect.y(), rect.width(), rect.height()};
  Color::Rgb rgb = color.rgba();
  SDL_SetRenderDrawColor(renderer, Color::red(rgb), Color::green(rgb), Color::blue(rgb), Color::alpha(rgb));
  SDL_RenderFillRect(renderer, &renderRect);
  SDL_SetRenderDrawColor(renderer, r, g, b, a);
}

void RenderImage::drawImage(const RectF& target, const Texture& image, const Rect& src) const {
  if (!m_locked) {
    APP_WARN("Trying to draw on an unlocked image");
    return;
  }
  auto* renderer = App::APP->getWindow()->getNativeRenderer();

  if (SDL_GetRenderTarget(renderer) != m_renderImage[m_workBuffer]) {
    APP_ERROR("RenderImage::drawImage() called when on non-matching render target!");
    return;
  }
  setCurrentCompositionMode();
  const SDL_FRect srcRect{static_cast<float>(src.x()), static_cast<float>(src.y()), static_cast<float>(src.width()), static_cast<float>(src.height())};
  const SDL_FRect destRect{target.x(), target.y(), target.width(), target.height()};
  SDL_RenderTexture(renderer, static_cast<SDL_Texture*>(image.get()), &srcRect, &destRect);
}

void RenderImage::drawImageRaw(const RectF& target, void* image, const Rect& src) const {
  if (!m_locked) {
    APP_WARN("Trying to draw on an unlocked image");
    return;
  }
  auto* renderer = App::APP->getWindow()->getNativeRenderer();

  if (SDL_GetRenderTarget(renderer) != m_renderImage[m_workBuffer]) {
    APP_ERROR("RenderImage::drawImageRaw() called when on non-matching render target!");
    return;
  }
  setCurrentCompositionMode();
  const SDL_FRect srcRect{static_cast<float>(src.x()), static_cast<float>(src.y()), static_cast<float>(src.width()), static_cast<float>(src.height())};
  const SDL_FRect destRect{target.x(), target.y(), target.width(), target.height()};
  SDL_RenderTexture(renderer, static_cast<SDL_Texture*>(image), &srcRect, &destRect);
}


code:
#pragma once
#include "OREMath/Color.hpp"
#include "OREMath/Rect.hpp"

#ifndef ImTextureID
typedef unsigned long long ImTextureID;
#endif

class Texture;
class RenderImage {

public:
  enum class CompositionMode {
    SourceOver,
    DestinationOver,
    Clear,
    Source,
    Destination,
    SourceIn,
    DestinationIn,
    SourceOut,
    DestinationOut,
    SourceAtop,
    DestinationAtop,
    Xor,
    Plus,
  };
  RenderImage() = default;
  RenderImage(int width, int height);

  void* get() const { return m_renderImage[m_currentBuffer]; }
  explicit operator ImTextureID() const { return reinterpret_cast<ImTextureID>(get()); }

  bool lock();
  void unlock();
  void eraseRect(const RectF& rect) const;
  void fillRect(const RectF& rect, const Color& color) const;
  void drawImage(const RectF& target, const Texture& image, const Rect& src) const;
  void drawImageRaw(const RectF& target, void* image, const Rect& src) const;

  void setSize(int width, int height);
  CompositionMode compositionMode() const { return m_compositionMode; }
  void setCompositionMode(const CompositionMode mode) { m_compositionMode = mode; }

  explicit operator bool() const { return get() != nullptr; }

private:
  void createTexture();
  void setCurrentCompositionMode() const;
  void* m_renderImage[2]{nullptr, nullptr};
  int m_currentBuffer = 1;
  int m_workBuffer = 0;
  bool m_bufferInvalid[2] = {true, true};
  CompositionMode m_compositionMode = CompositionMode::SourceOver;
  int m_width[2] = {0, 0};
  int m_height[2] = {0, 0};
  int m_pendingWidth = 0;
  int m_pendingHeight = 0;
  int m_oldBlend = 0;
  void* m_oldTarget{nullptr};
  bool m_locked{false};
};

repo_name: Antidote/openrpgeditor

license: mit


code:
#include "Core/Graphics/SideViewBattlerSheet.hpp"
#include "Core/ResourceManager.hpp"

struct SVMotion {
  int index;
  bool loop;
};

SideViewBattlerSheet::SideViewBattlerSheet(const std::string_view sheetName)
: m_texture(ResourceManager::instance()->loadSVActorImage(sheetName)) {}

SideViewAction SideViewBattlerSheet::getAction(const SideViewActionType actionType) const {
  if (!m_texture) {
    return {};
  }

  SideViewAction action;
  action.type = actionType;
  const int motionIndex = static_cast<int>(actionType);
  const float cw = m_texture.width() / 9;
  const float ch = m_texture.height() / 6;
  for (int pattern = 0; pattern < 3; ++pattern) {
    const float cx = std::floor(motionIndex % 3) + pattern;
    const float cy = std::floor(motionIndex / 3);
    action.frames[pattern].min.xr() = (cx * cw);
    action.frames[pattern].min.yr() = (cy * ch);
    action.frames[pattern].max.xr() = ((cx * cw) + cw);
    action.frames[pattern].max.yr() = ((cy * ch) + ch);
    action.frames[pattern].min.xr() /= m_texture.width();
    action.frames[pattern].min.yr() /= m_texture.height();
    action.frames[pattern].max.xr() /= m_texture.width();
    action.frames[pattern].max.yr() /= m_texture.height();
  }

  return action;
}


code:
#pragma once

#include "Core/Graphics/SimpleRect.hpp"
#include "Core/Graphics/Texture.hpp"
#include <array>
#include <string_view>

// clang-format off
enum class SideViewActionType {
  Invalid=-1,
  StepForward,     Thrust,       Escape,
  NormalStandby,   Swing,        Victory,
  ChantingStandby, Missile,      NearDeath,
  Guard,           GeneralSkill, StatusAilment,
  Damage,          Magic,        Sleep,
  Evade,           Item,         Dead
};
// clang-format on

struct SideViewAction {
  SideViewActionType type{SideViewActionType::Invalid};
  std::array<SimpleRect, 3> frames;
};
struct SideViewBattlerSheet {
  explicit SideViewBattlerSheet(const std::string_view sheetName);

  [[nodiscard]] SideViewAction getAction(SideViewActionType actionType) const;
  [[nodiscard]] const Texture& texture() const { return m_texture; }

  [[nodiscard]] int characterWidth() const { return m_texture.width() / 9; }
  [[nodiscard]] int characterHeight() const { return m_texture.height() / 6; }

private:
  Texture m_texture;
};


code:
#pragma once

#include <Math/OREMath/Point.hpp>

struct SimpleRect {
  PointF min;
  PointF max;
};


code:
#include "Core/Graphics/Texture.hpp"
#include "Core/Application.hpp"
#include "Core/Graphics/image_loader.h"
#include "SDL3/SDL.h"

#include <iostream>

Texture::Texture(std::string_view filename) {
  SDL_Texture* tmp = nullptr;
  if (LoadTextureFromFile(filename.data(), &tmp, &m_width, &m_height, App::APP->getWindow()->getNativeRenderer())) {
    m_texture = tmp;
    SDL_SetTextureScaleMode(static_cast<SDL_Texture*>(m_texture), SDL_SCALEMODE_NEAREST);
  } else {
    m_texture = nullptr;
  }
}

// Texture::Texture(const char* data, int length, int width, int height) {
//   SDL_Texture* tmp = nullptr;
//   if (LoadTextureFromMemory(data, length, &tmp, &m_width, &m_height, App::APP->getWindow()->getNativeRenderer())) {
//     m_texture = tmp;
//   } else {
//     m_texture = nullptr;
//   }
// }

void Texture::setFilter(FilterType filterType) {
  switch (filterType) {
  case Nearest:
    SDL_SetTextureScaleMode(static_cast<SDL_Texture*>(m_texture), SDL_SCALEMODE_NEAREST);
    break;
  case Linear:
    SDL_SetTextureScaleMode(static_cast<SDL_Texture*>(m_texture), SDL_SCALEMODE_LINEAR);
    break;
  }
}


code:
#pragma once

#include "imgui.h"

#include <string_view>

class Texture {
  friend class ResourceManager;

public:
  enum FilterType {
    Nearest,
    Linear,
  };
  Texture() = default;
  Texture(const Texture& other) {
    m_texture = other.m_texture;
    m_width = other.m_width;
    m_height = other.m_height;
  }

  Texture& operator=(const Texture& other) {
    m_texture = other.m_texture;
    m_width = other.m_width;
    m_height = other.m_height;
    return *this;
  }

  [[nodiscard]] void* get() const { return m_texture; }
  [[nodiscard]] int width() const { return m_width; }
  [[nodiscard]] int height() const { return m_height; }

  operator bool() const { return m_texture != nullptr; }
  operator ImTextureRef() const { return ImTextureRef(m_texture); }

  void invalidate() {
    m_texture = nullptr;
    m_width = 0;
    m_height = 0;
  }

  void setFilter(FilterType filterType);

private:
  explicit Texture(std::string_view filename);
  // explicit Texture(const char* data, int length, int width, int height);
  void* m_texture = nullptr;
  int m_width{};
  int m_height{};
};


code:
#include "Core/Graphics/TileMarker.hpp"

#include "Core/ResourceManager.hpp"

TileMarker::TileMarker(TileFlags flags, int pageIndex, int width, int height) {
  m_pageIndex = pageIndex;
  m_flags = flags;
  m_imageTexture = ResourceManager::instance()->loadTileMarkers(width, height);
  m_imageWidth = m_imageTexture.width() / kNumColumns;
  m_imageHeight = m_imageTexture.height() / kNumRows;
}
int TileMarker::imageWidth() const { return m_imageTexture.width(); }
int TileMarker::imageHeight() const { return m_imageTexture.height(); }
ImVec2 TileMarker::uv0(int markerIndex) const {
  const auto u = static_cast<float>((getMarkerUv(markerIndex).x) * m_imageWidth);
  const auto v = static_cast<float>((getMarkerUv(markerIndex).y) * m_imageHeight) + getMarkerUv(markerIndex).y;
  return {u / static_cast<float>(imageWidth()), v / static_cast<float>(imageHeight())};
}
ImVec2 TileMarker::uv1(int markerIndex) const {
  const auto u = static_cast<float>((getMarkerUv(markerIndex).x) * m_imageWidth) + (static_cast<float>(m_imageWidth));
  const auto v = static_cast<float>((getMarkerUv(markerIndex).y) * m_imageHeight) + (static_cast<float>(m_imageWidth));
  return {u / static_cast<float>(imageWidth()), v / static_cast<float>(imageHeight())};
}
ImVec2 TileMarker::uvTileSize() const { return {1.0f / (m_imageTexture.width() / m_imageWidth), 1.0f / (m_imageTexture.width() / m_imageHeight)}; }
ImVec2 TileMarker::rectSize() const { return ImVec2(m_imageWidth, m_imageHeight); }
ImVec2 TileMarker::passableOffset(bool isPassable, bool hasHigherTile) const { return hasHigherTile ? ImVec2{0.f, 0.75f} : (isPassable ? ImVec2{0.f, 0.f} : ImVec2{0.25f, 0.f}); }
ImVec2 TileMarker::getMarkerUv(int markerIndex) const {
  if (markerIndex > 15) {
    return {static_cast<float>(markerIndex - 16), 4};
  }
  if (markerIndex > 11) {
    return {static_cast<float>(markerIndex - 12), 3};
  }
  if (markerIndex > 7) {
    return {static_cast<float>(markerIndex - 8), 2};
  }
  if (markerIndex > 3) {
    return {static_cast<float>(markerIndex - 4), 1};
  }
  return {static_cast<float>(markerIndex), 0};
}
void TileMarker::setMarker(TileFlags flags) const {
  // m_markerType = flags;
}


code:
#pragma once

#include "Core/Graphics/Texture.hpp"
#include "Database/Globals.hpp"

struct TileMarker {
  static constexpr int kNumColumns = 4;
  static constexpr int kNumRows = 5;
  TileMarker() = delete;

  explicit TileMarker(TileFlags flags, int pageIndex, int width, int height);
  int imageWidth() const;
  int imageHeight() const;
  ImVec2 uv0(int markerIndex) const;
  ImVec2 uv1(int markerIndex) const;
  ImVec2 uvTileSize() const;
  ImVec2 rectSize() const;
  ImVec2 passableOffset(bool isPassable, bool hasHigherTile) const;
  ImVec2 getMarkerUv(int markerIndex) const;
  void setMarker(TileFlags flags) const;

  explicit operator bool() const { return m_imageTexture.operator bool(); }
  const Texture& texture() const { return m_imageTexture; }

private:
  Texture m_imageTexture;
  TileFlags m_flags;
  int m_pageIndex{0};
  int m_imageWidth{16};
  int m_imageHeight{16};
};


code:
#include <SDL3/SDL.h>
#include <SDL3/SDL_surface.h>
#include <stdbool.h>
#include <stdio.h>

#if _WIN32
#define STBI_WINDOWS_UTF8
#endif

#include "Core/Log.hpp"

#include <SDL3_image/SDL_image.h>
#ifdef __cplusplus
extern "C" {
#endif
bool LoadTextureFromFile(const char* filename, SDL_Texture** texture_ptr, int* width, int* height, SDL_Renderer* renderer) {
  const auto surface = IMG_Load(filename);
  if (!surface) {
    APP_ERROR("Failed to load image '{}': reason: {}\n", filename, SDL_GetError());
    return false;
  }
  const auto convertedSurface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ABGR8888);
  SDL_DestroySurface(surface);
  if (!convertedSurface) {
    APP_ERROR("Failed to convert surface to ABGR8888 for image '{}': reason: {}\n", filename, SDL_GetError());
    return false;
  }
  const auto texture = SDL_CreateTextureFromSurface(renderer, convertedSurface);
  SDL_DestroySurface(convertedSurface);
  if (!texture) {
    APP_ERROR("Failed to create texture for image '{}': reason: {}\n", filename, SDL_GetError());
    return false;
  }
  float _width = 0, _height = 0;
  SDL_GetTextureSize(texture, &_width, &_height);
  *width = _width;
  *height = _height;
  *texture_ptr = texture;
  return true;
}

bool LoadTextureFromMemory(const void* inData, int len, SDL_Texture** texture_ptr, int* width, int* height, SDL_Renderer* renderer) {
  // int channels = 0;
  // unsigned char* data = stbi_load_from_memory(inData, len, width, height, &channels, 0);
  //
  // if (data == NULL) {
  //   fprintf(stderr, "Failed to load image: reason: %s\n", stbi_failure_reason());
  //   return false;
  // }
  //
  // return LoadTexturePriv(texture_ptr, width, height, renderer, channels, data);
  return false;
}

#ifdef __cplusplus
}
#endif


code:
#ifndef IMAGE_LOADER_H_
#define IMAGE_LOADER_H_

#ifdef __cplusplus
extern "C" {
#endif
bool LoadTextureFromFile(const char* filename, struct SDL_Texture** texture_ptr, int* width, int* height, struct SDL_Renderer* renderer);

bool LoadTextureFromMemory(const void* data, int length, struct SDL_Texture** texture_ptr, int* width, int* height, struct SDL_Renderer* renderer);

#ifdef __cplusplus
}
#endif
#endif // IMAGE_LOADER