| @@ -60,15 +60,18 @@ public: | |||
| if (backgroundGraphics != nullptr) | |||
| col = backgroundGraphics->getBackgroundColour().contrasting(); | |||
| g.setColour (col.withAlpha (0.1f)); | |||
| const Rectangle<int> 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<float> 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> ((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<float> (0.0f, (float) y, (float) clip.getRight(), 1.0f)); | |||
| g.setColour (col.withAlpha (0.1f)); | |||
| g.fillRectList (gridLines); | |||
| } | |||
| } | |||
| @@ -45,17 +45,21 @@ LiveAudioInputDisplayComp::~LiveAudioInputDisplayComp() | |||
| void LiveAudioInputDisplayComp::paint (Graphics& g) | |||
| { | |||
| g.fillAll (Colours::black); | |||
| RectangleList<float> 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> ((float) x, midY - sampleSize, 1.0f, sampleSize * 2.0f)); | |||
| } | |||
| g.fillAll (Colours::black); | |||
| g.setColour (Colours::green); | |||
| g.fillRectList (waveform); | |||
| } | |||
| void LiveAudioInputDisplayComp::timerCallback() | |||
| @@ -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<float> 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> ((float) x, y - length * 0.5f, 1.0f, length)); | |||
| } | |||
| g.setColour (Colours::green.withAlpha ((float) owner.opacitySlider->getValue())); | |||
| g.setColour (Colours::blue.withAlpha ((float) owner.opacitySlider->getValue())); | |||
| g.fillRectList (verticalLines); | |||
| } | |||
| 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<float> 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<float> (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]); | |||
| } | |||
| @@ -390,16 +390,25 @@ public: | |||
| const MinMaxValue* cacheData = getData (channelNum, clip.getX() - area.getX()); | |||
| int x = clip.getX(); | |||
| RectangleList<float> 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<float> (x, top, 1.0f, bottom - top)); | |||
| } | |||
| x += 1.0f; | |||
| ++cacheData; | |||
| } | |||
| g.fillRectList (waveform); | |||
| } | |||
| } | |||
| } | |||
| @@ -348,6 +348,11 @@ void Graphics::fillRect (const float x, const float y, const float width, const | |||
| fillRect (Rectangle<float> (x, y, width, height)); | |||
| } | |||
| void Graphics::fillRectList (const RectangleList<float>& rectangles) const | |||
| { | |||
| context.fillRectList (rectangles); | |||
| } | |||
| void Graphics::setPixel (int x, int y) const | |||
| { | |||
| context.fillRect (Rectangle<int> (x, y, 1, 1), false); | |||
| @@ -241,33 +241,39 @@ public: | |||
| //============================================================================== | |||
| /** 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<int>& rectangle) const; | |||
| /** Fills a rectangle with the current colour or brush. */ | |||
| /** Fills a rectangle with the current colour or brush. | |||
| @see drawRect, fillRoundedRectangle | |||
| */ | |||
| void fillRect (const Rectangle<float>& 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; | |||
| This uses sub-pixel positioning so is slower than the fillRect method which | |||
| takes integer coordinates. | |||
| /** Fills a rectangle with the current colour or brush. | |||
| @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<float>& 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<float>& 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<float>& 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; | |||
| //============================================================================== | |||
| @@ -85,6 +85,8 @@ public: | |||
| //============================================================================== | |||
| virtual void fillRect (const Rectangle<int>&, bool replaceExistingContents) = 0; | |||
| virtual void fillRectList (const RectangleList<float>&) = 0; | |||
| virtual void fillPath (const Path&, const AffineTransform&) = 0; | |||
| virtual void drawImage (const Image&, const AffineTransform&) = 0; | |||
| @@ -351,7 +351,16 @@ void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle<int>& r, cons | |||
| p.addRectangle (r); | |||
| fillPath (p, AffineTransform::identity); | |||
| } | |||
| } | |||
| void LowLevelGraphicsPostScriptRenderer::fillRectList (const RectangleList<float>& list) | |||
| { | |||
| for (const Rectangle<float>* r = list.begin(), * const e = list.end(); r != e; ++r) | |||
| { | |||
| Path p; | |||
| p.addRectangle (*r); | |||
| fillPath (p, AffineTransform::identity); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| @@ -72,12 +72,10 @@ public: | |||
| //============================================================================== | |||
| void fillRect (const Rectangle<int>&, bool replaceExistingContents) override; | |||
| void fillRectList (const RectangleList<float>&) override; | |||
| void fillPath (const Path&, const AffineTransform&) override; | |||
| void drawImage (const Image&, const AffineTransform&) override; | |||
| void drawLine (const Line <float>&) override; | |||
| void drawVerticalLine (int x, float top, float bottom) override; | |||
| void drawHorizontalLine (int x, float top, float bottom) override; | |||
| @@ -29,7 +29,7 @@ EdgeTable::EdgeTable (const Rectangle<int>& 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<int>& area, | |||
| EdgeTable::EdgeTable (const Rectangle<int>& 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<int>& rectangleToAdd) | |||
| EdgeTable::EdgeTable (const RectangleList<int>& 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<int>* r = rectanglesToAdd.begin(), * const e = rectanglesToAdd.end(); r != e; ++r) | |||
| { | |||
| @@ -146,10 +140,49 @@ EdgeTable::EdgeTable (const RectangleList<int>& 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<float>& 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<float>* 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) | |||
| { | |||
| addEdgePointPair (x1, x2, y, y2 - y1); | |||
| } | |||
| else | |||
| { | |||
| addEdgePoint (x1, y, 255); | |||
| addEdgePoint (x2, y, -255); | |||
| ++y; | |||
| 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<float>& 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; | |||
| if (num > 0) | |||
| { | |||
| LineItem* items = reinterpret_cast<LineItem*> (lineStart + 1); | |||
| int level = 0; | |||
| { | |||
| // sort the X coords | |||
| LineSorter sorter; | |||
| sortArray (sorter, items, 0, num - 1, false); | |||
| } | |||
| if (useNonZeroWinding) | |||
| { | |||
| 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) | |||
| corrected = 255; | |||
| if (items[i].x == items[i + 1].x) | |||
| { | |||
| items[i].level += items[i + 1].level; | |||
| memmove (items + i + 1, items + i + 2, (num - i - 2) * sizeof (LineItem)); | |||
| --num; | |||
| --lineStart[0]; | |||
| --i; | |||
| } | |||
| } | |||
| int level = 0; | |||
| *line = corrected; | |||
| 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) | |||
| else | |||
| { | |||
| line += 2; | |||
| level += *line; | |||
| int corrected = abs (level); | |||
| if (corrected >> 8) | |||
| while (--num > 0) | |||
| { | |||
| corrected &= 511; | |||
| level += items->level; | |||
| int corrected = std::abs (level); | |||
| if (corrected >> 8) | |||
| corrected = 511 - corrected; | |||
| { | |||
| corrected &= 511; | |||
| if (corrected >> 8) | |||
| corrected = 511 - corrected; | |||
| } | |||
| items->level = corrected; | |||
| ++items; | |||
| } | |||
| *line = corrected; | |||
| } | |||
| 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; | |||
| } | |||
| remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); | |||
| jassert (numPoints < maxEdgesPerLine); | |||
| line = table + lineStrideElements * y; | |||
| } | |||
| break; | |||
| } | |||
| line[0]++; | |||
| int n = numPoints << 1; | |||
| line [n + 1] = x; | |||
| line [n + 2] = winding; | |||
| } | |||
| n -= 2; | |||
| } | |||
| void EdgeTable::addEdgePointPair (int x1, int x2, int y, int winding) | |||
| { | |||
| jassert (y >= 0 && y < bounds.getHeight()); | |||
| if (numPoints >= maxEdgesPerLine) | |||
| { | |||
| remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); | |||
| jassert (numPoints < maxEdgesPerLine); | |||
| line = table + lineStrideElements * y; | |||
| } | |||
| int* line = table + lineStrideElements * y; | |||
| const int numPoints = line[0]; | |||
| memmove (line + (n + 3), line + (n + 1), sizeof (int) * (size_t) ((numPoints << 1) - n)); | |||
| if (numPoints + 1 >= maxEdgesPerLine) | |||
| { | |||
| remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); | |||
| jassert (numPoints < maxEdgesPerLine); | |||
| line = table + lineStrideElements * y; | |||
| } | |||
| line [n + 1] = x; | |||
| line[0] += 2; | |||
| int n = numPoints << 1; | |||
| line [n + 1] = x1; | |||
| line [n + 2] = winding; | |||
| line[0]++; | |||
| line [n + 3] = x2; | |||
| line [n + 4] = -winding; | |||
| } | |||
| void EdgeTable::translate (float dx, const int dy) noexcept | |||
| @@ -55,6 +55,9 @@ public: | |||
| /** Creates an edge table containing a rectangle list. */ | |||
| explicit EdgeTable (const RectangleList<int>& rectanglesToAdd); | |||
| /** Creates an edge table containing a rectangle list. */ | |||
| explicit EdgeTable (const RectangleList<float>& rectanglesToAdd); | |||
| /** Creates an edge table containing a rectangle. */ | |||
| explicit EdgeTable (const Rectangle<float>& 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<int> table; | |||
| Rectangle<int> 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; | |||
| @@ -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 | |||
| @@ -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<int> delta) noexcept | |||
| @@ -1549,6 +1549,7 @@ struct ClipRegions | |||
| EdgeTableRegion (const Rectangle<int>& r) : edgeTable (r) {} | |||
| EdgeTableRegion (const Rectangle<float>& r) : edgeTable (r) {} | |||
| EdgeTableRegion (const RectangleList<int>& r) : edgeTable (r) {} | |||
| EdgeTableRegion (const RectangleList<float>& r) : edgeTable (r) {} | |||
| EdgeTableRegion (const Rectangle<int>& 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<float>& list) | |||
| { | |||
| if (clip != nullptr) | |||
| { | |||
| if (! transform.isRotated) | |||
| { | |||
| RectangleList<float> 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<int>& r, bool replace) override { stack->fillRect (r, replace); } | |||
| void fillRectList (const RectangleList<float>& 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> ((float) x, top, 1.0f, bottom - top)); } | |||
| @@ -60,6 +60,7 @@ public: | |||
| //============================================================================== | |||
| void fillRect (const Rectangle<int>&, bool replaceExistingContents) override; | |||
| void fillRectList (const RectangleList<float>&) override; | |||
| void fillPath (const Path&, const AffineTransform&) override; | |||
| void drawImage (const Image& sourceImage, const AffineTransform&) override; | |||
| @@ -556,6 +556,34 @@ void CoreGraphicsContext::drawHorizontalLine (const int y, float left, float rig | |||
| } | |||
| } | |||
| void CoreGraphicsContext::fillRectList (const RectangleList<float>& list) | |||
| { | |||
| HeapBlock<CGRect> rects (list.getNumRectangles()); | |||
| size_t num = 0; | |||
| for (const Rectangle<float>* 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) | |||
| @@ -185,6 +185,12 @@ public: | |||
| renderingTarget->SetTransform (D2D1::IdentityMatrix()); | |||
| } | |||
| void fillRectList (const RectangleList<float>& list) | |||
| { | |||
| for (const Rectangle<float>* r = list.begin(), * const e = list.end(); r != e; ++r) | |||
| fillRect (*r); | |||
| } | |||
| void fillPath (const Path& p, const AffineTransform& transform) | |||
| { | |||
| currentState->createBrush(); | |||
| @@ -136,7 +136,7 @@ static bool canUseMultiTouch() | |||
| return registerTouchWindow != nullptr; | |||
| } | |||
| static inline Rectangle<int> rectangleFromRECT (const RECT& r) noexcept | |||
| static Rectangle<int> rectangleFromRECT (const RECT& r) noexcept | |||
| { | |||
| return Rectangle<int>::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); | |||
| } | |||
| @@ -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<float>& 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<float> (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<int> 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<float> 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) | |||