GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/atlas/atlasmanager.cpp Lines: 0 220 0.0 %
Date: 2017-11-29 Branches: 0 194 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2012-2017  The ManaPlus Developers
4
 *
5
 *  This file is part of The ManaPlus Client.
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
18
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#ifdef USE_OPENGL
22
23
#include "resources/atlas/atlasmanager.h"
24
25
#include "settings.h"
26
27
#ifdef DEBUG_IMAGES
28
#include "logger.h"
29
#endif  // DEBUG_IMAGES
30
31
#include "fs/virtfs/rwops.h"
32
33
#include "utils/mathutils.h"
34
#include "utils/sdlcheckutils.h"
35
36
#include "resources/openglimagehelper.h"
37
38
#include "resources/atlas/atlasresource.h"
39
#include "resources/atlas/textureatlas.h"
40
41
#include "resources/dye/dye.h"
42
43
#include "resources/resourcemanager/resourcemanager.h"
44
45
PRAGMA48(GCC diagnostic push)
46
PRAGMA48(GCC diagnostic ignored "-Wshadow")
47
#ifndef SDL_BIG_ENDIAN
48
#include <SDL_endian.h>
49
#endif  // SDL_BYTEORDER
50
PRAGMA48(GCC diagnostic pop)
51
52
#include "utils/checkutils.h"
53
54
#include "debug.h"
55
56
AtlasManager::AtlasManager()
57
{
58
}
59
60
AtlasResource *AtlasManager::loadTextureAtlas(const std::string &name,
61
                                              const StringVect &files)
62
{
63
    BLOCK_START("AtlasManager::loadTextureAtlas")
64
    STD_VECTOR<TextureAtlas*> atlases;
65
    STD_VECTOR<Image*> images;
66
    AtlasResource *resource = new AtlasResource;
67
68
    loadImages(files, images);
69
    int maxSize = OpenGLImageHelper::getTextureSize();
70
#if !defined(ANDROID) && !defined(__APPLE__)
71
    int sz = settings.textureSize;
72
    if (maxSize > sz)
73
        maxSize = sz;
74
#endif  // !defined(ANDROID) && !defined(__APPLE__)
75
76
    // sorting images on atlases.
77
    simpleSort(name, atlases, images, maxSize);
78
79
    FOR_EACH (STD_VECTOR<TextureAtlas*>::iterator, it, atlases)
80
    {
81
        TextureAtlas *const atlas = *it;
82
        if (atlas == nullptr)
83
            continue;
84
85
        createSDLAtlas(atlas);
86
        if (atlas->atlasImage == nullptr)
87
            continue;
88
        convertAtlas(atlas);
89
        resource->atlases.push_back(atlas);
90
    }
91
92
    BLOCK_END("AtlasManager::loadTextureAtlas")
93
    return resource;
94
}
95
96
AtlasResource *AtlasManager::loadEmptyAtlas(const std::string &name,
97
                                            const StringVect &files)
98
{
99
    STD_VECTOR<TextureAtlas*> atlases;
100
    STD_VECTOR<Image*> images;
101
    AtlasResource *resource = new AtlasResource;
102
103
    loadEmptyImages(files, images);
104
105
    // sorting images on atlases.
106
    emptySort(name, atlases, images);
107
108
    FOR_EACH (STD_VECTOR<TextureAtlas*>::iterator, it, atlases)
109
    {
110
        TextureAtlas *const atlas = *it;
111
        if (atlas == nullptr)
112
            continue;
113
114
        atlas->atlasImage = new Image(0,
115
            1024, 1024,
116
            1024, 1024);
117
        // convert SDL images to OpenGL
118
        convertEmptyAtlas(atlas);
119
120
        resource->atlases.push_back(atlas);
121
    }
122
123
    BLOCK_END("AtlasManager::loadTextureAtlas")
124
    return resource;
125
}
126
127
void AtlasManager::loadImages(const StringVect &files,
128
                              STD_VECTOR<Image*> &images)
129
{
130
    BLOCK_START("AtlasManager::loadImages")
131
132
    FOR_EACH (StringVectCIter, it, files)
133
    {
134
        const std::string str = *it;
135
        // check is image with same name already in cache
136
        // and if yes, move it to deleted set
137
        Resource *const res = ResourceManager::getTempResource(str);
138
        if (res != nullptr)
139
        {
140
            // increase counter because in moveToDeleted it will be decreased.
141
            res->incRef();
142
            ResourceManager::moveToDeleted(res);
143
        }
144
145
        std::string path = str;
146
        const size_t p = path.find('|');
147
        Dye *d = nullptr;
148
        if (p != std::string::npos)
149
        {
150
            d = new Dye(path.substr(p + 1));
151
            path = path.substr(0, p);
152
        }
153
154
        SDL_RWops *const rw = VirtFs::rwopsOpenRead(path);
155
        if (rw != nullptr)
156
        {
157
            Image *const image = d != nullptr ?
158
                surfaceImageHelper->load(rw, *d)
159
                : surfaceImageHelper->load(rw);
160
161
            if (image != nullptr)
162
            {
163
                image->mIdPath = str;
164
#ifdef DEBUG_IMAGES
165
                logger->log("set name %p, %s", static_cast<void*>(image),
166
                    image->mIdPath.c_str());
167
#endif  // DEBUG_IMAGES
168
169
                images.push_back(image);
170
            }
171
        }
172
        delete d;
173
    }
174
    BLOCK_END("AtlasManager::loadImages")
175
}
176
177
void AtlasManager::loadEmptyImages(const StringVect &files,
178
                                   STD_VECTOR<Image*> &images)
