ManaPlus
atlasmanager.cpp
Go to the documentation of this file.
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 
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 
37 
40 
41 #include "resources/dye/dye.h"
42 
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 
57 {
58 }
59 
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 
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 
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();
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 
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();
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 
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 
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  {
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 
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  {
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 
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
517  }
518  }
519  }
520 }
521 
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
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
548  }
549  }
550  }
551  }
552  }
553 }
554 
555 #endif // USE_OPENGL
Image * image
Definition: atlasitem.h:53
#define FOR_EACH(type, iter, array)
Definition: foreach.h:24
AtlasManager() A_CONST
int width
Definition: atlasitem.h:57
ImageHelper * imageHelper
Definition: imagehelper.cpp:43
StringVect::const_iterator StringVectCIter
Definition: stringvector.h:30
SDL_RWops * rwopsOpenRead(const std::string &fname)
Definition: rwops.cpp:104
static AtlasResource * loadTextureAtlas(const std::string &name, const StringVect &files)
#define MSDL_FreeSurface(surface)
Definition: debug.h:53
#define BLOCK_START(name)
Definition: perfomance.h:78
#define BLOCK_END(name)
Definition: perfomance.h:79
std::vector< std::string > StringVect
Definition: stringvector.h:28
std::vector< TextureAtlas * > atlases
Definition: atlasresource.h:51
void moveToDeleted(Resource *const res)
Definition: dye.h:39
Logger * logger
Definition: logger.cpp:95
Settings settings
Definition: settings.cpp:31
int height
Definition: atlasitem.h:58
SDL_Surface * mSDLSurface
Definition: image.h:230
static void loadEmptyImages(const StringVect &files, std::vector< Image *> &images)
static void loadImages(const StringVect &files, std::vector< Image *> &images)
virtual Image * getSubImage(const int x, const int y, const int width, const int height)
Definition: image.cpp:465
int powerOfTwo(const unsigned int input)
Definition: mathutils.h:236
static void convertEmptyAtlas(TextureAtlas *const atlas)
static AtlasResource * loadEmptyAtlas(const std::string &name, const StringVect &files)
static void moveToDeleted(AtlasResource *const resource)
static void emptySort(const std::string &name, std::vector< TextureAtlas *> &atlases, const std::vector< Image *> &images)
#define PRAGMA48(str)
Definition: localconsts.h:214
static int getTextureSize()
Resource * getTempResource(const std::string &idPath)
virtual void incRef()
Definition: resource.cpp:37
virtual Image * loadSurface(SDL_Surface *const)
Definition: imagehelper.h:72
ImageHelper * surfaceImageHelper
Definition: imagehelper.cpp:44
static void simpleSort(const std::string &name, std::vector< TextureAtlas *> &atlases, const std::vector< Image *> &images, int size)
Image * atlasImage
Definition: textureatlas.h:74
std::string name
Definition: textureatlas.h:73
Definition: image.h:61
std::vector< AtlasItem * > items
Definition: textureatlas.h:77
#define restrict
Definition: localconsts.h:176
virtual void copySurfaceToImage(const Image *const image, const int x, const int y, SDL_Surface *const surface) const
Definition: imagehelper.h:85
static void injectToResources(const AtlasResource *const resource)
SDL_Rect mBounds
Definition: image.h:210
Image * load(SDL_RWops *const rw)
Definition: imagehelper.cpp:49
void log(const char *const log_text,...)
Definition: logger.cpp:243
std::string mIdPath
Definition: resource.h:83
std::string name
Definition: atlasitem.h:54
unsigned int textureSize
Definition: settings.h:130
static void convertAtlas(TextureAtlas *const atlas)
#define reportAlways(...)
Definition: checkutils.h:252
void decRef()
Definition: image.cpp:521
bool addResource(const std::string &idPath, Resource *const resource)
static void createSDLAtlas(TextureAtlas *const atlas)
#define MSDL_CreateRGBSurface(flags, w, h, d, r, g, b, a)
Definition: debug.h:54