-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathtexture_file_source.cpp
197 lines (179 loc) · 7.32 KB
/
texture_file_source.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright (C) 2020-2024 Sami Väisänen
// Copyright (C) 2020-2024 Ensisoft http://www.ensisoft.com
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "base/logging.h"
#include "data/writer.h"
#include "data/reader.h"
#include "graphics/algo.h"
#include "graphics/device.h"
#include "graphics/texture.h"
#include "graphics/image.h"
#include "graphics/texture_file_source.h"
#include "graphics/packer.h"
namespace gfx
{
std::string TextureFileSource::GetGpuId() const
{
// using the mFile URI is *not* enough to uniquely
// identify this texture object on the GPU because it's
// possible that the *same* texture object (same underlying file)
// is used with *different* flags in another material.
// in other words, "foo.png with pre-multiplied alpha" must be
// a different GPU texture object than "foo.png with straight alpha".
size_t gpu_hash = 0;
gpu_hash = base::hash_combine(gpu_hash, mFile);
gpu_hash = base::hash_combine(gpu_hash, mColorSpace);
gpu_hash = base::hash_combine(gpu_hash, mFlags);
gpu_hash = base::hash_combine(gpu_hash, mEffects);
return std::to_string(gpu_hash);
}
size_t TextureFileSource::GetHash() const
{
size_t hash = 0;
hash = base::hash_combine(hash, mFile);
hash = base::hash_combine(hash, mId);
hash = base::hash_combine(hash, mName);
hash = base::hash_combine(hash, mFlags);
hash = base::hash_combine(hash, mColorSpace);
hash = base::hash_combine(hash, mEffects);
return hash;
}
Texture* TextureFileSource::Upload(const Environment& env, Device& device) const
{
const auto& gpu_id = GetGpuId();
auto* texture = device.FindTexture(gpu_id);
if (texture && !env.dynamic_content)
return texture;
// compute content hash on first time or every time if dynamic_content
size_t content_hash = 0;
if (env.dynamic_content || !texture)
content_hash = base::hash_combine(0, mFile);
if (env.dynamic_content)
{
// check for dynamic changes.
if (texture && texture->GetContentHash() == content_hash)
return texture;
}
if (!texture)
{
texture = device.MakeTexture(gpu_id);
texture->SetName(mName.empty() ? mFile : mName);
texture->SetContentHash(0);
}
else if (texture->GetContentHash() == 0)
{
// if the texture already exists but hash is zero that means we've been here
// before and the texture has failed to load. In this case return early with
// a nullptr and skip subsequent load attempts.
// Note that in the editor if the user wants to reload a texture that was
// for example just modified by an image editor tool there's explicitly
// the reload mechanism that will nuke textures from the device.
// After that is done, the next Upload will again have to recreate the
// texture object on the device again.
return nullptr;
}
if (const auto& bitmap = GetData())
{
constexpr auto generate_mips = true;
constexpr auto skip_mips = false;
const auto sRGB = mColorSpace == ColorSpace::sRGB;
texture->SetContentHash(content_hash);
texture->Upload(bitmap->GetDataPtr(),
bitmap->GetWidth(),
bitmap->GetHeight(),
Texture::DepthToFormat(bitmap->GetDepthBits(), sRGB),
skip_mips);
texture->SetFilter(Texture::MinFilter::Linear);
texture->SetFilter(Texture::MagFilter::Linear);
const auto format = texture->GetFormat();
if (mEffects.any_bit() && format == Texture::Format::AlphaMask)
algo::ColorTextureFromAlpha(gpu_id, texture, &device);
if (mEffects.any_bit())
{
if (format == Texture::Format::RGBA || format == Texture::Format::sRGBA)
{
if (mEffects.test(Effect::Edges))
algo::DetectSpriteEdges(gpu_id, texture, &device);
if (mEffects.test(Effect::Blur))
algo::ApplyBlur(gpu_id, texture, &device);
} else WARN("Texture effects not supported on texture format. [name='%1', format='%2, effects=%3']", mName, format, mEffects);
}
texture->GenerateMips();
DEBUG("Uploaded texture file source texture. [name='%1', file='%2', effects=%3]", mName, mFile, mEffects);
return texture;
} else ERROR("Failed to upload texture source texture. [name='%1', file='%2']", mName, mFile);
return nullptr;
}
std::shared_ptr<IBitmap> TextureFileSource::GetData() const
{
DEBUG("Loading texture file. [file='%1']", mFile);
Image file(mFile);
if (!file.IsValid())
{
ERROR("Failed to load texture. [file='%1']", mFile);
return nullptr;
}
if (file.GetDepthBits() == 8)
return std::make_shared<AlphaMask>(file.AsBitmap<Pixel_A>());
else if (file.GetDepthBits() == 24)
return std::make_shared<RgbBitmap>(file.AsBitmap<Pixel_RGB>());
else if (file.GetDepthBits() == 32)
{
if (!TestFlag(Flags::PremulAlpha))
return std::make_shared<RgbaBitmap>(file.AsBitmap<Pixel_RGBA>());
auto view = file.GetPixelReadView<Pixel_RGBA>();
auto ret = std::make_shared<Bitmap<Pixel_RGBA>>();
ret->Resize(view.GetWidth(), view.GetHeight());
DEBUG("Performing alpha pre-multiply on texture. [file='%1']", mFile);
PremultiplyAlpha(ret->GetPixelWriteView(), view, true /* srgb */);
return ret;
}
else ERROR("Unexpected texture bit depth. [file='%1', depth=%2]", mFile, file.GetDepthBits());
return nullptr;
}
void TextureFileSource::IntoJson(data::Writer& data) const
{
data.Write("id", mId);
data.Write("file", mFile);
data.Write("name", mName);
data.Write("flags", mFlags);
data.Write("colorspace", mColorSpace);
data.Write("effects", mEffects);
}
bool TextureFileSource::FromJson(const data::Reader& data)
{
bool ok = true;
ok &= data.Read("id", &mId);
ok &= data.Read("file", &mFile);
ok &= data.Read("name", &mName);
ok &= data.Read("flags", &mFlags);
ok &= data.Read("colorspace", &mColorSpace);
if (data.HasValue("effects"))
ok &= data.Read("effects", &mEffects);
return ok;
}
void TextureFileSource::BeginPacking(TexturePacker* packer) const
{
packer->PackTexture(this, mFile);
packer->SetTextureFlag(this, TexturePacker::TextureFlags::AllowedToPack,
TestFlag(Flags::AllowPacking));
packer->SetTextureFlag(this, TexturePacker::TextureFlags::AllowedToResize,
TestFlag(Flags::AllowResizing));
}
void TextureFileSource::FinishPacking(const TexturePacker* packer)
{
mFile = packer->GetPackedTextureId(this);
}
} // namespace