~holger-seelig/titania/3.0

« back to all changes in this revision

Viewing changes to libtitania-x3d/Titania/X3D/Components/Texturing/ImageTexture.cpp

  • Committer: Holger Seelig
  • Date: 2013-08-05 19:51:24 UTC
  • Revision ID: holger.seelig@yahoo.de-20130805195124-weg56pajdi69mt1j
Test implementation of parallel load of images.

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
 *
4
4
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5
5
 *
6
 
 * Copyright create3000, Scheffelstra�e 31a, Leipzig, Germany 2011.
 
6
 * Copyright create3000, Scheffelstra�e 31a, Leipzig, Germany 2011.
7
7
 *
8
8
 * All rights reserved. Holger Seelig <holger.seelig@yahoo.de>.
9
9
 *
54
54
#include "../../Execution/X3DExecutionContext.h"
55
55
#include <iostream>
56
56
 
 
57
//
 
58
 
 
59
#include <future>
 
60
#include <glibmm/dispatcher.h>
 
61
 
57
62
namespace titania {
58
63
namespace X3D {
59
64
 
 
65
class Texture
 
66
{
 
67
public:
 
68
 
 
69
        typedef size_t size_type;
 
70
 
 
71
        Texture (const std::string & data) :
 
72
                image (getImage (data)),
 
73
                format (GL_RGB),
 
74
                components (3)
 
75
        { }
 
76
 
 
77
        GLenum
 
78
        getFormat ()
 
79
        { return format; }
 
80
        
 
81
        size_type
 
82
        getWidth () const
 
83
        { return image .size () .width (); }
 
84
 
 
85
        size_type
 
86
        getHeight () const
 
87
        { return image .size () .height (); }
 
88
 
 
89
        size_type
 
90
        getComponents () const
 
91
        { return components; }
 
92
        
 
93
        const void*
 
94
        getData ()
 
95
        { return blob .data (); }
 
96
 
 
97
        void
 
98
        process (const Color4f & borderColor, size_type borderWidth,
 
99
                 size_type minTextureSize, size_type maxTextureSize)
 
100
        {
 
101
                addBorder (borderColor, borderWidth);
 
102
 
 
103
                tryScaleImage (minTextureSize, maxTextureSize);
 
104
 
 
105
                refineImageFormat ();
 
106
 
 
107
                writeBlob ();
 
108
        }
 
109
 
 
110
        virtual
 
111
        ~Texture ()
 
112
        { }
 
113
 
 
114
 
 
115
private:
 
116
 
 
117
        static
 
118
        Magick::Image
 
119
        getImage (const std::string & data)
 
120
        {
 
121
                std::list <Magick::Image> images;
 
122
                Magick::readImages (&images, Magick::Blob (data .c_str (), data .length ()));
 
123
 
 
124
                switch (images .size ())
 
125
                {
 
126
                        case 0:  // I have no idea what to do now.
 
127
                                throw std::domain_error ("Image contains nothing.");
 
128
 
 
129
                        case 1:
 
130
                        {
 
131
                                // Image with one layer image.
 
132
                                return images .back ();
 
133
                        }
 
134
                        default:
 
135
                        {
 
136
                                // Flatten image with more than one layer.
 
137
                                Magick::Image image;
 
138
                                Magick::flattenImages (&image, images .begin (), images .end ());
 
139
                                return image;
 
140
                        }
 
141
                }
 
142
        }
 
143
 
 
144
        void
 
145
        addBorder (const Color4f & borderColor, size_type borderWidth)
 
146
        {
 
147
                if (borderWidth > 0)
 
148
                {
 
149
                        std::ostringstream color;
 
150
 
 
151
                        color
 
152
                                << std::hex
 
153
                                << '#'
 
154
                                << std::setfill ('0')
 
155
                                << std::setw (2) << (int) (uint8_t) (borderColor .r () * 255)
 
156
                                << std::setw (2) << (int) (uint8_t) (borderColor .g () * 255)
 
157
                                << std::setw (2) << (int) (uint8_t) (borderColor .b () * 255)
 
158
                                << std::setw (2) << (int) (uint8_t) (borderColor .a () * 255);
 
159
 
 
160
                        image .borderColor (Magick::Color (color .str ()));
 
161
 
 
162
                        image .border (Magick::Geometry (borderWidth, borderWidth));
 
163
                }
 
164
        }
 
165
 
 
166
        void
 
167
        tryScaleImage (size_type minTextureSize, size_type maxTextureSize)
 
168
        {
 
169
                size_t width  = getWidth ();
 
170
                size_t height = getHeight ();
 
171
 
 
172
                if (std::max (width, height) < minTextureSize)
 
173
                        return;
 
174
 
 
175
                bool needsScaling = false;
 
176
 
 
177
                if (not math::is_power_of_two (width))
 
178
                {
 
179
                        width        = std::min (math::next_power_of_two (width), maxTextureSize);
 
180
                        needsScaling = true;
 
181
                }
 
182
 
 
183
                if (not math::is_power_of_two (height))
 
184
                {
 
185
                        height       = std::min (math::next_power_of_two (height), maxTextureSize);
 
186
                        needsScaling = true;
 
187
                }
 
188
 
 
189
                if (needsScaling)
 
190
                {
 
191
                        std::clog
 
192
                                << "Warning: Texture needs scaling: scaling texture from "
 
193
                                << getWidth () << " x " << getHeight ()
 
194
                                << " to " << width << " x " << height << " pixel."
 
195
                                << std::endl;
 
196
 
 
197
                        scaleImage (width, height);
 
198
                }
 
199
        }
 
200
 
 
201
        void
 
202
        scaleImage (size_type width, size_type height)
 
203
        {
 
204
                Magick::Geometry geometry (width, height);
 
205
 
 
206
                geometry .aspect (true);
 
207
                image .filterType (Magick::LanczosFilter);
 
208
                image .zoom (geometry);
 
209
        }
 
210
 
 
211
        void
 
212
        refineImageFormat ()
 
213
        {
 
214
                switch (image .type ())
 
215
                {
 
216
                        case Magick::GrayscaleType:
 
217
                        {
 
218
                                if (not image .matte ())
 
219
                                {
 
220
                                        image .colorSpace (Magick::GRAYColorspace);
 
221
                                        image .magick ("GRAY");
 
222
                                        format      = GL_LUMINANCE;
 
223
                                        components  = 1;
 
224
                                        return;
 
225
                                }
 
226
                        }
 
227
                        case Magick::GrayscaleMatteType:
 
228
                        {
 
229
                                image .colorSpace (Magick::GRAYColorspace);
 
230
                                image .type (Magick::TrueColorMatteType);
 
231
                                image .magick ("RGBA");
 
232
                                format      = GL_RGBA;
 
233
                                components  = 2;
 
234
                                return;
 
235
                        }
 
236
                        case Magick::TrueColorType:
 
237
                        {
 
238
                                if (not image .matte ())
 
239
                                {
 
240
                                        image .colorSpace (Magick::RGBColorspace);
 
241
                                        image .magick ("RGB");
 
242
                                        format      = GL_RGB;
 
243
                                        components  = 3;
 
244
                                        return;
 
245
                                }
 
246
                        }
 
247
                        case Magick::TrueColorMatteType:
 
248
                        {
 
249
                                image .colorSpace (Magick::RGBColorspace);
 
250
                                image .magick ("RGBA");
 
251
                                format      = GL_RGBA;
 
252
                                components  = 4;
 
253
                                return;
 
254
                        }
 
255
                        default:
 
256
                        {
 
257
                                if (image .matte ())
 
258
                                {
 
259
                                        image .type (Magick::TrueColorMatteType);
 
260
                                        refineImageFormat ();
 
261
                                        return;
 
262
                                }
 
263
                                else
 
264
                                {
 
265
                                        image .type (Magick::TrueColorType);
 
266
                                        refineImageFormat ();
 
267
                                        return;
 
268
                                }
 
269
                        }
 
270
                }
 
271
        }
 
272
        
 
273
        void
 
274
        writeBlob ()
 
275
        {
 
276
                image .interlaceType (Magick::NoInterlace);
 
277
                image .endian (Magick::LSBEndian);
 
278
                image .depth (8);
 
279
                image .write (&blob);
 
280
        }
 
281
 
 
282
        ///  @name Properties
 
283
 
 
284
        Magick::Image image;
 
285
        GLenum        format;
 
286
        size_type     components;
 
287
        Magick::Blob  blob;
 
288
 
 
289
};
 
290
 
 
291
class ImageTexture::Thread :
 
292
        public X3DInput
 
293
{
 
294
public:
 
295
 
 
296
        typedef Texture::size_type size_type;
 
297
 
 
298
        Thread (ImageTexture* const imageTexture) :
 
299
                imageTexture (imageTexture),
 
300
                future (std::async (std::launch::async, std::mem_fn (&Thread::loadAsync), this,
 
301
                                    imageTexture -> url (),
 
302
                                    imageTexture -> getTextureProperties () -> borderColor (),
 
303
                                    imageTexture -> getTextureProperties () -> borderWidth (),
 
304
                                    8,
 
305
                                    imageTexture -> getBrowser () -> getRenderingProperties () -> maxTextureSize ()))
 
306
        {
 
307
                imageTexture -> getBrowser () -> prepareEvents .addInterest (this, &Thread::prepareEvents);
 
308
                imageTexture -> getBrowser () -> notify ();
 
309
        }
 
310
 
 
311
        virtual
 
312
        ~Thread ()
 
313
        {
 
314
                if (future .valid ())
 
315
                        future .wait ();
 
316
        }
 
317
 
 
318
private:
 
319
 
 
320
        void
 
321
        prepareEvents ()
 
322
        {
 
323
                imageTexture -> getBrowser () -> notify ();
 
324
 
 
325
                if (future .valid ())
 
326
                {
 
327
                        auto status = future .wait_for (std::chrono::milliseconds (0));
 
328
 
 
329
                        if (status == std::future_status::ready)
 
330
                        {
 
331
                                imageTexture -> getBrowser () -> prepareEvents .removeInterest (this, &Thread::prepareEvents);
 
332
                                imageTexture -> set_image (future .get ());
 
333
                        }
 
334
                }
 
335
        }
 
336
 
 
337
        TexturePtr
 
338
        loadAsync (const MFString & url,
 
339
                   const Color4f & borderColor, size_type borderWidth,
 
340
                   size_type minTextureSize, size_type maxTextureSize)
 
341
        {
 
342
                std::lock_guard <std::mutex> lock (getThread ());
 
343
 
 
344
                for (const auto & URL : url)
 
345
                {
 
346
                        try
 
347
                        {
 
348
                                TexturePtr texture (new Texture (imageTexture -> loadDocument (URL)));
 
349
 
 
350
                                texture -> process (borderColor, borderWidth, minTextureSize, maxTextureSize);
 
351
 
 
352
                                return texture;
 
353
                        }
 
354
                        catch (const X3DError & error)
 
355
                        {
 
356
                                std::clog << "ImageTexture: " << error .what () << std::endl;
 
357
                        }
 
358
                        catch (const std::exception & error)
 
359
                        {
 
360
                                std::clog << "Bad Image: " << error .what () << ", in URL '" << imageTexture -> getWorldURL () << "'" << std::endl;
 
361
                        }
 
362
                }
 
363
 
 
364
                return nullptr;
 
365
        }
 
366
        
 
367
        std::mutex &
 
368
        getThread ()
 
369
        {
 
370
                std::lock_guard <std::mutex> lock (mutex);
 
371
                threadIndex = (threadIndex + 1) % threads .size ();
 
372
                return threads [threadIndex];
 
373
        }
 
374
        //
 
375
        
 
376
        static size_t                   threadIndex;
 
377
        static std::deque <std::mutex>  threads;
 
378
        std::mutex                      mutex;
 
379
 
 
380
        ImageTexture* const      imageTexture;
 
381
        std::future <TexturePtr> future;
 
382
 
 
383
};
 
384
 
 
385
size_t                   ImageTexture::Thread::threadIndex = 0;
 
386
std::deque <std::mutex>  ImageTexture::Thread::threads (4);
 
387
 
60
388
ImageTexture::ImageTexture (X3DExecutionContext* const executionContext) :
61
389
             X3DBaseNode (executionContext -> getBrowser (), executionContext),
62
390
        X3DTexture2DNode (),
63
 
