1/*!
2 * \file image.cpp
3 * \brief file image.cpp
4 *
5 * Copyright 2016 by Intel.
6 *
7 * Contact: kevin.rogovin@gmail.com
8 *
9 * This Source Code Form is subject to the
10 * terms of the Mozilla Public License, v. 2.0.
11 * If a copy of the MPL was not distributed with
12 * this file, You can obtain one at
13 * http://mozilla.org/MPL/2.0/.
14 *
15 * \author Kevin Rogovin <kevin.rogovin@gmail.com>
16 *
17 */
18
19
20#include <list>
21#include <map>
22#include <mutex>
23#include <fastuidraw/image.hpp>
24#include <fastuidraw/image_atlas.hpp>
25#include <private/array3d.hpp>
26#include <private/util_private.hpp>
27
28namespace
29{
30 /*
31 * Copies from src the rectangle:
32 * [source_x, source_x + dest_dim) x [source_y, source_y + dest_dim)
33 * to dest.
34 *
35 * If source_x is negative, then pads each line with the value
36 * from src at x=0.
37 *
38 * If source_y is negative, the source for those lines is
39 * taken from src at y=0.
40 *
41 * For pixel (x,y) with x < 0, takes the value
42 * from source at (0, y).
43 *
44 * For pixel (x,y) with y < 0, takes the value
45 * from source at (x, 0).
46 *
47 * For pixels (x,y) with x >= src_dims.x(), takes the value
48 * from source at (src_dims.x() - 1, y).
49 *
50 * For pixels (x,y) with y >= src_dims.y(), takes the value
51 * from source at (x, src_dims.y() - 1).
52 *
53 * \param dest_dim width and height of destination
54 * \param src_dims width and height of source
55 * \param source_x, source_y location within src from which to copy
56 * \param src: src pixels
57 * \param dest: destination pixels
58 *
59 * If all texel are the same value, returns true.
60 */
61 template<typename T, typename S>
62 void
63 copy_sub_data(fastuidraw::c_array<T> dest,
64 int w, int h,
65 fastuidraw::c_array<const S> src,
66 int source_x, int source_y,
67 fastuidraw::ivec2 src_dims)
68 {
69 using namespace fastuidraw;
70
71 FASTUIDRAWassert(w > 0);
72 FASTUIDRAWassert(h > 0);
73 FASTUIDRAWassert(src_dims.x() > 0);
74 FASTUIDRAWassert(src_dims.y() > 0);
75
76 for(int src_y = source_y, dst_y = 0; dst_y < h; ++src_y, ++dst_y)
77 {
78 c_array<const S> line_src;
79 c_array<T> line_dest;
80 int dst_x, src_x, src_start;
81
82 line_dest = dest.sub_array(dst_y * w, w);
83 if (src_y < 0)
84 {
85 src_start = 0;
86 }
87 else if (src_y >= src_dims.y())
88 {
89 src_start = (src_dims.y() - 1) * src_dims.x();
90 }
91 else
92 {
93 src_start = src_y * src_dims.x();
94 }
95 line_src = src.sub_array(src_start, src_dims.x());
96
97 for(src_x = source_x, dst_x = 0; src_x < 0; ++src_x, ++dst_x)
98 {
99 line_dest[dst_x] = line_src[0];
100 }
101
102 for(src_x = t_max(0, source_x);
103 src_x < src_dims.x() && dst_x < w;
104 ++src_x, ++dst_x)
105 {
106 line_dest[dst_x] = line_src[src_x];
107 }
108
109 for(;dst_x < w; ++dst_x)
110 {
111 line_dest[dst_x] = line_src[src_dims.x() - 1];
112 }
113 }
114 }
115
116 fastuidraw::ivec2
117 divide_up(fastuidraw::ivec2 numerator, int denominator)
118 {
119 fastuidraw::ivec2 R;
120 R = numerator / denominator;
121 if (numerator.x() % denominator != 0)
122 {
123 ++R.x();
124 }
125 if (numerator.y() % denominator != 0)
126 {
127 ++R.y();
128 }
129 return R;
130 }
131
132 int
133 number_index_tiles_needed(fastuidraw::ivec2 number_color_tiles,
134 int index_tile_size)
135 {
136 int return_value = 1;
137 fastuidraw::ivec2 tile_count = divide_up(number_color_tiles, index_tile_size);
138
139 while(tile_count.x() > 1 || tile_count.y() > 1)
140 {
141 return_value += tile_count.x() * tile_count.y();
142 tile_count = divide_up(tile_count, index_tile_size);
143 }
144
145 return return_value;
146 }
147
148 class BackingStorePrivate
149 {
150 public:
151 BackingStorePrivate(fastuidraw::ivec3 whl):
152 m_dimensions(whl)
153 {}
154
155 BackingStorePrivate(int w, int h, int num_layers):
156 m_dimensions(w, h, num_layers)
157 {}
158
159 fastuidraw::ivec3 m_dimensions;
160 };
161
162 class inited_bool
163 {
164 public:
165 bool m_value;
166 inited_bool(bool b = false):m_value(b) {}
167 };
168
169 class tile_allocator
170 {
171 public:
172 tile_allocator(int tile_size, fastuidraw::ivec3 store_dimensions);
173
174 ~tile_allocator();
175
176 fastuidraw::ivec3
177 allocate_tile(void);
178
179 void
180 delete_tile(fastuidraw::ivec3 v);
181
182 int
183 number_free(void) const;
184
185 bool
186 resize_to_fit(int num_tiles);
187
188 void
189 lock_resources(void);
190
191 void
192 unlock_resources(void);
193
194 int
195 tile_size(void) const
196 {
197 return m_tile_size;
198 }
199
200 const fastuidraw::ivec3&
201 num_tiles(void) const
202 {
203 return m_num_tiles;
204 }
205
206 private:
207 void
208 delete_tile_implement(fastuidraw::ivec3 v);
209
210 int m_tile_size;
211 fastuidraw::ivec3 m_next_tile;
212 fastuidraw::ivec3 m_num_tiles;
213 std::vector<fastuidraw::ivec3> m_free_tiles;
214 int m_tile_count;
215
216 int m_lock_resources_counter;
217 std::vector<fastuidraw::ivec3> m_delayed_free_tiles;
218
219 #ifdef FASTUIDRAW_DEBUG
220 fastuidraw::array3d<inited_bool> m_tile_allocated;
221 #endif
222 };
223
224 class ResourceReleaseActionList
225 {
226 public:
227 ResourceReleaseActionList(void):
228 m_lock_resources_counter(0)
229 {}
230
231 ~ResourceReleaseActionList()
232 {
233 FASTUIDRAWassert(m_lock_resources_counter == 0);
234 FASTUIDRAWassert(m_delete_actions.empty());
235 }
236
237 void
238 add_action(const fastuidraw::reference_counted_ptr<fastuidraw::Image::ResourceReleaseAction> &action)
239 {
240 if (m_lock_resources_counter != 0u)
241 {
242 m_delete_actions.push_back(action);
243 }
244 else
245 {
246 action->action();
247 }
248 }
249
250 void
251 lock_resources(void)
252 {
253 ++m_lock_resources_counter;
254 }
255
256 void
257 unlock_resources(void)
258 {
259 FASTUIDRAWassert(m_lock_resources_counter >= 1);
260 --m_lock_resources_counter;
261 if (m_lock_resources_counter == 0)
262 {
263 for (const auto &v : m_delete_actions)
264 {
265 v->action();
266 }
267 m_delete_actions.clear();
268 }
269 }
270
271 private:
272 std::vector<fastuidraw::reference_counted_ptr<fastuidraw::Image::ResourceReleaseAction> > m_delete_actions;
273 unsigned int m_lock_resources_counter;
274 };
275
276 template<typename T>
277 fastuidraw::ivec3
278 dimensions_of_store(const fastuidraw::reference_counted_ptr<T> &store)
279 {
280 return (store) ?
281 store->dimensions() :
282 fastuidraw::ivec3(0, 0, 0);
283 }
284
285 class ImageAtlasPrivate
286 {
287 public:
288 ImageAtlasPrivate(int pcolor_tile_size, int pindex_tile_size,
289 const fastuidraw::reference_counted_ptr<fastuidraw::AtlasColorBackingStoreBase> &pcolor_store,
290 const fastuidraw::reference_counted_ptr<fastuidraw::AtlasIndexBackingStoreBase> &pindex_store):
291 m_color_store(pcolor_store),
292 m_color_store_constant(m_color_store),
293 m_color_tiles(pcolor_tile_size, dimensions_of_store(pcolor_store)),
294 m_index_store(pindex_store),
295 m_index_store_constant(m_index_store),
296 m_index_tiles(pindex_tile_size, dimensions_of_store(pindex_store))
297 {}
298
299 int
300 number_free_index_tiles(void);
301
302 fastuidraw::ivec3
303 add_index_tile(fastuidraw::c_array<const fastuidraw::ivec3> data);
304
305 fastuidraw::ivec3
306 add_index_tile_index_data(fastuidraw::c_array<const fastuidraw::ivec3> data);
307
308 void
309 delete_index_tile(fastuidraw::ivec3 tile);
310
311 fastuidraw::ivec3
312 add_color_tile(fastuidraw::ivec2 src_xy,
313 const fastuidraw::ImageSourceBase &image_data);
314
315 fastuidraw::ivec3
316 add_color_tile(fastuidraw::u8vec4 color_data);
317
318 void
319 delete_color_tile(fastuidraw::ivec3 tile);
320
321 int
322 number_free_color_tiles(void);
323
324 void
325 resize_to_fit(int num_color_tiles, int num_index_tiles);
326
327 int
328 index_tile_size(void)
329 {
330 return m_index_tiles.tile_size();
331 }
332
333 int
334 color_tile_size(void)
335 {
336 return m_color_tiles.tile_size();
337 }
338
339 std::mutex m_mutex;
340 ResourceReleaseActionList m_delete_actions;
341
342 fastuidraw::reference_counted_ptr<fastuidraw::AtlasColorBackingStoreBase> m_color_store;
343 fastuidraw::reference_counted_ptr<const fastuidraw::AtlasColorBackingStoreBase> m_color_store_constant;
344 tile_allocator m_color_tiles;
345
346 fastuidraw::reference_counted_ptr<fastuidraw::AtlasIndexBackingStoreBase> m_index_store;
347 fastuidraw::reference_counted_ptr<const fastuidraw::AtlasIndexBackingStoreBase> m_index_store_constant;
348 tile_allocator m_index_tiles;
349 };
350
351 /* TODO: take into account for repeated tile colors. */
352 bool
353 enough_room_in_atlas(fastuidraw::ivec2 number_color_tiles,
354 ImageAtlasPrivate *C, int &total_index)
355 {
356 int total_color;
357
358 total_color = number_color_tiles.x() * number_color_tiles.y();
359 total_index = number_index_tiles_needed(number_color_tiles, C->index_tile_size());
360
361 //std::cout << "Need " << total_color << " have: " << C->number_free_color_tiles() << "\n"
362 // << "Need " << total_index << " have: " << C->number_free_index_tiles() << "\n";
363
364 return total_color <= C->number_free_color_tiles()
365 && total_index <= C->number_free_index_tiles();
366 }
367
368 class per_color_tile
369 {
370 public:
371 per_color_tile(const fastuidraw::ivec3 t, bool b):
372 m_tile(t), m_non_repeat_color(b)
373 {}
374
375 operator fastuidraw::ivec3() const
376 {
377 return m_tile;
378 }
379
380 fastuidraw::ivec3 m_tile;
381 bool m_non_repeat_color;
382 };
383
384 class ImagePrivate
385 {
386 public:
387 ImagePrivate(fastuidraw::ImageAtlas &patlas,
388 ImageAtlasPrivate *atlas_private,
389 int w, int h,
390 const fastuidraw::ImageSourceBase &image_data);
391
392 ImagePrivate(fastuidraw::ImageAtlas &patlas,
393 ImageAtlasPrivate *atlas_private, int w, int h,
394 unsigned int m, fastuidraw::Image::type_t t, uint64_t handle,
395 enum fastuidraw::Image::format_t fmt,
396 const fastuidraw::reference_counted_ptr<fastuidraw::Image::ResourceReleaseAction> &action):
397 m_atlas(&patlas),
398 m_atlas_private(atlas_private),
399 m_action(action),
400 m_dimensions(w, h),
401 m_number_levels(m),
402 m_type(t),
403 m_format(fmt),
404 m_num_color_tiles(-1, -1),
405 m_master_index_tile(-1, -1, -1),
406 m_master_index_tile_dims(-1.0f, -1.0f),
407 m_number_index_lookups(0),
408 m_dimensions_index_divisor(-1.0f),
409 m_bindless_handle(handle)
410 {
411 }
412
413 ~ImagePrivate();
414
415 void
416 create_color_tiles(const fastuidraw::ImageSourceBase &image_data);
417
418 void
419 create_index_tiles(void);
420
421 template<typename T>
422 fastuidraw::ivec2
423 create_index_layer(fastuidraw::c_array<const T> src_tiles,
424 fastuidraw::ivec2 src_dims,
425 std::list<std::vector<fastuidraw::ivec3> > &destination);
426
427 fastuidraw::reference_counted_ptr<fastuidraw::ImageAtlas> m_atlas;
428 ImageAtlasPrivate *m_atlas_private;
429 fastuidraw::reference_counted_ptr<fastuidraw::Image::ResourceReleaseAction> m_action;
430 fastuidraw::ivec2 m_dimensions;
431 int m_number_levels;
432
433 enum fastuidraw::Image::type_t m_type;
434 enum fastuidraw::Image::format_t m_format;
435
436 /* Data for when the image has type on_atlas */
437 fastuidraw::ivec2 m_num_color_tiles;
438 std::map<fastuidraw::u8vec4, fastuidraw::ivec3> m_repeated_tiles;
439 std::vector<per_color_tile> m_color_tiles;
440 std::list<std::vector<fastuidraw::ivec3> > m_index_tiles;
441 fastuidraw::ivec3 m_master_index_tile;
442 fastuidraw::vec2 m_master_index_tile_dims;
443 unsigned int m_number_index_lookups;
444 float m_dimensions_index_divisor;
445
446 /* data for when image has different type than on_atlas */
447 uint64_t m_bindless_handle;
448 };
449}
450
451/////////////////////////////////////////////
452//ImagePrivate methods
453ImagePrivate::
454ImagePrivate(fastuidraw::ImageAtlas &patlas,
455 ImageAtlasPrivate *atlas_private, int w, int h,
456 const fastuidraw::ImageSourceBase &image_data):
457 m_atlas(&patlas),
458 m_atlas_private(atlas_private),
459 m_dimensions(w, h),
460 m_number_levels(image_data.number_levels()),
461 m_type(fastuidraw::Image::on_atlas),
462 m_format(image_data.format()),
463 m_bindless_handle(-1)
464{
465 using namespace fastuidraw;
466
467 FASTUIDRAWassert(m_dimensions.x() > 0);
468 FASTUIDRAWassert(m_dimensions.y() > 0);
469 FASTUIDRAWassert(m_atlas);
470
471 create_color_tiles(image_data);
472 create_index_tiles();
473
474 /* Mipmap filtering cannot go beyond the tile size or the
475 * size of the image.
476 */
477 int max_levels;
478 max_levels = 1u + uint32_log2(t_min(m_atlas_private->color_tile_size(), t_min(w, h)));
479 m_number_levels = t_min(max_levels, m_number_levels);
480}
481
482ImagePrivate::
483~ImagePrivate()
484{
485 for(const per_color_tile &C : m_color_tiles)
486 {
487 if (C.m_non_repeat_color)
488 {
489 m_atlas_private->delete_color_tile(C.m_tile);
490 }
491 }
492
493 for(const auto &C : m_repeated_tiles)
494 {
495 m_atlas_private->delete_color_tile(C.second);
496 }
497
498 for(const auto &tile_array: m_index_tiles)
499 {
500 for(const fastuidraw::ivec3 &index_tile : tile_array)
501 {
502 m_atlas_private->delete_index_tile(index_tile);
503 }
504 }
505
506 if (m_action)
507 {
508 m_atlas->queue_resource_release_action(m_action);
509 }
510}
511
512void
513ImagePrivate::
514create_color_tiles(const fastuidraw::ImageSourceBase &image_data)
515{
516 int tile_interior_size;
517 int color_tile_size;
518
519 color_tile_size = m_atlas_private->color_tile_size();
520 tile_interior_size = color_tile_size;
521 m_num_color_tiles = divide_up(m_dimensions, tile_interior_size);
522 m_master_index_tile_dims = fastuidraw::vec2(m_dimensions) / static_cast<float>(tile_interior_size);
523 m_dimensions_index_divisor = static_cast<float>(tile_interior_size);
524
525 unsigned int savings(0);
526 for(int ty = 0, source_y = 0;
527 ty < m_num_color_tiles.y();
528 ++ty, source_y += tile_interior_size)
529 {
530 for(int tx = 0, source_x = 0;
531 tx < m_num_color_tiles.x();
532 ++tx, source_x += tile_interior_size)
533 {
534 fastuidraw::ivec3 new_tile;
535 fastuidraw::ivec2 src_xy(source_x, source_y);
536 bool all_same_color;
537 fastuidraw::u8vec4 same_color_value;
538
539 all_same_color = image_data.all_same_color(src_xy, color_tile_size, &same_color_value);
540
541 if (all_same_color)
542 {
543 std::map<fastuidraw::u8vec4, fastuidraw::ivec3>::iterator iter;
544
545 iter = m_repeated_tiles.find(same_color_value);
546 if (iter != m_repeated_tiles.end())
547 {
548 new_tile = iter->second;
549 ++savings;
550 }
551 else
552 {
553 new_tile = m_atlas_private->add_color_tile(same_color_value);
554 m_repeated_tiles[same_color_value] = new_tile;
555 }
556 }
557 else
558 {
559 new_tile = m_atlas_private->add_color_tile(src_xy, image_data);
560 }
561
562 m_color_tiles.push_back(per_color_tile(new_tile, !all_same_color) );
563 }
564 }
565
566 FASTUIDRAWunused(savings);
567 //std::cout << "Saved " << savings << " out of "
568 // << m_num_color_tiles.x() * m_num_color_tiles.y()
569 // << " tiles from repeat color magicks\n";
570}
571
572
573/*
574 * returns the number of index tiles needed to
575 * store the created index data.
576 */
577template<typename T>
578fastuidraw::ivec2
579ImagePrivate::
580create_index_layer(fastuidraw::c_array<const T> src_tiles,
581 fastuidraw::ivec2 src_dims,
582 std::list<std::vector<fastuidraw::ivec3> > &destination)
583{
584 int index_tile_size;
585 fastuidraw::ivec2 num_index_tiles;
586
587 index_tile_size = m_atlas_private->index_tile_size();
588 num_index_tiles = divide_up(src_dims, index_tile_size);
589
590 destination.push_back(std::vector<fastuidraw::ivec3>());
591
592 std::vector<fastuidraw::ivec3> vtile_data(index_tile_size * index_tile_size);
593 fastuidraw::c_array<fastuidraw::ivec3> tile_data;
594 tile_data = fastuidraw::make_c_array(vtile_data);
595
596 for(int source_y = 0; source_y < src_dims.y(); source_y += index_tile_size)
597 {
598 for(int source_x = 0; source_x < src_dims.x(); source_x += index_tile_size)
599 {
600 fastuidraw::ivec3 new_tile;
601 copy_sub_data<fastuidraw::ivec3, T>(tile_data, index_tile_size, index_tile_size,
602 src_tiles, source_x, source_y,
603 src_dims);
604 new_tile = m_atlas_private->add_index_tile(tile_data);
605 destination.back().push_back(new_tile);
606 }
607 }
608 return num_index_tiles;
609}
610
611void
612ImagePrivate::
613create_index_tiles(void)
614{
615 fastuidraw::ivec2 num_index_tiles;
616 int level(2);
617 float findex_tile_size;
618
619 findex_tile_size = static_cast<float>(m_atlas_private->index_tile_size());
620 num_index_tiles = create_index_layer<per_color_tile>(fastuidraw::make_c_array(m_color_tiles),
621 m_num_color_tiles, m_index_tiles);
622
623 for(level = 2; num_index_tiles.x() > 1 || num_index_tiles.y() > 1; ++level)
624 {
625 num_index_tiles = create_index_layer<fastuidraw::ivec3>(fastuidraw::make_c_array(m_index_tiles.back()),
626 num_index_tiles, m_index_tiles);
627 m_dimensions_index_divisor *= findex_tile_size;
628 m_master_index_tile_dims /= findex_tile_size;
629 }
630
631 FASTUIDRAWassert(m_index_tiles.back().size() == 1);
632 m_master_index_tile = m_index_tiles.back()[0];
633 m_number_index_lookups = m_index_tiles.size();
634}
635
636///////////////////////////////////////////
637// tile_allocator methods
638tile_allocator::
639tile_allocator(int tile_size, fastuidraw::ivec3 store_dimensions):
640 m_tile_size(tile_size),
641 m_next_tile(0, 0, 0),
642 m_num_tiles(tile_size > 0 ? store_dimensions.x() / m_tile_size : 0,
643 tile_size > 0 ? store_dimensions.y() / m_tile_size : 0,
644 tile_size > 0 ? store_dimensions.z() : 0),
645 m_tile_count(0),
646 m_lock_resources_counter(0)
647#ifdef FASTUIDRAW_DEBUG
648 ,
649 m_tile_allocated(m_num_tiles.x(), m_num_tiles.y(), m_num_tiles.z())
650#endif
651{
652 FASTUIDRAWassert(m_tile_size == 0 || store_dimensions.x() % m_tile_size == 0);
653 FASTUIDRAWassert(m_tile_size == 0 || store_dimensions.y() % m_tile_size == 0);
654}
655
656tile_allocator::
657~tile_allocator()
658{
659 FASTUIDRAWassert(m_lock_resources_counter == 0);
660 FASTUIDRAWassert(m_tile_count == 0);
661}
662
663fastuidraw::ivec3
664tile_allocator::
665allocate_tile(void)
666{
667 fastuidraw::ivec3 return_value;
668 if (m_free_tiles.empty())
669 {
670 if (m_next_tile.x() < m_num_tiles.x() || m_next_tile.y() < m_num_tiles.y() || m_next_tile.z() < m_num_tiles.z())
671 {
672 return_value = m_next_tile;
673 ++m_next_tile.x();
674 if (m_next_tile.x() == m_num_tiles.x())
675 {
676 m_next_tile.x() = 0;
677 ++m_next_tile.y();
678 if (m_next_tile.y() == m_num_tiles.y())
679 {
680 m_next_tile.y() = 0;
681 ++m_next_tile.z();
682 }
683 }
684 }
685 else
686 {
687 FASTUIDRAWassert(!"Color tile room exhausted");
688 return fastuidraw::ivec3(-1, -1,-1);
689 }
690 }
691 else
692 {
693 return_value = m_free_tiles.back();
694 m_free_tiles.pop_back();
695 }
696
697 #ifdef FASTUIDRAW_DEBUG
698 {
699 FASTUIDRAWassert(!m_tile_allocated(return_value.x(), return_value.y(), return_value.z()).m_value);
700 m_tile_allocated(return_value.x(), return_value.y(), return_value.z()) = true;
701 }
702 #endif
703
704 ++m_tile_count;
705 return return_value;
706}
707
708void
709tile_allocator::
710lock_resources(void)
711{
712 ++m_lock_resources_counter;
713}
714
715void
716tile_allocator::
717unlock_resources(void)
718{
719 FASTUIDRAWassert(m_lock_resources_counter >= 1);
720 --m_lock_resources_counter;
721 if (m_lock_resources_counter == 0)
722 {
723 for(unsigned int i = 0, endi = m_delayed_free_tiles.size(); i < endi; ++i)
724 {
725 delete_tile_implement(m_delayed_free_tiles[i]);
726 }
727 m_delayed_free_tiles.clear();
728 }
729}
730
731void
732tile_allocator::
733delete_tile(fastuidraw::ivec3 v)
734{
735 if (m_lock_resources_counter == 0)
736 {
737 delete_tile_implement(v);
738 }
739 else
740 {
741 m_delayed_free_tiles.push_back(v);
742 }
743}
744
745void
746tile_allocator::
747delete_tile_implement(fastuidraw::ivec3 v)
748{
749 FASTUIDRAWassert(m_lock_resources_counter == 0);
750 #ifdef FASTUIDRAW_DEBUG
751 {
752 FASTUIDRAWassert(m_tile_allocated(v.x(), v.y(), v.z()).m_value);
753 m_tile_allocated(v.x(), v.y(), v.z()) = false;
754 }
755 #endif
756
757 --m_tile_count;
758 m_free_tiles.push_back(v);
759}
760
761int
762tile_allocator::
763number_free(void) const
764{
765 return m_num_tiles.x() * m_num_tiles.y() * m_num_tiles.z() - m_tile_count;
766}
767
768bool
769tile_allocator::
770resize_to_fit(int num_tiles)
771{
772 if (num_tiles > number_free())
773 {
774 /* resize to fit the number of needed tiles,
775 * compute how many more tiles needed and from
776 * there compute how many more layers needed.
777 */
778 int needed_tiles, tiles_per_layer, needed_layers;
779 needed_tiles = num_tiles - number_free();
780 tiles_per_layer = m_num_tiles.x() * m_num_tiles.y();
781 needed_layers = needed_tiles / tiles_per_layer;
782 if (needed_tiles > needed_layers * tiles_per_layer)
783 {
784 ++needed_layers;
785 }
786
787 /* TODO:
788 * Should we resize at powers of 2, or just to what is
789 * needed?
790 */
791 m_num_tiles.z() += needed_layers;
792 #ifdef FASTUIDRAW_DEBUG
793 {
794 m_tile_allocated.resize(m_num_tiles.x(), m_num_tiles.y(), m_num_tiles.z());
795 }
796 #endif
797
798 return true;
799 }
800 else
801 {
802 return false;
803 }
804}
805
806/////////////////////////////////////////
807// ImageAtlasPrivate methods
808int
809ImageAtlasPrivate::
810number_free_index_tiles(void)
811{
812 std::lock_guard<std::mutex> M(m_mutex);
813 return m_index_tiles.number_free();
814}
815
816fastuidraw::ivec3
817ImageAtlasPrivate::
818add_index_tile(fastuidraw::c_array<const fastuidraw::ivec3> data)
819{
820 fastuidraw::ivec3 return_value;
821 std::lock_guard<std::mutex> M(m_mutex);
822
823 return_value = m_index_tiles.allocate_tile();
824 if (return_value != fastuidraw::ivec3(-1, -1, -1))
825 {
826 m_index_store->set_data(return_value.x() * m_index_tiles.tile_size(),
827 return_value.y() * m_index_tiles.tile_size(),
828 return_value.z(),
829 m_index_tiles.tile_size(),
830 m_index_tiles.tile_size(),
831 data);
832 }
833
834 return return_value;
835}
836
837void
838ImageAtlasPrivate::
839delete_index_tile(fastuidraw::ivec3 tile)
840{
841 std::lock_guard<std::mutex> M(m_mutex);
842 m_index_tiles.delete_tile(tile);
843}
844
845int
846ImageAtlasPrivate::
847number_free_color_tiles(void)
848{
849 std::lock_guard<std::mutex> M(m_mutex);
850 return m_color_tiles.number_free();
851}
852
853fastuidraw::ivec3
854ImageAtlasPrivate::
855add_color_tile(fastuidraw::u8vec4 color_data)
856{
857 fastuidraw::ivec3 return_value;
858 fastuidraw::ivec2 dst_xy;
859 int sz;
860
861 std::lock_guard<std::mutex> M(m_mutex);
862 return_value = m_color_tiles.allocate_tile();
863 if (return_value != fastuidraw::ivec3(-1, -1, -1))
864 {
865 dst_xy.x() = return_value.x() * m_color_tiles.tile_size();
866 dst_xy.y() = return_value.y() * m_color_tiles.tile_size();
867 sz = m_color_tiles.tile_size();
868
869 for (int level = 0; sz > 0; ++level, sz /= 2, dst_xy /= 2)
870 {
871 m_color_store->set_data(level, dst_xy, return_value.z(),
872 sz, color_data);
873 }
874 }
875
876 return return_value;
877}
878
879fastuidraw::ivec3
880ImageAtlasPrivate::
881add_color_tile(fastuidraw::ivec2 src_xy,
882 const fastuidraw::ImageSourceBase &image_data)
883{
884 fastuidraw::ivec3 return_value;
885 fastuidraw::ivec2 dst_xy;
886 int sz, level, end_level;
887
888 std::lock_guard<std::mutex> M(m_mutex);
889 return_value = m_color_tiles.allocate_tile();
890 if (return_value != fastuidraw::ivec3(-1, -1, -1))
891 {
892 dst_xy.x() = return_value.x() * m_color_tiles.tile_size();
893 dst_xy.y() = return_value.y() * m_color_tiles.tile_size();
894 sz = m_color_tiles.tile_size();
895 end_level = image_data.number_levels();
896
897 for (level = 0; level < end_level && sz > 0; ++level, sz /= 2, dst_xy /= 2, src_xy /= 2)
898 {
899 m_color_store->set_data(level, dst_xy, return_value.z(), src_xy, sz, image_data);
900 }
901
902 for (; sz > 0; ++level, sz /= 2, dst_xy /= 2, src_xy /= 2, sz /= 2)
903 {
904 m_color_store->set_data(level, dst_xy, return_value.z(), sz,
905 fastuidraw::u8vec4(255u, 255u, 0u, 255u));
906 }
907 }
908
909 return return_value;
910}
911
912void
913ImageAtlasPrivate::
914delete_color_tile(fastuidraw::ivec3 tile)
915{
916 std::lock_guard<std::mutex> M(m_mutex);
917 m_color_tiles.delete_tile(tile);
918}
919
920void
921ImageAtlasPrivate::
922resize_to_fit(int num_color_tiles, int num_index_tiles)
923{
924 std::lock_guard<std::mutex> M(m_mutex);
925 if (m_color_tiles.resize_to_fit(num_color_tiles))
926 {
927 m_color_store->resize(m_color_tiles.num_tiles().z());
928 }
929 if (m_index_tiles.resize_to_fit(num_index_tiles))
930 {
931 m_index_store->resize(m_index_tiles.num_tiles().z());
932 }
933}
934
935////////////////////////////////////////
936// fastuidraw::ImageSourceCArray methods
937fastuidraw::ImageSourceCArray::
938ImageSourceCArray(uvec2 dimensions,
939 c_array<const c_array<const u8vec4> > pdata,
940 enum Image::format_t fmt):
941 m_dimensions(dimensions),
942 m_data(pdata),
943 m_format(fmt)
944{}
945
946bool
947fastuidraw::ImageSourceCArray::
948all_same_color(ivec2 location, int square_size, u8vec4 *dst) const
949{
950 location.x() = t_max(location.x(), 0);
951 location.y() = t_max(location.y(), 0);
952
953 location.x() = t_min(int(m_dimensions.x()) - 1, location.x());
954 location.y() = t_min(int(m_dimensions.y()) - 1, location.y());
955
956 square_size = t_min(int(m_dimensions.x()) - location.x(), square_size);
957 square_size = t_min(int(m_dimensions.y()) - location.y(), square_size);
958
959 *dst = m_data[0][location.x() + location.y() * m_dimensions.x()];
960 for (int y = 0, sy = location.y(); y < square_size; ++y, ++sy)
961 {
962 for (int x = 0, sx = location.x(); x < square_size; ++x, ++sx)
963 {
964 if (*dst != m_data[0][sx + sy * m_dimensions.x()])
965 {
966 return false;
967 }
968 }
969 }
970 return true;
971}
972
973unsigned int
974fastuidraw::ImageSourceCArray::
975number_levels(void) const
976{
977 return m_data.size();
978}
979
980void
981fastuidraw::ImageSourceCArray::
982fetch_texels(unsigned int mipmap_level, ivec2 location,
983 unsigned int w, unsigned int h,
984 c_array<u8vec4> dst) const
985{
986 if (mipmap_level >= m_data.size())
987 {
988 std::fill(dst.begin(), dst.end(), u8vec4(255u, 255u, 0u, 255u));
989 }
990 else
991 {
992 copy_sub_data(dst, w, h,
993 m_data[mipmap_level],
994 location.x(), location.y(),
995 ivec2(m_dimensions.x() >> mipmap_level,
996 m_dimensions.y() >> mipmap_level));
997 }
998}
999
1000enum fastuidraw::Image::format_t
1001fastuidraw::ImageSourceCArray::
1002format(void) const
1003{
1004 return m_format;
1005}
1006
1007//////////////////////////////////////////////////
1008// fastuidraw::AtlasColorBackingStoreBase methods
1009fastuidraw::AtlasColorBackingStoreBase::
1010AtlasColorBackingStoreBase(ivec3 whl)
1011{
1012 m_d = FASTUIDRAWnew BackingStorePrivate(whl);
1013}
1014
1015fastuidraw::AtlasColorBackingStoreBase::
1016AtlasColorBackingStoreBase(int w, int h, int num_layers)
1017{
1018 m_d = FASTUIDRAWnew BackingStorePrivate(w, h, num_layers);
1019}
1020
1021fastuidraw::AtlasColorBackingStoreBase::
1022~AtlasColorBackingStoreBase()
1023{
1024 BackingStorePrivate *d;
1025 d = static_cast<BackingStorePrivate*>(m_d);
1026 FASTUIDRAWdelete(d);
1027 m_d = nullptr;
1028}
1029
1030fastuidraw::ivec3
1031fastuidraw::AtlasColorBackingStoreBase::
1032dimensions(void) const
1033{
1034 BackingStorePrivate *d;
1035 d = static_cast<BackingStorePrivate*>(m_d);
1036 return d->m_dimensions;
1037}
1038
1039void
1040fastuidraw::AtlasColorBackingStoreBase::
1041resize(int new_num_layers)
1042{
1043 BackingStorePrivate *d;
1044
1045 d = static_cast<BackingStorePrivate*>(m_d);
1046 FASTUIDRAWassert(new_num_layers > d->m_dimensions.z());
1047 resize_implement(new_num_layers);
1048 d->m_dimensions.z() = new_num_layers;
1049}
1050
1051///////////////////////////////////////////////
1052// fastuidraw::AtlasIndexBackingStoreBase methods
1053fastuidraw::AtlasIndexBackingStoreBase::
1054AtlasIndexBackingStoreBase(ivec3 whl)
1055{
1056 m_d = FASTUIDRAWnew BackingStorePrivate(whl);
1057}
1058
1059fastuidraw::AtlasIndexBackingStoreBase::
1060AtlasIndexBackingStoreBase(int w, int h, int l)
1061{
1062 m_d = FASTUIDRAWnew BackingStorePrivate(w, h, l);
1063}
1064
1065fastuidraw::AtlasIndexBackingStoreBase::
1066~AtlasIndexBackingStoreBase()
1067{
1068 BackingStorePrivate *d;
1069 d = static_cast<BackingStorePrivate*>(m_d);
1070 FASTUIDRAWdelete(d);
1071 m_d = nullptr;
1072}
1073
1074fastuidraw::ivec3
1075fastuidraw::AtlasIndexBackingStoreBase::
1076dimensions(void) const
1077{
1078 BackingStorePrivate *d;
1079 d = static_cast<BackingStorePrivate*>(m_d);
1080 return d->m_dimensions;
1081}
1082
1083void
1084fastuidraw::AtlasIndexBackingStoreBase::
1085resize(int new_num_layers)
1086{
1087 BackingStorePrivate *d;
1088
1089 d = static_cast<BackingStorePrivate*>(m_d);
1090 FASTUIDRAWassert(new_num_layers > d->m_dimensions.z());
1091 resize_implement(new_num_layers);
1092 d->m_dimensions.z() = new_num_layers;
1093}
1094
1095//////////////////////////////
1096// fastuidraw::ImageAtlas methods
1097fastuidraw::ImageAtlas::
1098ImageAtlas(int pcolor_tile_size, int pindex_tile_size,
1099 fastuidraw::reference_counted_ptr<AtlasColorBackingStoreBase> pcolor_store,
1100 fastuidraw::reference_counted_ptr<AtlasIndexBackingStoreBase> pindex_store)
1101{
1102 m_d = FASTUIDRAWnew ImageAtlasPrivate(pcolor_tile_size, pindex_tile_size,
1103 pcolor_store, pindex_store);
1104}
1105
1106fastuidraw::ImageAtlas::
1107~ImageAtlas()
1108{
1109 ImageAtlasPrivate *d;
1110 d = static_cast<ImageAtlasPrivate*>(m_d);
1111 FASTUIDRAWdelete(d);
1112 m_d = nullptr;
1113}
1114
1115void
1116fastuidraw::ImageAtlas::
1117lock_resources(void)
1118{
1119 ImageAtlasPrivate *d;
1120 d = static_cast<ImageAtlasPrivate*>(m_d);
1121
1122 std::lock_guard<std::mutex> M(d->m_mutex);
1123 d->m_color_tiles.lock_resources();
1124 d->m_index_tiles.lock_resources();
1125 d->m_delete_actions.lock_resources();
1126}
1127
1128void
1129fastuidraw::ImageAtlas::
1130unlock_resources(void)
1131{
1132 ImageAtlasPrivate *d;
1133 d = static_cast<ImageAtlasPrivate*>(m_d);
1134
1135 std::lock_guard<std::mutex> M(d->m_mutex);
1136 d->m_color_tiles.unlock_resources();
1137 d->m_index_tiles.unlock_resources();
1138 d->m_delete_actions.unlock_resources();
1139}
1140
1141int
1142fastuidraw::ImageAtlas::
1143color_tile_size(void) const
1144{
1145 ImageAtlasPrivate *d;
1146 d = static_cast<ImageAtlasPrivate*>(m_d);
1147 return d->color_tile_size();
1148}
1149
1150int
1151fastuidraw::ImageAtlas::
1152index_tile_size(void) const
1153{
1154 ImageAtlasPrivate *d;
1155 d = static_cast<ImageAtlasPrivate*>(m_d);
1156 return d->index_tile_size();
1157}
1158
1159void
1160fastuidraw::ImageAtlas::
1161flush(void) const
1162{
1163 ImageAtlasPrivate *d;
1164 d = static_cast<ImageAtlasPrivate*>(m_d);
1165 std::lock_guard<std::mutex> M(d->m_mutex);
1166
1167 if (d->m_index_store)
1168 {
1169 d->m_index_store->flush();
1170 }
1171
1172 if (d->m_color_store)
1173 {
1174 d->m_color_store->flush();
1175 }
1176}
1177
1178const fastuidraw::reference_counted_ptr<const fastuidraw::AtlasColorBackingStoreBase>&
1179fastuidraw::ImageAtlas::
1180color_store(void) const
1181{
1182 ImageAtlasPrivate *d;
1183 d = static_cast<ImageAtlasPrivate*>(m_d);
1184 return d->m_color_store_constant;
1185}
1186
1187const fastuidraw::reference_counted_ptr<const fastuidraw::AtlasIndexBackingStoreBase>&
1188fastuidraw::ImageAtlas::
1189index_store(void) const
1190{
1191 ImageAtlasPrivate *d;
1192 d = static_cast<ImageAtlasPrivate*>(m_d);
1193 return d->m_index_store_constant;
1194}
1195
1196void
1197fastuidraw::ImageAtlas::
1198queue_resource_release_action(const reference_counted_ptr<Image::ResourceReleaseAction> &action)
1199{
1200 if (action)
1201 {
1202 ImageAtlasPrivate *d;
1203 d = static_cast<ImageAtlasPrivate*>(m_d);
1204
1205 std::lock_guard<std::mutex> M(d->m_mutex);
1206 d->m_delete_actions.add_action(action);
1207 }
1208}
1209
1210fastuidraw::reference_counted_ptr<fastuidraw::Image>
1211fastuidraw::ImageAtlas::
1212create_image_on_atlas(int w, int h, const ImageSourceBase &image_data)
1213{
1214 int tile_interior_size;
1215 ivec2 num_color_tiles;
1216 int index_tiles;
1217 ImageAtlasPrivate *d;
1218
1219 d = static_cast<ImageAtlasPrivate*>(m_d);
1220 if (w <= 0 || h <= 0 || !d->m_color_store || !d->m_index_store)
1221 {
1222 return reference_counted_ptr<Image>();
1223 }
1224
1225 tile_interior_size = color_tile_size();
1226 if (tile_interior_size <= 0)
1227 {
1228 return reference_counted_ptr<Image>();
1229 }
1230
1231 num_color_tiles = divide_up(ivec2(w, h), tile_interior_size);
1232 if (!enough_room_in_atlas(num_color_tiles, d, index_tiles))
1233 {
1234 /* TODO:
1235 * there actually might be enough room if we take into account
1236 * the savings from repeated tiles. The correct thing is to
1237 * delay this until iamge construction, check if it succeeded
1238 * and if not then delete it and return an invalid handle.
1239 */
1240 d->resize_to_fit(num_color_tiles.x() * num_color_tiles.y(), index_tiles);
1241 }
1242
1243 return FASTUIDRAWnew Image(*this, w, h, image_data);
1244}
1245
1246fastuidraw::reference_counted_ptr<fastuidraw::Image>
1247fastuidraw::ImageAtlas::
1248create_non_atlas(int w, int h, const ImageSourceBase &image_data)
1249{
1250 reference_counted_ptr<Image> return_value;
1251
1252 return_value = create_image_bindless(w, h, image_data);
1253 if (!return_value)
1254 {
1255 return_value = create_image_context_texture2d(w, h, image_data);
1256 }
1257 return return_value;
1258}
1259
1260fastuidraw::reference_counted_ptr<fastuidraw::Image>
1261fastuidraw::ImageAtlas::
1262create(int w, int h, const ImageSourceBase &image_data,
1263 enum Image::type_t type)
1264{
1265 reference_counted_ptr<Image> return_value;
1266 vecN<enum Image::type_t, 2> try_types;
1267
1268 try_types[0] = type;
1269 switch (type)
1270 {
1271 case Image::bindless_texture2d:
1272 try_types[1] = Image::on_atlas;
1273 break;
1274 case Image::on_atlas:
1275 try_types[1] = Image::bindless_texture2d;
1276 break;
1277 default:
1278 try_types[1] = type;
1279 }
1280
1281 for (unsigned int i = 0; i < 2 && !return_value; ++i)
1282 {
1283 if (try_types[i] == Image::bindless_texture2d)
1284 {
1285 return_value = create_image_bindless(w, h, image_data);
1286 }
1287 else if (try_types[i] == Image::on_atlas)
1288 {
1289 return_value = create_image_on_atlas(w, h, image_data);
1290 }
1291 }
1292
1293 if (return_value)
1294 {
1295 return return_value;
1296 }
1297
1298 return_value = create_image_context_texture2d(w, h, image_data);
1299 return return_value;
1300}
1301
1302//////////////////////////////////////
1303// fastuidraw::Image methods
1304fastuidraw::Image::
1305Image(ImageAtlas &patlas, int w, int h, unsigned int m,
1306 enum type_t type, uint64_t handle, enum format_t fmt,
1307 const reference_counted_ptr<Image::ResourceReleaseAction> &action)
1308{
1309 ImageAtlasPrivate *atlas_private;
1310 atlas_private = static_cast<ImageAtlasPrivate*>(patlas.m_d);
1311 m_d = FASTUIDRAWnew ImagePrivate(patlas, atlas_private, w, h, m, type, handle, fmt, action);
1312}
1313
1314fastuidraw::Image::
1315Image(ImageAtlas &patlas, int w, int h,
1316 const ImageSourceBase &image_data)
1317{
1318 ImageAtlasPrivate *atlas_private;
1319 atlas_private = static_cast<ImageAtlasPrivate*>(patlas.m_d);
1320 m_d = FASTUIDRAWnew ImagePrivate(patlas, atlas_private, w, h, image_data);
1321}
1322
1323fastuidraw::Image::
1324~Image()
1325{
1326 ImagePrivate *d;
1327 d = static_cast<ImagePrivate*>(m_d);
1328 FASTUIDRAWdelete(d);
1329 m_d = nullptr;
1330}
1331
1332
1333unsigned int
1334fastuidraw::Image::
1335number_index_lookups(void) const
1336{
1337 ImagePrivate *d;
1338 d = static_cast<ImagePrivate*>(m_d);
1339 return d->m_number_index_lookups;
1340}
1341
1342fastuidraw::ivec2
1343fastuidraw::Image::
1344dimensions(void) const
1345{
1346 ImagePrivate *d;
1347 d = static_cast<ImagePrivate*>(m_d);
1348 return d->m_dimensions;
1349}
1350
1351unsigned int
1352fastuidraw::Image::
1353number_mipmap_levels(void) const
1354{
1355 ImagePrivate *d;
1356 d = static_cast<ImagePrivate*>(m_d);
1357 return d->m_number_levels;
1358}
1359
1360fastuidraw::ivec3
1361fastuidraw::Image::
1362master_index_tile(void) const
1363{
1364 ImagePrivate *d;
1365 d = static_cast<ImagePrivate*>(m_d);
1366 return d->m_master_index_tile;
1367}
1368
1369fastuidraw::vec2
1370fastuidraw::Image::
1371master_index_tile_dims(void) const
1372{
1373 ImagePrivate *d;
1374 d = static_cast<ImagePrivate*>(m_d);
1375 return d->m_master_index_tile_dims;
1376}
1377
1378float
1379fastuidraw::Image::
1380dimensions_index_divisor(void) const
1381{
1382 ImagePrivate *d;
1383 d = static_cast<ImagePrivate*>(m_d);
1384 return d->m_dimensions_index_divisor;
1385}
1386
1387enum fastuidraw::Image::type_t
1388fastuidraw::Image::
1389type(void) const
1390{
1391 ImagePrivate *d;
1392 d = static_cast<ImagePrivate*>(m_d);
1393 return d->m_type;
1394}
1395
1396uint64_t
1397fastuidraw::Image::
1398bindless_handle(void) const
1399{
1400 ImagePrivate *d;
1401 d = static_cast<ImagePrivate*>(m_d);
1402 return d->m_bindless_handle;
1403}
1404
1405enum fastuidraw::Image::format_t
1406fastuidraw::Image::
1407format(void) const
1408{
1409 ImagePrivate *d;
1410 d = static_cast<ImagePrivate*>(m_d);
1411 return d->m_format;
1412}
1413