@@ -33,35 +33,52 @@ GLubyte *CpuTextureManager::getTextureData(const Texture &texture)
3333 return it->second ;
3434}
3535
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)
3742{
38- static const std::vector<QPoint> empty ;
43+ dst. clear () ;
3944
4045 if (!texture.isValid ())
41- return empty ;
46+ return ;
4247
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 ();
4551
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 ;
5168 } else
52- return it-> second ;
69+ readTexture (texture, skinSize, effectMask, effects, nullptr , dst) ;
5370}
5471
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)
5673{
5774 const int width = texture.width ();
5875 const int height = texture.height ();
5976
60- if (!effects. empty () ) {
77+ if (effectMask != 0 ) {
6178 // Get local position with effect transform
6279 QVector2D transformedCoords;
6380 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);
6582 x = transformedCoords.x () * width;
6683 y = transformedCoords.y () * height;
6784 }
@@ -72,25 +89,25 @@ QRgb CpuTextureManager::getPointColor(const Texture &texture, int x, int y, cons
7289 GLubyte *pixels = getTextureData (texture);
7390 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 ]);
7491
75- if (effects. empty () )
92+ if (effectMask == 0 )
7693 return color;
7794 else
78- return EffectTransform::transformColor (effects, color);
95+ return EffectTransform::transformColor (effectMask, effects, color);
7996}
8097
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)
8299{
83100 // https://github.com/scratchfoundation/scratch-render/blob/7b823985bc6fe92f572cc3276a8915e550f7c5e6/src/Silhouette.js#L219-L226
84101 const int width = texture.width ();
85102 const int height = texture.height ();
86103 int x = localPoint.x ();
87104 int y = localPoint.y ();
88105
89- if (!effects. empty () ) {
106+ if (effectMask != 0 ) {
90107 // Get local position with effect transform
91108 QVector2D transformedCoords;
92109 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);
94111 x = transformedCoords.x () * width;
95112 y = transformedCoords.y () * height;
96113 }
@@ -118,7 +135,24 @@ void CpuTextureManager::removeTexture(const Texture &texture)
118135 }
119136}
120137
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
122156{
123157 if (!texture.isValid ())
124158 return false ;
@@ -146,37 +180,134 @@ bool CpuTextureManager::addTexture(const Texture &texture)
146180 GLubyte *pixels = new GLubyte[width * height * 4 ]; // 4 channels (RGBA)
147181 glF.glReadPixels (0 , 0 , width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
148182
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);
156187
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 ));
161191 }
162192
163- delete[] tempRow;
193+ int leftEndPointIndex = -1 ;
194+ int rightEndPointIndex = -1 ;
164195
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 ()); };
168197
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
170200 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+ }
173228
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--;
177271 }
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]);
178284 }
179285
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+
180311 // Cleanup
181312 glF.glBindFramebuffer (GL_FRAMEBUFFER, 0 );
182313 glF.glDeleteFramebuffers (1 , &fbo);
0 commit comments