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