ManaPlus
atlasmanager.cpp
Go to the documentation of this file.
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 
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 
38 
41 
42 #include "resources/dye/dye.h"
43 
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 
58 {
59 }
60 
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 
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 
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();
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 
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();
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 
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 
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  {
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 
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  {
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 
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
518  }
519  }
520  }
521 }
522 
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
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
549  }
550  }
551  }
552  }
553  }
554 }
555 
556 #endif // USE_OPENGL
#define reportAlways(...)
Definition: checkutils.h:253
static void injectToResources(const AtlasResource *const resource)
static AtlasResource * loadTextureAtlas(const std::string &name, const StringVect &files)
static void loadEmptyImages(const StringVect &files, std::vector< Image * > &images)
static AtlasResource * loadEmptyAtlas(const std::string &name, const StringVect &files)
static void loadImages(const StringVect &files, std::vector< Image * > &images)
static void emptySort(const std::string &name, std::vector< TextureAtlas * > &atlases, const std::vector< Image * > &images)
static void convertAtlas(TextureAtlas *const atlas)
static void convertEmptyAtlas(TextureAtlas *const atlas)
static void simpleSort(const std::string &name, std::vector< TextureAtlas * > &atlases, const std::vector< Image * > &images, int size)
static void moveToDeleted(AtlasResource *const resource)
static void createSDLAtlas(TextureAtlas *const atlas)
std::vector< TextureAtlas * > atlases
Definition: atlasresource.h:52
Definition: dye.h:41
virtual void copySurfaceToImage(const Image *const image, const int x, const int y, SDL_Surface *const surface) const
Definition: imagehelper.h:86
virtual Image * loadSurface(SDL_Surface *const)
Definition: imagehelper.h:73
Image * load(SDL_RWops *const rw)
Definition: imagehelper.cpp:50
void log(const char *const log_text,...)
Definition: logger.cpp:269
static int getTextureSize()
virtual void incRef()
Definition: resource.cpp:38
unsigned int textureSize
Definition: settings.h:132
#define MSDL_CreateRGBSurface(flags, w, h, d, r, g, b, a)
Definition: debug.h:55
#define MSDL_FreeSurface(surface)
Definition: debug.h:54
#define FOR_EACH(type, iter, array)
Definition: foreach.h:25
ImageHelper * imageHelper
Definition: imagehelper.cpp:44
ImageHelper * surfaceImageHelper
Definition: imagehelper.cpp:45
#define restrict
Definition: localconsts.h:165
#define PRAGMA48(str)
Definition: localconsts.h:199
Logger * logger
Definition: logger.cpp:89
int powerOfTwo(const unsigned int input)
Definition: mathutils.h:237
int size()
Definition: emotedb.cpp:306
void moveToDeleted(Resource *const res)
bool addResource(const std::string &idPath, Resource *const resource)
Resource * getTempResource(const std::string &idPath)
SDL_RWops * rwopsOpenRead(const std::string &fname)
Definition: rwops.cpp:107
#define BLOCK_END(name)
Definition: perfomance.h:80
#define BLOCK_START(name)
Definition: perfomance.h:79
Settings settings
Definition: settings.cpp:32
StringVect::const_iterator StringVectCIter
Definition: stringvector.h:31
std::vector< std::string > StringVect
Definition: stringvector.h:29
int height
Definition: atlasitem.h:59
Image * image
Definition: atlasitem.h:54
int width
Definition: atlasitem.h:58
std::string name
Definition: atlasitem.h:55
std::string name
Definition: textureatlas.h:74
Image * atlasImage
Definition: textureatlas.h:75
std::vector< AtlasItem * > items
Definition: textureatlas.h:78