            X3DUrlObject ()
 
391
            X3DUrlObject (),
 
392
                  thread ()
64
393
{
65
394
        setComponent ("Texturing");
66
395
        setTypeName ("ImageTexture");
90
419
}
91
420
 
92
421
void
 
422
ImageTexture::set_image (const TexturePtr & texture)
 
423
{
 
424
        __LOG__ << texture .get () << " : " << url () << std::endl;
 
425
 
 
426
        if (texture)
 
427
        {
 
428
                setImage (texture -> getComponents (),
 
429
                          texture -> getFormat (),
 
430
                          texture -> getWidth (), texture -> getHeight (),
 
431
                          texture -> getData ());
 
432
 
 
433
                setLoadState (COMPLETE_STATE);
 
434
        }
 
435
        else
 
436
                setLoadState (FAILED_STATE);
 
437
}
 
438
 
 
439
void
93
440
ImageTexture::update ()
94
441
{
95
442
        setLoadState (NOT_STARTED_STATE);
104
451
 
105
452
        setLoadState (IN_PROGRESS_STATE);
106
453
 
107
 
        // Load image.
108
 
 
109
 
        for (const auto & URL : url ())
110
 
        {
111
 
                try
112
 
                {
113
 
                        setImage (loadDocument (URL));
114
 
 
115
 
                        setLoadState (COMPLETE_STATE);
116
 
                        return;
117
 
                }
118
 
                catch (const X3DError & error)
119
 
                {
120
 
                        std::clog << "ImageTexture: " << error .what () << std::endl;
121
 
                }
122
 
                catch (const std::exception & error)
123
 
                {
124
 
                        std::clog << "Bad Image: " << error .what () << ", in URL '" << getWorldURL () << "'" << std::endl;
125
 
                }
126
 
        }
127
 
 
128
 
        setLoadState (FAILED_STATE);
 
454
        thread .reset (new Thread (this));
 
455
 
 
456
//      if (checkLoadState () == COMPLETE_STATE or checkLoadState () == IN_PROGRESS_STATE)
 
457
//              return;
 
458
//
 
459
//      setLoadState (IN_PROGRESS_STATE);
 
460
//
 
461
//      // Load image.
 
462
//
 
463
//      for (const auto & URL : url ())
 
464
//      {
 
465
//              try
 
466
//              {
 
467
//                      setImage (loadDocument (URL));
 
468
//
 
469
//                      setLoadState (COMPLETE_STATE);
 
470
//                      return;
 
471
//              }
 
472
//              catch (const X3DError & error)
 
473
//              {
 
474
//                      std::clog << "ImageTexture: " << error .what () << std::endl;
 
475
//              }
 
476
//              catch (const std::exception & error)
 
477
//              {
 
478
//                      std::clog << "Bad Image: " << error .what () << ", in URL '" << getWorldURL () << "'" << std::endl;
 
479
//              }
 
480
//      }
 
481
//
 
482
//      setLoadState (FAILED_STATE);
129
483
}
130
484
 
131
485
void
159
513
void
160
514
ImageTexture::dispose ()
161
515
{
 
516
        thread .reset ();
 
517
 
162
518
        X3DUrlObject::dispose ();
163
519
        X3DTexture2DNode::dispose ();
164
520
}