From dbf7053861407afaae75d2a63b00fa11282ed848 Mon Sep 17 00:00:00 2001 From: jules Date: Fri, 30 Aug 2013 18:20:51 +0100 Subject: [PATCH] New method Graphics::fillRectList(), which performs better (and looks better when scaled) than multiple calls to fillRect or drawVerticalLine. Also fixed DPI detection in Windows. --- .../ui/jucer_SnapGridPainter.h | 13 +- .../Source/demos/AudioDemoTabComponent.cpp | 10 +- .../Source/demos/RenderingTestComponent.cpp | 39 ++- .../gui/juce_AudioThumbnail.cpp | 17 +- .../contexts/juce_GraphicsContext.cpp | 5 + .../contexts/juce_GraphicsContext.h | 39 +-- .../contexts/juce_LowLevelGraphicsContext.h | 2 + ...uce_LowLevelGraphicsPostScriptRenderer.cpp | 9 + .../juce_LowLevelGraphicsPostScriptRenderer.h | 4 +- .../juce_graphics/geometry/juce_EdgeTable.cpp | 232 ++++++++++++------ .../juce_graphics/geometry/juce_EdgeTable.h | 9 + .../geometry/juce_RectangleList.h | 10 + .../native/juce_RenderingHelpers.h | 26 +- .../native/juce_mac_CoreGraphicsContext.h | 1 + .../native/juce_mac_CoreGraphicsContext.mm | 28 +++ .../juce_win32_Direct2DGraphicsContext.cpp | 6 + .../native/juce_win32_Windowing.cpp | 18 +- .../code_editor/juce_CodeEditorComponent.cpp | 32 ++- 18 files changed, 357 insertions(+), 143 deletions(-) diff --git a/extras/Introjucer/Source/ComponentEditor/ui/jucer_SnapGridPainter.h b/extras/Introjucer/Source/ComponentEditor/ui/jucer_SnapGridPainter.h index 7e1369e8a4..65c53a8819 100644 --- a/extras/Introjucer/Source/ComponentEditor/ui/jucer_SnapGridPainter.h +++ b/extras/Introjucer/Source/ComponentEditor/ui/jucer_SnapGridPainter.h @@ -60,15 +60,18 @@ public: if (backgroundGraphics != nullptr) col = backgroundGraphics->getBackgroundColour().contrasting(); - g.setColour (col.withAlpha (0.1f)); - const Rectangle clip (g.getClipBounds()); - for (int y = clip.getY() - (clip.getY() % snapGridSize); y < clip.getBottom(); y += snapGridSize) - g.drawHorizontalLine (y, 0.0f, (float) clip.getRight()); + RectangleList gridLines; for (int x = clip.getX() - (clip.getX() % snapGridSize); x < clip.getRight(); x += snapGridSize) - g.drawVerticalLine (x, 0.0f, (float) clip.getBottom()); + gridLines.addWithoutMerging (Rectangle ((float) x, 0.0f, 1.0f, (float) clip.getBottom())); + + for (int y = clip.getY() - (clip.getY() % snapGridSize); y < clip.getBottom(); y += snapGridSize) + gridLines.addWithoutMerging (Rectangle (0.0f, (float) y, (float) clip.getRight(), 1.0f)); + + g.setColour (col.withAlpha (0.1f)); + g.fillRectList (gridLines); } } diff --git a/extras/JuceDemo/Source/demos/AudioDemoTabComponent.cpp b/extras/JuceDemo/Source/demos/AudioDemoTabComponent.cpp index 1cb6ebbf6f..830b808b75 100644 --- a/extras/JuceDemo/Source/demos/AudioDemoTabComponent.cpp +++ b/extras/JuceDemo/Source/demos/AudioDemoTabComponent.cpp @@ -45,17 +45,21 @@ LiveAudioInputDisplayComp::~LiveAudioInputDisplayComp() void LiveAudioInputDisplayComp::paint (Graphics& g) { - g.fillAll (Colours::black); + RectangleList waveform; - g.setColour (Colours::green); const float midY = getHeight() * 0.5f; int sampleNum = (nextSample + numElementsInArray (samples) - 1); for (int x = jmin (getWidth(), (int) numElementsInArray (samples)); --x >= 0;) { const float sampleSize = midY * samples [sampleNum-- % numElementsInArray (samples)]; - g.drawVerticalLine (x, midY - sampleSize, midY + sampleSize); + waveform.addWithoutMerging (Rectangle ((float) x, midY - sampleSize, 1.0f, sampleSize * 2.0f)); } + + g.fillAll (Colours::black); + + g.setColour (Colours::green); + g.fillRectList (waveform); } void LiveAudioInputDisplayComp::timerCallback() diff --git a/extras/JuceDemo/Source/demos/RenderingTestComponent.cpp b/extras/JuceDemo/Source/demos/RenderingTestComponent.cpp index 0dbb3a04b1..f40de402df 100644 --- a/extras/JuceDemo/Source/demos/RenderingTestComponent.cpp +++ b/extras/JuceDemo/Source/demos/RenderingTestComponent.cpp @@ -304,26 +304,39 @@ private: void drawLines (Graphics& g) { - g.setColour (Colours::blue.withAlpha ((float) owner.opacitySlider->getValue())); - - for (int x = 0; x < getWidth(); ++x) { - float y = getHeight() * 0.3f; - float width = y * fabsf (sinf (x / 100.0f + 2.0f * bouncingNumber[1])); - g.drawVerticalLine (x, y - width, y + width); + RectangleList verticalLines; + + for (int x = 0; x < getWidth(); ++x) + { + float y = getHeight() * 0.3f; + float length = y * fabsf (sinf (x / 100.0f + 2.0f * bouncingNumber[1])); + verticalLines.addWithoutMerging (Rectangle ((float) x, y - length * 0.5f, 1.0f, length)); + } + + g.setColour (Colours::blue.withAlpha ((float) owner.opacitySlider->getValue())); + g.fillRectList (verticalLines); } - g.setColour (Colours::green.withAlpha ((float) owner.opacitySlider->getValue())); - - for (int y = 0; y < getHeight(); ++y) { - float x = getWidth() * 0.3f; - float width = x * fabsf (sinf (y / 100.0f + 2.0f * bouncingNumber[2])); - g.drawHorizontalLine (y, x - width, x + width); + RectangleList horizontalLines; + + for (int y = 0; y < getHeight(); ++y) + { + float x = getWidth() * 0.3f; + float length = x * fabsf (sinf (y / 100.0f + 2.0f * bouncingNumber[2])); + horizontalLines.addWithoutMerging (Rectangle (x - length * 0.5f, (float) y, length, 1.0f)); + } + + g.setColour (Colours::green.withAlpha ((float) owner.opacitySlider->getValue())); + g.fillRectList (horizontalLines); } g.setColour (Colours::red.withAlpha ((float) owner.opacitySlider->getValue())); - g.drawLine (bouncingPointX[0], bouncingPointY[0], bouncingPointX[1], bouncingPointY[1]); + + g.drawLine (bouncingPointX[0], bouncingPointY[0], + bouncingPointX[1], bouncingPointY[1]); + g.drawLine (getWidth() - bouncingPointX[0], getHeight() - bouncingPointY[0], getWidth() - bouncingPointX[1], getHeight() - bouncingPointY[1]); } diff --git a/modules/juce_audio_utils/gui/juce_AudioThumbnail.cpp b/modules/juce_audio_utils/gui/juce_AudioThumbnail.cpp index 1e6f83baed..43d32f6365 100644 --- a/modules/juce_audio_utils/gui/juce_AudioThumbnail.cpp +++ b/modules/juce_audio_utils/gui/juce_AudioThumbnail.cpp @@ -390,16 +390,25 @@ public: const MinMaxValue* cacheData = getData (channelNum, clip.getX() - area.getX()); - int x = clip.getX(); + RectangleList waveform; + + float x = (float) clip.getX(); + for (int w = clip.getWidth(); --w >= 0;) { if (cacheData->isNonZero()) - g.drawVerticalLine (x, jmax (midY - cacheData->getMaxValue() * vscale - 0.3f, topY), - jmin (midY - cacheData->getMinValue() * vscale + 0.3f, bottomY)); + { + const float top = jmax (midY - cacheData->getMaxValue() * vscale - 0.3f, topY); + const float bottom = jmin (midY - cacheData->getMinValue() * vscale + 0.3f, bottomY); - ++x; + waveform.addWithoutMerging (Rectangle (x, top, 1.0f, bottom - top)); + } + + x += 1.0f; ++cacheData; } + + g.fillRectList (waveform); } } } diff --git a/modules/juce_graphics/contexts/juce_GraphicsContext.cpp b/modules/juce_graphics/contexts/juce_GraphicsContext.cpp index 4ddc765d18..2500fd28d7 100644 --- a/modules/juce_graphics/contexts/juce_GraphicsContext.cpp +++ b/modules/juce_graphics/contexts/juce_GraphicsContext.cpp @@ -348,6 +348,11 @@ void Graphics::fillRect (const float x, const float y, const float width, const fillRect (Rectangle (x, y, width, height)); } +void Graphics::fillRectList (const RectangleList& rectangles) const +{ + context.fillRectList (rectangles); +} + void Graphics::setPixel (int x, int y) const { context.fillRect (Rectangle (x, y, 1, 1), false); diff --git a/modules/juce_graphics/contexts/juce_GraphicsContext.h b/modules/juce_graphics/contexts/juce_GraphicsContext.h index 40fd42a9e9..c1a530bedc 100644 --- a/modules/juce_graphics/contexts/juce_GraphicsContext.h +++ b/modules/juce_graphics/contexts/juce_GraphicsContext.h @@ -241,33 +241,39 @@ public: //============================================================================== /** Fills a rectangle with the current colour or brush. + @see drawRect, fillRoundedRectangle + */ + void fillRect (const Rectangle& rectangle) const; + /** Fills a rectangle with the current colour or brush. + @see drawRect, fillRoundedRectangle + */ + void fillRect (const Rectangle& rectangle) const; + + /** Fills a rectangle with the current colour or brush. @see drawRect, fillRoundedRectangle */ void fillRect (int x, int y, int width, int height) const; - /** Fills a rectangle with the current colour or brush. */ - void fillRect (const Rectangle& rectangle) const; - - /** Fills a rectangle with the current colour or brush. */ - void fillRect (const Rectangle& rectangle) const; - /** Fills a rectangle with the current colour or brush. - - This uses sub-pixel positioning so is slower than the fillRect method which - takes integer coordinates. + @see drawRect, fillRoundedRectangle */ void fillRect (float x, float y, float width, float height) const; - /** Uses the current colour or brush to fill a rectangle with rounded corners. + /** Fills a set of rectangles using the current colour or brush. + If you have a lot of rectangles to draw, it may be more efficient + to create a RectangleList and use this method than to call fillRect() + multiple times. + */ + void fillRectList (const RectangleList& rectangles) const; + /** Uses the current colour or brush to fill a rectangle with rounded corners. @see drawRoundedRectangle, Path::addRoundedRectangle */ void fillRoundedRectangle (float x, float y, float width, float height, float cornerSize) const; /** Uses the current colour or brush to fill a rectangle with rounded corners. - @see drawRoundedRectangle, Path::addRoundedRectangle */ void fillRoundedRectangle (const Rectangle& rectangle, @@ -286,8 +292,7 @@ public: @see fillRect */ - void drawRect (int x, int y, int width, int height, - int lineThickness = 1) const; + void drawRect (int x, int y, int width, int height, int lineThickness = 1) const; /** Draws four lines to form a rectangular outline, using the current colour or brush. @@ -296,8 +301,7 @@ public: @see fillRect */ - void drawRect (float x, float y, float width, float height, - float lineThickness = 1.0f) const; + void drawRect (float x, float y, float width, float height, float lineThickness = 1.0f) const; /** Draws four lines to form a rectangular outline, using the current colour or brush. @@ -331,7 +335,10 @@ public: void drawRoundedRectangle (const Rectangle& rectangle, float cornerSize, float lineThickness) const; - /** Draws a 1x1 pixel using the current colour or brush. */ + /** Draws a 1x1 pixel using the current colour or brush. + Note that because the context may be transformed, this is effectively the same as + calling fillRect (x, y, 1, 1), and the actual result may involve multiple pixels. + */ void setPixel (int x, int y) const; //============================================================================== diff --git a/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h b/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h index a5cbea5634..124699d2e3 100644 --- a/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h +++ b/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h @@ -85,6 +85,8 @@ public: //============================================================================== virtual void fillRect (const Rectangle&, bool replaceExistingContents) = 0; + virtual void fillRectList (const RectangleList&) = 0; + virtual void fillPath (const Path&, const AffineTransform&) = 0; virtual void drawImage (const Image&, const AffineTransform&) = 0; diff --git a/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp b/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp index 48ffbe3890..77480367ae 100644 --- a/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp +++ b/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp @@ -351,7 +351,16 @@ void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle& r, cons p.addRectangle (r); fillPath (p, AffineTransform::identity); } +} +void LowLevelGraphicsPostScriptRenderer::fillRectList (const RectangleList& list) +{ + for (const Rectangle* r = list.begin(), * const e = list.end(); r != e; ++r) + { + Path p; + p.addRectangle (*r); + fillPath (p, AffineTransform::identity); + } } //============================================================================== diff --git a/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h b/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h index 9b60b36a3e..d62739e934 100644 --- a/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h +++ b/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h @@ -72,12 +72,10 @@ public: //============================================================================== void fillRect (const Rectangle&, bool replaceExistingContents) override; + void fillRectList (const RectangleList&) override; void fillPath (const Path&, const AffineTransform&) override; - void drawImage (const Image&, const AffineTransform&) override; - void drawLine (const Line &) override; - void drawVerticalLine (int x, float top, float bottom) override; void drawHorizontalLine (int x, float top, float bottom) override; diff --git a/modules/juce_graphics/geometry/juce_EdgeTable.cpp b/modules/juce_graphics/geometry/juce_EdgeTable.cpp index 918ccd4240..3fa7914675 100644 --- a/modules/juce_graphics/geometry/juce_EdgeTable.cpp +++ b/modules/juce_graphics/geometry/juce_EdgeTable.cpp @@ -29,7 +29,7 @@ EdgeTable::EdgeTable (const Rectangle& area, const Path& path, const AffineTransform& transform) : bounds (area), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), + lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1), needToCheckEmptinesss (true) { table.malloc ((size_t) ((bounds.getHeight() + 1) * lineStrideElements)); @@ -103,10 +103,10 @@ EdgeTable::EdgeTable (const Rectangle& area, EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) : bounds (rectangleToAdd), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), + lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1), needToCheckEmptinesss (true) { - table.malloc ((size_t) (jmax (1, bounds.getHeight()) * lineStrideElements)); + allocate(); table[0] = 0; const int x1 = rectangleToAdd.getX() << 8; @@ -127,17 +127,11 @@ EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) : bounds (rectanglesToAdd.getBounds()), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), + lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1), needToCheckEmptinesss (true) { - table.malloc ((size_t) (jmax (1, bounds.getHeight()) * lineStrideElements)); - - int* t = table; - for (int i = bounds.getHeight(); --i >= 0;) - { - *t = 0; - t += lineStrideElements; - } + allocate(); + clearLineSizes(); for (const Rectangle* r = rectanglesToAdd.begin(), * const e = rectanglesToAdd.end(); r != e; ++r) { @@ -146,10 +140,49 @@ EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) int y = r->getY() - bounds.getY(); for (int j = r->getHeight(); --j >= 0;) + addEdgePointPair (x1, x2, y++, 255); + } + + sanitiseLevels (true); +} + +EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) + : bounds (rectanglesToAdd.getBounds().getSmallestIntegerContainer()), + maxEdgesPerLine (rectanglesToAdd.getNumRectangles() * 2), + lineStrideElements (rectanglesToAdd.getNumRectangles() * 4 + 1), + needToCheckEmptinesss (true) +{ + bounds.setHeight (bounds.getHeight() + 1); + allocate(); + clearLineSizes(); + + for (const Rectangle* r = rectanglesToAdd.begin(), * const e = rectanglesToAdd.end(); r != e; ++r) + { + const int x1 = roundToInt (r->getX() * 256.0f); + const int x2 = roundToInt (r->getRight() * 256.0f); + + const int y1 = roundToInt (r->getY() * 256.0f) - (bounds.getY() << 8); + const int y2 = roundToInt (r->getBottom() * 256.0f) - (bounds.getY() << 8); + + if (x2 <= x1 || y2 <= y1) + continue; + + int y = y1 >> 8; + const int lastLine = y2 >> 8; + + if (y == lastLine) { - addEdgePoint (x1, y, 255); - addEdgePoint (x2, y, -255); - ++y; + addEdgePointPair (x1, x2, y, y2 - y1); + } + else + { + addEdgePointPair (x1, x2, y++, 255 - (y1 & 255)); + + while (y < lastLine) + addEdgePointPair (x1, x2, y++, 255); + + jassert (y < bounds.getHeight()); + addEdgePointPair (x1, x2, y, y2 & 255); } } @@ -166,7 +199,7 @@ EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) needToCheckEmptinesss (true) { jassert (! rectangleToAdd.isEmpty()); - table.malloc ((size_t) (jmax (1, bounds.getHeight()) * lineStrideElements)); + allocate(); table[0] = 0; const int x1 = roundToInt (rectangleToAdd.getX() * 256.0f); @@ -246,7 +279,7 @@ EdgeTable& EdgeTable::operator= (const EdgeTable& other) lineStrideElements = other.lineStrideElements; needToCheckEmptinesss = other.needToCheckEmptinesss; - table.malloc ((size_t) (jmax (1, bounds.getHeight()) * lineStrideElements)); + allocate(); copyEdgeTableData (table, lineStrideElements, other.table, lineStrideElements, bounds.getHeight()); return *this; } @@ -256,6 +289,21 @@ EdgeTable::~EdgeTable() } //============================================================================== +void EdgeTable::allocate() +{ + table.malloc ((size_t) (jmax (1, bounds.getHeight()) * lineStrideElements)); +} + +void EdgeTable::clearLineSizes() noexcept +{ + int* t = table; + for (int i = bounds.getHeight(); --i >= 0;) + { + *t = 0; + t += lineStrideElements; + } +} + void EdgeTable::copyEdgeTableData (int* dest, const int destLineStride, const int* src, const int srcLineStride, int numLines) noexcept { while (--numLines >= 0) @@ -266,54 +314,83 @@ void EdgeTable::copyEdgeTableData (int* dest, const int destLineStride, const in } } +struct EdgeTable::LineSorter +{ + static int compareElements (const LineItem& item1, const LineItem& item2) noexcept + { + return item1.x - item2.x; + } +}; + void EdgeTable::sanitiseLevels (const bool useNonZeroWinding) noexcept { // Convert the table from relative windings to absolute levels.. int* lineStart = table; - for (int i = bounds.getHeight(); --i >= 0;) + for (int y = bounds.getHeight(); --y >= 0;) { - int* line = lineStart; - lineStart += lineStrideElements; + int num = lineStart[0]; - int num = *line; - if (num == 0) - continue; - - int level = 0; - - if (useNonZeroWinding) + if (num > 0) { - while (--num > 0) - { - line += 2; - level += *line; - int corrected = abs (level); - if (corrected >> 8) - corrected = 255; + LineItem* items = reinterpret_cast (lineStart + 1); - *line = corrected; + { + // sort the X coords + LineSorter sorter; + sortArray (sorter, items, 0, num - 1, false); } - } - else - { - while (--num > 0) + + // merge duplicate X coords + for (int i = 0; i < num - 1; ++i) { - line += 2; - level += *line; - int corrected = abs (level); - if (corrected >> 8) + if (items[i].x == items[i + 1].x) { - corrected &= 511; - if (corrected >> 8) - corrected = 511 - corrected; + items[i].level += items[i + 1].level; + memmove (items + i + 1, items + i + 2, (num - i - 2) * sizeof (LineItem)); + --num; + --lineStart[0]; + --i; } - - *line = corrected; } + + int level = 0; + + if (useNonZeroWinding) + { + while (--num > 0) + { + level += items->level; + int corrected = std::abs (level); + if (corrected >> 8) + corrected = 255; + + items->level = corrected; + ++items; + } + } + else + { + while (--num > 0) + { + level += items->level; + int corrected = std::abs (level); + if (corrected >> 8) + { + corrected &= 511; + if (corrected >> 8) + corrected = 511 - corrected; + } + + items->level = corrected; + ++items; + } + } + + items->level = 0; // force the last level to 0, just in case something went wrong in creating the table } - line[2] = 0; // force the last level to 0, just in case something went wrong in creating the table + lineStart += lineStrideElements; } } @@ -351,41 +428,40 @@ void EdgeTable::addEdgePoint (const int x, const int y, const int winding) int* line = table + lineStrideElements * y; const int numPoints = line[0]; - int n = numPoints << 1; - if (n > 0) + if (numPoints >= maxEdgesPerLine) { - while (n > 0) - { - const int cx = line [n - 1]; - - if (cx <= x) - { - if (cx == x) - { - line [n] += winding; - return; - } - - break; - } - - n -= 2; - } - - if (numPoints >= maxEdgesPerLine) - { - remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); - jassert (numPoints < maxEdgesPerLine); - line = table + lineStrideElements * y; - } - - memmove (line + (n + 3), line + (n + 1), sizeof (int) * (size_t) ((numPoints << 1) - n)); + remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); + jassert (numPoints < maxEdgesPerLine); + line = table + lineStrideElements * y; } + line[0]++; + int n = numPoints << 1; line [n + 1] = x; line [n + 2] = winding; - line[0]++; +} + +void EdgeTable::addEdgePointPair (int x1, int x2, int y, int winding) +{ + jassert (y >= 0 && y < bounds.getHeight()); + + int* line = table + lineStrideElements * y; + const int numPoints = line[0]; + + if (numPoints + 1 >= maxEdgesPerLine) + { + remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); + jassert (numPoints < maxEdgesPerLine); + line = table + lineStrideElements * y; + } + + line[0] += 2; + int n = numPoints << 1; + line [n + 1] = x1; + line [n + 2] = winding; + line [n + 3] = x2; + line [n + 4] = -winding; } void EdgeTable::translate (float dx, const int dy) noexcept diff --git a/modules/juce_graphics/geometry/juce_EdgeTable.h b/modules/juce_graphics/geometry/juce_EdgeTable.h index 40cea224d5..13591ac597 100644 --- a/modules/juce_graphics/geometry/juce_EdgeTable.h +++ b/modules/juce_graphics/geometry/juce_EdgeTable.h @@ -55,6 +55,9 @@ public: /** Creates an edge table containing a rectangle list. */ explicit EdgeTable (const RectangleList& rectanglesToAdd); + /** Creates an edge table containing a rectangle list. */ + explicit EdgeTable (const RectangleList& rectanglesToAdd); + /** Creates an edge table containing a rectangle. */ explicit EdgeTable (const Rectangle& rectangleToAdd); @@ -185,12 +188,18 @@ public: private: //============================================================================== // table line format: number of points; point0 x, point0 levelDelta, point1 x, point1 levelDelta, etc + struct LineItem { int x, level; }; + struct LineSorter; + HeapBlock table; Rectangle bounds; int maxEdgesPerLine, lineStrideElements; bool needToCheckEmptinesss; + void allocate(); + void clearLineSizes() noexcept; void addEdgePoint (int x, int y, int winding); + void addEdgePointPair (int x1, int x2, int y, int winding); void remapTableForNumEdges (int newNumEdgesPerLine); void intersectWithEdgeTableLine (int y, const int* otherLine); void clipEdgeTableLineToRange (int* line, int x1, int x2) noexcept; diff --git a/modules/juce_graphics/geometry/juce_RectangleList.h b/modules/juce_graphics/geometry/juce_RectangleList.h index 42a2e0ac84..19bb7f5608 100644 --- a/modules/juce_graphics/geometry/juce_RectangleList.h +++ b/modules/juce_graphics/geometry/juce_RectangleList.h @@ -610,6 +610,16 @@ public: *r *= scaleFactor; } + /** Applies a transform to all the rectangles. + Obviously this will create a mess if the transform involves any + rotation or skewing. + */ + void transformAll (const AffineTransform& transform) noexcept + { + for (RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) + *r = r->transformedBy (transform); + } + //============================================================================== /** Creates a Path object to represent this region. */ Path toPath() const diff --git a/modules/juce_graphics/native/juce_RenderingHelpers.h b/modules/juce_graphics/native/juce_RenderingHelpers.h index 9decd68142..34f253b1b4 100644 --- a/modules/juce_graphics/native/juce_RenderingHelpers.h +++ b/modules/juce_graphics/native/juce_RenderingHelpers.h @@ -88,7 +88,7 @@ public: float getPhysicalPixelScaleFactor() const noexcept { - return isOnlyTranslated ? 1.0f : complexTransform.getScaleFactor(); + return isOnlyTranslated ? 1.0f : std::abs (complexTransform.getScaleFactor()); } void moveOriginInDeviceSpace (Point delta) noexcept @@ -1549,6 +1549,7 @@ struct ClipRegions EdgeTableRegion (const Rectangle& r) : edgeTable (r) {} EdgeTableRegion (const Rectangle& r) : edgeTable (r) {} EdgeTableRegion (const RectangleList& r) : edgeTable (r) {} + EdgeTableRegion (const RectangleList& r) : edgeTable (r) {} EdgeTableRegion (const Rectangle& bounds, const Path& p, const AffineTransform& t) : edgeTable (bounds, p, t) {} EdgeTableRegion (const EdgeTableRegion& other) : Base(), edgeTable (other.edgeTable) {} @@ -2207,6 +2208,28 @@ public: } } + void fillRectList (const RectangleList& list) + { + if (clip != nullptr) + { + if (! transform.isRotated) + { + RectangleList transformed (list); + + if (transform.isOnlyTranslated) + transformed.offsetAll (transform.offset.toFloat()); + else + transformed.transformAll (transform.getTransform()); + + fillShape (new EdgeTableRegionType (transformed), false); + } + else + { + fillPath (list.toPath(), AffineTransform::identity); + } + } + } + void fillPath (const Path& path, const AffineTransform& t) { if (clip != nullptr) @@ -2582,6 +2605,7 @@ public: void setOpacity (float newOpacity) override { stack->fillType.setOpacity (newOpacity); } void setInterpolationQuality (Graphics::ResamplingQuality quality) override { stack->interpolationQuality = quality; } void fillRect (const Rectangle& r, bool replace) override { stack->fillRect (r, replace); } + void fillRectList (const RectangleList& list) override { stack->fillRectList (list); } void fillPath (const Path& path, const AffineTransform& t) override { stack->fillPath (path, t); } void drawImage (const Image& im, const AffineTransform& t) override { stack->drawImage (im, t); } void drawVerticalLine (int x, float top, float bottom) override { if (top < bottom) stack->fillRect (Rectangle ((float) x, top, 1.0f, bottom - top)); } diff --git a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h index f791f6548c..f916ba4560 100644 --- a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h +++ b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h @@ -60,6 +60,7 @@ public: //============================================================================== void fillRect (const Rectangle&, bool replaceExistingContents) override; + void fillRectList (const RectangleList&) override; void fillPath (const Path&, const AffineTransform&) override; void drawImage (const Image& sourceImage, const AffineTransform&) override; diff --git a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm index 6e28293f6c..38d86c8432 100644 --- a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm +++ b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm @@ -556,6 +556,34 @@ void CoreGraphicsContext::drawHorizontalLine (const int y, float left, float rig } } +void CoreGraphicsContext::fillRectList (const RectangleList& list) +{ + HeapBlock rects (list.getNumRectangles()); + + size_t num = 0; + for (const Rectangle* r = list.begin(), * const e = list.end(); r != e; ++r) + rects[num++] = CGRectMake (r->getX(), flipHeight - r->getBottom(), r->getWidth(), r->getHeight()); + + if (state->fillType.isColour()) + { + CGContextFillRects (context, rects, num); + } + else if (state->fillType.isGradient()) + { + CGContextSaveGState (context); + CGContextFillRects (context, rects, num); + drawGradient(); + CGContextRestoreGState (context); + } + else + { + CGContextSaveGState (context); + CGContextFillRects (context, rects, num); + drawImage (state->fillType.image, state->fillType.transform, true); + CGContextRestoreGState (context); + } +} + void CoreGraphicsContext::setFont (const Font& newFont) { if (state->font != newFont) diff --git a/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp b/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp index e0855a979a..81d24668d8 100644 --- a/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp +++ b/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp @@ -185,6 +185,12 @@ public: renderingTarget->SetTransform (D2D1::IdentityMatrix()); } + void fillRectList (const RectangleList& list) + { + for (const Rectangle* r = list.begin(), * const e = list.end(); r != e; ++r) + fillRect (*r); + } + void fillPath (const Path& p, const AffineTransform& transform) { currentState->createBrush(); diff --git a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index 19ecd05cb8..e576b7496d 100644 --- a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -136,7 +136,7 @@ static bool canUseMultiTouch() return registerTouchWindow != nullptr; } -static inline Rectangle rectangleFromRECT (const RECT& r) noexcept +static Rectangle rectangleFromRECT (const RECT& r) noexcept { return Rectangle::leftTopRightBottom ((int) r.left, (int) r.top, (int) r.right, (int) r.bottom); } @@ -164,18 +164,22 @@ static void setDPIAwareness() if (JUCEApplication::isStandaloneApp()) { if (setProcessDPIAware == nullptr) + { setProcessDPIAware = (SetProcessDPIAwareFunc) getUser32Function ("SetProcessDPIAware"); - if (setProcessDPIAware != nullptr) - setProcessDPIAware(); + if (setProcessDPIAware != nullptr) + setProcessDPIAware(); + } } } -inline double getDPI() +static double getDPI() { + setDPIAwareness(); + HDC dc = GetDC (0); const double dpi = (GetDeviceCaps (dc, LOGPIXELSX) - + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0; + + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0; ReleaseDC (0, dc); return dpi; } @@ -1366,7 +1370,7 @@ private: return ! component.isOpaque(); } - inline bool hasTitleBar() const noexcept { return (styleFlags & windowHasTitleBar) != 0; } + bool hasTitleBar() const noexcept { return (styleFlags & windowHasTitleBar) != 0; } void setIcon (const Image& newIcon) @@ -3234,7 +3238,7 @@ void Desktop::Displays::findDisplays (float masterScale) d.dpi = dpi; if (i == 0) - d.userArea = d.userArea.getIntersection (rectangleFromRECT (workArea)); + d.userArea = d.userArea.getIntersection (rectangleFromRECT (workArea) / masterScale); displays.add (d); } diff --git a/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp b/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp index b1b9532da8..331bff0e90 100644 --- a/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp +++ b/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp @@ -80,18 +80,17 @@ public: return true; } - void draw (CodeEditorComponent& owner, Graphics& g, const Font& fontToUse, - const float rightClip, const float x, const int y, - const int lineH, const float characterWidth, - const Colour highlightColour) const + void getHighlightArea (RectangleList& area, float x, int y, int lineH, float characterWidth) const { if (highlightColumnStart < highlightColumnEnd) - { - g.setColour (highlightColour); - g.fillRect (roundToInt (x + highlightColumnStart * characterWidth), y, - roundToInt ((highlightColumnEnd - highlightColumnStart) * characterWidth), lineH); - } + area.addWithoutMerging (Rectangle (x + highlightColumnStart * characterWidth, (float) y, + (highlightColumnEnd - highlightColumnStart) * characterWidth, (float) lineH)); + } + void draw (CodeEditorComponent& owner, Graphics& g, const Font& fontToUse, + const float rightClip, const float x, const int y, + const int lineH, const float characterWidth) const + { Colour lastColour (0x00000001); AttributedString as; @@ -466,7 +465,6 @@ void CodeEditorComponent::paint (Graphics& g) g.reduceClipRegion (gutterSize, 0, verticalScrollBar.getX() - gutterSize, horizontalScrollBar.getY()); g.setFont (font); - const Colour highlightColour (findColour (CodeEditorComponent::highlightColourId)); const Rectangle clip (g.getClipBounds()); const int firstLineToDraw = jmax (0, clip.getY() / lineHeight); @@ -474,10 +472,18 @@ void CodeEditorComponent::paint (Graphics& g) const float x = (float) (gutterSize - xOffset * charWidth); const float rightClip = (float) clip.getRight(); + { + RectangleList highlightArea; + + for (int i = firstLineToDraw; i < lastLineToDraw; ++i) + lines.getUnchecked(i)->getHighlightArea (highlightArea, x, lineHeight * i, lineHeight, charWidth); + + g.setColour (findColour (CodeEditorComponent::highlightColourId)); + g.fillRectList (highlightArea); + } + for (int i = firstLineToDraw; i < lastLineToDraw; ++i) - lines.getUnchecked(i)->draw (*this, g, font, rightClip, - x, lineHeight * i, lineHeight, - charWidth, highlightColour); + lines.getUnchecked(i)->draw (*this, g, font, rightClip, x, lineHeight * i, lineHeight, charWidth); } void CodeEditorComponent::setScrollbarThickness (const int thickness)