1 /** @file texture.cpp Logical texture resource.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2005-2015 Daniel Swanson <danij@dengine.net>
5 *
6 * @par License
7 * GPL: http://www.gnu.org/licenses/gpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15 * Public License for more details. You should have received a copy of the GNU
16 * General Public License along with this program; if not, write to the Free
17 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA</small>
19 */
20
21 #include "doomsday/resource/texture.h"
22 #include "doomsday/resource/resources.h"
23 #include "doomsday/resource/composite.h"
24 #include "doomsday/resource/texturemanifest.h"
25 #include "doomsday/resource/textures.h"
26 #include "doomsday/console/cmd.h"
27
28 #include <de/Error>
29 #include <de/Log>
30 #include <de/memory.h>
31 #include <QMap>
32
33 using namespace de;
34
35 namespace res {
36
37 typedef QHash<Texture::AnalysisId, void *> Analyses;
38
DENG2_PIMPL(Texture)39 DENG2_PIMPL(Texture)
40 {
41 TextureManifest &manifest;
42 Flags flags;
43
44 /// User data associated with this texture.
45 void *userData;
46
47 /// World dimensions in map coordinate space units.
48 Vector2ui dimensions;
49
50 /// World origin offset in map coordinate space units.
51 Vector2i origin;
52
53 /// Image analysis data, used for various purposes according to context.
54 Analyses analyses;
55
56 Impl(Public *i, TextureManifest &_manifest)
57 : Base(i)
58 , manifest(_manifest)
59 , userData(0)
60 {}
61
62 ~Impl()
63 {
64 self().clearAnalyses();
65 }
66
67 /// Notify iterested parties of a change in world dimensions.
68 void notifyDimensionsChanged()
69 {
70 DENG2_FOR_PUBLIC_AUDIENCE(DimensionsChange, i) i->textureDimensionsChanged(self());
71 }
72 };
73
Texture(TextureManifest & manifest)74 Texture::Texture(TextureManifest &manifest) : d(new Impl(this, manifest))
75 {
76 setFlags(manifest.flags());
77 setDimensions(manifest.logicalDimensions());
78 setOrigin(manifest.origin());
79 }
80
~Texture()81 Texture::~Texture()
82 {
83 DENG2_FOR_AUDIENCE(Deletion, i) i->textureBeingDeleted(*this);
84
85 if (!manifest().schemeName().compareWithoutCase("Textures"))
86 {
87 Composite *pcTex = reinterpret_cast<Composite *>(userDataPointer());
88 if (pcTex) delete pcTex;
89 }
90 }
91
manifest() const92 TextureManifest &Texture::manifest() const
93 {
94 return d->manifest;
95 }
96
setUserDataPointer(void * newUserData)97 void Texture::setUserDataPointer(void *newUserData)
98 {
99 if (d->userData && newUserData)
100 {
101 LOG_AS("Texture::setUserDataPointer");
102 LOGDEV_RES_MSG("User data already present for \"%s\" %p, will be replaced")
103 << d->manifest.composeUri() << this;
104 }
105 d->userData = newUserData;
106 }
107
userDataPointer() const108 void *Texture::userDataPointer() const
109 {
110 return d->userData;
111 }
112
dimensions() const113 Vector2ui const &Texture::dimensions() const
114 {
115 return d->dimensions;
116 }
117
setDimensions(Vector2ui const & newDimensions)118 void Texture::setDimensions(Vector2ui const &newDimensions)
119 {
120 if (d->dimensions != newDimensions)
121 {
122 d->dimensions = newDimensions;
123 d->notifyDimensionsChanged();
124 }
125 }
126
setWidth(duint newWidth)127 void Texture::setWidth(duint newWidth)
128 {
129 if (d->dimensions.x != newWidth)
130 {
131 d->dimensions.x = newWidth;
132 d->notifyDimensionsChanged();
133 }
134 }
135
setHeight(duint newHeight)136 void Texture::setHeight(duint newHeight)
137 {
138 if (d->dimensions.y != newHeight)
139 {
140 d->dimensions.y = newHeight;
141 d->notifyDimensionsChanged();
142 }
143 }
144
origin() const145 Vector2i const &Texture::origin() const
146 {
147 return d->origin;
148 }
149
setOrigin(Vector2i const & newOrigin)150 void Texture::setOrigin(Vector2i const &newOrigin)
151 {
152 if (d->origin != newOrigin)
153 {
154 d->origin = newOrigin;
155 }
156 }
157
release()158 void Texture::release(/*TextureVariantSpec *spec*/)
159 {}
160
clearAnalyses()161 void Texture::clearAnalyses()
162 {
163 foreach (void *data, d->analyses)
164 {
165 M_Free(data);
166 }
167 d->analyses.clear();
168 }
169
analysisDataPointer(AnalysisId analysisId) const170 void *Texture::analysisDataPointer(AnalysisId analysisId) const
171 {
172 auto const &analyses = d.getConst()->analyses;
173 auto found = analyses.find(analysisId);
174 if (found != analyses.end())
175 {
176 return found.value();
177 }
178 return nullptr;
179 }
180
setAnalysisDataPointer(AnalysisId analysisId,void * newData)181 void Texture::setAnalysisDataPointer(AnalysisId analysisId, void *newData)
182 {
183 LOG_AS("Texture::attachAnalysis");
184 void *existingData = analysisDataPointer(analysisId);
185 if (existingData)
186 {
187 #ifdef DENG2_DEBUG
188 if (newData)
189 {
190 LOGDEV_RES_VERBOSE("Image analysis (id:%i) already present for \"%s\", will be replaed")
191 << int(analysisId) << d->manifest.composeUri();
192 }
193 #endif
194 M_Free(existingData);
195 }
196 d->analyses.insert(analysisId, newData);
197 }
198
flags() const199 Texture::Flags Texture::flags() const
200 {
201 return d->flags;
202 }
203
setFlags(Texture::Flags flagsToChange,FlagOp operation)204 void Texture::setFlags(Texture::Flags flagsToChange, FlagOp operation)
205 {
206 applyFlagOperation(d->flags, flagsToChange, operation);
207 }
208
description() const209 String Texture::description() const
210 {
211 String str = String("Texture " _E(b) "%1" _E(.)).arg(manifest().composeUri().asText());
212 #ifdef DENG_DEBUG
213 str += String(" [addr:0x%1]").arg(de::dintptr(this), 0, 16);
214 #endif
215 str += _E(l) " Dimensions:" _E(.)
216 + (width() == 0 && height() == 0? String("unknown (not yet prepared)")
217 : dimensions().asText())
218 + _E(l) " Source:" _E(.) + manifest().sourceDescription();
219 return str;
220 }
221
D_CMD(InspectTexture)222 D_CMD(InspectTexture)
223 {
224 DENG2_UNUSED(src);
225
226 de::Uri search = de::Uri::fromUserInput(&argv[1], argc - 1);
227
228 if (!search.scheme().isEmpty() &&
229 !Resources::get().textures().isKnownTextureScheme(search.scheme()))
230 {
231 LOG_RES_WARNING("Unknown scheme %s") << search.scheme();
232 return false;
233 }
234
235 try
236 {
237 TextureManifest &manifest = Resources::get().textures().textureManifest(search);
238 if (manifest.hasTexture())
239 {
240 //Texture &texture = ;
241 //String variantCountText;
242
243 LOG_RES_MSG("%s") << manifest.texture().description();
244
245 /*LOG_RES_MSG("Texture " _E(b) "%s" _E(.) "%s"
246 "\n" _E(l) "Dimensions: " _E(.) _E(i) "%s" _E(.)
247 " " _E(l) "Source: " _E(.) _E(i) "%s")
248 << manifest.composeUri()
249 << variantCountText
250 << (texture.width() == 0 &&
251 texture.height() == 0? String("unknown (not yet prepared)")
252 : texture.dimensions().asText())
253 << manifest.sourceDescription();*/
254 }
255 else
256 {
257 LOG_RES_MSG("%s") << manifest.description();
258 }
259 return true;
260 }
261 catch (Resources::MissingResourceManifestError const &er)
262 {
263 LOG_RES_WARNING("%s.") << er.asText();
264 }
265 return false;
266 }
267
consoleRegister()268 void Texture::consoleRegister() // static
269 {
270 C_CMD("inspecttexture", "ss", InspectTexture)
271 C_CMD("inspecttexture", "s", InspectTexture)
272 }
273
274 } // namespace res
275