hdr_image.cpp 4.5 KB
Newer Older
TheNumbat's avatar
TheNumbat committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

#include "hdr_image.h"
#include "../lib/log.h"

#include <sf_libs/stb_image.h>
#include <sf_libs/tinyexr.h>

HDR_Image::HDR_Image() : w(0), h(0) {}

HDR_Image::HDR_Image(size_t w, size_t h) : w(w), h(h) {
    assert(w > 0 && h > 0);
    pixels.resize(w * h);
}

HDR_Image HDR_Image::copy() const {
    HDR_Image ret;
    ret.resize(w, h);
    ret.pixels.insert(ret.pixels.begin(), pixels.begin(), pixels.end());
    ret.last_path = last_path;
    ret.dirty = true;
    ret.exposure = exposure;
    return ret;
}

TheNumbat's avatar
TheNumbat committed
25
std::pair<size_t, size_t> HDR_Image::dimension() const { return {w, h}; }
TheNumbat's avatar
TheNumbat committed
26
27
28
29
30
31
32
33
34
35

void HDR_Image::resize(size_t _w, size_t _h) {
    w = _w;
    h = _h;
    pixels.clear();
    pixels.resize(w * h);
    dirty = true;
}

void HDR_Image::clear(Spectrum color) {
TheNumbat's avatar
TheNumbat committed
36
37
    for (auto &s : pixels)
        s = color;
TheNumbat's avatar
TheNumbat committed
38
39
40
    dirty = true;
}

TheNumbat's avatar
TheNumbat committed
41
Spectrum &HDR_Image::at(size_t i) {
TheNumbat's avatar
TheNumbat committed
42
43
44
45
46
47
48
49
50
51
    assert(i < w * h);
    dirty = true;
    return pixels[i];
}

Spectrum HDR_Image::at(size_t i) const {
    assert(i < w * h);
    return pixels[i];
}

TheNumbat's avatar
TheNumbat committed
52
Spectrum &HDR_Image::at(size_t x, size_t y) {
TheNumbat's avatar
TheNumbat committed
53
54
55
56
57
58
59
60
61
62
63
64
65
66
    assert(x < w && y < h);
    size_t idx = y * w + x;
    dirty = true;
    return pixels[idx];
}

Spectrum HDR_Image::at(size_t x, size_t y) const {
    assert(x < w && y < h);
    size_t idx = y * w + x;
    return pixels[idx];
}

std::string HDR_Image::load_from(std::string file) {

TheNumbat's avatar
TheNumbat committed
67
    if (IsEXR(file.c_str()) == TINYEXR_SUCCESS) {
TheNumbat's avatar
TheNumbat committed
68
69

        int n_w, n_h;
TheNumbat's avatar
TheNumbat committed
70
71
        float *data;
        const char *err = nullptr;
TheNumbat's avatar
TheNumbat committed
72
73
74
75
76
77
78
79
80

        int ret = LoadEXR(&data, &n_w, &n_h, file.c_str(), &err);

        if (ret != TINYEXR_SUCCESS) {

            if (err) {
                std::string err_s(err);
                FreeEXRErrorMessage(err);
                return err_s;
TheNumbat's avatar
TheNumbat committed
81
82
            } else
                return "Unknown failure.";
TheNumbat's avatar
TheNumbat committed
83
84
85
86
87

        } else {

            resize(n_w, n_h);

TheNumbat's avatar
TheNumbat committed
88
89
            for (size_t j = 0; j < h; j++) {
                for (size_t i = 0; i < w; i++) {
TheNumbat's avatar
TheNumbat committed
90
91
                    size_t didx = 4 * (j * w + i);
                    size_t pidx = (h - j - 1) * w + i;
TheNumbat's avatar
TheNumbat committed
92
                    pixels[pidx] = Spectrum(data[didx], data[didx + 1], data[didx + 2]);
TheNumbat's avatar
TheNumbat committed
93
94
                    if (!pixels[pidx].valid())
                        pixels[pidx] = {};
TheNumbat's avatar
TheNumbat committed
95
96
97
98
99
100
101
102
103
                }
            }

            free(data);
        }

    } else {

        stbi_set_flip_vertically_on_load(true);
TheNumbat's avatar
TheNumbat committed
104

TheNumbat's avatar
TheNumbat committed
105
106
107
        int n_w, n_h, channels;
        unsigned char *data = stbi_load(file.c_str(), &n_w, &n_h, &channels, 0);

TheNumbat's avatar
TheNumbat committed
108
109
110
111
        if (!data)
            return std::string(stbi_failure_reason());
        if (channels < 3)
            return "Image has less than 3 color channels.";
TheNumbat's avatar
TheNumbat committed
112
113
114

        resize(n_w, n_h);

TheNumbat's avatar
TheNumbat committed
115
        for (size_t i = 0; i < w * h * channels; i += channels) {
TheNumbat's avatar
TheNumbat committed
116
            float r = data[i] / 255.0f;
TheNumbat's avatar
TheNumbat committed
117
118
119
            float g = data[i + 1] / 255.0f;
            float b = data[i + 2] / 255.0f;
            pixels[i / channels] = Spectrum(r, g, b);
TheNumbat's avatar
TheNumbat committed
120
121
122
123
        }

        stbi_image_free(data);

TheNumbat's avatar
TheNumbat committed
124
125
126
127
128
        for (size_t i = 0; i < pixels.size(); i++) {
            pixels[i].make_linear();
            if (!pixels[i].valid())
                pixels[i] = {};
        }
TheNumbat's avatar
TheNumbat committed
129
130
131
132
133
134
135
    }

    last_path = file;
    dirty = true;
    return {};
}

