]> git.sesse.net Git - casparcg/blob - core/producer/text/utils/texture_atlas.cpp
Misc modifications to fix problems found by static code analysis and some simplificat...
[casparcg] / core / producer / text / utils / texture_atlas.cpp
1 /* ============================================================================
2  * Freetype GL - A C OpenGL Freetype engine
3  * Platform:    Any
4  * WWW:         http://code.google.com/p/freetype-gl/
5  * ----------------------------------------------------------------------------
6  * Copyright 2011,2012 Nicolas P. Rougier. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  *  1. Redistributions of source code must retain the above copyright notice,
12  *     this list of conditions and the following disclaimer.
13  *
14  *  2. Redistributions in binary form must reproduce the above copyright
15  *     notice, this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY NICOLAS P. ROUGIER ''AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
21  * EVENT SHALL NICOLAS P. ROUGIER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * The views and conclusions contained in the software and documentation are
30  * those of the authors and should not be interpreted as representing official
31  * policies, either expressed or implied, of Nicolas P. Rougier.
32  * ============================================================================
33  */
34
35 #include <list>
36 #include <vector>
37 #include "texture_atlas.h"
38
39 namespace caspar { namespace core { namespace text {
40
41
42 struct texture_atlas::impl
43 {
44 private:
45         struct node
46         {
47                 int x;
48                 int y;
49                 int width;
50         };
51
52         typedef std::list<node> node_list;
53         typedef std::list<node>::iterator node_iterator;
54
55 public:
56         impl(const size_t width, const size_t height, const size_t depth) : width_(width), height_(height), depth_(depth), used_(0), data_(width*height*depth, 0)
57         {
58                 // We want a one pixel border around the whole atlas to avoid any artefact when sampling texture
59                 node n = {1, 1, (int)width_ - 2};
60                 nodes_.push_back(n);
61         }
62
63         rect get_region(int width, int height)
64         {
65                 rect region = {0,0,(int)width,(int)height};
66
67                 int best_height = INT_MAX;
68                 int best_width = INT_MAX;
69                 node_iterator best_it = nodes_.end();
70
71                 auto it = nodes_.begin();
72                 for(; it != nodes_.end(); ++it)
73                 {
74                         int y = fit(it, width, height);
75                         if( y >= 0 )
76                         {
77                                 if( ( (y + height) < best_height ) ||
78                                         ( ((y + height) == best_height) && ((*it).width < best_width)) )
79                                 {
80                                         best_height = y + height;
81                                         best_it = it;
82                                         best_width = (*it).width;
83                                         region.x = (*it).x;
84                                         region.y = (int)y;
85                                 }
86                         }
87                 }
88    
89                 if(best_it == nodes_.end())
90                 {
91                         region.x = -1;
92                         region.y = -1;
93                         region.width = 0;
94                         region.height = 0;
95                         return region;
96                 }
97
98                 node new_node;
99                 new_node.x = region.x;
100                 new_node.y = region.y + (int)height;
101                 new_node.width = (int)width;
102
103                 best_it = nodes_.insert(best_it, new_node);
104
105                 for(auto it = ++best_it; it != nodes_.end(); ++it)
106                 {
107                         auto prev = it; --prev;
108
109                         if ((*it).x < ((*prev).x + (*prev).width) )
110                         {
111                                 int shrink = (*prev).x + (*prev).width - (*it).x;
112                                 (*it).x += shrink;
113                                 (*it).width -= shrink;
114                                 if ((*it).width <= 0)
115                                 {
116                                         nodes_.erase(it);
117                                         it = prev;
118                                 }
119                                 else
120                                         break;
121                         }
122                         else
123                                 break;
124                 }
125
126                 merge();
127                 used_ += width * height;
128                 return region;
129         }
130
131         //the data parameter points to bitmap-data that is 8-bit grayscale.
132         void set_region(const size_t x, const size_t y, const size_t width, const size_t height, const unsigned char *src, const size_t stride, const color<float>& col)
133         {
134                 //this assumes depth_ is set to 4
135                 for(size_t i=0; i<height; ++i)
136                 {
137                         for(size_t j=0; j<width; ++j)
138                         {
139                                 unsigned char* pixel = &data_[((y+i)*width_ + x + j)*depth_];
140                                 unsigned char value = src[i*stride + j];
141                                 pixel[0] = (unsigned char)(value*col.b);
142                                 pixel[1] = (unsigned char)(value*col.g);
143                                 pixel[2] = (unsigned char)(value*col.r);
144                                 pixel[3] = (unsigned char)(value*col.a);
145                         }
146                 }
147         }
148
149         void clear()
150         {
151                 nodes_.clear();
152                 used_ = 0;
153
154                 node n = {1,1,(int)width_-2};
155                 nodes_.push_back(n);
156                 data_.assign(width_*height_*depth_, 0);
157         }
158
159         size_t depth() { return depth_; }
160         size_t width() { return width_; }
161         size_t height() { return height_; }
162         unsigned char* data() { return data_.data(); }
163
164 private:
165         int fit(node_iterator it, const size_t width, const size_t height)
166         {
167                 int x = (*it).x;
168                 int y = (*it).y;
169                 int width_left = (int)width;
170
171                 if ((x + width) > (width_ - 1))
172                         return -1;
173
174                 while( width_left > 0 && it != nodes_.end())
175                 {
176                         if((*it).y > y)
177                                 y = (*it).y;
178                         if((y + height) > (height_ - 1))
179                                 return -1;
180
181                         width_left -= (*it).width;
182                         ++it;
183                 }
184
185                 return y;
186         }
187
188         void merge()
189         {
190                 auto it = nodes_.begin();
191                 while(true)
192                 {
193                         auto next = it; ++next;
194                         if(next == nodes_.end())
195                                 break;
196
197                         if((*it).y == (*next).y)
198                         {
199                                 (*it).width += (*next).width;
200                                 nodes_.erase(next);
201                         }
202                         else
203                                 ++it;
204                 }
205         }
206
207         node_list nodes_;
208
209         size_t width_;
210         size_t height_;
211         size_t depth_;
212
213         std::vector<unsigned char> data_;
214         size_t used_;
215 };
216
217 texture_atlas::texture_atlas(const size_t w, const size_t h, const size_t d) : impl_(new impl(w, h, d)) {}
218 rect texture_atlas::get_region(int width, int height) { return impl_->get_region(width, height); }
219 void texture_atlas::set_region(const size_t x, const size_t y, const size_t width, const size_t height, const unsigned char *src, const size_t stride, const color<float>& col)
220         { impl_->set_region(x, y, width, height, src, stride, col); }
221
222 void texture_atlas::clear() { impl_->clear(); }
223
224 size_t texture_atlas::width() { return impl_->width(); }
225 size_t texture_atlas::height() { return impl_->height(); }
226 size_t texture_atlas::depth() { return impl_->depth(); }
227 unsigned char* texture_atlas::data() { return impl_->data(); }
228
229 }}}