diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp index 31223494d9..a1368eab05 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp @@ -258,14 +258,14 @@ public: : width (w), height (h), data (w * h) { - buffer.readPixels (data, Rectangle (0, 0, w, h)); + buffer.readPixels (data, 0, Rectangle (0, 0, w, h)); } bool restore (OpenGLFrameBuffer& buffer) { if (buffer.initialise (width, height)) { - buffer.writePixels (data, Rectangle (0, 0, width, height)); + buffer.writePixels (data, 0, 4, Rectangle (0, 0, width, height)); return true; } @@ -286,7 +286,7 @@ OpenGLFrameBuffer::~OpenGLFrameBuffer() {} bool OpenGLFrameBuffer::initialise (int width, int height) { pimpl = nullptr; - pimpl = new Pimpl (width, height, true, false); + pimpl = new Pimpl (width, height, false, false); if (! pimpl->ok) pimpl = nullptr; @@ -294,6 +294,18 @@ bool OpenGLFrameBuffer::initialise (int width, int height) return pimpl != nullptr; } +bool OpenGLFrameBuffer::initialise (const Image& content) +{ + if (initialise (content.getWidth(), content.getHeight())) + { + Image::BitmapData bitmap (content, Image::BitmapData::readOnly); + return writePixels (bitmap.data, bitmap.lineStride / bitmap.pixelStride, + bitmap.pixelStride, content.getBounds()); + } + + return false; +} + void OpenGLFrameBuffer::release() { pimpl = nullptr; @@ -311,11 +323,14 @@ void OpenGLFrameBuffer::saveAndRelease() bool OpenGLFrameBuffer::reloadSavedCopy() { - if (savedState != nullptr - && savedState->restore (*this)) + if (savedState != nullptr) { - savedState = nullptr; - return true; + ScopedPointer state (savedState); + + if (state->restore (*this)) + return true; + + savedState = state; } return false; @@ -349,7 +364,7 @@ void OpenGLFrameBuffer::clear (const Colour& colour) } } -bool OpenGLFrameBuffer::readPixels (void* target, const Rectangle& area) +bool OpenGLFrameBuffer::readPixels (void* target, int lineStride, const Rectangle& area) { if (! makeCurrentTarget()) return false; @@ -357,6 +372,7 @@ bool OpenGLFrameBuffer::readPixels (void* target, const Rectangle& area) OpenGLHelpers::prepareFor2D (pimpl->width, pimpl->height); glPixelStorei (GL_PACK_ALIGNMENT, 4); + glPixelStorei (GL_PACK_ROW_LENGTH, lineStride); glReadPixels (area.getX(), area.getY(), area.getWidth(), area.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, target); glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); @@ -364,13 +380,16 @@ bool OpenGLFrameBuffer::readPixels (void* target, const Rectangle& area) return true; } -bool OpenGLFrameBuffer::writePixels (const void* data, const Rectangle& area) +bool OpenGLFrameBuffer::writePixels (const void* data, int lineStride, int pixelStride, const Rectangle& area) { if (! makeCurrentTarget()) return false; OpenGLHelpers::prepareFor2D (pimpl->width, pimpl->height); + jassert (pixelStride == 3 || pixelStride == 4); // can only handle RGB or ARGB + const int format = pixelStride == 3 ? GL_RGB : GL_BGRA_EXT; + glDisable (GL_DEPTH_TEST); glDisable (GL_BLEND); @@ -385,9 +404,10 @@ bool OpenGLFrameBuffer::writePixels (const void* data, const Rectangle& are glEnable (GL_TEXTURE_2D); glBindTexture (GL_TEXTURE_2D, temporaryTexture); - glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + glPixelStorei (GL_UNPACK_ALIGNMENT, pixelStride); + glPixelStorei (GL_UNPACK_ROW_LENGTH, lineStride); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, area.getWidth(), area.getHeight(), 0, - GL_BGRA_EXT, GL_UNSIGNED_BYTE, data); + format, GL_UNSIGNED_BYTE, data); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -404,8 +424,9 @@ bool OpenGLFrameBuffer::writePixels (const void* data, const Rectangle& are #else glRasterPos2i (area.getX(), area.getY()); glBindTexture (GL_TEXTURE_2D, 0); - glPixelStorei (GL_UNPACK_ALIGNMENT, 4); - glDrawPixels (area.getWidth(), area.getHeight(), GL_BGRA_EXT, GL_UNSIGNED_BYTE, data); + glPixelStorei (GL_UNPACK_ALIGNMENT, pixelStride); + glPixelStorei (GL_UNPACK_ROW_LENGTH, lineStride); + glDrawPixels (area.getWidth(), area.getHeight(), format, GL_UNSIGNED_BYTE, data); #endif glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); @@ -441,350 +462,6 @@ void OpenGLFrameBuffer::draw3D (float x1, float y1, float z1, } } -//============================================================================== -// This breaks down a path into a series of horizontal strips of trapezoids.. -class TrapezoidedPath -{ -public: - TrapezoidedPath (const Path& p) - : firstSlice (nullptr), - windingMask (p.isUsingNonZeroWinding() ? -1 : 1) - { - for (PathFlatteningIterator iter (p); iter.next();) - addLine (floatToInt (iter.x1), floatToInt (iter.y1), - floatToInt (iter.x2), floatToInt (iter.y2)); - } - - ~TrapezoidedPath() - { - for (HorizontalSlice* s = firstSlice; s != nullptr;) - { - const ScopedPointer deleter (s); - s = s->next; - } - } - - template - void iterate (Consumer& consumer) const - { - for (HorizontalSlice* s = firstSlice; s != nullptr; s = s->next) - s->iterate (consumer, windingMask); - } - -private: - void addLine (int x1, int y1, int x2, int y2) - { - int winding = 1; - - if (y2 < y1) - { - std::swap (x1, x2); - std::swap (y1, y2); - winding = -1; - } - - HorizontalSlice* last = nullptr; - HorizontalSlice* s = firstSlice; - - while (y2 > y1) - { - if (s == nullptr) - { - insert (last, new HorizontalSlice (nullptr, x1, y1, x2, y2, winding)); - break; - } - - if (s->y2 > y1) - { - if (y1 < s->y1) - { - if (y2 <= s->y1) - { - insert (last, new HorizontalSlice (s, x1, y1, x2, y2, winding)); - break; - } - else - { - const int newX = x1 + (s->y1 - y1) * (x2 - x1) / (y2 - y1); - HorizontalSlice* const newSlice = new HorizontalSlice (s, x1, y1, newX, s->y1, winding); - insert (last, newSlice); - last = newSlice; - x1 = newX; - y1 = s->y1; - continue; - } - } - else if (y1 > s->y1) - { - s->split (y1); - s = s->next; - jassert (s != nullptr); - } - - jassert (y1 == s->y1); - - if (y2 > s->y2) - { - const int newY = s->y2; - const int newX = x1 + (newY - y1) * (x2 - x1) / (y2 - y1); - s->addLine (x1, newX, winding); - x1 = newX; - y1 = newY; - } - else - { - if (y2 < s->y2) - s->split (y2); - - jassert (y2 == s->y2); - s->addLine (x1, x2, winding); - break; - } - } - - last = s; - s = s->next; - } - } - - struct HorizontalSlice - { - HorizontalSlice (const HorizontalSlice& other, HorizontalSlice* const next_, int y1_, int y2_) - : next (next_), y1 (y1_), y2 (y2_), segments (other.segments) - { - } - - HorizontalSlice (HorizontalSlice* const next_, int x1, int y1_, int x2, int y2_, int winding) - : next (next_), y1 (y1_), y2 (y2_) - { - jassert (next != this); - jassert (y2 > y1); - segments.ensureStorageAllocated (32); - segments.add (LineSegment (x1, x2, winding)); - } - - void addLine (const int x1, const int x2, int winding) - { - const int dy = y2 - y1; - - for (int i = 0; i < segments.size(); ++i) - { - const LineSegment& l = segments.getReference (i); - - const int diff1 = l.x1 - x1; - const int diff2 = l.x2 - x2; - - if ((diff1 < 0) == (diff2 > 0)) - { - const int dx1 = l.x2 - l.x1; - const int dx2 = x2 - x1; - const int dxDiff = dx2 - dx1; - - if (dxDiff != 0) - { - const int intersectionY = (dy * diff1) / dxDiff; - - if (intersectionY > 0 && intersectionY < dy) - { - const int intersectionX = x1 + (intersectionY * dx2) / dy; - split (intersectionY + y1); - next->addLine (intersectionX, x2, winding); - addLine (x1, intersectionX, winding); - return; - } - } - } - - if (diff1 + diff2 > 0) - { - segments.insert (i, LineSegment (x1, x2, winding)); - return; - } - } - - segments.add (LineSegment (x1, x2, winding)); - } - - void split (const int newY) - { - jassert (newY > y1 && newY < y2); - - const int dy1 = newY - y1; - const int dy2 = y2 - y1; - next = new HorizontalSlice (*this, next, newY, y2); - y2 = newY; - - LineSegment* const oldSegments = segments.getRawDataPointer(); - LineSegment* const newSegments = next->segments.getRawDataPointer(); - - for (int i = 0; i < segments.size(); ++i) - { - LineSegment& l = oldSegments[i]; - const int newX = l.x1 + dy1 * (l.x2 - l.x1) / dy2; - newSegments[i].x1 = newX; - l.x2 = newX; - } - } - - template - void iterate (Consumer& consumer, const int windingMask) - { - jassert (segments.size() > 0); - - const float fy1 = intToFloat (y1); - const float fy2 = intToFloat (y2); - - const LineSegment* s1 = segments.getRawDataPointer(); - const LineSegment* s2 = s1; - int winding = s1->winding; - - for (int i = segments.size(); --i > 0;) - { - ++s2; - winding += s2->winding; - - if ((winding & windingMask) == 0) - { - const float ax1 = intToFloat (s1->x1); - const float ax2 = intToFloat (s1->x2); - - if (s1->x1 == s2->x1) - consumer.addTriangle (ax1, fy1, ax2, fy2, intToFloat (s2->x2), fy2); - else if (s1->x2 == s2->x2) - consumer.addTriangle (ax1, fy1, intToFloat (s2->x1), fy1, ax2, fy2); - else - consumer.addTrapezoid (fy1, fy2, ax1, ax2, intToFloat (s2->x1), intToFloat (s2->x2)); - - s1 = s2 + 1; - } - } - } - - HorizontalSlice* next; - int y1, y2; - - private: - struct LineSegment - { - inline LineSegment (int x1_, int x2_, int winding_) noexcept - : x1 (x1_), x2 (x2_), winding (winding_) {} - - int x1, x2; - int winding; - }; - - Array segments; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HorizontalSlice); - }; - - HorizontalSlice* firstSlice; - const int windingMask; - - inline void insert (HorizontalSlice* const last, HorizontalSlice* const newOne) noexcept - { - if (last == nullptr) - firstSlice = newOne; - else - last->next = newOne; - } - - enum { factor = 128 }; - static inline int floatToInt (const float n) noexcept { return roundToInt (n * (float) factor); } - static inline float intToFloat (const int n) noexcept { return n * (1.0f / (float) factor); } - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TrapezoidedPath); -}; - -//============================================================================== -// Breaks a path into a set of openGL triangles.. -class TriangulatedPath -{ -public: - TriangulatedPath (const Path& path) - { - startNewBlock(); - - TrapezoidedPath (path).iterate (*this); - } - - void draw (const int oversamplingLevel) const - { - glColor4f (1.0f, 1.0f, 1.0f, 1.0f / (oversamplingLevel * oversamplingLevel)); - - glTranslatef (-0.5f, -0.5f, 0.0f); - const float inc = 1.0f / oversamplingLevel; - - for (int y = oversamplingLevel; --y >= 0;) - { - for (int x = oversamplingLevel; --x >= 0;) - { - glTranslatef (inc, 0.0f, 0.0f); - - for (int i = 0; i < blocks.size(); ++i) - blocks.getUnchecked(i)->draw(); - } - - glTranslatef (-1.0f, inc, 0.0f); - } - } - - void addTriangle (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3) - { - if (currentBlock->numDone >= trianglesPerBlock) - startNewBlock(); - - GLfloat* t = currentBlock->getNextTriangle(); - *t++ = x1; *t++ = y1; *t++ = x2; *t++ = y2; *t++ = x3; *t++ = y3; - - currentBlock->numDone++; - } - - void addTrapezoid (GLfloat y1, GLfloat y2, GLfloat x1, GLfloat x2, GLfloat x3, GLfloat x4) - { - if (currentBlock->numDone >= trianglesPerBlock - 1) - startNewBlock(); - - GLfloat* t = currentBlock->getNextTriangle(); - *t++ = x1; *t++ = y1; *t++ = x2; *t++ = y2; *t++ = x3; *t++ = y1; - *t++ = x4; *t++ = y2; *t++ = x2; *t++ = y2; *t++ = x3; *t++ = y1; - - currentBlock->numDone += 2; - } - -private: - // Some GL implementations can't take very large triangle lists, so store - // the list as a series of blocks containing this max number of triangles. - enum { trianglesPerBlock = 256 }; - - struct TriangleBlock - { - TriangleBlock() noexcept : numDone (0) {} - - void draw() const - { - glVertexPointer (2, GL_FLOAT, 0, triangles); - glDrawArrays (GL_TRIANGLES, 0, numDone * 3); - } - - inline GLfloat* getNextTriangle() noexcept { return triangles + numDone * 6; } - - int numDone; - GLfloat triangles [trianglesPerBlock * 6]; - }; - - void startNewBlock() - { - currentBlock = new TriangleBlock(); - blocks.add (currentBlock); - } - - OwnedArray blocks; - TriangleBlock* currentBlock; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TriangulatedPath); -}; - //============================================================================== void OpenGLFrameBuffer::createAlphaChannelFromPath (const Path& path, const int oversamplingLevel) { diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h index 7dbdd6701d..9ccb0e114c 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h @@ -49,6 +49,12 @@ public: */ bool initialise (int width, int height); + /** Tries to allocates a buffer containing a copy of a given image. + Note that a valid openGL context must be selected when you call this method, + or it will fail. + */ + bool initialise (const Image& content); + /** Releases the buffer, if one has been allocated. Any saved state that was created with saveAndRelease() will also be freed by this call. */ @@ -102,11 +108,19 @@ public: float x4, float y4, float z4, const Colour& colour) const; - /** Reads an area of pixels from the framebuffer into a specified pixel array. */ - bool readPixels (void* target, const Rectangle& area); + /** Reads an area of pixels from the framebuffer into a 32-bit ARGB pixel array. + The lineStride is measured as a number of pixels, not bytes - pass a stride + of 0 to indicate a packed array. + */ + bool readPixels (void* targetData, int lineStride, const Rectangle& sourceArea); - /** Writes an area of pixels into the framebuffer from a specified pixel array. */ - bool writePixels (const void* target, const Rectangle& area); + /** Writes an area of pixels into the framebuffer from a specified pixel array. + The lineStride is measured as a number of pixels, not bytes - pass a stride + of 0 to indicate a packed array. + */ + bool writePixels (const void* srcData, + int srcLineStride, int srcPixelStride, + const Rectangle& targetArea); /** This will render an anti-aliased path into just the alpha channel of this framebuffer. diff --git a/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp b/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp index 709c43ea69..f41f8910e2 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp @@ -121,6 +121,7 @@ void OpenGLHelpers::drawQuad3D (float x1, float y1, float z1, glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); } +//============================================================================== namespace OpenGLGradientHelpers { void drawTriangles (GLenum mode, const GLfloat* vertices, const GLfloat* textureCoords, const int numElements) @@ -180,27 +181,23 @@ namespace OpenGLGradientHelpers const float sourceRadius = jmax (Point (screenRadius, 0.0f).transformedBy (inverse).getDistanceFromOrigin(), Point (0.0f, screenRadius).transformedBy (inverse).getDistanceFromOrigin()); - const int numDivisions = 80; - GLfloat vertices [6 + numDivisions * 4]; - GLfloat textureCoords [6 + numDivisions * 4]; + const int numDivisions = 90; + GLfloat vertices [4 + numDivisions * 2]; + GLfloat textureCoords [4 + numDivisions * 2]; { - const float originalRadius = grad.point1.getDistanceFrom (grad.point2); - const float texturePos = sourceRadius / originalRadius; - GLfloat* t = textureCoords; *t++ = 0.0f; *t++ = 0.0f; + const float originalRadius = grad.point1.getDistanceFrom (grad.point2); + const float texturePos = sourceRadius / originalRadius; + for (int i = numDivisions + 1; --i >= 0;) { *t++ = texturePos; *t++ = 0.0f; - *t++ = texturePos; - *t++ = 1.0f; } - - jassert (t == textureCoords + numElementsInArray (vertices)); } { @@ -208,33 +205,28 @@ namespace OpenGLGradientHelpers *v++ = centre.getX(); *v++ = centre.getY(); - const Point first (grad.point1.translated (sourceRadius, -sourceRadius).transformedBy (transform)); - Point last (first); - - for (int i = 0; i < numDivisions; ++i) - { - const float angle = (i + 1) * (float_Pi * 4.0f / numDivisions); - const Point next (grad.point1.translated (std::sin (angle) * sourceRadius, - std::cos (angle) * -sourceRadius) - .transformedBy (transform)); - *v++ = last.getX(); - *v++ = last.getY(); - *v++ = next.getX(); - *v++ = next.getY(); - last = next; - } - - *v++ = last.getX(); - *v++ = last.getY(); + const Point first (grad.point1.translated (0, -sourceRadius) + .transformedBy (transform)); *v++ = first.getX(); *v++ = first.getY(); - jassert (v == vertices + numElementsInArray (vertices)); + for (int i = 1; i < numDivisions; ++i) + { + const float angle = i * (float_Pi * 2.0f / numDivisions); + const Point p (grad.point1.translated (std::sin (angle) * sourceRadius, + std::cos (angle) * -sourceRadius) + .transformedBy (transform)); + *v++ = p.getX(); + *v++ = p.getY(); + } + + *v++ = first.getX(); + *v++ = first.getY(); } glEnable (GL_SCISSOR_TEST); glScissor (rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); - drawTriangles (GL_TRIANGLE_FAN, vertices, textureCoords, numDivisions + 3); + drawTriangles (GL_TRIANGLE_FAN, vertices, textureCoords, numDivisions + 2); glDisable (GL_SCISSOR_TEST); } } @@ -256,10 +248,375 @@ void OpenGLHelpers::fillRectWithColourGradient (const Rectangle& rect, else glEnable (GL_BLEND); - if (gradient.isRadial) - OpenGLGradientHelpers::fillWithRadialGradient (rect, gradient, transform); + if (gradient.point1 == gradient.point2) + { + fillRectWithColour (rect, gradient.getColourAtPosition (1.0)); + } else - OpenGLGradientHelpers::fillWithLinearGradient (rect, gradient, transform, textureSize); + { + if (gradient.isRadial) + OpenGLGradientHelpers::fillWithRadialGradient (rect, gradient, transform); + else + OpenGLGradientHelpers::fillWithLinearGradient (rect, gradient, transform, textureSize); + } +} + +void OpenGLHelpers::fillRectWithColour (const Rectangle& rect, const Colour& colour) +{ + glEnableClientState (GL_VERTEX_ARRAY); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + glDisableClientState (GL_COLOR_ARRAY); + glDisableClientState (GL_NORMAL_ARRAY); + + const GLfloat vertices[] = { (float) rect.getX(), (float) rect.getY(), + (float) rect.getRight(), (float) rect.getY(), + (float) rect.getX(), (float) rect.getBottom(), + (float) rect.getRight(), (float) rect.getBottom() }; + + setColour (colour); + glVertexPointer (2, GL_FLOAT, 0, vertices); + glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); +} + +//============================================================================== +// This breaks down a path into a series of horizontal strips of trapezoids.. +class TriangulatedPath::TrapezoidedPath +{ +public: + TrapezoidedPath (const Path& p) + : firstSlice (nullptr), + windingMask (p.isUsingNonZeroWinding() ? -1 : 1) + { + for (PathFlatteningIterator iter (p); iter.next();) + addLine (floatToInt (iter.x1), floatToInt (iter.y1), + floatToInt (iter.x2), floatToInt (iter.y2)); + } + + ~TrapezoidedPath() + { + for (HorizontalSlice* s = firstSlice; s != nullptr;) + { + const ScopedPointer deleter (s); + s = s->next; + } + } + + template + void iterate (Consumer& consumer) const + { + for (HorizontalSlice* s = firstSlice; s != nullptr; s = s->next) + s->iterate (consumer, windingMask); + } + +private: + void addLine (int x1, int y1, int x2, int y2) + { + int winding = 1; + + if (y2 < y1) + { + std::swap (x1, x2); + std::swap (y1, y2); + winding = -1; + } + + HorizontalSlice* last = nullptr; + HorizontalSlice* s = firstSlice; + + while (y2 > y1) + { + if (s == nullptr) + { + insert (last, new HorizontalSlice (nullptr, x1, y1, x2, y2, winding)); + break; + } + + if (s->y2 > y1) + { + if (y1 < s->y1) + { + if (y2 <= s->y1) + { + insert (last, new HorizontalSlice (s, x1, y1, x2, y2, winding)); + break; + } + else + { + const int newX = x1 + (s->y1 - y1) * (x2 - x1) / (y2 - y1); + HorizontalSlice* const newSlice = new HorizontalSlice (s, x1, y1, newX, s->y1, winding); + insert (last, newSlice); + last = newSlice; + x1 = newX; + y1 = s->y1; + continue; + } + } + else if (y1 > s->y1) + { + s->split (y1); + s = s->next; + jassert (s != nullptr); + } + + jassert (y1 == s->y1); + + if (y2 > s->y2) + { + const int newY = s->y2; + const int newX = x1 + (newY - y1) * (x2 - x1) / (y2 - y1); + s->addLine (x1, newX, winding); + x1 = newX; + y1 = newY; + } + else + { + if (y2 < s->y2) + s->split (y2); + + jassert (y2 == s->y2); + s->addLine (x1, x2, winding); + break; + } + } + + last = s; + s = s->next; + } + } + + struct HorizontalSlice + { + HorizontalSlice (const HorizontalSlice& other, HorizontalSlice* const next_, int y1_, int y2_) + : next (next_), y1 (y1_), y2 (y2_), segments (other.segments) + { + } + + HorizontalSlice (HorizontalSlice* const next_, int x1, int y1_, int x2, int y2_, int winding) + : next (next_), y1 (y1_), y2 (y2_) + { + jassert (next != this); + jassert (y2 > y1); + segments.ensureStorageAllocated (32); + segments.add (LineSegment (x1, x2, winding)); + } + + void addLine (const int x1, const int x2, int winding) + { + const int dy = y2 - y1; + + for (int i = 0; i < segments.size(); ++i) + { + const LineSegment& l = segments.getReference (i); + + const int diff1 = l.x1 - x1; + const int diff2 = l.x2 - x2; + + if ((diff1 < 0) == (diff2 > 0)) + { + const int dx1 = l.x2 - l.x1; + const int dx2 = x2 - x1; + const int dxDiff = dx2 - dx1; + + if (dxDiff != 0) + { + const int intersectionY = (dy * diff1) / dxDiff; + + if (intersectionY > 0 && intersectionY < dy) + { + const int intersectionX = x1 + (intersectionY * dx2) / dy; + split (intersectionY + y1); + next->addLine (intersectionX, x2, winding); + addLine (x1, intersectionX, winding); + return; + } + } + } + + if (diff1 + diff2 > 0) + { + segments.insert (i, LineSegment (x1, x2, winding)); + return; + } + } + + segments.add (LineSegment (x1, x2, winding)); + } + + void split (const int newY) + { + jassert (newY > y1 && newY < y2); + + const int dy1 = newY - y1; + const int dy2 = y2 - y1; + next = new HorizontalSlice (*this, next, newY, y2); + y2 = newY; + + LineSegment* const oldSegments = segments.getRawDataPointer(); + LineSegment* const newSegments = next->segments.getRawDataPointer(); + + for (int i = 0; i < segments.size(); ++i) + { + LineSegment& l = oldSegments[i]; + const int newX = l.x1 + dy1 * (l.x2 - l.x1) / dy2; + newSegments[i].x1 = newX; + l.x2 = newX; + } + } + + template + void iterate (Consumer& consumer, const int windingMask) + { + jassert (segments.size() > 0); + + const float fy1 = intToFloat (y1); + const float fy2 = intToFloat (y2); + + const LineSegment* s1 = segments.getRawDataPointer(); + const LineSegment* s2 = s1; + int winding = s1->winding; + + for (int i = segments.size(); --i > 0;) + { + ++s2; + winding += s2->winding; + + if ((winding & windingMask) == 0) + { + const float ax1 = intToFloat (s1->x1); + const float ax2 = intToFloat (s1->x2); + + if (s1->x1 == s2->x1) + consumer.addTriangle (ax1, fy1, ax2, fy2, intToFloat (s2->x2), fy2); + else if (s1->x2 == s2->x2) + consumer.addTriangle (ax1, fy1, intToFloat (s2->x1), fy1, ax2, fy2); + else + consumer.addTrapezoid (fy1, fy2, ax1, ax2, intToFloat (s2->x1), intToFloat (s2->x2)); + + s1 = s2 + 1; + } + } + } + + HorizontalSlice* next; + int y1, y2; + + private: + struct LineSegment + { + inline LineSegment (int x1_, int x2_, int winding_) noexcept + : x1 (x1_), x2 (x2_), winding (winding_) {} + + int x1, x2; + int winding; + }; + + Array segments; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HorizontalSlice); + }; + + HorizontalSlice* firstSlice; + const int windingMask; + + inline void insert (HorizontalSlice* const last, HorizontalSlice* const newOne) noexcept + { + if (last == nullptr) + firstSlice = newOne; + else + last->next = newOne; + } + + enum { factor = 128 }; + static inline int floatToInt (const float n) noexcept { return roundToInt (n * (float) factor); } + static inline float intToFloat (const int n) noexcept { return n * (1.0f / (float) factor); } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TrapezoidedPath); +}; + +//============================================================================== +struct TriangulatedPath::TriangleBlock +{ + TriangleBlock() noexcept + : numVertices (0), + triangles (maxVerticesPerBlock) + {} + + void draw() const + { + glVertexPointer (2, GL_FLOAT, 0, triangles); + glDrawArrays (GL_TRIANGLES, 0, numVertices / 2); + } + + inline GLfloat* getNextTriangle() noexcept { return triangles + numVertices; } + void optimiseStorage() { triangles.realloc (numVertices); } + + // Some GL implementations can't take very large triangle lists, so store + // the list as a series of blocks containing this max number of triangles. + enum { maxVerticesPerBlock = 256 * 6 }; + + unsigned int numVertices; + HeapBlock triangles; +}; + +TriangulatedPath::TriangulatedPath (const Path& path) +{ + startNewBlock(); + TrapezoidedPath (path).iterate (*this); +} + +void TriangulatedPath::draw (const int oversamplingLevel) const +{ + glColor4f (1.0f, 1.0f, 1.0f, 1.0f / (oversamplingLevel * oversamplingLevel)); + + glTranslatef (-0.5f, -0.5f, 0.0f); + const float inc = 1.0f / oversamplingLevel; + + for (int y = oversamplingLevel; --y >= 0;) + { + for (int x = oversamplingLevel; --x >= 0;) + { + glTranslatef (inc, 0.0f, 0.0f); + + for (int i = 0; i < blocks.size(); ++i) + blocks.getUnchecked(i)->draw(); + } + + glTranslatef (-1.0f, inc, 0.0f); + } +} + +void TriangulatedPath::optimiseStorage() +{ + currentBlock->optimiseStorage(); +} + +void TriangulatedPath::startNewBlock() +{ + currentBlock = new TriangleBlock(); + blocks.add (currentBlock); +} + +void TriangulatedPath::addTriangle (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3) +{ + if (currentBlock->numVertices >= TriangleBlock::maxVerticesPerBlock) + startNewBlock(); + + GLfloat* t = currentBlock->getNextTriangle(); + *t++ = x1; *t++ = y1; *t++ = x2; *t++ = y2; *t++ = x3; *t++ = y3; + + currentBlock->numVertices += 6; +} + +void TriangulatedPath::addTrapezoid (GLfloat y1, GLfloat y2, GLfloat x1, GLfloat x2, GLfloat x3, GLfloat x4) +{ + if (currentBlock->numVertices >= TriangleBlock::maxVerticesPerBlock - 6) + startNewBlock(); + + GLfloat* t = currentBlock->getNextTriangle(); + *t++ = x1; *t++ = y1; *t++ = x2; *t++ = y2; *t++ = x3; *t++ = y1; + *t++ = x4; *t++ = y2; *t++ = x2; *t++ = y2; *t++ = x3; *t++ = y1; + + currentBlock->numVertices += 12; } END_JUCE_NAMESPACE diff --git a/modules/juce_opengl/opengl/juce_OpenGLHelpers.h b/modules/juce_opengl/opengl/juce_OpenGLHelpers.h index 042224e4e0..202eb7e988 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLHelpers.h +++ b/modules/juce_opengl/opengl/juce_OpenGLHelpers.h @@ -26,6 +26,7 @@ #ifndef __JUCE_OPENGLHELPERS_JUCEHEADER__ #define __JUCE_OPENGLHELPERS_JUCEHEADER__ +//============================================================================== /** A set of miscellaneous openGL helper functions. */ @@ -61,11 +62,48 @@ public: float x4, float y4, float z4, const Colour& colour); + /** Fills a rectangle with the specified colour. */ + static void fillRectWithColour (const Rectangle& rect, + const Colour& colour); + /** Fills a rectangle with the specified gradient. */ static void fillRectWithColourGradient (const Rectangle& rect, const ColourGradient& gradient, const AffineTransform& transform); }; +//============================================================================== +/** Holds a set of OpenGL triangles, having generated them from a Path object. +*/ +class JUCE_API TriangulatedPath +{ +public: + TriangulatedPath (const Path& path); + + /** Renders the path, using a jittered oversampling method. + The oversampling level is the square root of the number of times it + should be oversampled, so 3 or 4 might be reasonable. + */ + void draw (int oversamplingLevel) const; + + /** Reduces the memory footprint of this object to the minimum possible. */ + void optimiseStorage(); + +private: + class TrapezoidedPath; + friend class TrapezoidedPath; + + void startNewBlock(); + void addTriangle (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3); + void addTrapezoid (GLfloat y1, GLfloat y2, GLfloat x1, GLfloat x2, GLfloat x3, GLfloat x4); + + struct TriangleBlock; + friend class OwnedArray; + OwnedArray blocks; + TriangleBlock* currentBlock; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TriangulatedPath); +}; + #endif // __JUCE_OPENGLHELPERS_JUCEHEADER__ diff --git a/modules/juce_opengl/opengl/juce_OpenGLImage.cpp b/modules/juce_opengl/opengl/juce_OpenGLImage.cpp index 7bb1a04193..d931eeb166 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLImage.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLImage.cpp @@ -74,7 +74,7 @@ namespace OpenGLImageHelpers { static void read (OpenGLFrameBuffer& frameBuffer, Image::BitmapData& bitmapData, int x, int y) { - frameBuffer.readPixels (bitmapData.data, Rectangle (x, y, bitmapData.width, bitmapData.height)); + frameBuffer.readPixels (bitmapData.data, 0, Rectangle (x, y, bitmapData.width, bitmapData.height)); } }; @@ -86,7 +86,7 @@ namespace OpenGLImageHelpers void write (const void* const data) const noexcept { - frameBuffer.writePixels (data, area); + frameBuffer.writePixels (data, 0, 4, area); } OpenGLFrameBuffer& frameBuffer; diff --git a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp index ec844fc5e7..051c6ff46c 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp @@ -61,8 +61,6 @@ void OpenGLTexture::create (const int w, const int h) glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glPixelStorei (GL_UNPACK_ALIGNMENT, 4); } void OpenGLTexture::load (const Image& image) @@ -71,8 +69,11 @@ void OpenGLTexture::load (const Image& image) Image::BitmapData srcData (image, Image::BitmapData::readOnly); + glPixelStorei (GL_UNPACK_ALIGNMENT, srcData.pixelFormat); + glPixelStorei (GL_UNPACK_ROW_LENGTH, srcData.lineStride); + glTexImage2D (GL_TEXTURE_2D, 0, internalGLTextureFormat, width, height, 0, - image.getFormat() == Image::RGB ? GL_RGB : GL_BGRA_EXT, + srcData.pixelFormat == Image::RGB ? GL_RGB : GL_BGRA_EXT, GL_UNSIGNED_BYTE, srcData.data); } @@ -80,6 +81,7 @@ void OpenGLTexture::load (const PixelARGB* const pixels, const int w, const int { create (w, h); + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); glTexImage2D (GL_TEXTURE_2D, 0, internalGLTextureFormat, w, h, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels); }