1 // Copyright 2008-present Contributors to the OpenImageIO project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/OpenImageIO/oiio/blob/master/LICENSE.md
4 
5 
6 /// \file
7 /// Non-public classes used internally by TextureSystemImpl.
8 
9 
10 #ifndef OPENIMAGEIO_TEXTURE_PVT_H
11 #define OPENIMAGEIO_TEXTURE_PVT_H
12 
13 #include <OpenImageIO/simd.h>
14 #include <OpenImageIO/texture.h>
15 
16 OIIO_NAMESPACE_BEGIN
17 
18 class ImageCache;
19 class Filter1D;
20 
21 namespace pvt {
22 
23 class TextureSystemImpl;
24 
25 #ifndef OPENIMAGEIO_IMAGECACHE_PVT_H
26 class ImageCacheImpl;
27 class ImageCacheFile;
28 class ImageCacheTile;
29 class ImageCacheTileRef;
30 #endif
31 
32 
33 
34 /// Working implementation of the abstract TextureSystem class.
35 ///
36 class TextureSystemImpl : public TextureSystem {
37 public:
38     typedef ImageCacheFile TextureFile;
39 
40     TextureSystemImpl(ImageCache* imagecache);
41     virtual ~TextureSystemImpl();
42 
43     virtual bool attribute(string_view name, TypeDesc type, const void* val);
attribute(string_view name,int val)44     virtual bool attribute(string_view name, int val)
45     {
46         return attribute(name, TypeDesc::INT, &val);
47     }
attribute(string_view name,float val)48     virtual bool attribute(string_view name, float val)
49     {
50         return attribute(name, TypeDesc::FLOAT, &val);
51     }
attribute(string_view name,double val)52     virtual bool attribute(string_view name, double val)
53     {
54         float f = (float)val;
55         return attribute(name, TypeDesc::FLOAT, &f);
56     }
attribute(string_view name,string_view val)57     virtual bool attribute(string_view name, string_view val)
58     {
59         const char* s = val.c_str();
60         return attribute(name, TypeDesc::STRING, &s);
61     }
62 
63     virtual bool getattribute(string_view name, TypeDesc type, void* val) const;
getattribute(string_view name,int & val)64     virtual bool getattribute(string_view name, int& val) const
65     {
66         return getattribute(name, TypeDesc::INT, &val);
67     }
getattribute(string_view name,float & val)68     virtual bool getattribute(string_view name, float& val) const
69     {
70         return getattribute(name, TypeDesc::FLOAT, &val);
71     }
getattribute(string_view name,double & val)72     virtual bool getattribute(string_view name, double& val) const
73     {
74         float f;
75         bool ok = getattribute(name, TypeDesc::FLOAT, &f);
76         if (ok)
77             val = f;
78         return ok;
79     }
getattribute(string_view name,char ** val)80     virtual bool getattribute(string_view name, char** val) const
81     {
82         return getattribute(name, TypeDesc::STRING, val);
83     }
getattribute(string_view name,std::string & val)84     virtual bool getattribute(string_view name, std::string& val) const
85     {
86         const char* s;
87         bool ok = getattribute(name, TypeDesc::STRING, &s);
88         if (ok)
89             val = s;
90         return ok;
91     }
92 
93 
94     // Retrieve options
get_commontoworld(Imath::M44f & result)95     void get_commontoworld(Imath::M44f& result) const { result = m_Mc2w; }
96 
97     virtual Perthread* get_perthread_info(Perthread* thread_info = NULL)
98     {
99         return (Perthread*)m_imagecache->get_perthread_info(
100             (ImageCachePerThreadInfo*)thread_info);
101     }
create_thread_info()102     virtual Perthread* create_thread_info()
103     {
104         OIIO_ASSERT(m_imagecache);
105         return (Perthread*)m_imagecache->create_thread_info();
106     }
destroy_thread_info(Perthread * threadinfo)107     virtual void destroy_thread_info(Perthread* threadinfo)
108     {
109         OIIO_ASSERT(m_imagecache);
110         m_imagecache->destroy_thread_info((ImageCachePerThreadInfo*)threadinfo);
111     }
112 
get_texture_handle(ustring filename,Perthread * thread)113     virtual TextureHandle* get_texture_handle(ustring filename,
114                                               Perthread* thread)
115     {
116         PerThreadInfo* thread_info = thread
117                                          ? ((PerThreadInfo*)thread)
118                                          : m_imagecache->get_perthread_info();
119         return (TextureHandle*)find_texturefile(filename, thread_info);
120     }
121 
good(TextureHandle * texture_handle)122     virtual bool good(TextureHandle* texture_handle)
123     {
124         return texture_handle && !((TextureFile*)texture_handle)->broken();
125     }
126 
127     virtual bool texture(ustring filename, TextureOpt& options, float s,
128                          float t, float dsdx, float dtdx, float dsdy,
129                          float dtdy, int nchannels, float* result,
130                          float* dresultds = NULL, float* dresultdt = NULL);
131     virtual bool texture(TextureHandle* texture_handle, Perthread* thread_info,
132                          TextureOpt& options, float s, float t, float dsdx,
133                          float dtdx, float dsdy, float dtdy, int nchannels,
134                          float* result, float* dresultds = NULL,
135                          float* dresultdt = NULL);
136     virtual bool texture(ustring filename, TextureOptBatch& options,
137                          Tex::RunMask mask, const float* s, const float* t,
138                          const float* dsdx, const float* dtdx,
139                          const float* dsdy, const float* dtdy, int nchannels,
140                          float* result, float* dresultds = nullptr,
141                          float* dresultdt = nullptr);
142     virtual bool texture(TextureHandle* texture_handle, Perthread* thread_info,
143                          TextureOptBatch& options, Tex::RunMask mask,
144                          const float* s, const float* t, const float* dsdx,
145                          const float* dtdx, const float* dsdy,
146                          const float* dtdy, int nchannels, float* result,
147                          float* dresultds = nullptr,
148                          float* dresultdt = nullptr);
149     virtual bool texture(ustring filename, TextureOptions& options,
150                          Runflag* runflags, int beginactive, int endactive,
151                          VaryingRef<float> s, VaryingRef<float> t,
152                          VaryingRef<float> dsdx, VaryingRef<float> dtdx,
153                          VaryingRef<float> dsdy, VaryingRef<float> dtdy,
154                          int nchannels, float* result, float* dresultds = NULL,
155                          float* dresultdt = NULL);
156     virtual bool texture(TextureHandle* texture_handle, Perthread* thread_info,
157                          TextureOptions& options, Runflag* runflags,
158                          int beginactive, int endactive, VaryingRef<float> s,
159                          VaryingRef<float> t, VaryingRef<float> dsdx,
160                          VaryingRef<float> dtdx, VaryingRef<float> dsdy,
161                          VaryingRef<float> dtdy, int nchannels, float* result,
162                          float* dresultds = NULL, float* dresultdt = NULL);
163 
164     virtual bool texture3d(ustring filename, TextureOpt& options,
165                            const Imath::V3f& P, const Imath::V3f& dPdx,
166                            const Imath::V3f& dPdy, const Imath::V3f& dPdz,
167                            int nchannels, float* result,
168                            float* dresultds = NULL, float* dresultdt = NULL,
169                            float* dresultdr = NULL);
170     virtual bool texture3d(TextureHandle* texture_handle,
171                            Perthread* thread_info, TextureOpt& options,
172                            const Imath::V3f& P, const Imath::V3f& dPdx,
173                            const Imath::V3f& dPdy, const Imath::V3f& dPdz,
174                            int nchannels, float* result,
175                            float* dresultds = NULL, float* dresultdt = NULL,
176                            float* dresultdr = NULL);
177     virtual bool texture3d(ustring filename, TextureOptBatch& options,
178                            Tex::RunMask mask, const float* P, const float* dPdx,
179                            const float* dPdy, const float* dPdz, int nchannels,
180                            float* result, float* dresultds = nullptr,
181                            float* dresultdt = nullptr,
182                            float* dresultdr = nullptr);
183     virtual bool texture3d(TextureHandle* texture_handle,
184                            Perthread* thread_info, TextureOptBatch& options,
185                            Tex::RunMask mask, const float* P, const float* dPdx,
186                            const float* dPdy, const float* dPdz, int nchannels,
187                            float* result, float* dresultds = nullptr,
188                            float* dresultdt = nullptr,
189                            float* dresultdr = nullptr);
190     virtual bool texture3d(ustring filename, TextureOptions& options,
191                            Runflag* runflags, int beginactive, int endactive,
192                            VaryingRef<Imath::V3f> P,
193                            VaryingRef<Imath::V3f> dPdx,
194                            VaryingRef<Imath::V3f> dPdy,
195                            VaryingRef<Imath::V3f> dPdz, int nchannels,
196                            float* result, float* dresultds = NULL,
197                            float* dresultdt = NULL, float* dresultdr = NULL);
198     virtual bool texture3d(TextureHandle* texture_handle,
199                            Perthread* thread_info, TextureOptions& options,
200                            Runflag* runflags, int beginactive, int endactive,
201                            VaryingRef<Imath::V3f> P,
202                            VaryingRef<Imath::V3f> dPdx,
203                            VaryingRef<Imath::V3f> dPdy,
204                            VaryingRef<Imath::V3f> dPdz, int nchannels,
205                            float* result, float* dresultds = NULL,
206                            float* dresultdt = NULL, float* dresultdr = NULL);
207 
shadow(ustring,TextureOpt &,const Imath::V3f &,const Imath::V3f &,const Imath::V3f &,float *,float *,float *)208     virtual bool shadow(ustring /*filename*/, TextureOpt& /*options*/,
209                         const Imath::V3f& /*P*/, const Imath::V3f& /*dPdx*/,
210                         const Imath::V3f& /*dPdy*/, float* /*result*/,
211                         float* /*dresultds*/, float* /*dresultdt*/)
212     {
213         return false;
214     }
shadow(TextureHandle *,Perthread *,TextureOpt &,const Imath::V3f &,const Imath::V3f &,const Imath::V3f &,float *,float *,float *)215     virtual bool shadow(TextureHandle* /*texture_handle*/,
216                         Perthread* /*thread_info*/, TextureOpt& /*options*/,
217                         const Imath::V3f& /*P*/, const Imath::V3f& /*dPdx*/,
218                         const Imath::V3f& /*dPdy*/, float* /*result*/,
219                         float* /*dresultds*/, float* /*dresultdt*/)
220     {
221         return false;
222     }
shadow(ustring,TextureOptBatch &,Tex::RunMask,const float *,const float *,const float *,float *,float *,float *)223     virtual bool shadow(ustring /*filename*/, TextureOptBatch& /*options*/,
224                         Tex::RunMask /*mask*/, const float* /*P*/,
225                         const float* /*dPdx*/, const float* /*dPdy*/,
226                         float* /*result*/, float* /*dresultds*/,
227                         float* /*dresultdt*/)
228     {
229         return false;
230     }
shadow(TextureHandle *,Perthread *,TextureOptBatch &,Tex::RunMask,const float *,const float *,const float *,float *,float *,float *)231     virtual bool shadow(TextureHandle* /*texture_handle*/,
232                         Perthread* /*thread_info*/,
233                         TextureOptBatch& /*options*/, Tex::RunMask /*mask*/,
234                         const float* /*P*/, const float* /*dPdx*/,
235                         const float* /*dPdy*/, float* /*result*/,
236                         float* /*dresultds*/, float* /*dresultdt*/)
237     {
238         return false;
239     }
shadow(ustring,TextureOptions &,Runflag *,int,int,VaryingRef<Imath::V3f>,VaryingRef<Imath::V3f>,VaryingRef<Imath::V3f>,float *,float *,float *)240     virtual bool shadow(ustring /*filename*/, TextureOptions& /*options*/,
241                         Runflag* /*runflags*/, int /*beginactive*/,
242                         int /*endactive*/, VaryingRef<Imath::V3f> /*P*/,
243                         VaryingRef<Imath::V3f> /*dPdx*/,
244                         VaryingRef<Imath::V3f> /*dPdy*/, float* /*result*/,
245                         float* /*dresultds*/, float* /*dresultdt*/)
246     {
247         return false;
248     }
shadow(TextureHandle *,Perthread *,TextureOptions &,Runflag *,int,int,VaryingRef<Imath::V3f>,VaryingRef<Imath::V3f>,VaryingRef<Imath::V3f>,float *,float *,float *)249     virtual bool shadow(TextureHandle* /*texture_handle*/,
250                         Perthread* /*thread_info*/, TextureOptions& /*options*/,
251                         Runflag* /*runflags*/, int /*beginactive*/,
252                         int /*endactive*/, VaryingRef<Imath::V3f> /*P*/,
253                         VaryingRef<Imath::V3f> /*dPdx*/,
254                         VaryingRef<Imath::V3f> /*dPdy*/, float* /*result*/,
255                         float* /*dresultds*/, float* /*dresultdt*/)
256     {
257         return false;
258     }
259 
260 
261     virtual bool environment(ustring filename, TextureOpt& options,
262                              const Imath::V3f& R, const Imath::V3f& dRdx,
263                              const Imath::V3f& dRdy, int nchannels,
264                              float* result, float* dresultds = NULL,
265                              float* dresultdt = NULL);
266     virtual bool environment(TextureHandle* texture_handle,
267                              Perthread* thread_info, TextureOpt& options,
268                              const Imath::V3f& R, const Imath::V3f& dRdx,
269                              const Imath::V3f& dRdy, int nchannels,
270                              float* result, float* dresultds = NULL,
271                              float* dresultdt = NULL);
272     virtual bool environment(ustring filename, TextureOptBatch& options,
273                              Tex::RunMask mask, const float* R,
274                              const float* dRdx, const float* dRdy,
275                              int nchannels, float* result,
276                              float* dresultds = nullptr,
277                              float* dresultdt = nullptr);
278     virtual bool environment(TextureHandle* texture_handle,
279                              Perthread* thread_info, TextureOptBatch& options,
280                              Tex::RunMask mask, const float* R,
281                              const float* dRdx, const float* dRdy,
282                              int nchannels, float* result,
283                              float* dresultds = nullptr,
284                              float* dresultdt = nullptr);
285     virtual bool environment(ustring filename, TextureOptions& options,
286                              Runflag* runflags, int beginactive, int endactive,
287                              VaryingRef<Imath::V3f> R,
288                              VaryingRef<Imath::V3f> dRdx,
289                              VaryingRef<Imath::V3f> dRdy, int nchannels,
290                              float* result, float* dresultds = NULL,
291                              float* dresultdt = NULL);
292     virtual bool environment(TextureHandle* texture_handle,
293                              Perthread* thread_info, TextureOptions& options,
294                              Runflag* runflags, int beginactive, int endactive,
295                              VaryingRef<Imath::V3f> R,
296                              VaryingRef<Imath::V3f> dRdx,
297                              VaryingRef<Imath::V3f> dRdy, int nchannels,
298                              float* result, float* dresultds = NULL,
299                              float* dresultdt = NULL);
300 
301     virtual std::string resolve_filename(const std::string& filename) const;
302 
303     virtual bool get_texture_info(ustring filename, int subimage,
304                                   ustring dataname, TypeDesc datatype,
305                                   void* data);
306     virtual bool get_texture_info(TextureHandle* texture_handle,
307                                   Perthread* thread_info, int subimage,
308                                   ustring dataname, TypeDesc datatype,
309                                   void* data);
310 
311     virtual bool get_imagespec(ustring filename, int subimage, ImageSpec& spec);
312     virtual bool get_imagespec(TextureHandle* texture_handle,
313                                Perthread* thread_info, int subimage,
314                                ImageSpec& spec);
315 
316     virtual const ImageSpec* imagespec(ustring filename, int subimage = 0);
317     virtual const ImageSpec* imagespec(TextureHandle* texture_handle,
318                                        Perthread* thread_info = NULL,
319                                        int subimage           = 0);
320 
321     virtual bool get_texels(ustring filename, TextureOpt& options, int miplevel,
322                             int xbegin, int xend, int ybegin, int yend,
323                             int zbegin, int zend, int chbegin, int chend,
324                             TypeDesc format, void* result);
325     virtual bool get_texels(TextureHandle* texture_handle,
326                             Perthread* thread_info, TextureOpt& options,
327                             int miplevel, int xbegin, int xend, int ybegin,
328                             int yend, int zbegin, int zend, int chbegin,
329                             int chend, TypeDesc format, void* result);
330 
331     virtual std::string geterror() const;
332     virtual std::string getstats(int level = 1, bool icstats = true) const;
333     virtual void reset_stats();
334 
335     virtual void invalidate(ustring filename, bool force);
336     virtual void invalidate_all(bool force = false);
337     virtual void close(ustring filename);
338     virtual void close_all();
339 
delete(void * todel)340     void operator delete(void* todel) { ::delete ((char*)todel); }
341 
342     typedef bool (*wrap_impl)(int& coord, int origin, int width);
343 
344     /// Return an opaque, non-owning pointer to the underlying ImageCache
345     /// (if there is one).
imagecache()346     virtual ImageCache* imagecache() const { return m_imagecache; }
347 
348 private:
349     typedef ImageCacheTileRef TileRef;
350     typedef ImageCachePerThreadInfo PerThreadInfo;
351 
352     void init();
353 
354     /// Find the TextureFile record for the named texture, or NULL if no
355     /// such file can be found.
find_texturefile(ustring filename,PerThreadInfo * thread_info)356     TextureFile* find_texturefile(ustring filename, PerThreadInfo* thread_info)
357     {
358         return m_imagecache->find_file(filename, thread_info);
359     }
verify_texturefile(TextureFile * texturefile,PerThreadInfo * thread_info)360     TextureFile* verify_texturefile(TextureFile* texturefile,
361                                     PerThreadInfo* thread_info)
362     {
363         texturefile = m_imagecache->verify_file(texturefile, thread_info);
364         if (!texturefile || texturefile->broken()) {
365             std::string err = m_imagecache->geterror();
366             if (err.size())
367                 errorf("%s", err);
368 #if 0
369             // If the file is "broken", at least one verbose error message
370             // has already been issued about it, so don't belabor the point.
371             // But for debugging purposes, these might help:
372             else if (texturefile && texturefile->broken())
373                 error ("(unknown error - broken texture \"%s\")", texturefile->filename());
374             else
375                 error ("(unknown error - NULL texturefile)");
376 #endif
377         }
378         return texturefile;
379     }
380 
381     /// Find the tile specified by id.  Just a pass-through to the
382     /// underlying ImageCache.
find_tile(const TileID & id,PerThreadInfo * thread_info,bool mark_same_tile_used)383     bool find_tile(const TileID& id, PerThreadInfo* thread_info,
384                    bool mark_same_tile_used)
385     {
386         return m_imagecache->find_tile(id, thread_info, mark_same_tile_used);
387     }
388 
389     // Define a prototype of a member function pointer for texture
390     // lookups.
391     // If simd is nonzero, it's guaranteed that all float* inputs and
392     // outputs are padded to length 'simd' and aligned to a simd*4-byte
393     // boundary (for example, 4 for SSE). This means that the functions can
394     // behave AS IF the number of channels being retrieved is simd, and any
395     // extra values returned will be discarded by the caller.
396     typedef bool (TextureSystemImpl::*texture_lookup_prototype)(
397         TextureFile& texfile, PerThreadInfo* thread_info, TextureOpt& options,
398         int nchannels_result, int actualchannels, float _s, float _t,
399         float _dsdx, float _dtdx, float _dsdy, float _dtdy, float* result,
400         float* dresultds, float* resultdt);
401 
402     /// Look up texture from just ONE point
403     ///
404     bool texture_lookup(TextureFile& texfile, PerThreadInfo* thread_info,
405                         TextureOpt& options, int nchannels_result,
406                         int actualchannels, float _s, float _t, float _dsdx,
407                         float _dtdx, float _dsdy, float _dtdy, float* result,
408                         float* dresultds, float* resultdt);
409 
410     bool texture_lookup_nomip(TextureFile& texfile, PerThreadInfo* thread_info,
411                               TextureOpt& options, int nchannels_result,
412                               int actualchannels, float _s, float _t,
413                               float _dsdx, float _dtdx, float _dsdy,
414                               float _dtdy, float* result, float* dresultds,
415                               float* resultdt);
416 
417     bool texture_lookup_trilinear_mipmap(
418         TextureFile& texfile, PerThreadInfo* thread_info, TextureOpt& options,
419         int nchannels_result, int actualchannels, float _s, float _t,
420         float _dsdx, float _dtdx, float _dsdy, float _dtdy, float* result,
421         float* dresultds, float* resultdt);
422 
423     // For the samplers, it's guaranteed that all float* inputs and outputs
424     // are padded to length 'simd' and aligned to a simd*4-byte boundary
425     // (for example, 4 for SSE). This means that the functions can behave AS
426     // IF the number of channels being retrieved is simd, and any extra
427     // values returned will be discarded by the caller.
428     typedef bool (TextureSystemImpl::*sampler_prototype)(
429         int nsamples, const float* s, const float* t, int level,
430         TextureFile& texturefile, PerThreadInfo* thread_info,
431         TextureOpt& options, int nchannels_result, int actualchannels,
432         const float* weight, simd::vfloat4* accum, simd::vfloat4* daccumds,
433         simd::vfloat4* daccumdt);
434     bool sample_closest(int nsamples, const float* s, const float* t, int level,
435                         TextureFile& texturefile, PerThreadInfo* thread_info,
436                         TextureOpt& options, int nchannels_result,
437                         int actualchannels, const float* weight,
438                         simd::vfloat4* accum, simd::vfloat4* daccumds,
439                         simd::vfloat4* daccumdt);
440     bool sample_bilinear(int nsamples, const float* s, const float* t,
441                          int level, TextureFile& texturefile,
442                          PerThreadInfo* thread_info, TextureOpt& options,
443                          int nchannels_result, int actualchannels,
444                          const float* weight, simd::vfloat4* accum,
445                          simd::vfloat4* daccumds, simd::vfloat4* daccumdt);
446     bool sample_bicubic(int nsamples, const float* s, const float* t, int level,
447                         TextureFile& texturefile, PerThreadInfo* thread_info,
448                         TextureOpt& options, int nchannels_result,
449                         int actualchannels, const float* weight,
450                         simd::vfloat4* accum, simd::vfloat4* daccumds,
451                         simd::vfloat4* daccumdt);
452 
453     // Define a prototype of a member function pointer for texture3d
454     // lookups.
455     typedef bool (TextureSystemImpl::*texture3d_lookup_prototype)(
456         TextureFile& texfile, PerThreadInfo* thread_info, TextureOpt& options,
457         int nchannels_result, int actualchannels, const Imath::V3f& P,
458         const Imath::V3f& dPdx, const Imath::V3f& dPdy, const Imath::V3f& dPdz,
459         float* result, float* dresultds, float* dresultdt, float* dresultdr);
460     bool texture3d_lookup_nomip(TextureFile& texfile,
461                                 PerThreadInfo* thread_info, TextureOpt& options,
462                                 int nchannels_result, int actualchannels,
463                                 const Imath::V3f& P, const Imath::V3f& dPdx,
464                                 const Imath::V3f& dPdy, const Imath::V3f& dPdz,
465                                 float* result, float* dresultds,
466                                 float* dresultdt, float* dresultdr);
467     typedef bool (TextureSystemImpl::*accum3d_prototype)(
468         const Imath::V3f& P, int level, TextureFile& texturefile,
469         PerThreadInfo* thread_info, TextureOpt& options, int nchannels_result,
470         int actualchannels, float weight, float* accum, float* daccumds,
471         float* daccumdt, float* daccumdr);
472     bool accum3d_sample_closest(const Imath::V3f& P, int level,
473                                 TextureFile& texturefile,
474                                 PerThreadInfo* thread_info, TextureOpt& options,
475                                 int nchannels_result, int actualchannels,
476                                 float weight, float* accum, float* daccumds,
477                                 float* daccumdt, float* daccumdr);
478     bool accum3d_sample_bilinear(const Imath::V3f& P, int level,
479                                  TextureFile& texturefile,
480                                  PerThreadInfo* thread_info,
481                                  TextureOpt& options, int nchannels_result,
482                                  int actualchannels, float weight, float* accum,
483                                  float* daccumds, float* daccumdt,
484                                  float* daccumdr);
485 
486     /// Helper function to calculate the anisotropic aspect ratio from
487     /// the major and minor ellipse axis lengths.  The "clamped" aspect
488     /// ratio is returned (possibly adjusting major and minorlength to
489     /// conform to the aniso limits) but the true aspect is stored in
490     /// 'trueaspect'.
491     static float anisotropic_aspect(float& majorlength, float& minorlength,
492                                     TextureOpt& options, float& trueaspect);
493 
494     /// Convert texture coordinates (s,t), which range on 0-1 for the
495     /// "full" image boundary, to texel coordinates (i+ifrac,j+jfrac)
496     /// where (i,j) is the texel to the immediate upper left of the
497     /// sample position, and ifrac and jfrac are the fractional (0-1)
498     /// portion of the way to the next texel to the right or down,
499     /// respectively.
500     void st_to_texel(float s, float t, TextureFile& texturefile,
501                      const ImageSpec& spec, int& i, int& j, float& ifrac,
502                      float& jfrac);
503 
504     /// Called when the requested texture is missing, fills in the
505     /// results.
506     bool missing_texture(TextureOpt& options, int nchannels, float* result,
507                          float* dresultds, float* dresultdt,
508                          float* dresultdr = NULL);
509 
510     /// Handle gray-to-RGB promotion.
511     void fill_gray_channels(const ImageSpec& spec, int nchannels, float* result,
512                             float* dresultds, float* dresultdt,
513                             float* dresultdr = NULL);
514 
515     static bool wrap_periodic_sharedborder(int& coord, int origin, int width);
516     static const wrap_impl wrap_functions[];
517 
518     /// Helper function for lat-long environment maps: compute a "pole"
519     /// pixel that's the average of all of row y.  This will only be
520     /// called for levels where the whole mipmap level fits on one tile.
521     const float* pole_color(TextureFile& texturefile,
522                             PerThreadInfo* thread_info,
523                             const ImageCacheFile::LevelInfo& levelinfo,
524                             TileRef& tile, int subimage, int miplevel,
525                             int pole);
526     /// Helper function for lat-long environment maps: called near pole
527     /// regions, this figures out the average pole color and fades to it
528     /// right at the pole, and also adjusts weight so that the regular
529     /// interpolated texture color will be added in correctly.
530     /// This should only be called on the edge texels.
531     void fade_to_pole(float t, float* accum, float& weight,
532                       TextureFile& texturefile, PerThreadInfo* thread_info,
533                       const ImageCacheFile::LevelInfo& levelinfo,
534                       TextureOpt& options, int miplevel, int nchannels);
535 
536     /// Perform short unit tests.
537     void unit_test_texture();
538 
539     /// Internal error reporting routine, with printf-like arguments.
540     template<typename... Args>
errorf(const char * fmt,const Args &...args)541     void errorf(const char* fmt, const Args&... args) const
542     {
543         append_error(Strutil::sprintf(fmt, args...));
544     }
545     /// Internal error reporting routine, with std::format-like arguments.
546     template<typename... Args>
error(const char * fmt,const Args &...args)547     void error(const char* fmt, const Args&... args) const
548     {
549         append_error(Strutil::fmt::format(fmt, args...));
550     }
551 
552     /// Append a string to the current error message
553     void append_error(const std::string& message) const;
554 
555     void printstats() const;
556 
557     // Debugging aid
558     void visualize_ellipse(const std::string& name, float dsdx, float dtdx,
559                            float dsdy, float dtdy, float sblur, float tblur);
560 
561     ImageCacheImpl* m_imagecache = nullptr;
562     bool m_imagecache_owner      = false;  ///< True if we own the ImageCache
563     Imath::M44f m_Mw2c;                    ///< world-to-"common" matrix
564     Imath::M44f m_Mc2w;                    ///< common-to-world matrix
565     bool m_gray_to_rgb;       ///< automatically copy gray to rgb channels?
566     bool m_flip_t;            ///< Flip direction of t coord?
567     int m_max_tile_channels;  ///< narrow tile ID channel range when
568                               ///<   the file has more channels
569     /// Saved error string, per-thread
570     ///
571     mutable thread_specific_ptr<std::string> m_errormessage;
572     Filter1D* hq_filter;  ///< Better filter for magnification
573     int m_statslevel;
574     friend class TextureSystem;
575 };
576 
577 
578 
579 inline float
anisotropic_aspect(float & majorlength,float & minorlength,TextureOpt & options,float & trueaspect)580 TextureSystemImpl::anisotropic_aspect(float& majorlength, float& minorlength,
581                                       TextureOpt& options, float& trueaspect)
582 {
583     float aspect = Imath::clamp(majorlength / minorlength, 1.0f, 1.0e6f);
584     trueaspect   = aspect;
585     if (aspect > options.anisotropic) {
586         aspect = options.anisotropic;
587         // We have to clamp the ellipse to the maximum amount of anisotropy
588         // that we allow.  How do we do it?
589         // a. Widen the short axis so we never alias along the major
590         //    axis, but we over-blur along the minor axis.  I've never
591         //    been happy with this, it visibly overblurs.
592         // b. Clamp the long axis so we don't blur, but might alias.
593         // c. Split the difference, take the geometric mean, this makes
594         //    it slightly too blurry along the minor axis, slightly
595         //    aliasing along the major axis.  You can't please everybody.
596         if (options.conservative_filter) {
597 #if 0
598             // Solution (a) -- never alias by blurring more along minor axis
599             minorlength = majorlength / options.anisotropic;
600 #else
601             // Solution (c) -- this is our default, usually a nice balance.
602             // We used to take the geometric mean...
603             //            majorlength = sqrtf ((majorlength) *
604             //                                 (minorlength * options.anisotropic));
605             // ...but frankly I find the average to be a little more
606             // visually pleasing.
607             majorlength
608                 = 0.5f * ((majorlength) + (minorlength * options.anisotropic));
609             minorlength = majorlength / options.anisotropic;
610 #endif
611         } else {
612             // Solution (b) -- alias slightly, never overblur
613             majorlength = minorlength * options.anisotropic;
614         }
615     }
616     return aspect;
617 }
618 
619 
620 
621 inline void
st_to_texel(float s,float t,TextureFile & texturefile,const ImageSpec & spec,int & i,int & j,float & ifrac,float & jfrac)622 TextureSystemImpl::st_to_texel(float s, float t, TextureFile& texturefile,
623                                const ImageSpec& spec, int& i, int& j,
624                                float& ifrac, float& jfrac)
625 {
626     // As passed in, (s,t) map the texture to (0,1).  Remap to texel coords.
627     // Note that we have two modes, depending on the m_sample_border.
628     if (texturefile.m_sample_border == 0) {
629         // texel samples are at 0.5/res, 1.5/res, ..., (res-0.5)/res,
630         s = s * spec.width + spec.x - 0.5f;
631         t = t * spec.height + spec.y - 0.5f;
632     } else {
633         // first and last rows/columns are *exactly* on the boundary,
634         // so samples are at 0, 1/(res-1), ..., 1.
635         s = s * (spec.width - 1) + spec.x;
636         t = t * (spec.height - 1) + spec.y;
637     }
638     ifrac = floorfrac(s, &i);
639     jfrac = floorfrac(t, &j);
640     // Now (i,j) are the integer coordinates of the texel to the
641     // immediate "upper left" of the lookup point, and (ifrac,jfrac) are
642     // the amount that the lookup point is actually offset from the
643     // texel center (with (1,1) being all the way to the next texel down
644     // and to the right).
645 }
646 
647 
648 
649 }  // end namespace pvt
650 
651 OIIO_NAMESPACE_END
652 
653 #endif  // OPENIMAGEIO_TEXTURE_PVT_H
654