TheNumbat's avatar
TheNumbat committed
136
std::string HDR_Image::loaded_from() const { return last_path; }
TheNumbat's avatar
TheNumbat committed
137
138
139

void HDR_Image::tonemap(float e) const {

TheNumbat's avatar
TheNumbat committed
140
    if (e <= 0.0f) {
TheNumbat's avatar
TheNumbat committed
141
        e = exposure;
TheNumbat's avatar
TheNumbat committed
142
    } else if (e != exposure) {
TheNumbat's avatar
TheNumbat committed
143
144
145
146
        exposure = e;
        dirty = true;
    }

TheNumbat's avatar
TheNumbat committed
147
148
    if (!dirty)
        return;
TheNumbat's avatar
TheNumbat committed
149
150
151
152
153
154
155
156

    std::vector<unsigned char> data;
    tonemap_to(data, e);
    render_tex.image((int)w, (int)h, data.data());

    dirty = false;
}

TheNumbat's avatar
TheNumbat committed
157
const GL::Tex2D &HDR_Image::get_texture(float e) const {
TheNumbat's avatar
TheNumbat committed
158
159
160
161
    tonemap(e);
    return render_tex;
}

TheNumbat's avatar
TheNumbat committed
162
163
164
void HDR_Image::tonemap_to(std::vector<unsigned char> &data, float e) const {

    if (e <= 0.0f) {
TheNumbat's avatar
TheNumbat committed
165
166
167
        e = exposure;
    }

TheNumbat's avatar
TheNumbat committed
168
    if (data.size() != w * h * 4)
TheNumbat's avatar
TheNumbat committed
169
170
        data.resize(w * h * 4);

TheNumbat's avatar
TheNumbat committed
171
172
173
    for (size_t j = 0; j < h; j++) {
        for (size_t i = 0; i < w; i++) {

TheNumbat's avatar
TheNumbat committed
174
            size_t pidx = (h - j - 1) * w + i;
TheNumbat's avatar
TheNumbat committed
175
176
            const Spectrum &sample = pixels[pidx];

TheNumbat's avatar
TheNumbat committed
177
178
179
            float r = 1.0f - std::exp(-sample.r * exposure);
            float g = 1.0f - std::exp(-sample.g * exposure);
            float b = 1.0f - std::exp(-sample.b * exposure);
TheNumbat's avatar
TheNumbat committed
180
181

            Spectrum out(r, g, b);
TheNumbat's avatar
TheNumbat committed
182
            out.make_srgb();
TheNumbat's avatar
TheNumbat committed
183

TheNumbat's avatar
TheNumbat committed
184
185
            size_t didx = 4 * (j * w + i);
            data[didx] = (unsigned char)std::round(out.r * 255.0f);
TheNumbat's avatar
TheNumbat committed
186
187
188
            data[didx + 1] = (unsigned char)std::round(out.g * 255.0f);
            data[didx + 2] = (unsigned char)std::round(out.b * 255.0f);
            data[didx + 3] = 255;
TheNumbat's avatar
TheNumbat committed
189
190
191
        }
    }
}