@@ -19,63 +19,134 @@ with this program; if not, write to the Free Software Foundation, Inc.,
19
19
#include " imagefilters.h"
20
20
#include " util/numeric.h"
21
21
#include < cmath>
22
+ #include < cassert>
23
+ #include < vector>
24
+
25
+ // Simple 2D bitmap class with just the functionality needed here
26
+ class Bitmap {
27
+ u32 linesize, lines;
28
+ std::vector<u8> data;
29
+
30
+ static inline u32 bytepos (u32 index) { return index >> 3 ; }
31
+ static inline u8 bitpos (u32 index) { return index & 7 ; }
32
+
33
+ public:
34
+ Bitmap (u32 width, u32 height) : linesize(width), lines(height),
35
+ data (bytepos(width * height) + 1) {}
36
+
37
+ inline bool get (u32 x, u32 y) const {
38
+ u32 index = y * linesize + x;
39
+ return data[bytepos (index )] & (1 << bitpos (index ));
40
+ }
41
+
42
+ inline void set (u32 x, u32 y) {
43
+ u32 index = y * linesize + x;
44
+ data[bytepos (index )] |= 1 << bitpos (index );
45
+ }
46
+
47
+ inline bool all () const {
48
+ for (u32 i = 0 ; i < data.size () - 1 ; i++) {
49
+ if (data[i] != 0xff )
50
+ return false ;
51
+ }
52
+ // last byte not entirely filled
53
+ for (u8 i = 0 ; i < bitpos (linesize * lines); i++) {
54
+ bool value_of_bit = data.back () & (1 << i);
55
+ if (!value_of_bit)
56
+ return false ;
57
+ }
58
+ return true ;
59
+ }
60
+
61
+ inline void copy (Bitmap &to) const {
62
+ assert (to.linesize == linesize && to.lines == lines);
63
+ to.data = data;
64
+ }
65
+ };
22
66
23
67
/* Fill in RGB values for transparent pixels, to correct for odd colors
24
68
* appearing at borders when blending. This is because many PNG optimizers
25
69
* like to discard RGB values of transparent pixels, but when blending then
26
- * with non-transparent neighbors, their RGB values will shpw up nonetheless.
70
+ * with non-transparent neighbors, their RGB values will show up nonetheless.
27
71
*
28
72
* This function modifies the original image in-place.
29
73
*
30
74
* Parameter "threshold" is the alpha level below which pixels are considered
31
- * transparent. Should be 127 for 3d where alpha is threshold, but 0 for
32
- * 2d where alpha is blended .
75
+ * transparent. Should be 127 when the texture is used with ALPHA_CHANNEL_REF,
76
+ * 0 when alpha blending is used .
33
77
*/
34
78
void imageCleanTransparent (video::IImage *src, u32 threshold)
35
79
{
36
80
core::dimension2d<u32> dim = src->getDimension ();
37
81
38
- // Walk each pixel looking for fully transparent ones.
82
+ Bitmap bitmap (dim.Width , dim.Height );
83
+
84
+ // First pass: Mark all opaque pixels
39
85
// Note: loop y around x for better cache locality.
40
86
for (u32 ctry = 0 ; ctry < dim.Height ; ctry++)
41
87
for (u32 ctrx = 0 ; ctrx < dim.Width ; ctrx++) {
88
+ if (src->getPixel (ctrx, ctry).getAlpha () > threshold)
89
+ bitmap.set (ctrx, ctry);
90
+ }
91
+
92
+ // Exit early if all pixels opaque
93
+ if (bitmap.all ())
94
+ return ;
95
+
96
+ Bitmap newmap = bitmap;
97
+
98
+ // Then repeatedly look for transparent pixels, filling them in until
99
+ // we're finished (capped at 50 iterations).
100
+ for (u32 iter = 0 ; iter < 50 ; iter++) {
42
101
43
- // Ignore opaque pixels.
44
- irr::video::SColor c = src->getPixel (ctrx, ctry);
45
- if (c.getAlpha () > threshold)
102
+ for (u32 ctry = 0 ; ctry < dim.Height ; ctry++)
103
+ for (u32 ctrx = 0 ; ctrx < dim.Width ; ctrx++) {
104
+ // Skip pixels we have already processed
105
+ if (bitmap.get (ctrx, ctry))
46
106
continue ;
47
107
48
- // Sample size and total weighted r, g, b values.
108
+ video::SColor c = src->getPixel (ctrx, ctry);
109
+
110
+ // Sample size and total weighted r, g, b values
49
111
u32 ss = 0 , sr = 0 , sg = 0 , sb = 0 ;
50
112
51
- // Walk each neighbor pixel (clipped to image bounds).
113
+ // Walk each neighbor pixel (clipped to image bounds)
52
114
for (u32 sy = (ctry < 1 ) ? 0 : (ctry - 1 );
53
115
sy <= (ctry + 1 ) && sy < dim.Height ; sy++)
54
116
for (u32 sx = (ctrx < 1 ) ? 0 : (ctrx - 1 );
55
117
sx <= (ctrx + 1 ) && sx < dim.Width ; sx++) {
56
-
57
- // Ignore transparent pixels.
58
- irr::video::SColor d = src->getPixel (sx, sy);
59
- if (d.getAlpha () <= threshold)
118
+ // Ignore pixels we haven't processed
119
+ if (!bitmap.get (sx, sy))
60
120
continue ;
61
-
62
- // Add RGB values weighted by alpha.
63
- u32 a = d.getAlpha ();
121
+
122
+ // Add RGB values weighted by alpha IF the pixel is opaque, otherwise
123
+ // use full weight since we want to propagate colors.
124
+ video::SColor d = src->getPixel (sx, sy);
125
+ u32 a = d.getAlpha () <= threshold ? 255 : d.getAlpha ();
64
126
ss += a;
65
127
sr += a * d.getRed ();
66
128
sg += a * d.getGreen ();
67
129
sb += a * d.getBlue ();
68
130
}
69
131
70
- // If we found any neighbor RGB data, set pixel to average
71
- // weighted by alpha.
132
+ // Set pixel to average weighted by alpha
72
133
if (ss > 0 ) {
73
134
c.setRed (sr / ss);
74
135
c.setGreen (sg / ss);
75
136
c.setBlue (sb / ss);
76
137
src->setPixel (ctrx, ctry, c);
138
+ newmap.set (ctrx, ctry);
77
139
}
78
140
}
141
+
142
+ if (newmap.all ())
143
+ return ;
144
+
145
+ // Apply changes to bitmap for next run. This is done so we don't introduce
146
+ // a bias in color propagation in the direction pixels are processed.
147
+ newmap.copy (bitmap);
148
+
149
+ }
79
150
}
80
151
81
152
/* Scale a region of an image into another image, using nearest-neighbor with
0 commit comments