GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/atlas/atlasmanager.cpp Lines: 0 218 0.0 %
Date: 2021-03-17 Branches: 0 192 0.0 %

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