@@ -33,35 +33,52 @@ GLubyte *CpuTextureManager::getTextureData(const Texture &texture)
33
33
return it->second ;
34
34
}
35
35
36
- const std::vector<QPoint> &CpuTextureManager::getTextureConvexHullPoints (const Texture &texture)
36
+ void CpuTextureManager::getTextureConvexHullPoints (
37
+ const Texture &texture,
38
+ const QSize &skinSize,
39
+ ShaderManager::Effect effectMask,
40
+ const std::unordered_map<ShaderManager::Effect, double > &effects,
41
+ std::vector<QPoint> &dst)
37
42
{
38
- static const std::vector<QPoint> empty ;
43
+ dst. clear () ;
39
44
40
45
if (!texture.isValid ())
41
- return empty ;
46
+ return ;
42
47
43
- const GLuint handle = texture.handle ();
44
- auto it = m_convexHullPoints.find (handle);
48
+ // Remove effects that don't change shape
49
+ if (effectMask != 0 ) {
50
+ const auto &allEffects = ShaderManager::effects ();
45
51
46
- if (it == m_convexHullPoints.cend ()) {
47
- if (addTexture (texture))
48
- return m_convexHullPoints[handle];
49
- else
50
- return empty;
52
+ for (ShaderManager::Effect effect : allEffects) {
53
+ if ((effectMask & effect) != 0 && !ShaderManager::effectShapeChanges (effect))
54
+ effectMask &= ~effect;
55
+ }
56
+ }
57
+
58
+ // If there are no shape-changing effects, use cached hull points
59
+ if (effectMask == 0 ) {
60
+ const GLuint handle = texture.handle ();
61
+ auto it = m_convexHullPoints.find (handle);
62
+
63
+ if (it == m_convexHullPoints.cend ()) {
64
+ if (addTexture (texture))
65
+ dst = m_convexHullPoints[handle];
66
+ } else
67
+ dst = it->second ;
51
68
} else
52
- return it-> second ;
69
+ readTexture (texture, skinSize, effectMask, effects, nullptr , dst) ;
53
70
}
54
71
55
- QRgb CpuTextureManager::getPointColor (const Texture &texture, int x, int y, const std::unordered_map<ShaderManager::Effect, double > &effects)
72
+ QRgb CpuTextureManager::getPointColor (const Texture &texture, int x, int y, ShaderManager::Effect effectMask, const std::unordered_map<ShaderManager::Effect, double > &effects)
56
73
{
57
74
const int width = texture.width ();
58
75
const int height = texture.height ();
59
76
60
- if (!effects. empty () ) {
77
+ if (effectMask != 0 ) {
61
78
// Get local position with effect transform
62
79
QVector2D transformedCoords;
63
80
const QVector2D localCoords (x / static_cast <float >(width), y / static_cast <float >(height));
64
- EffectTransform::transformPoint (effects, localCoords, transformedCoords);
81
+ EffectTransform::transformPoint (effectMask, effects, texture. size () , localCoords, transformedCoords);
65
82
x = transformedCoords.x () * width;
66
83
y = transformedCoords.y () * height;
67
84
}
@@ -72,25 +89,25 @@ QRgb CpuTextureManager::getPointColor(const Texture &texture, int x, int y, cons
72
89
GLubyte *pixels = getTextureData (texture);
73
90
QRgb color = qRgba (pixels[(y * width + x) * 4 ], pixels[(y * width + x) * 4 + 1 ], pixels[(y * width + x) * 4 + 2 ], pixels[(y * width + x) * 4 + 3 ]);
74
91
75
- if (effects. empty () )
92
+ if (effectMask == 0 )
76
93
return color;
77
94
else
78
- return EffectTransform::transformColor (effects, color);
95
+ return EffectTransform::transformColor (effectMask, effects, color);
79
96
}
80
97
81
- bool CpuTextureManager::textureContainsPoint (const Texture &texture, const QPointF &localPoint, const std::unordered_map<ShaderManager::Effect, double > &effects)
98
+ bool CpuTextureManager::textureContainsPoint (const Texture &texture, const QPointF &localPoint, ShaderManager::Effect effectMask, const std::unordered_map<ShaderManager::Effect, double > &effects)
82
99
{
83
100
// https://github.com/scratchfoundation/scratch-render/blob/7b823985bc6fe92f572cc3276a8915e550f7c5e6/src/Silhouette.js#L219-L226
84
101
const int width = texture.width ();
85
102
const int height = texture.height ();
86
103
int x = localPoint.x ();
87
104
int y = localPoint.y ();
88
105
89
- if (!effects. empty () ) {
106
+ if (effectMask != 0 ) {
90
107
// Get local position with effect transform
91
108
QVector2D transformedCoords;
92
109
const QVector2D localCoords (x / static_cast <float >(width), y / static_cast <float >(height));
93
- EffectTransform::transformPoint (effects, localCoords, transformedCoords);
110
+ EffectTransform::transformPoint (effectMask, effects, texture. size () , localCoords, transformedCoords);
94
111
x = transformedCoords.x () * width;
95
112
y = transformedCoords.y () * height;
96
113
}
@@ -118,7 +135,24 @@ void CpuTextureManager::removeTexture(const Texture &texture)
118
135
}
119
136
}
120
137
121
- bool CpuTextureManager::addTexture (const Texture &texture)
138
+ bool CpuTextureManager::addTexture (const Texture &tex)
139
+ {
140
+ if (!tex.isValid ())
141
+ return false ;
142
+
143
+ const GLuint handle = tex.handle ();
144
+ m_textureData[handle] = nullptr ;
145
+ m_convexHullPoints[handle] = {};
146
+ return readTexture (tex, QSize (), ShaderManager::Effect::NoEffect, {}, &m_textureData[handle], m_convexHullPoints[handle]);
147
+ }
148
+
149
+ bool CpuTextureManager::readTexture (
150
+ const Texture &texture,
151
+ const QSize &skinSize,
152
+ ShaderManager::Effect effectMask,
153
+ const std::unordered_map<ShaderManager::Effect, double > &effects,
154
+ GLubyte **data,
155
+ std::vector<QPoint> &points) const
122
156
{
123
157
if (!texture.isValid ())
124
158
return false ;
@@ -146,37 +180,134 @@ bool CpuTextureManager::addTexture(const Texture &texture)
146
180
GLubyte *pixels = new GLubyte[width * height * 4 ]; // 4 channels (RGBA)
147
181
glF.glReadPixels (0 , 0 , width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
148
182
149
- // Flip vertically
150
- int rowSize = width * 4 ;
151
- GLubyte *tempRow = new GLubyte[rowSize];
152
-
153
- for (size_t i = 0 ; i < height / 2 ; ++i) {
154
- size_t topRowIndex = i * rowSize;
155
- size_t bottomRowIndex = (height - 1 - i) * rowSize;
183
+ std::vector<QPoint> leftHull;
184
+ std::vector<QPoint> rightHull;
185
+ leftHull.reserve (height);
186
+ rightHull.reserve (height);
156
187
157
- // Swap rows
158
- memcpy (tempRow, &pixels[topRowIndex], rowSize);
159
- memcpy (&pixels[topRowIndex], &pixels[bottomRowIndex], rowSize);
160
- memcpy (&pixels[bottomRowIndex], tempRow, rowSize);
188
+ for (int x = 0 ; x < height; x++) {
189
+ leftHull.push_back (QPoint (-1 , -1 ));
190
+ rightHull.push_back (QPoint (-1 , -1 ));
161
191
}
162
192
163
- delete[] tempRow;
193
+ int leftEndPointIndex = -1 ;
194
+ int rightEndPointIndex = -1 ;
164
195
165
- m_textureData[handle] = pixels;
166
- m_convexHullPoints[handle] = {};
167
- std::vector<QPoint> &hullPoints = m_convexHullPoints[handle];
196
+ auto determinant = [](const QPoint &A, const QPoint &B, const QPoint &C) { return (B.x () - A.x ()) * (C.y () - A.y ()) - (B.y () - A.y ()) * (C.x () - A.x ()); };
168
197
169
- // Get convex hull points
198
+ // Get convex hull points (flipped vertically)
199
+ // https://github.com/scratchfoundation/scratch-render/blob/0f6663f3148b4f994d58e19590e14c152f1cc2f8/src/RenderWebGL.js#L1829-L1955
170
200
for (int y = 0 ; y < height; y++) {
171
- for (int x = 0 ; x < width; x++) {
172
- int index = (y * width + x) * 4 ; // 4 channels (RGBA)
201
+ QPoint currentPoint;
202
+ int x;
203
+ const int flippedY = height - 1 - y;
204
+
205
+ for (x = 0 ; x < width; x++) {
206
+ int transformedX = x;
207
+ int transformedY = flippedY;
208
+
209
+ if (effectMask != 0 ) {
210
+ // Get local position with effect transform
211
+ QVector2D transformedCoords;
212
+ const QVector2D localCoords (transformedX / static_cast <float >(width), transformedY / static_cast <float >(height));
213
+ EffectTransform::transformPoint (effectMask, effects, skinSize, localCoords, transformedCoords);
214
+ transformedX = transformedCoords.x () * width;
215
+ transformedY = transformedCoords.y () * height;
216
+ }
217
+
218
+ if ((transformedX >= 0 && transformedX < width) && (transformedY >= 0 && transformedY < height)) {
219
+ int index = (transformedY * width + transformedX) * 4 ;
220
+
221
+ if (pixels[index + 3 ] > 0 ) {
222
+ currentPoint.setX (x);
223
+ currentPoint.setY (y);
224
+ break ;
225
+ }
226
+ }
227
+ }
173
228
174
- // Check alpha channel
175
- if (pixels[index + 3 ] > 0 )
176
- hullPoints.push_back (QPoint (x, y));
229
+ if (x >= width)
230
+ continue ;
231
+
232
+ while (leftEndPointIndex > 0 ) {
233
+ if (determinant (leftHull[leftEndPointIndex], leftHull[leftEndPointIndex - 1 ], currentPoint) > 0 )
234
+ break ;
235
+ else {
236
+ leftEndPointIndex--;
237
+ }
238
+ }
239
+
240
+ leftHull[++leftEndPointIndex] = currentPoint;
241
+
242
+ for (x = width - 1 ; x >= 0 ; x--) {
243
+ int transformedX = x;
244
+ int transformedY = flippedY;
245
+
246
+ if (effectMask != 0 ) {
247
+ // Get local position with effect transform
248
+ QVector2D transformedCoords;
249
+ const QVector2D localCoords (transformedX / static_cast <float >(width), transformedY / static_cast <float >(height));
250
+ EffectTransform::transformPoint (effectMask, effects, skinSize, localCoords, transformedCoords);
251
+ transformedX = transformedCoords.x () * width;
252
+ transformedY = transformedCoords.y () * height;
253
+ }
254
+
255
+ if ((transformedX >= 0 && transformedX < width) && (transformedY >= 0 && transformedY < height)) {
256
+ int index = (transformedY * width + transformedX) * 4 ;
257
+
258
+ if (pixels[index + 3 ] > 0 ) {
259
+ currentPoint.setX (x);
260
+ currentPoint.setY (y);
261
+ break ;
262
+ }
263
+ }
264
+ }
265
+
266
+ while (rightEndPointIndex > 0 ) {
267
+ if (determinant (rightHull[rightEndPointIndex], rightHull[rightEndPointIndex - 1 ], currentPoint) < 0 )
268
+ break ;
269
+ else
270
+ rightEndPointIndex--;
177
271
}
272
+
273
+ rightHull[++rightEndPointIndex] = currentPoint;
274
+ }
275
+
276
+ points.clear ();
277
+ points.reserve ((leftEndPointIndex + 1 ) + (rightEndPointIndex + 1 ));
278
+
279
+ long i;
280
+
281
+ for (i = 0 ; i < leftHull.size (); i++) {
282
+ if (leftHull[i].x () >= 0 )
283
+ points.push_back (leftHull[i]);
178
284
}
179
285
286
+ for (i = rightEndPointIndex; i >= 0 ; --i)
287
+ if (rightHull[i].x () >= 0 )
288
+ points.push_back (rightHull[i]);
289
+
290
+ if (data) {
291
+ // Flip vertically
292
+ int rowSize = width * 4 ;
293
+ GLubyte *tempRow = new GLubyte[rowSize];
294
+
295
+ for (size_t i = 0 ; i < height / 2 ; ++i) {
296
+ size_t topRowIndex = i * rowSize;
297
+ size_t bottomRowIndex = (height - 1 - i) * rowSize;
298
+
299
+ // Swap rows
300
+ memcpy (tempRow, &pixels[topRowIndex], rowSize);
301
+ memcpy (&pixels[topRowIndex], &pixels[bottomRowIndex], rowSize);
302
+ memcpy (&pixels[bottomRowIndex], tempRow, rowSize);
303
+ }
304
+
305
+ delete[] tempRow;
306
+
307
+ *data = pixels;
308
+ } else
309
+ delete[] pixels;
310
+
180
311
// Cleanup
181
312
glF.glBindFramebuffer (GL_FRAMEBUFFER, 0 );
182
313
glF.glDeleteFramebuffers (1 , &fbo);
0 commit comments