1 /*
2  * Copyright (C) 2006-2019 Christopho, Solarus - http://www.solarus-games.org
3  *
4  * Solarus is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Solarus is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 #include "solarus/core/Debug.h"
18 #include "solarus/core/PixelBits.h"
19 #include "solarus/core/Rectangle.h"
20 #include "solarus/core/System.h"
21 #include "solarus/graphics/Surface.h"
22 #include <SDL.h>
23 #include <algorithm>
24 #include <iostream> // print functions
25 
26 namespace Solarus {
27 
28 /**
29  * \brief Creates a pixel bits object.
30  * \param surface The surface where the image is.
31  * \param image_position Position of the image on this surface.
32  */
PixelBits(const Surface & surface,const Rectangle & image_position)33 PixelBits::PixelBits(const Surface& surface, const Rectangle& image_position):
34   width(0),
35   height(0),
36   nb_integers_per_row(0),
37   bits() {
38 
39   // Create a list of boolean values representing the transparency of each pixel.
40   // This list is implemented as bit fields.
41 
42   // Clip the rectangle passed as parameter.
43   const Rectangle clipped_image_position(
44       image_position.get_intersection(Rectangle(surface.get_size()))
45   );
46 
47   if (clipped_image_position.is_flat()) {
48     return;
49   }
50 
51   width = clipped_image_position.get_width();
52   height = clipped_image_position.get_height();
53 
54   nb_integers_per_row = width >> 5; // width / 32
55   if ((width & 31) != 0) { // width % 32 != 0
56     nb_integers_per_row++;
57   }
58 
59   int pixel_index = clipped_image_position.get_y() * surface.get_width() + clipped_image_position.get_x();
60 
61   bits.resize(height);
62   for (int i = 0; i < height; ++i) {
63     bits[i].resize(nb_integers_per_row);
64 
65     // Fill the bits for this row, using nb_integers_per_row sequences of 32 bits.
66     int k = -1;
67     uint32_t mask = 0x00000000;  // Current bit in the sequence of 32 bits.
68     for (int j = 0; j < width; ++j) {
69       if (mask == 0x00000000) {
70         // Time for a new sequence of 32 bits.
71         k++;
72         mask = 0x80000000;
73         bits[i][k] = 0x00000000;  // Initialize the sequence to transparent.
74       }
75 
76       // If the pixel is opaque.
77       if (!surface.is_pixel_transparent(pixel_index)) {
78         bits[i][k] |= mask;
79       }
80 
81       mask >>= 1;
82       ++pixel_index;
83     }
84     pixel_index += surface.get_width() - width;
85   }
86 }
87 
88 /**
89  * \brief Detects whether the image represented by these pixel bits is
90  * overlapping another image.
91  * \param other The other image.
92  * \param location1 Position of the upper-left corner of this image on the map.
93  * \param location2 Position of the upper-left corner of the other image.
94  * \return \c true if there is a collision.
95  */
test_aligned_collision(const PixelBits & other,const Point & location1,const Point & location2) const96 bool PixelBits::test_aligned_collision(const PixelBits& other,
97     const Point &location1,
98     const Point &location2
99 ) const {
100   const bool debug_pixel_collisions = false;
101 
102   if (bits.empty()) {
103     // No image.
104     return false;
105   }
106 
107   // Compute both bounding boxes.
108   const Rectangle bounding_box1(location1.x, location1.y, width, height);
109   const Rectangle bounding_box2(location2.x, location2.y, other.width, other.height);
110 
111   // Check collision between the two bounding boxes.
112   if (!bounding_box1.overlaps(bounding_box2)) {
113     return false;
114   }
115 
116   if (debug_pixel_collisions) {
117     std::cout << System::now() << "\n bounding box collision\n";
118     std::cout << "rect1 = " << bounding_box1 << "\n";
119     std::cout << "rect2 = " << bounding_box2 << "\n";
120     print();
121     other.print();
122   }
123 
124   // Compute the intersection between both rectangles.
125   const int intersection_x = std::max(bounding_box1.get_x(), bounding_box2.get_x());
126   const int intersection_y = std::max(bounding_box1.get_y(), bounding_box2.get_y());
127   const Rectangle intersection(
128       intersection_x,
129       intersection_y,
130       std::min(bounding_box1.get_x() + bounding_box1.get_width(),
131           bounding_box2.get_x() + bounding_box2.get_width()) - intersection_x,
132       std::min(bounding_box1.get_y() + bounding_box1.get_height(),
133           bounding_box2.get_y() + bounding_box2.get_height()) - intersection_y);
134 
135   if (debug_pixel_collisions) {
136     std::cout << "intersection: " << intersection << "\n";
137   }
138 
139   // compute the relative position of the intersection rectangle for each bounding_box
140   Point offset1 = intersection.get_xy() - bounding_box1.get_xy();
141   Point offset2 = intersection.get_xy() - bounding_box2.get_xy();
142 
143   if (debug_pixel_collisions) {
144     std::cout << "offset1.x = " << offset1.x << ", offset1.y = " << offset1.y;
145     std::cout << ", offset2.x = " << offset2.x << ", offset2.y = " << offset2.y << std::endl;
146   }
147 
148   // For each row of the intersection, we will call row 'a' the row coming from the right bounding box
149   // and row 'b' the one coming from the left bounding box.
150 
151   std::vector<std::vector<uint32_t>>::const_iterator rows_a;           // for each row: array of masks of the right bounding box
152   std::vector<std::vector<uint32_t>>::const_iterator rows_b;           // for each row: array of masks of the left bounding box
153 
154   int nb_masks_per_row_a;    // number of masks on row a that are in the intersection (row b may have one more mask)
155   int nb_masks_per_row_b;    // number of masks on row b that are in the intersection
156   int nb_unused_masks_row_b; // number of unused masks on row b (i.e. before the intersection)
157   int nb_unused_bits_row_b;  // number of unused bits on the first used mask of row b
158   int nb_used_bits_row_b;    // number of bits used on the first used mask of row b
159 
160   // make sure row a starts after row b
161   if (bounding_box1.get_x() > bounding_box2.get_x()) {
162     rows_a = this->bits.begin() + offset1.y;
163     rows_b = other.bits.begin() + offset2.y;
164     nb_unused_masks_row_b = offset2.x >> 5;
165     nb_unused_bits_row_b = offset2.x & 31;
166   }
167   else {
168     rows_a = other.bits.begin() + offset2.y;
169     rows_b = this->bits.begin() + offset1.y;
170     nb_unused_masks_row_b = offset1.x >> 5;
171     nb_unused_bits_row_b = offset1.x & 31;
172   }
173   nb_used_bits_row_b = 32 - nb_unused_bits_row_b;
174 
175   // compute the number of masks in the intersection on row a
176   nb_masks_per_row_a = intersection.get_width() >> 5;
177   if ((intersection.get_width() & 31) != 0) {
178     nb_masks_per_row_a++;
179   }
180 
181   // compute the number of masks in the intersection on row b
182   nb_masks_per_row_b = (intersection.get_width() + nb_unused_bits_row_b) >> 5;
183   if (((intersection.get_width() + nb_unused_bits_row_b) & 31) != 0) {
184     nb_masks_per_row_b++;
185   }
186 
187   // check the collisions each row of the intersection rectangle
188   for (int i = 0; i < intersection.get_height(); ++i) {
189 
190     // current row
191     const std::vector<uint32_t>& bits_a = *rows_a;
192     const std::vector<uint32_t>& bits_b = *rows_b;
193 
194     ++rows_a;
195     ++rows_b;
196 
197     if (debug_pixel_collisions) {
198       std::cout << "*** checking row " << i << " of the intersection rectangle\n";
199     }
200 
201     // Check each mask.
202     for (int j = 0; j < nb_masks_per_row_a; ++j) {
203 
204       // We compare the left of a with the right part of b,
205       // and the right part of a with the right part of the next b if any.
206 
207       uint32_t mask_a = bits_a[j];
208       uint32_t mask_b = bits_b[j + nb_unused_masks_row_b];
209       uint32_t mask_a_left = mask_a >> nb_unused_bits_row_b;
210       uint32_t next_mask_b_left = 0x00000000;
211       if (j + 1 < nb_masks_per_row_a ||            // Not the last a-mask.
212           nb_masks_per_row_b > nb_masks_per_row_a  // Last a-mask but there exists one more b-mask.
213           ) {
214         // We are still inside the intersection: next_mask_b_left exists.
215         next_mask_b_left = bits_b[j + nb_unused_masks_row_b + 1] >> nb_used_bits_row_b;
216       }
217 
218       if (debug_pixel_collisions) {
219         std::cout << "mask_a = ";
220         print_mask(mask_a);
221         std::cout << ", mask b = ";
222         print_mask(mask_b);
223         std::cout << "\n";
224       }
225 
226       if (((mask_a_left & mask_b) | (mask_a & next_mask_b_left)) != 0x00000000) {
227         return true;
228       }
229     }
230   }
231 
232   return false;
233 }
234 
235 /**
236  * @brief test collision of this pixmap with another one
237  * @param other other PixelBits
238  * @param transform1 transform of this pixel bits
239  * @param transform2 transform of the other pixel bits
240  * @return true if pixel bits intersects
241  *
242  * Both pixelbits are potentially transformed, if one of them is not trivially translated
243  * a slower algorithm is used where one of the pixelbits is projected on the other
244  */
test_collision(const PixelBits & other,const Transform & transform1,const Transform & transform2) const245 bool PixelBits::test_collision(const PixelBits &other,
246                     const Transform &transform1, const Transform &transform2) const {
247   if(transform1.aligned() && transform2.aligned()) {
248     return test_aligned_collision(other,transform1.position,transform2.position);
249   } else {
250     return test_oriented_collision(other,transform1,transform2);
251   }
252 }
253 
254 /**
255  * @brief slower collision test taking rotation and scale into account
256  * @param other other pixelbits
257  * @param transform1 transform applied to this pixelbits
258  * @param transform2 transform applied to the other pixel bits
259  * @return
260  */
test_oriented_collision(const PixelBits & other,const Transform & transform1,const Transform & transform2) const261 bool PixelBits::test_oriented_collision(const PixelBits &other,
262                              const Transform& transform1,
263                              const Transform& transform2) const {
264   if(!transform1.obb_intersect(Size(width,height),transform2,Size(other.width,other.height))) {
265     return false;
266   }
267   glm::vec2 origin,vx,vy;
268   transform1.compute_collision_data(transform2,origin,vx,vy);
269   for(int y = 0; y < height; y++) {
270     for(int x = 0; x < width; x++) {
271       if(at(x,y)){
272         glm::vec2 p = origin+vx*(float)x+vy*(float)y;
273         int ox = p.x;
274         int oy = p.y;
275 
276         if(other.at(ox,oy)) {
277           return true;
278         }
279       }
280     }
281   }
282   return false;
283 }
284 
285 /**
286  * @brief access the pixel at given coords
287  * @param x x coord in the pixel map
288  * @param y y coord in the pixel map
289  * @return
290  */
at(int x,int y) const291 bool PixelBits::at(int x, int y) const {
292   if(x < 0 || y < 0 || x >= width || y >= height) {
293     return false;
294   }
295   const auto& row = bits.at(y);
296   int index = x / 32;
297   int offset = x % 32;
298   return (row[index] << offset) & 0x80000000;
299 }
300 
301 /**
302  * \brief Prints an ASCII representation of the pixels (for debugging purposes only).
303  */
print() const304 void PixelBits::print() const {
305 
306   std::cout << "frame size is " << width << " x " << height << std::endl;
307   for (int i = 0; i < height; i++) {
308     for (int j = 0; j < width; j++) {
309       if (at(j,i)) {
310         std::cout << "X ";
311       }
312       else {
313         std::cout << ". ";
314       }
315     }
316     std::cout << std::endl;
317   }
318 }
319 
320 /**
321  * \brief Prints an ASCII representation of a 32-bit mask (for debugging purposes only).
322  */
print_mask(uint32_t mask) const323 void PixelBits::print_mask(uint32_t mask) const {
324 
325   for (int i = 0; i < 32; i++) {
326     std::cout << (((mask & 0x80000000) != 0x00000000) ? "X" : ".");
327     mask <<= 1;
328   }
329 }
330 
331 }
332 
333