QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgstextureatlasgenerator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgstextureatlasgenerator.cpp
3 --------------------------------------
4 Date : September 2025
5 Copyright : (C) 2025 by Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgscolorrampimpl.h"
19
20#include <QPainter>
21
22// rectpack2D library
23#include <finders_interface.h>
24
26
27class QgsTextureRect
28{
29 public:
30 QgsTextureRect( const rectpack2D::rect_xywh &rect, int id, const QImage &image = QImage() )
31 : rect( rect )
32 , id( id )
33 , image( image )
34 {
35 }
36
37 // get_rect must be implemented for rectpack2D compatibility:
38 auto &get_rect()
39 {
40 return rect;
41 }
42 const auto &get_rect() const
43 {
44 return rect;
45 }
46
47 QRect asQRect() const
48 {
49 return QRect( rect.x, rect.y, rect.w, rect.h );
50 }
51
52 rectpack2D::rect_xywh rect;
53 int id = 0;
54 QImage image;
55};
56
58
59
64
66{
67 return static_cast< int >( mRects.size() );
68}
69
70QRect QgsTextureAtlas::rect( int id ) const
71{
72 return mRects[id].asQRect();
73}
74
76{
77 if ( mAtlasSize.isEmpty() )
78 return QImage();
79
80 QImage res( mAtlasSize, QImage::Format_ARGB32_Premultiplied );
81 res.fill( Qt::transparent );
82
83 QPainter painter( &res );
84 for ( const QgsTextureRect &rect : mRects )
85 {
86 if ( !rect.image.isNull() )
87 {
88 painter.drawImage( rect.asQRect(), rect.image );
89 }
90 }
91 painter.end();
92
93 return res;
94}
95
97{
98 if ( mAtlasSize.isEmpty() )
99 return QImage();
100
101 QImage res( mAtlasSize, QImage::Format_ARGB32_Premultiplied );
102 res.fill( Qt::transparent );
103
104 QPainter painter( &res );
105 painter.setPen( Qt::NoPen );
107 ramp.setTotalColorCount( static_cast< int >( mRects.size() ) );
108 double index = 0;
109 for ( const QgsTextureRect &rect : mRects )
110 {
111 const QColor color = ramp.color( index / ( static_cast< int >( mRects.size() ) - 1 ) );
112 index += 1;
113 painter.setBrush( QBrush( color ) );
114 painter.drawRect( rect.asQRect() );
115 }
116 painter.end();
117
118 return res;
119}
120
121
122//
123// QgsTextureAtlasGenerator
124//
125
126QgsTextureAtlas QgsTextureAtlasGenerator::createFromRects( const QVector<QRect> &rectangles, int maxSide )
127{
128 std::vector< QgsTextureRect > rects;
129 rects.reserve( rectangles.size() );
130 int index = 0;
131 for ( const QRect &rect : rectangles )
132 {
133 rects.emplace_back( QgsTextureRect( rectpack2D::rect_xywh( 0, 0, rect.width(), rect.height() ), index++ ) );
134 }
135 return generateAtlas( std::move( rects ), maxSide );
136}
137
138QgsTextureAtlas QgsTextureAtlasGenerator::createFromImages( const QVector<QImage> &images, int maxSide )
139{
140 std::vector< QgsTextureRect > rects;
141 rects.reserve( images.size() );
142 int index = 0;
143 for ( const QImage &image : images )
144 {
145 rects.emplace_back( QgsTextureRect( rectpack2D::rect_xywh( 0, 0, image.width(), image.height() ), index++, image ) );
146 }
147 return generateAtlas( std::move( rects ), maxSide );
148}
149
150QgsTextureAtlas QgsTextureAtlasGenerator::generateAtlas( std::vector< QgsTextureRect > rects, int maxSide )
151{
152 using spacesType = rectpack2D::empty_spaces<false, rectpack2D::default_empty_spaces>;
153
154 bool result = true;
155 auto reportSuccessful = []( rectpack2D::rect_xywh & ) {
156 return rectpack2D::callback_result::CONTINUE_PACKING;
157 };
158
159 auto reportUnsuccessful = [&result]( rectpack2D::rect_xywh & ) {
160 result = false;
161 return rectpack2D::callback_result::ABORT_PACKING;
162 };
163
164 const auto discardStep = -4;
165
166 auto byWidth = []( const rectpack2D::rect_xywh *a, const rectpack2D::rect_xywh *b ) {
167 return a->w > b->w;
168 };
169
170 const rectpack2D::rect_wh resultSize = rectpack2D::find_best_packing<spacesType>(
171 rects,
172 rectpack2D::make_finder_input(
173 maxSide,
174 discardStep,
175 reportSuccessful,
176 reportUnsuccessful,
177 rectpack2D::flipping_option::DISABLED
178 ),
179 byWidth
180 );
181
182 if ( !result )
183 return QgsTextureAtlas();
184
185 // rectpack2D::find_best_packing will have rearranged rects. Sort it back to the original order
186 // so that we can retrieve the results by their original indices.
187 std::sort( rects.begin(), rects.end(), []( const QgsTextureRect &a, const QgsTextureRect &b ) {
188 return a.id < b.id;
189 } );
190
191 QgsTextureAtlas res;
192 res.mRects = std::move( rects );
193 res.mAtlasSize = QSize( resultSize.w, resultSize.h );
194 return res;
195}
A color ramp consisting of random colors, constrained within component ranges.
virtual void setTotalColorCount(int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
QColor color(double value) const override
Returns the color corresponding to a specified value.
static QgsTextureAtlas createFromRects(const QVector< QRect > &rectangles, int maxSide=1000)
Creates a texture atlas for a set of rectangles.
static QgsTextureAtlas createFromImages(const QVector< QImage > &images, int maxSide=1000)
Creates a texture atlas for a set of images.
Encapsulates a texture atlas.
int count() const
Returns the number of textures in the atlas.
QgsTextureAtlas & operator=(const QgsTextureAtlas &other)
QRect rect(int index) const
Returns the packed rectangle for the texture with the specified index.
QImage renderDebugTexture() const
Renders a debug texture.
QImage renderAtlasTexture() const
Renders the combined texture atlas, containing all source images.