179
{
180
    BLOCK_START("AtlasManager::loadEmptyImages")
181
182
    FOR_EACH (StringVectCIter, it, files)
183
    {
184
        const std::string str = *it;
185
        // check is image with same name already in cache
186
        // and if yes, move it to deleted set
187
        Resource *const res = ResourceManager::getTempResource(str);
188
        if (res != nullptr)
189
        {
190
            // increase counter because in moveToDeleted it will be decreased.
191
            res->incRef();
192
            ResourceManager::moveToDeleted(res);
193
        }
194
195
        Image *const image = new Image(0,
196
            2048, 2048,
197
            2048, 2048);
198
        image->mIdPath = str;
199
        images.push_back(image);
200
    }
201
    BLOCK_END("AtlasManager::loadEmptyImages")
202
}
203
204
void AtlasManager::simpleSort(const std::string &restrict name,
205
                              STD_VECTOR<TextureAtlas*> &restrict atlases,
206
                              const STD_VECTOR<Image*> &restrict images,
207
                              int size)
208
{
209
    BLOCK_START("AtlasManager::simpleSort")
210
    int x = 0;
211
    int y = 0;
212
    int tempHeight = 0;
213
    TextureAtlas *atlas = new TextureAtlas;
214
    STD_VECTOR<Image*>::const_iterator it = images.begin();
215
    const STD_VECTOR<Image*>::const_iterator it_end = images.end();
216
    for (it = images.begin(); it != it_end; ++ it)
217
    {
218
        const Image *const img = *it;
219
        if (img != nullptr)
220
        {
221
            atlas->name = std::string("atlas_").append(name).append(
222
                "_").append(img->mIdPath);
223
            break;
224
        }
225
    }
226
227
    for (it = images.begin(); it != it_end; ++ it)
228
    {
229
        Image *const img = *it;
230
        if (img != nullptr)
231
        {
232
            AtlasItem *const item = new AtlasItem(img);
233
            item->name = img->mIdPath;
234
            // start next line
235
            if (x + img->mBounds.w > size)
236
            {
237
                x = 0;
238
                y += tempHeight;
239
                tempHeight = 0;
240
            }
241
242
            // can't put image with this height
243
            if (y + img->mBounds.h > size)
244
            {
245
                x = 0;
246
                y = 0;
247
                atlases.push_back(atlas);
248
                atlas = new TextureAtlas;
249
                atlas->name = std::string("atlas_").append(name).append(
250
                    "_").append(img->mIdPath);
251
            }
252
253
            if (img->mBounds.h > tempHeight)
254
                tempHeight = img->mBounds.h;
255
256
            item->x = x;
257
            item->y = y;
258
            atlas->items.push_back(item);
259
260
            // continue put textures in line
261
            x += img->mBounds.w;
262
            if (x > atlas->width)
263
                atlas->width = x;
264
            if (y + img->mBounds.h > atlas->height)
265
                atlas->height = y + img->mBounds.h;
266
        }
267
    }
268
    if (!atlas->items.empty())
269
        atlases.push_back(atlas);
270
    else
271
        delete atlas;
272
    BLOCK_END("AtlasManager::simpleSort")
273
}
274
275
void AtlasManager::emptySort(const std::string &restrict name,
276
                             STD_VECTOR<TextureAtlas*> &restrict atlases,
277
                             const STD_VECTOR<Image*> &restrict images)
