blob: af9a70e6fd8b24098d7ca7fa5a805c588443344e [file] [log] [blame]
#ifndef RTW_STB_IMAGE_H
#define RTW_STB_IMAGE_H
//==============================================================================================
// Originally written in 2016 by Peter Shirley <ptrshrl@gmail.com>
//
// To the extent possible under law, the author(s) have dedicated all copyright
// and related and neighboring rights to this software to the public domain
// worldwide. This software is distributed without any warranty.
//
// You should have received a copy (see file COPYING.txt) of the CC0 Public
// Domain Dedication along with this software. If not, see
// <http://creativecommons.org/publicdomain/zero/1.0/>.
//
// The original source code is from
// https://github.com/RayTracing/raytracing.github.io/tree/release/src/TheNextWeek
//
// Changes to the original code follow the following license.
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//==============================================================================================
// Disable strict warnings for this header from the Microsoft Visual C++ compiler.
#ifdef _MSC_VER
#pragma warning (push, 0)
#endif
#define STB_IMAGE_IMPLEMENTATION
#include "../external/stb_image.h"
#include "DeviceArray.h"
#include "Map.h"
#include "String.h"
#include <cstdlib>
#include <iostream>
class rtw_image {
public:
__host__ __device__ rtw_image() : data(nullptr) {}
__host__ __device__ rtw_image(int width, int height, unsigned char *data_)
: data(data_), image_width(width), image_height(height) {
bytes_per_scanline = image_width * bytes_per_pixel;
}
__host__ __device__ rtw_image(const char *image_filename) {
TexDBTy *pDB;
#if __HIP_DEVICE_COMPILE__
pDB = DevTexDB;
#else
pDB = &TexDB;
#endif
auto Loc = pDB->find(image_filename);
if (Loc == pDB->end()) {
printf("texture %s not found. Please pre_load it.\n", image_filename);
__builtin_trap();
}
rtw_image *img = Loc->second.get();
image_width = img->image_width;
image_height = img->image_height;
data = img->data;
bytes_per_scanline = image_width * bytes_per_pixel;
}
static void pre_load(const char *image_filename) {
// Loads image data from the specified file. If the RTW_IMAGES environment
// variable is defined, looks only in that directory for the image file.
// If the image was not found, searches for the specified image file first
// from the current directory, then in the images/ subdirectory, then the
// _parent's_ images/ subdirectory, and then _that_ parent, on so on, for
// six levels up. If the image was not loaded successfully, width() and
// height() will return 0.
auto filename = std::string(image_filename);
auto imagedir = getenv("RTW_IMAGES");
// Hunt for the image file in some likely locations.
if (imagedir && load(std::string(imagedir) + "/" + image_filename))
return;
if (load(filename))
return;
if (load("images/" + filename))
return;
if (load("../images/" + filename))
return;
if (load("../../images/" + filename))
return;
if (load("../../../images/" + filename))
return;
if (load("../../../../images/" + filename))
return;
if (load("../../../../../images/" + filename))
return;
if (load("../../../../../../images/" + filename))
return;
std::cerr << "ERROR: Could not load image file '" << image_filename
<< "'.\n";
}
__host__ __device__ ~rtw_image() {
// We preload all textures in a global database and assume the textures
// persist the whole program.
// STBI_FREE(data);
}
static __global__ void textureLoadKernel(int width, int height,
const char *name,
unsigned char *data_) {
if (!DevTexDB)
DevTexDB = new TexDBTy;
auto Loc = DevTexDB->find(name);
if (Loc == DevTexDB->end())
(*DevTexDB)[name] = makeShared<rtw_image>(width, height, data_);
}
static bool load(const std::string filename) {
// Loads image data from the given file name. Returns true if the load
// succeeded.
auto n =
bytes_per_pixel; // Dummy out parameter: original components per pixel
int width = 0;
int height = 0;
unsigned char *data_ =
stbi_load(filename.c_str(), &width, &height, &n, bytes_per_pixel);
if (data_ != nullptr) {
size_t lastSlashPos = filename.find_last_of("/");
std::string name = filename;
if (lastSlashPos != std::string::npos)
name = name.substr(lastSlashPos + 1);
auto Loc = TexDB.find(name.c_str());
if (Loc == TexDB.end()) {
TexDB[name.c_str()] = makeShared<rtw_image>(width, height, data_);
DeviceArray<char> devName(name.length() + 1, name.c_str());
DeviceArray<unsigned char> devTexture(width * height * n, data_);
textureLoadKernel<<<1, 1>>>(width, height, devName.getDevicePtr(),
devTexture.getDevicePtr());
}
}
return data_ != nullptr;
}
__host__ __device__ int width() const {
return (data == nullptr) ? 0 : image_width;
}
__host__ __device__ int height() const {
return (data == nullptr) ? 0 : image_height;
}
__host__ __device__ const unsigned char *pixel_data(int x, int y) const {
// Return the address of the three bytes of the pixel at x,y (or magenta
// if no data).
static unsigned char magenta[] = {255, 0, 255};
if (data == nullptr)
return magenta;
x = clamp(x, 0, image_width);
y = clamp(y, 0, image_height);
return data + y * bytes_per_scanline + x * bytes_per_pixel;
}
private:
static const int bytes_per_pixel = 3;
unsigned char *data = nullptr;
int image_width = 0, image_height = 0;
int bytes_per_scanline = 0;
__host__ __device__ static int clamp(int x, int low, int high) {
// Return the value clamped to the range [low, high).
if (x < low)
return low;
if (x < high)
return x;
return high - 1;
}
typedef Map<String, SharedPtr<rtw_image>> TexDBTy;
static TexDBTy TexDB;
__device__ static TexDBTy *DevTexDB;
};
// Restore MSVC compiler warnings
#ifdef _MSC_VER
#pragma warning (pop)
#endif
#endif