278
{
279
    BLOCK_START("AtlasManager::simpleSort")
280
    TextureAtlas *atlas = new TextureAtlas;
281
    STD_VECTOR<Image*>::const_iterator it = images.begin();
282
    const STD_VECTOR<Image*>::const_iterator it_end = images.end();
283
    for (it = images.begin(); it != it_end; ++ it)
284
    {
285
        const Image *const img = *it;
286
        if (img != nullptr)
287
        {
288
            atlas->name = std::string("atlas_").append(name).append(
289
                "_").append(img->mIdPath);
290
            break;
291
        }
292
    }
293
294
    for (it = images.begin(); it != it_end; ++ it)
295
    {
296
        Image *const img = *it;
297
        if (img != nullptr)
298
        {
299
            AtlasItem *const item = new AtlasItem(img);
300
            item->name = img->mIdPath;
301
            item->x = 0;
302
            item->y = 0;
303
            atlas->items.push_back(item);
304
        }
305
    }
306
    if (!atlas->items.empty())
307
        atlases.push_back(atlas);
308
    else
309
        delete atlas;
310
    BLOCK_END("AtlasManager::simpleSort")
311
}
312
313
void AtlasManager::createSDLAtlas(TextureAtlas *const atlas)
314
{
315
    BLOCK_START("AtlasManager::createSDLAtlas")
316
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
317
    const unsigned int rmask = 0xff000000;
318
    const unsigned int gmask = 0x00ff0000;
319
    const unsigned int bmask = 0x0000ff00;
320
    const unsigned int amask = 0x000000ff;
321
#else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
322
323
    const unsigned int rmask = 0x000000ff;
324
    const unsigned int gmask = 0x0000ff00;
325
    const unsigned int bmask = 0x00ff0000;
326
    const unsigned int amask = 0xff000000;
327
#endif  // SDL_BYTEORDER == SDL_BIG_ENDIAN
328
329
    // do not create atlas based on only one image
330
    if (atlas->items.size() == 1)
331
    {
332
        logger->log("Skip atlas creation because only one image in atlas.");
333
        BLOCK_END("AtlasManager::createSDLAtlas")
334
        return;
335
    }
336
337
    // using only power of two sizes.
338
    atlas->width = powerOfTwo(atlas->width);
339
    atlas->height = powerOfTwo(atlas->height);
340
341
    const int width = atlas->width;
342
    const int height = atlas->height;
343
    BLOCK_START("AtlasManager::createSDLAtlas create surface")
344
    // temp SDL surface for atlas
345
    SDL_Surface *const surface = MSDL_CreateRGBSurface(SDL_SWSURFACE,
346
        width, height, 32U, rmask, gmask, bmask, amask);
347
    if (surface == nullptr)
348
    {
349
        reportAlways("Error creating surface for atlas. Size: %dx%d",
350
            width,
351
            height);
352
        BLOCK_END("AtlasManager::createSDLAtlas")
353
        return;
354
    }
355
    BLOCK_END("AtlasManager::createSDLAtlas create surface")
356
357
#ifdef OPENGLERRORS
358
    logger->log("OpenGL debug: creating atlase %dx%d", width, height);
359
#endif  // OPENGLERRORS
360
361
    Image *image = imageHelper->loadSurface(surface);
362
    // free SDL atlas surface
363
    MSDL_FreeSurface(surface);
364
    if (image == nullptr)
365
    {
366
        reportAlways("Error converting surface to texture. Size: %dx%d",
367
            width,
368
            height);
369
        return;
370
    }
371
372
    // drawing SDL images to surface
373
    FOR_EACH (STD_VECTOR<AtlasItem*>::iterator, it, atlas->items)
374
    {
375
        AtlasItem *const item = *it;
376
        imageHelper->copySurfaceToImage(image, item->x, item->y,
377
            item->image->mSDLSurface);
378
#ifdef OPENGLERRORS
379
        logger->log("OpenGL debug:  put atlas image %s (size %dx%d),"
380
            " into %d,%d to %d,%d",
381
            item->name.c_str(),
382
            item->image->mSDLSurface->w,
383
            item->image->mSDLSurface->h,
384
            item->x,
385
            item->y,
386
            item->x + item->image->mSDLSurface->w - 1,
387
            item->y + item->image->mSDLSurface->h - 1);
388
        if (item->x >= width)
389
            logger->log("OpenGL error: start x position outside of atlas");
390
        if (item->y >= height)
391
            logger->log("OpenGL error: start y position outside of atlas");
392
        if (item->x + item->image->mSDLSurface->w - 1 >= width)
393
            logger->log("OpenGL error: end x position outside of atlas");
394
        if (item->y + item->image->mSDLSurface->h - 1 >= height)
395
            logger->log("OpenGL error: end y position outside of atlas");
396
#endif  // OPENGLERRORS
397
    }
398
    atlas->atlasImage = image;
399
    BLOCK_END("AtlasManager::createSDLAtlas")
400
}
401
402
void AtlasManager::convertEmptyAtlas(TextureAtlas *const atlas)
403
{
404
    // no check for null pointer in atlas because it was in caller
405
    // convert surface to OpemGL image
406
    Image *const oldImage = atlas->atlasImage;
407
408
    if (oldImage->mSDLSurface != nullptr)
409
    {
410
        atlas->atlasImage = imageHelper->loadSurface(
411
            atlas->atlasImage->mSDLSurface);
412
        oldImage->decRef();
413
    }
414
415
    Image *const image = atlas->atlasImage;
416
    if (image == nullptr)
417
        return;
418
419
    image->mIdPath = atlas->name;
420
#ifdef DEBUG_IMAGES
421
    logger->log("set name %p, %s", static_cast<void*>(image),
422
        image->mIdPath.c_str());
423
#endif  // DEBUG_IMAGES
424
425
    image->incRef();
426
427
    FOR_EACH (STD_VECTOR<AtlasItem*>::iterator, it, atlas->items)
428
    {
429
        AtlasItem *const item = *it;
430
        // delete SDL Image
431
        delete item->image;
432
        // store OpenGL image
433
        item->image = image->getSubImage(item->x, item->y,
434
            item->width, item->height);
435
        Image *const image2 = item->image;
436
        if (image2 != nullptr)
437
        {
438
            image2->mIdPath = item->name;
439
#ifdef DEBUG_IMAGES
440
            logger->log("set empty name %p, %s", static_cast<void*>(image2),
441
                image2->mIdPath.c_str());
442
#endif  // DEBUG_IMAGES
443
444
            image2->incRef();
445
        }
446
    }
447
}
448
449
void AtlasManager::convertAtlas(TextureAtlas *const atlas)
450
{
451
    // no check for null pointer in atlas because it was in caller
452
    // convert surface to OpemGL image
453
    Image *const oldImage = atlas->atlasImage;
454
455
    if (oldImage->mSDLSurface != nullptr)
456
    {
457
        atlas->atlasImage = imageHelper->loadSurface(
458
            atlas->atlasImage->mSDLSurface);
459
        oldImage->decRef();
460
    }
461
462
    Image *const image = atlas->atlasImage;
463
    if (image == nullptr)
464
        return;
465
466
    image->mIdPath = atlas->name;
467
#ifdef DEBUG_IMAGES
468
    logger->log("set name %p, %s", static_cast<void*>(image),
469
        image->mIdPath.c_str());
470
#endif  // DEBUG_IMAGES
471
472
    image->incRef();
473
474
    FOR_EACH (STD_VECTOR<AtlasItem*>::iterator, it, atlas->items)
475
    {
476
        AtlasItem *const item = *it;
477
        // delete SDL Image
478
        delete item->image;
479
        // store OpenGL image
480
        item->image = image->getSubImage(item->x, item->y,
481
            item->width, item->height);
482
        Image *const image2 = item->image;
483
        if (image2 != nullptr)
484
        {
485
            image2->mIdPath = item->name;
486
#ifdef DEBUG_IMAGES
487
            logger->log("set name %p, %s", static_cast<void*>(image2),
488
                image2->mIdPath.c_str());
489
#endif  // DEBUG_IMAGES
490
491
            image2->incRef();
492
        }
493
    }
494
}
495
496
void AtlasManager::injectToResources(const AtlasResource *const resource)
497
{
498
    if (resource == nullptr)
499
        return;
500
    FOR_EACH (STD_VECTOR<TextureAtlas*>::const_iterator,
501
              it, resource->atlases)
502
    {
503
        // add each atlas image to resources
504
        TextureAtlas *const atlas = *it;
505
        if (atlas != nullptr)
506
        {
507
            Image *const image = atlas->atlasImage;
508
            if (image != nullptr)
509
                ResourceManager::addResource(atlas->name, image);
510
            FOR_EACH (STD_VECTOR<AtlasItem*>::iterator, it2, atlas->items)
511
            {
512
                AtlasItem *const item = *it2;
513
                if (item == nullptr)
514
                    continue;
515
                // add each atlas sub image to resources
516
                ResourceManager::addResource(item->name, item->image);
517
            }
518
        }
519
    }
520
}
521
522
void AtlasManager::moveToDeleted(AtlasResource *const resource)
523
{
524
    if (resource == nullptr)
525
        return;
526
    FOR_EACH (STD_VECTOR<TextureAtlas*>::iterator, it, resource->atlases)
527
    {
528
        // move each atlas image to deleted
529
        TextureAtlas *const atlas = *it;
530
        if (atlas != nullptr)
531
        {
532
            Image *const image = atlas->atlasImage;
533
            if (image != nullptr)
534
            {
535
                // move each atlas image to deleted
536
                ResourceManager::moveToDeleted(image);
537
            }
538
            FOR_EACH (STD_VECTOR<AtlasItem*>::iterator, it2, atlas->items)
539
            {
540
                AtlasItem *const item = *it2;
541
                if (item != nullptr)
542
                {
543
                    Image *const image2 = item->image;
544
                    if (image2 != nullptr)
545
                    {
546
                        // move each atlas sub image to deleted
547
                        ResourceManager::moveToDeleted(image2);
548
                    }
549
                }
550
            }
551
        }
552
    }
553
}
554
555
#endif  // USE_OPENGL