diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 06cbe698d4..9b623d8674 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -1653,7 +1653,7 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_SystemStats.cpp ***/ BEGIN_JUCE_NAMESPACE -const String SystemStats::getJUCEVersion() throw() +const String SystemStats::getJUCEVersion() { return "JUCE v" + String (JUCE_MAJOR_VERSION) + "." + String (JUCE_MINOR_VERSION) @@ -10545,7 +10545,7 @@ namespace NumberToStringConverters { juce_wchar* const end = buffer + numChars; juce_wchar* t = end; - int64 v = (int64) (pow (10.0, numDecPlaces) * fabs (n) + 0.5); + int64 v = (int64) (pow (10.0, numDecPlaces) * std::abs (n) + 0.5); *--t = (juce_wchar) 0; while (numDecPlaces >= 0 || v > 0) @@ -16565,7 +16565,7 @@ void ValueTree::writeToStream (OutputStream& output) ValueTree ValueTree::readFromStream (InputStream& input) { - String type (input.readString()); + const String type (input.readString()); if (type.isEmpty()) return ValueTree::invalid; @@ -20013,7 +20013,7 @@ int64 AudioFormatReader::searchForLevel (int64 startSample, if (usesFloatingPointData) { - const float sample1 = fabsf (((float*) tempBuffer[0]) [index]); + const float sample1 = std::abs (((float*) tempBuffer[0]) [index]); if (sample1 >= magnitudeRangeMinimum && sample1 <= magnitudeRangeMaximum) @@ -20022,7 +20022,7 @@ int64 AudioFormatReader::searchForLevel (int64 startSample, } else if (numChannels > 1) { - const float sample2 = fabsf (((float*) tempBuffer[1]) [index]); + const float sample2 = std::abs (((float*) tempBuffer[1]) [index]); matches = (sample2 >= magnitudeRangeMinimum && sample2 <= magnitudeRangeMaximum); @@ -23813,14 +23813,14 @@ void ResamplingAudioSource::createLowPass (const double frequencyRatio) const double n = 1.0 / tan (double_Pi * jmax (0.001, proportionalRate)); const double nSquared = n * n; - const double c1 = 1.0 / (1.0 + sqrt (2.0) * n + nSquared); + const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); setFilterCoefficients (c1, c1 * 2.0f, c1, 1.0, c1 * 2.0 * (1.0 - nSquared), - c1 * (1.0 - sqrt (2.0) * n + nSquared)); + c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); } void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6) @@ -23922,7 +23922,7 @@ void ToneGeneratorAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& for (int i = 0; i < info.numSamples; ++i) { - const float sample = amplitude * (float) sin (currentPhase); + const float sample = amplitude * (float) std::sin (currentPhase); currentPhase += phasePerSample; for (int j = info.buffer->getNumChannels(); --j >= 0;) @@ -24541,7 +24541,7 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat float s = 0; for (int i = 0; i < numInputChannels; ++i) - s += fabsf (inputChannelData[i][j]); + s += std::abs (inputChannelData[i][j]); s /= numInputChannels; @@ -24838,7 +24838,7 @@ void AudioDeviceManager::playTestSound() const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); for (int i = 0; i < soundLength; ++i) - samples[i] = amplitude * (float) sin (i * phasePerSample); + samples[i] = amplitude * (float) std::sin (i * phasePerSample); newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f); newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f); @@ -26097,7 +26097,7 @@ float AudioSampleBuffer::getRMSLevel (const int channel, sum += sample * sample; } - return (float) sqrt (sum / numSamples); + return (float) std::sqrt (sum / numSamples); } void AudioSampleBuffer::readFromAudioReader (AudioFormatReader* reader, @@ -26316,14 +26316,14 @@ void IIRFilter::makeLowPass (const double sampleRate, const double n = 1.0 / tan (double_Pi * frequency / sampleRate); const double nSquared = n * n; - const double c1 = 1.0 / (1.0 + sqrt (2.0) * n + nSquared); + const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); setCoefficients (c1, c1 * 2.0f, c1, 1.0, c1 * 2.0 * (1.0 - nSquared), - c1 * (1.0 - sqrt (2.0) * n + nSquared)); + c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); } void IIRFilter::makeHighPass (const double sampleRate, @@ -26331,14 +26331,14 @@ void IIRFilter::makeHighPass (const double sampleRate, { const double n = tan (double_Pi * frequency / sampleRate); const double nSquared = n * n; - const double c1 = 1.0 / (1.0 + sqrt (2.0) * n + nSquared); + const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); setCoefficients (c1, c1 * -2.0f, c1, 1.0, c1 * 2.0 * (nSquared - 1.0), - c1 * (1.0 - sqrt (2.0) * n + nSquared)); + c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); } void IIRFilter::makeLowShelf (const double sampleRate, @@ -26353,8 +26353,8 @@ void IIRFilter::makeLowShelf (const double sampleRate, const double aminus1 = A - 1.0; const double aplus1 = A + 1.0; const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; - const double coso = cos (omega); - const double beta = sin (omega) * sqrt (A) / Q; + const double coso = std::cos (omega); + const double beta = std::sin (omega) * std::sqrt (A) / Q; const double aminus1TimesCoso = aminus1 * coso; setCoefficients (A * (aplus1 - aminus1TimesCoso + beta), @@ -26377,8 +26377,8 @@ void IIRFilter::makeHighShelf (const double sampleRate, const double aminus1 = A - 1.0; const double aplus1 = A + 1.0; const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; - const double coso = cos (omega); - const double beta = sin (omega) * sqrt (A) / Q; + const double coso = std::cos (omega); + const double beta = std::sin (omega) * std::sqrt (A) / Q; const double aminus1TimesCoso = aminus1 * coso; setCoefficients (A * (aplus1 + aminus1TimesCoso + beta), @@ -26399,8 +26399,8 @@ void IIRFilter::makeBandPass (const double sampleRate, const double A = jmax (0.0f, gainFactor); const double omega = (double_Pi * 2.0 * jmax (centreFrequency, 2.0)) / sampleRate; - const double alpha = 0.5 * sin (omega) / Q; - const double c2 = -2.0 * cos (omega); + const double alpha = 0.5 * std::sin (omega) / Q; + const double c2 = -2.0 * std::cos (omega); const double alphaTimesA = alpha * A; const double alphaOverA = alpha / A; @@ -45564,39 +45564,70 @@ static bool isIdentifierStart (const juce_wchar c) throw() static bool isIdentifierBody (const juce_wchar c) throw() { - return CharacterFunctions::isLetter (c) - || CharacterFunctions::isDigit (c) + return CharacterFunctions::isLetterOrDigit (c) || c == '_' || c == '@'; } -static int parseIdentifier (CodeDocument::Iterator& source) throw() +static bool isReservedKeyword (const juce_wchar* const token, const int tokenLength) throw() { - static const juce_wchar* keywords2Char[] = - { T("if"), T("do"), T("or"), 0 }; + static const juce_wchar* const keywords2Char[] = + { T("if"), T("do"), T("or"), T("id"), 0 }; - static const juce_wchar* keywords3Char[] = + static const juce_wchar* const keywords3Char[] = { T("for"), T("int"), T("new"), T("try"), T("xor"), T("and"), T("asm"), T("not"), 0 }; - static const juce_wchar* keywords4Char[] = + static const juce_wchar* const keywords4Char[] = { T("bool"), T("void"), T("this"), T("true"), T("long"), T("else"), T("char"), T("enum"), T("case"), T("goto"), T("auto"), 0 }; - static const juce_wchar* keywords5Char[] = + static const juce_wchar* const keywords5Char[] = { T("while"), T("bitor"), T("break"), T("catch"), T("class"), T("compl"), T("const"), T("false"), T("float"), T("short"), T("throw"), T("union"), T("using"), T("or_eq"), 0 }; - static const juce_wchar* keywords6Char[] = + static const juce_wchar* const keywords6Char[] = { T("return"), T("struct"), T("and_eq"), T("bitand"), T("delete"), T("double"), T("extern"), T("friend"), T("inline"), T("not_eq"), T("public"), T("sizeof"), T("static"), T("signed"), T("switch"), T("typeid"), T("wchar_t"), T("xor_eq"), 0}; - static const juce_wchar* keywordsOther[] = + static const juce_wchar* const keywordsOther[] = { T("const_cast"), T("continue"), T("default"), T("explicit"), T("mutable"), T("namespace"), T("operator"), T("private"), T("protected"), T("register"), T("reinterpret_cast"), T("static_cast"), T("template"), T("typedef"), T("typename"), T("unsigned"), T("virtual"), T("volatile"), T("@implementation"), T("@interface"), T("@end"), T("@synthesize"), T("@dynamic"), T("@public"), T("@private"), T("@property"), T("@protected"), T("@class"), 0 }; + const juce_wchar* const* k; + + switch (tokenLength) + { + case 2: k = keywords2Char; break; + case 3: k = keywords3Char; break; + case 4: k = keywords4Char; break; + case 5: k = keywords5Char; break; + case 6: k = keywords6Char; break; + + default: + if (tokenLength < 2 || tokenLength > 16) + return false; + + k = keywordsOther; + break; + } + + int i = 0; + while (k[i] != 0) + { + if (k[i][0] == token[0] && CharacterFunctions::compare (k[i], token) == 0) + return true; + + ++i; + } + + return false; +} + +static int parseIdentifier (CodeDocument::Iterator& source) throw() +{ int tokenLength = 0; juce_wchar possibleIdentifier [19]; @@ -45613,26 +45644,9 @@ static int parseIdentifier (CodeDocument::Iterator& source) throw() if (tokenLength > 1 && tokenLength <= 16) { possibleIdentifier [tokenLength] = 0; - const juce_wchar** k; - - switch (tokenLength) - { - case 2: k = keywords2Char; break; - case 3: k = keywords3Char; break; - case 4: k = keywords4Char; break; - case 5: k = keywords5Char; break; - case 6: k = keywords6Char; break; - default: k = keywordsOther; break; - } - - int i = 0; - while (k[i] != 0) - { - if (k[i][0] == possibleIdentifier[0] && CharacterFunctions::compare (k[i], possibleIdentifier) == 0) - return CPlusPlusCodeTokeniser::tokenType_builtInKeyword; - ++i; - } + if (isReservedKeyword (possibleIdentifier, tokenLength)) + return CPlusPlusCodeTokeniser::tokenType_builtInKeyword; } return CPlusPlusCodeTokeniser::tokenType_identifier; @@ -46063,25 +46077,28 @@ int CPlusPlusCodeTokeniser::readNextToken (CodeDocument::Iterator& source) break; } - //jassert (result != tokenType_unknown); return result; } const StringArray CPlusPlusCodeTokeniser::getTokenTypes() { - StringArray s; - s.add ("Error"); - s.add ("Comment"); - s.add ("C++ keyword"); - s.add ("Identifier"); - s.add ("Integer literal"); - s.add ("Float literal"); - s.add ("String literal"); - s.add ("Operator"); - s.add ("Bracket"); - s.add ("Punctuation"); - s.add ("Preprocessor line"); - return s; + const char* const types[] = + { + "Error", + "Comment", + "C++ keyword", + "Identifier", + "Integer literal", + "Float literal", + "String literal", + "Operator", + "Bracket", + "Punctuation", + "Preprocessor line", + 0 + }; + + return StringArray (types); } const Colour CPlusPlusCodeTokeniser::getDefaultColour (const int tokenType) @@ -46107,6 +46124,11 @@ const Colour CPlusPlusCodeTokeniser::getDefaultColour (const int tokenType) return Colours::black; } +bool CPlusPlusCodeTokeniser::isReservedKeyword (const String& token) throw() +{ + return CppTokeniser::isReservedKeyword (token, token.length()); +} + END_JUCE_NAMESPACE /*** End of inlined file: juce_CPlusPlusCodeTokeniser.cpp ***/ @@ -48867,7 +48889,7 @@ void Slider::buttonClicked (Button* button) double Slider::constrainedValue (double value) const { if (interval > 0) - value = minimum + interval * floor ((value - minimum) / interval + 0.5); + value = minimum + interval * std::floor ((value - minimum) / interval + 0.5); if (value <= minimum || maximum <= minimum) value = minimum; @@ -49177,9 +49199,9 @@ void Slider::mouseDown (const MouseEvent& e) { const float mousePos = (float) (isVertical() ? e.y : e.x); - const float normalPosDistance = fabsf (getLinearSliderPos (currentValue.getValue()) - mousePos); - const float minPosDistance = fabsf (getLinearSliderPos (valueMin.getValue()) - 0.1f - mousePos); - const float maxPosDistance = fabsf (getLinearSliderPos (valueMax.getValue()) + 0.1f - mousePos); + const float normalPosDistance = std::abs (getLinearSliderPos (currentValue.getValue()) - mousePos); + const float minPosDistance = std::abs (getLinearSliderPos (valueMin.getValue()) - 0.1f - mousePos); + const float maxPosDistance = std::abs (getLinearSliderPos (valueMax.getValue()) + 0.1f - mousePos); if (style == TwoValueHorizontal || style == TwoValueVertical) { @@ -49311,9 +49333,9 @@ void Slider::modifierKeysChanged (const ModifierKeys& modifiers) static double smallestAngleBetween (double a1, double a2) { - return jmin (fabs (a1 - a2), - fabs (a1 + double_Pi * 2.0 - a2), - fabs (a2 + double_Pi * 2.0 - a1)); + return jmin (std::abs (a1 - a2), + std::abs (a1 + double_Pi * 2.0 - a2), + std::abs (a2 + double_Pi * 2.0 - a1)); } void Slider::mouseDrag (const MouseEvent& e) @@ -49329,13 +49351,13 @@ void Slider::mouseDrag (const MouseEvent& e) if (dx * dx + dy * dy > 25) { - double angle = atan2 ((double) dx, (double) -dy); + double angle = std::atan2 ((double) dx, (double) -dy); while (angle < 0.0) angle += double_Pi * 2.0; if (rotaryStop && ! e.mouseWasClicked()) { - if (fabs (angle - lastAngle) > double_Pi) + if (std::abs (angle - lastAngle) > double_Pi) { if (angle >= lastAngle) angle -= double_Pi * 2.0; @@ -49438,7 +49460,7 @@ void Slider::mouseDrag (const MouseEvent& e) if (speed != 0) { speed = 0.2 * velocityModeSensitivity - * (1.0 + sin (double_Pi * (1.5 + jmin (0.5, velocityModeOffset + * (1.0 + std::sin (double_Pi * (1.5 + jmin (0.5, velocityModeOffset + jmax (0.0, (double) (speed - velocityModeThreshold)) / maxSpeed)))); @@ -49525,7 +49547,7 @@ void Slider::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float w const double newValue = proportionOfLengthToValue (jlimit (0.0, 1.0, currentPos + proportionDelta)); double delta = (newValue != value) - ? jmax (fabs (newValue - value), interval) : 0; + ? jmax (std::abs (newValue - value), interval) : 0; if (value > newValue) delta = -delta; @@ -49872,7 +49894,7 @@ void TableHeaderComponent::resizeColumnsToFit (int firstColumnIndex, int targetT if (ci->isVisible()) { const int newWidth = jlimit (ci->minimumWidth, ci->maximumWidth, - (int) floor (sor.getItemSize (visIndex++))); + (int) std::floor (sor.getItemSize (visIndex++))); if (newWidth != ci->width) { @@ -60411,8 +60433,8 @@ void ComponentBoundsConstrainer::checkBounds (Rectangle& bounds, } else { - const double oldRatio = (old.getHeight() > 0) ? fabs (old.getWidth() / (double) old.getHeight()) : 0.0; - const double newRatio = fabs (w / (double) h); + const double oldRatio = (old.getHeight() > 0) ? std::abs (old.getWidth() / (double) old.getHeight()) : 0.0; + const double newRatio = std::abs (w / (double) h); adjustWidth = (oldRatio > newRatio); } @@ -63431,9 +63453,15 @@ void Viewport::setSingleStepSizes (const int stepX, const int stepY) void Viewport::setScrollBarsShown (const bool showVerticalScrollbarIfNeeded, const bool showHorizontalScrollbarIfNeeded) { - showVScrollbar = showVerticalScrollbarIfNeeded; - showHScrollbar = showHorizontalScrollbarIfNeeded; - updateVisibleRegion(); + if (showVScrollbar != showVerticalScrollbarIfNeeded + || showHScrollbar != showHorizontalScrollbarIfNeeded) + { + showVScrollbar = showVerticalScrollbarIfNeeded; + showHScrollbar = showHorizontalScrollbarIfNeeded; + horizontalScrollBar->setVisible (true); + verticalScrollBar->setVisible (true); + updateVisibleRegion(); + } } void Viewport::setScrollBarThickness (const int thickness) @@ -69647,7 +69675,7 @@ void DragAndDropContainer::startDragging (const String& sourceDescription, for (int x = dragImage->getWidth(); --x >= 0;) { const int dx = x - clipped.getX(); - const int distance = roundToInt (sqrt (dx * dx + dy)); + const int distance = roundToInt (std::sqrt (dx * dx + dy)); if (distance > lo) { @@ -71535,7 +71563,7 @@ public: { const float newLevel = (float) manager->getCurrentInputLevel(); - if (fabsf (level - newLevel) > 0.005f) + if (std::abs (level - newLevel) > 0.005f) { level = newLevel; repaint(); @@ -74060,7 +74088,7 @@ void MagnifierComponent::paint (Graphics& g) } Image temp (Image::ARGB, jmax (w, srcX + srcW), jmax (h, srcY + srcH), false); - temp.clear (srcX, srcY, srcW, srcH); + temp.clear (Rectangle (srcX, srcY, srcW, srcH)); { Graphics g2 (temp); @@ -75698,7 +75726,7 @@ public: setFont (font); setText (message, false); - bestWidth = 2 * (int) sqrt (font.getHeight() * font.getStringWidth (message)); + bestWidth = 2 * (int) std::sqrt (font.getHeight() * font.getStringWidth (message)); setColour (TextEditor::backgroundColourId, Colours::transparentBlack); setColour (TextEditor::outlineColourId, Colours::transparentBlack); @@ -75833,7 +75861,7 @@ void AlertWindow::updateLayout (const bool onlyIncreaseSize) const int wid = jmax (font.getStringWidth (text), font.getStringWidth (getName())); - const int sw = (int) sqrt (font.getHeight() * wid); + const int sw = (int) std::sqrt (font.getHeight() * wid); int w = jmin (300 + sw * 2, (int) (getParentWidth() * 0.7f)); const int edgeGap = 10; const int labelHeight = 18; @@ -78252,8 +78280,8 @@ namespace ColourHelpers { s = jmin (1.0f, s); h = jlimit (0.0f, 1.0f, h); - h = (h - floorf (h)) * 6.0f + 0.00001f; // need a small adjustment to compensate for rounding errors - const float f = h - floorf (h); + h = (h - std::floor (h)) * 6.0f + 0.00001f; // need a small adjustment to compensate for rounding errors + const float f = h - std::floor (h); const uint8 x = (uint8) roundToInt (v * (1.0f - s)); const float y = v * (1.0f - s * f); @@ -78594,7 +78622,7 @@ const Colour Colour::withRotatedHue (const float amountToRotate) const throw() getHSB (h, s, b); h += amountToRotate; - h -= floorf (h); + h -= std::floor (h); return Colour (h, s, b, getAlpha()); } @@ -78695,8 +78723,8 @@ const Colour Colour::contrasting (const Colour& colour1, for (float i = 0.0f; i < 1.0f; i += 0.02f) { - const float d1 = fabsf (i - b1); - const float d2 = fabsf (i - b2); + const float d1 = std::abs (i - b1); + const float d2 = std::abs (i - b2); const float dist = jmin (d1, d2, 1.0f - d1, 1.0f - d2); if (dist > bestDist) @@ -79274,7 +79302,7 @@ EdgeTable::EdgeTable (const Rectangle& bounds_, { const double startX = 256.0f * iter.x1; const double multiplier = (iter.x2 - iter.x1) / (iter.y2 - iter.y1); - const int stepSize = jlimit (1, 256, 256 / (1 + (int) fabs (multiplier))); + const int stepSize = jlimit (1, 256, 256 / (1 + (int) std::abs (multiplier))); do { @@ -79355,22 +79383,25 @@ EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) sanitiseLevels (true); } -EdgeTable::EdgeTable (const float x, const float y, const float w, const float h) - : bounds (Rectangle ((int) floorf (x), roundToInt (y * 256.0f) >> 8, 2 + (int) w, 2 + (int) h)), +EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) + : bounds (Rectangle ((int) std::floor (rectangleToAdd.getX()), + roundToInt (rectangleToAdd.getY() * 256.0f) >> 8, + 2 + (int) rectangleToAdd.getWidth(), + 2 + (int) rectangleToAdd.getHeight())), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), needToCheckEmptinesss (true) { - jassert (w > 0 && h > 0); + jassert (! rectangleToAdd.isEmpty()); table.malloc (jmax (1, bounds.getHeight()) * lineStrideElements); table[0] = 0; - const int x1 = roundToInt (x * 256.0f); - const int x2 = roundToInt ((x + w) * 256.0f); + const int x1 = roundToInt (rectangleToAdd.getX() * 256.0f); + const int x2 = roundToInt (rectangleToAdd.getRight() * 256.0f); - int y1 = roundToInt (y * 256.0f) - (bounds.getY() << 8); + int y1 = roundToInt (rectangleToAdd.getY() * 256.0f) - (bounds.getY() << 8); jassert (y1 < 256); - int y2 = roundToInt ((y + h) * 256.0f) - (bounds.getY() << 8); + int y2 = roundToInt (rectangleToAdd.getBottom() * 256.0f) - (bounds.getY() << 8); if (x2 <= x1 || y2 <= y1) { @@ -79431,7 +79462,6 @@ EdgeTable::EdgeTable (const float x, const float y, const float w, const float h } EdgeTable::EdgeTable (const EdgeTable& other) - : table (0) { operator= (other); } @@ -79584,9 +79614,9 @@ void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw line[0]++; } -void EdgeTable::translate (float dx, int dy) throw() +void EdgeTable::translate (float dx, const int dy) throw() { - bounds.setPosition (bounds.getX() + (int) floorf (dx), bounds.getY() + dy); + bounds.translate ((int) std::floor (dx), dy); int* lineStart = table; const int intDx = (int) (dx * 256.0f); @@ -81256,7 +81286,7 @@ template class SolidColourEdgeTableRenderer { public: - SolidColourEdgeTableRenderer (const Image::BitmapData& data_, const PixelARGB& colour) throw() + SolidColourEdgeTableRenderer (const Image::BitmapData& data_, const PixelARGB& colour) : data (data_), sourceColour (colour) { @@ -81392,8 +81422,8 @@ public: p2 = l2.getPointAlongLineProportionally (l2.findNearestPointTo (p1)); } - vertical = fabs (p1.getX() - p2.getX()) < 0.001f; - horizontal = fabs (p1.getY() - p2.getY()) < 0.001f; + vertical = std::abs (p1.getX() - p2.getX()) < 0.001f; + horizontal = std::abs (p1.getY() - p2.getY()) < 0.001f; if (vertical) { @@ -81445,7 +81475,7 @@ class RadialGradientPixelGenerator { public: RadialGradientPixelGenerator (const ColourGradient& gradient, const AffineTransform&, - const PixelARGB* const lookupTable_, const int numEntries_) throw() + const PixelARGB* const lookupTable_, const int numEntries_) : lookupTable (lookupTable_), numEntries (numEntries_), gx1 (gradient.x1), @@ -81455,8 +81485,8 @@ public: const float gdx = gradient.x1 - gradient.x2; const float gdy = gradient.y1 - gradient.y2; maxDist = gdx * gdx + gdy * gdy; - invScale = numEntries / sqrt (maxDist); - jassert (roundToInt (sqrt (maxDist) * invScale) <= numEntries); + invScale = numEntries / std::sqrt (maxDist); + jassert (roundToInt (std::sqrt (maxDist) * invScale) <= numEntries); } forcedinline void setY (const int y) throw() @@ -81471,7 +81501,7 @@ public: x *= x; x += dy; - return lookupTable [x >= maxDist ? numEntries : roundToInt (sqrt (x) * invScale)]; + return lookupTable [x >= maxDist ? numEntries : roundToInt (std::sqrt (x) * invScale)]; } protected: @@ -81488,7 +81518,7 @@ class TransformedRadialGradientPixelGenerator : public RadialGradientPixelGene { public: TransformedRadialGradientPixelGenerator (const ColourGradient& gradient, const AffineTransform& transform, - const PixelARGB* const lookupTable_, const int numEntries_) throw() + const PixelARGB* const lookupTable_, const int numEntries_) : RadialGradientPixelGenerator (gradient, transform, lookupTable_, numEntries_), inverseTransform (transform.inverted()) { @@ -81513,7 +81543,7 @@ public: if (x >= maxDist) return lookupTable [numEntries]; else - return lookupTable [jmin (numEntries, roundToInt (sqrt (x) * invScale))]; + return lookupTable [jmin (numEntries, roundToInt (std::sqrt (x) * invScale))]; } private: @@ -81529,7 +81559,7 @@ class GradientEdgeTableRenderer : public GradientType { public: GradientEdgeTableRenderer (const Image::BitmapData& destData_, const ColourGradient& gradient, const AffineTransform& transform, - const PixelARGB* const lookupTable_, const int numEntries_) throw() + const PixelARGB* const lookupTable_, const int numEntries_) : GradientType (gradient, transform, lookupTable_, numEntries_ - 1), destData (destData_) { @@ -81588,7 +81618,7 @@ public: ImageFillEdgeTableRenderer (const Image::BitmapData& destData_, const Image::BitmapData& srcData_, const int extraAlpha_, - const int x, const int y) throw() + const int x, const int y) : destData (destData_), srcData (srcData_), extraAlpha (extraAlpha_ + 1), @@ -81649,7 +81679,7 @@ public: } } - void clipEdgeTableLine (EdgeTable& et, int x, int y, int width) throw() + void clipEdgeTableLine (EdgeTable& et, int x, int y, int width) { jassert (x - xOffset >= 0 && x + width - xOffset <= srcData.width); SrcPixelType* s = (SrcPixelType*) srcData.getLinePointer (y - yOffset); @@ -81694,7 +81724,7 @@ public: const Image::BitmapData& srcData_, const AffineTransform& transform, const int extraAlpha_, - const bool betterQuality_) throw() + const bool betterQuality_) : interpolator (transform), destData (destData_), srcData (srcData_), @@ -81709,7 +81739,7 @@ public: scratchBuffer.malloc (scratchSize); } - ~TransformedImageFillEdgeTableRenderer() throw() + ~TransformedImageFillEdgeTableRenderer() { } @@ -81761,7 +81791,7 @@ public: } } - void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width) throw() + void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width) { if (width > scratchSize) { @@ -82079,66 +82109,928 @@ private: TransformedImageFillEdgeTableRenderer& operator= (const TransformedImageFillEdgeTableRenderer&); }; +class ClipRegionBase : public ReferenceCountedObject +{ +public: + ClipRegionBase() {} + virtual ~ClipRegionBase() {} + + typedef ReferenceCountedObjectPtr Ptr; + + virtual const Ptr clone() const = 0; + + const Ptr clipTo (ClipRegionBase* other); + virtual const Ptr clipToRectangle (const Rectangle& r) = 0; + virtual const Ptr clipToRectangleList (const RectangleList& r) = 0; + virtual const Ptr excludeClipRectangle (const Rectangle& r) = 0; + virtual const Ptr clipToPath (const Path& p, const AffineTransform& transform) = 0; + virtual const Ptr clipToEdgeTable (const EdgeTable& et) = 0; + virtual const Ptr clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& t, const bool betterQuality) = 0; + + virtual bool clipRegionIntersects (const Rectangle& r) const = 0; + virtual const Rectangle getClipBounds() const = 0; + + virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour, bool replaceContents) const = 0; + virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour) const = 0; + virtual void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const = 0; + virtual void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const = 0; + virtual void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, const AffineTransform& t, bool betterQuality, bool tiledFill) const = 0; + virtual void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, int x, int y, bool tiledFill) const = 0; + +protected: + + template + static void renderImageTransformedInternal (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, + const Rectangle& srcClip, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) + { + switch (destData.pixelFormat) + { + case Image::ARGB: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + default: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + } + break; + + case Image::RGB: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + default: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + } + break; + + default: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + default: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + } + break; + } + } + + template + static void renderImageUntransformedInternal (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, int x, int y, bool tiledFill) + { + switch (destData.pixelFormat) + { + case Image::ARGB: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + default: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + } + break; + + case Image::RGB: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + default: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + } + break; + + default: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + default: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + } + break; + } + } + + template + static void renderSolidFill (Iterator& iter, const Image::BitmapData& destData, const PixelARGB& fillColour, const bool replaceContents, DestPixelType*) + { + jassert (destData.pixelStride == sizeof (DestPixelType)); + if (replaceContents) + { + SolidColourEdgeTableRenderer r (destData, fillColour); + iter.iterate (r); + } + else + { + SolidColourEdgeTableRenderer r (destData, fillColour); + iter.iterate (r); + } + } + + template + static void renderGradient (Iterator& iter, const Image::BitmapData& destData, const ColourGradient& g, const AffineTransform& transform, + const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity, DestPixelType*) + { + jassert (destData.pixelStride == sizeof (DestPixelType)); + + if (g.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); + iter.iterate (renderer); + } + else + { + GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); + iter.iterate (renderer); + } + } + else + { + GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); + iter.iterate (renderer); + } + } +}; + +class ClipRegion_EdgeTable : public ClipRegionBase +{ +public: + ClipRegion_EdgeTable (const EdgeTable& e) : edgeTable (e) {} + ClipRegion_EdgeTable (const Rectangle& r) : edgeTable (r) {} + ClipRegion_EdgeTable (const Rectangle& r) : edgeTable (r) {} + ClipRegion_EdgeTable (const RectangleList& r) : edgeTable (r) {} + ClipRegion_EdgeTable (const Rectangle& bounds, const Path& p, const AffineTransform& t) : edgeTable (bounds, p, t) {} + ClipRegion_EdgeTable (const ClipRegion_EdgeTable& other) : edgeTable (other.edgeTable) {} + ~ClipRegion_EdgeTable() {} + + const Ptr clone() const + { + return new ClipRegion_EdgeTable (*this); + } + + const Ptr clipToRectangle (const Rectangle& r) + { + edgeTable.clipToRectangle (r); + return edgeTable.isEmpty() ? 0 : this; + } + + const Ptr clipToRectangleList (const RectangleList& r) + { + EdgeTable et (r); + edgeTable.clipToEdgeTable (et); + return edgeTable.isEmpty() ? 0 : this; + } + + const Ptr excludeClipRectangle (const Rectangle& r) + { + edgeTable.excludeRectangle (r); + return edgeTable.isEmpty() ? 0 : this; + } + + const Ptr clipToPath (const Path& p, const AffineTransform& transform) + { + EdgeTable et (edgeTable.getMaximumBounds(), p, transform); + edgeTable.clipToEdgeTable (et); + return edgeTable.isEmpty() ? 0 : this; + } + + const Ptr clipToEdgeTable (const EdgeTable& et) + { + edgeTable.clipToEdgeTable (et); + return edgeTable.isEmpty() ? 0 : this; + } + + const Ptr clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& transform, const bool betterQuality) + { + const Image::BitmapData srcData (image, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight()); + + if (transform.isOnlyTranslation()) + { + // If our translation doesn't involve any distortion, just use a simple blit.. + const int tx = (int) (transform.getTranslationX() * 256.0f); + const int ty = (int) (transform.getTranslationY() * 256.0f); + + if ((! betterQuality) || ((tx | ty) & 224) == 0) + { + const int imageX = ((tx + 128) >> 8); + const int imageY = ((ty + 128) >> 8); + + if (image.getFormat() == Image::ARGB) + straightClipImage (srcData, imageX, imageY, (PixelARGB*) 0); + else + straightClipImage (srcData, imageX, imageY, (PixelAlpha*) 0); + + return edgeTable.isEmpty() ? 0 : this; + } + } + + if (transform.isSingularity()) + return 0; + + { + Path p; + p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height); + EdgeTable et2 (edgeTable.getMaximumBounds(), p, transform); + edgeTable.clipToEdgeTable (et2); + } + + if (! edgeTable.isEmpty()) + { + if (image.getFormat() == Image::ARGB) + transformedClipImage (srcData, transform, betterQuality, (PixelARGB*) 0); + else + transformedClipImage (srcData, transform, betterQuality, (PixelAlpha*) 0); + } + + return edgeTable.isEmpty() ? 0 : this; + } + + bool clipRegionIntersects (const Rectangle& r) const + { + return edgeTable.getMaximumBounds().intersects (r); + } + + const Rectangle getClipBounds() const + { + return edgeTable.getMaximumBounds(); + } + + void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour, bool replaceContents) const + { + const Rectangle totalClip (edgeTable.getMaximumBounds()); + const Rectangle clipped (totalClip.getIntersection (area)); + + if (! clipped.isEmpty()) + { + ClipRegion_EdgeTable et (clipped); + et.edgeTable.clipToEdgeTable (edgeTable); + et.fillAllWithColour (destData, colour, replaceContents); + } + } + + void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour) const + { + const Rectangle totalClip (edgeTable.getMaximumBounds().toFloat()); + const Rectangle clipped (totalClip.getIntersection (area)); + + if (! clipped.isEmpty()) + { + ClipRegion_EdgeTable et (clipped); + et.edgeTable.clipToEdgeTable (edgeTable); + et.fillAllWithColour (destData, colour, false); + } + } + + void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const + { + switch (destData.pixelFormat) + { + case Image::ARGB: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelARGB*) 0); break; + case Image::RGB: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelRGB*) 0); break; + default: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelAlpha*) 0); break; + } + } + + void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const + { + HeapBlock lookupTable; + const int numLookupEntries = gradient.createLookupTable (transform, lookupTable); + jassert (numLookupEntries > 0); + + switch (destData.pixelFormat) + { + case Image::ARGB: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; + case Image::RGB: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; + default: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; + } + } + + void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) const + { + renderImageTransformedInternal (edgeTable, destData, srcData, srcClip, alpha, transform, betterQuality, tiledFill); + } + + void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, int x, int y, bool tiledFill) const + { + renderImageUntransformedInternal (edgeTable, destData, srcData, srcClip, alpha, x, y, tiledFill); + } + + EdgeTable edgeTable; + +private: + + template + void transformedClipImage (const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality, const SrcPixelType*) + { + TransformedImageFillEdgeTableRenderer renderer (srcData, srcData, transform, 255, betterQuality); + + for (int y = 0; y < edgeTable.getMaximumBounds().getHeight(); ++y) + renderer.clipEdgeTableLine (edgeTable, edgeTable.getMaximumBounds().getX(), y + edgeTable.getMaximumBounds().getY(), + edgeTable.getMaximumBounds().getWidth()); + } + + template + void straightClipImage (const Image::BitmapData& srcData, int imageX, int imageY, const SrcPixelType*) + { + Rectangle r (imageX, imageY, srcData.width, srcData.height); + edgeTable.clipToRectangle (r); + + ImageFillEdgeTableRenderer renderer (srcData, srcData, 255, imageX, imageY); + + for (int y = 0; y < r.getHeight(); ++y) + renderer.clipEdgeTableLine (edgeTable, r.getX(), y + r.getY(), r.getWidth()); + } + + ClipRegion_EdgeTable& operator= (const ClipRegion_EdgeTable&); +}; + +class ClipRegion_RectangleList : public ClipRegionBase +{ +public: + ClipRegion_RectangleList (const Rectangle& r) : clip (r) {} + ClipRegion_RectangleList (const RectangleList& r) : clip (r) {} + ClipRegion_RectangleList (const ClipRegion_RectangleList& other) : clip (other.clip) {} + ~ClipRegion_RectangleList() {} + + const Ptr clone() const + { + return new ClipRegion_RectangleList (*this); + } + + const Ptr clipToRectangle (const Rectangle& r) + { + clip.clipTo (r); + return clip.isEmpty() ? 0 : this; + } + + const Ptr clipToRectangleList (const RectangleList& r) + { + clip.clipTo (r); + return clip.isEmpty() ? 0 : this; + } + + const Ptr excludeClipRectangle (const Rectangle& r) + { + clip.subtract (r); + return clip.isEmpty() ? 0 : this; + } + + const Ptr clipToPath (const Path& p, const AffineTransform& transform) + { + return Ptr (new ClipRegion_EdgeTable (clip))->clipToPath (p, transform); + } + + const Ptr clipToEdgeTable (const EdgeTable& et) + { + return Ptr (new ClipRegion_EdgeTable (clip))->clipToEdgeTable (et); + } + + const Ptr clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& transform, const bool betterQuality) + { + return Ptr (new ClipRegion_EdgeTable (clip))->clipToImageAlpha (image, srcClip, transform, betterQuality); + } + + bool clipRegionIntersects (const Rectangle& r) const + { + return clip.intersects (r); + } + + const Rectangle getClipBounds() const + { + return clip.getBounds(); + } + + void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour, bool replaceContents) const + { + SubRectangleIterator iter (clip, area); + + switch (destData.pixelFormat) + { + case Image::ARGB: renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) 0); break; + case Image::RGB: renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) 0); break; + default: renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) 0); break; + } + } + + void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour) const + { + SubRectangleIteratorFloat iter (clip, area); + + switch (destData.pixelFormat) + { + case Image::ARGB: renderSolidFill (iter, destData, colour, false, (PixelARGB*) 0); break; + case Image::RGB: renderSolidFill (iter, destData, colour, false, (PixelRGB*) 0); break; + default: renderSolidFill (iter, destData, colour, false, (PixelAlpha*) 0); break; + } + } + + void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const + { + switch (destData.pixelFormat) + { + case Image::ARGB: renderSolidFill (*this, destData, colour, replaceContents, (PixelARGB*) 0); break; + case Image::RGB: renderSolidFill (*this, destData, colour, replaceContents, (PixelRGB*) 0); break; + default: renderSolidFill (*this, destData, colour, replaceContents, (PixelAlpha*) 0); break; + } + } + + void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const + { + HeapBlock lookupTable; + const int numLookupEntries = gradient.createLookupTable (transform, lookupTable); + jassert (numLookupEntries > 0); + + switch (destData.pixelFormat) + { + case Image::ARGB: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; + case Image::RGB: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; + default: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; + } + } + + void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) const + { + renderImageTransformedInternal (*this, destData, srcData, srcClip, alpha, transform, betterQuality, tiledFill); + } + + void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, int x, int y, bool tiledFill) const + { + renderImageUntransformedInternal (*this, destData, srcData, srcClip, alpha, x, y, tiledFill); + } + + RectangleList clip; + + template + void iterate (Renderer& r) const throw() + { + RectangleList::Iterator iter (clip); + + while (iter.next()) + { + const Rectangle rect (*iter.getRectangle()); + const int x = rect.getX(); + const int w = rect.getWidth(); + jassert (w > 0); + const int bottom = rect.getBottom(); + + for (int y = rect.getY(); y < bottom; ++y) + { + r.setEdgeTableYPos (y); + r.handleEdgeTableLine (x, w, 255); + } + } + } + +private: + + class SubRectangleIterator + { + public: + SubRectangleIterator (const RectangleList& clip_, const Rectangle& area_) + : clip (clip_), area (area_) + { + } + + template + void iterate (Renderer& r) const throw() + { + RectangleList::Iterator iter (clip); + + while (iter.next()) + { + const Rectangle rect (iter.getRectangle()->getIntersection (area)); + + if (! rect.isEmpty()) + { + const int x = rect.getX(); + const int w = rect.getWidth(); + const int bottom = rect.getBottom(); + + for (int y = rect.getY(); y < bottom; ++y) + { + r.setEdgeTableYPos (y); + r.handleEdgeTableLine (x, w, 255); + } + } + } + } + + private: + const RectangleList& clip; + const Rectangle area; + }; + + class SubRectangleIteratorFloat + { + public: + SubRectangleIteratorFloat (const RectangleList& clip_, const Rectangle& area_) + : clip (clip_), area (area_) + { + } + + template + void iterate (Renderer& r) const throw() + { + int left = roundToInt (area.getX() * 256.0f); + int top = roundToInt (area.getY() * 256.0f); + int right = roundToInt (area.getRight() * 256.0f); + int bottom = roundToInt (area.getBottom() * 256.0f); + + int totalTop, totalLeft, totalBottom, totalRight; + int topAlpha, leftAlpha, bottomAlpha, rightAlpha; + + if ((top >> 8) == (bottom >> 8)) + { + topAlpha = bottom - top; + bottomAlpha = 0; + totalTop = top >> 8; + totalBottom = bottom = top = totalTop + 1; + } + else + { + if ((top & 255) == 0) + { + topAlpha = 0; + top = totalTop = (top >> 8); + } + else + { + topAlpha = 255 - (top & 255); + totalTop = (top >> 8); + top = totalTop + 1; + } + + bottomAlpha = bottom & 255; + bottom >>= 8; + totalBottom = bottom + (bottomAlpha != 0 ? 1 : 0); + } + + if ((left >> 8) == (right >> 8)) + { + leftAlpha = right - left; + rightAlpha = 0; + totalLeft = (left >> 8); + totalRight = right = left = totalLeft + 1; + } + else + { + if ((left & 255) == 0) + { + leftAlpha = 0; + left = totalLeft = (left >> 8); + } + else + { + leftAlpha = 255 - (left & 255); + totalLeft = (left >> 8); + left = totalLeft + 1; + } + + rightAlpha = right & 255; + right >>= 8; + totalRight = right + (rightAlpha != 0 ? 1 : 0); + } + + RectangleList::Iterator iter (clip); + + while (iter.next()) + { + const int clipLeft = iter.getRectangle()->getX(); + const int clipRight = iter.getRectangle()->getRight(); + const int clipTop = iter.getRectangle()->getY(); + const int clipBottom = iter.getRectangle()->getBottom(); + + if (totalBottom > clipTop && totalTop < clipBottom && totalRight > clipLeft && totalLeft < clipRight) + { + if (right - left == 1 && leftAlpha + rightAlpha == 0) // special case for 1-pix vertical lines + { + if (topAlpha != 0 && totalTop >= clipTop) + { + r.setEdgeTableYPos (totalTop); + r.handleEdgeTablePixel (left, topAlpha); + } + + const int endY = jmin (bottom, clipBottom); + for (int y = jmax (clipTop, top); y < endY; ++y) + { + r.setEdgeTableYPos (y); + r.handleEdgeTablePixel (left, 255); + } + + if (bottomAlpha != 0 && bottom < clipBottom) + { + r.setEdgeTableYPos (bottom); + r.handleEdgeTablePixel (left, bottomAlpha); + } + } + else + { + const int clippedLeft = jmax (left, clipLeft); + const int clippedWidth = jmin (right, clipRight) - clippedLeft; + const bool doLeftAlpha = leftAlpha != 0 && totalLeft >= clipLeft; + const bool doRightAlpha = rightAlpha != 0 && right < clipRight; + + if (topAlpha != 0 && totalTop >= clipTop) + { + r.setEdgeTableYPos (totalTop); + + if (doLeftAlpha) + r.handleEdgeTablePixel (totalLeft, (leftAlpha * topAlpha) >> 8); + + if (clippedWidth > 0) + r.handleEdgeTableLine (clippedLeft, clippedWidth, topAlpha); + + if (doRightAlpha) + r.handleEdgeTablePixel (right, (rightAlpha * topAlpha) >> 8); + } + + const int endY = jmin (bottom, clipBottom); + for (int y = jmax (clipTop, top); y < endY; ++y) + { + r.setEdgeTableYPos (y); + + if (doLeftAlpha) + r.handleEdgeTablePixel (totalLeft, leftAlpha); + + if (clippedWidth > 0) + r.handleEdgeTableLine (clippedLeft, clippedWidth, 255); + + if (doRightAlpha) + r.handleEdgeTablePixel (right, rightAlpha); + } + + if (bottomAlpha != 0 && bottom < clipBottom) + { + r.setEdgeTableYPos (bottom); + + if (doLeftAlpha) + r.handleEdgeTablePixel (totalLeft, (leftAlpha * bottomAlpha) >> 8); + + if (clippedWidth > 0) + r.handleEdgeTableLine (clippedLeft, clippedWidth, bottomAlpha); + + if (doRightAlpha) + r.handleEdgeTablePixel (right, (rightAlpha * bottomAlpha) >> 8); + } + } + } + } + } + + private: + const RectangleList& clip; + const Rectangle& area; + }; + + ClipRegion_RectangleList& operator= (const ClipRegion_RectangleList&); +}; + +const ClipRegionBase::Ptr ClipRegionBase::clipTo (ClipRegionBase* const other) +{ + ClipRegion_EdgeTable* et = dynamic_cast (other); + + if (et != 0) + return clipToEdgeTable (et->edgeTable); + + ClipRegion_RectangleList* rl = dynamic_cast (other); + + if (rl != 0) + return clipToRectangleList (rl->clip); + + jassertfalse + return 0; +} + class LLGCSavedState { public: - LLGCSavedState (const Rectangle& clip_, const int xOffset_, const int yOffset_, - const Font& font_, const FillType& fillType_, - const Graphics::ResamplingQuality interpolationQuality_) throw() - : edgeTable (new EdgeTableHolder (EdgeTable (clip_))), + LLGCSavedState (const Rectangle& clip_, const int xOffset_, const int yOffset_) + : clip (new ClipRegion_RectangleList (clip_)), + xOffset (xOffset_), yOffset (yOffset_), + interpolationQuality (Graphics::mediumResamplingQuality) + { + } + + LLGCSavedState (const RectangleList& clip_, const int xOffset_, const int yOffset_) + : clip (new ClipRegion_RectangleList (clip_)), xOffset (xOffset_), yOffset (yOffset_), - font (font_), fillType (fillType_), - interpolationQuality (interpolationQuality_) + interpolationQuality (Graphics::mediumResamplingQuality) { } - LLGCSavedState (const LLGCSavedState& other) throw() - : edgeTable (other.edgeTable), xOffset (other.xOffset), + LLGCSavedState (const LLGCSavedState& other) + : clip (other.clip), xOffset (other.xOffset), yOffset (other.yOffset), font (other.font), fillType (other.fillType), interpolationQuality (other.interpolationQuality) { } - ~LLGCSavedState() throw() + ~LLGCSavedState() { } - bool clipToRectangle (const Rectangle& r) throw() + void setOrigin (const int x, const int y) throw() { - dupeEdgeTableIfMultiplyReferenced(); - edgeTable->edgeTable.clipToRectangle (r.translated (xOffset, yOffset)); - return ! edgeTable->edgeTable.isEmpty(); + xOffset += x; + yOffset += y; } - bool clipToRectangleList (const RectangleList& r) throw() + bool clipToRectangle (const Rectangle& r) { - dupeEdgeTableIfMultiplyReferenced(); - RectangleList offsetList (r); - offsetList.offsetAll (xOffset, yOffset); - EdgeTable e2 (offsetList); - edgeTable->edgeTable.clipToEdgeTable (e2); + if (clip != 0) + { + cloneClipIfMultiplyReferenced(); + clip = clip->clipToRectangle (r.translated (xOffset, yOffset)); + } - return ! edgeTable->edgeTable.isEmpty(); + return clip != 0; } - bool excludeClipRectangle (const Rectangle& r) throw() + bool clipToRectangleList (const RectangleList& r) { - dupeEdgeTableIfMultiplyReferenced(); - edgeTable->edgeTable.excludeRectangle (r.translated (xOffset, yOffset)); - return ! edgeTable->edgeTable.isEmpty(); + if (clip != 0) + { + cloneClipIfMultiplyReferenced(); + + RectangleList offsetList (r); + offsetList.offsetAll (xOffset, yOffset); + clip = clip->clipToRectangleList (offsetList); + } + + return clip != 0; } - void clipToPath (const Path& p, const AffineTransform& transform) throw() + bool excludeClipRectangle (const Rectangle& r) { - dupeEdgeTableIfMultiplyReferenced(); - EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform.translated ((float) xOffset, (float) yOffset)); - edgeTable->edgeTable.clipToEdgeTable (et); + if (clip != 0) + { + cloneClipIfMultiplyReferenced(); + clip = clip->excludeClipRectangle (r.translated (xOffset, yOffset)); + } + + return clip != 0; } - void fillEdgeTable (Image& image, EdgeTable& et, const bool replaceContents = false) throw() + void clipToPath (const Path& p, const AffineTransform& transform) { - et.clipToEdgeTable (edgeTable->edgeTable); + if (clip != 0) + { + cloneClipIfMultiplyReferenced(); + clip = clip->clipToPath (p, transform.translated ((float) xOffset, (float) yOffset)); + } + } + void clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& t) + { + if (clip != 0) + { + if (image.hasAlphaChannel()) + { + cloneClipIfMultiplyReferenced(); + clip = clip->clipToImageAlpha (image, srcClip, t.translated ((float) xOffset, (float) yOffset), + interpolationQuality != Graphics::lowResamplingQuality); + } + else + { + Path p; + p.addRectangle (srcClip); + clipToPath (p, t); + } + } + } + + bool clipRegionIntersects (const Rectangle& r) const + { + return clip != 0 && clip->clipRegionIntersects (r.translated (xOffset, yOffset)); + } + + const Rectangle getClipBounds() const + { + return clip == 0 ? Rectangle() : clip->getClipBounds().translated (-xOffset, -yOffset); + } + + void fillRect (Image& image, const Rectangle& r, const bool replaceContents) + { + if (clip != 0) + { + if (fillType.isColour()) + { + Image::BitmapData destData (image, 0, 0, image.getWidth(), image.getHeight(), true); + clip->fillRectWithColour (destData, r.translated (xOffset, yOffset), fillType.colour.getPixelARGB(), replaceContents); + } + else + { + const Rectangle totalClip (clip->getClipBounds()); + const Rectangle clipped (totalClip.getIntersection (r.translated (xOffset, yOffset))); + + if (! clipped.isEmpty()) + fillShape (image, new ClipRegion_RectangleList (clipped), false); + } + } + } + + void fillRect (Image& image, const Rectangle& r) + { + if (clip != 0) + { + if (fillType.isColour()) + { + Image::BitmapData destData (image, 0, 0, image.getWidth(), image.getHeight(), true); + clip->fillRectWithColour (destData, r.translated ((float) xOffset, (float) yOffset), fillType.colour.getPixelARGB()); + } + else + { + const Rectangle totalClip (clip->getClipBounds().toFloat()); + const Rectangle clipped (totalClip.getIntersection (r.translated ((float) xOffset, (float) yOffset))); + + if (! clipped.isEmpty()) + fillShape (image, new ClipRegion_EdgeTable (clipped), false); + } + } + } + + void fillPath (Image& image, const Path& path, const AffineTransform& transform) + { + if (clip != 0) + fillShape (image, new ClipRegion_EdgeTable (clip->getClipBounds(), path, transform.translated ((float) xOffset, (float) yOffset)), false); + } + + void fillEdgeTable (Image& image, const EdgeTable& edgeTable, const float x, const int y) + { + if (clip != 0) + { + ClipRegion_EdgeTable* edgeTableClip = new ClipRegion_EdgeTable (edgeTable); + ClipRegionBase::Ptr shapeToFill (edgeTableClip); + edgeTableClip->edgeTable.translate (x + xOffset, y + yOffset); + fillShape (image, shapeToFill, false); + } + } + + void fillShape (Image& image, ClipRegionBase::Ptr shapeToFill, const bool replaceContents) + { + jassert (clip != 0); + + shapeToFill = shapeToFill->clipTo (clip); + + if (shapeToFill != 0) + fillShapeWithoutClipping (image, shapeToFill, replaceContents); + } + + void fillShapeWithoutClipping (Image& image, const ClipRegionBase::Ptr& shapeToFill, const bool replaceContents) + { Image::BitmapData destData (image, 0, 0, image.getWidth(), image.getHeight(), true); if (fillType.isGradient()) @@ -82160,36 +83052,20 @@ public: transform = AffineTransform::identity; } - HeapBlock lookupTable; - const int numLookupEntries = g2.createLookupTable (transform, lookupTable); - jassert (numLookupEntries > 0); - - switch (image.getFormat()) - { - case Image::ARGB: renderGradient (et, destData, g2, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; - case Image::RGB: renderGradient (et, destData, g2, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; - default: renderGradient (et, destData, g2, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; - } + shapeToFill->fillAllWithGradient (destData, g2, transform, isIdentity); } else if (fillType.isTiledImage()) { - renderImage (image, *(fillType.image), fillType.image->getBounds(), fillType.transform, &et); + renderImage (image, *(fillType.image), fillType.image->getBounds(), fillType.transform, shapeToFill); } else { - const PixelARGB fillColour (fillType.colour.getPixelARGB()); - - switch (image.getFormat()) - { - case Image::ARGB: renderSolidFill (et, destData, fillColour, replaceContents, (PixelARGB*) 0); break; - case Image::RGB: renderSolidFill (et, destData, fillColour, replaceContents, (PixelRGB*) 0); break; - default: renderSolidFill (et, destData, fillColour, replaceContents, (PixelAlpha*) 0); break; - } + shapeToFill->fillAllWithColour (destData, fillType.colour.getPixelARGB(), replaceContents); } } void renderImage (Image& destImage, const Image& sourceImage, const Rectangle& srcClip, - const AffineTransform& t, const EdgeTable* const tiledFillClipRegion) throw() + const AffineTransform& t, const ClipRegionBase* const tiledFillClipRegion) { const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); @@ -82211,15 +83087,15 @@ public: if (tiledFillClipRegion != 0) { - blittedRenderImage (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, tx, ty, true); + tiledFillClipRegion->renderImageUntransformed (destData, srcData, srcClip, alpha, tx, ty, true); } else { - EdgeTable et (Rectangle (tx, ty, srcClip.getWidth(), srcClip.getHeight()).getIntersection (destImage.getBounds())); - et.clipToEdgeTable (edgeTable->edgeTable); + ClipRegionBase::Ptr c (new ClipRegion_EdgeTable (Rectangle (tx, ty, srcClip.getWidth(), srcClip.getHeight()).getIntersection (destImage.getBounds()))); + c = c->clipTo (clip); - if (! et.isEmpty()) - blittedRenderImage (sourceImage, destImage, et, destData, srcData, alpha, tx, ty, false); + if (c != 0) + c->renderImageUntransformed (destData, srcData, srcClip, alpha, tx, ty, false); } return; @@ -82231,291 +83107,48 @@ public: if (tiledFillClipRegion != 0) { - transformedRenderImage (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, transform, betterQuality, true); + tiledFillClipRegion->renderImageTransformed (destData, srcData, srcClip, alpha, transform, betterQuality, true); } else { Path p; p.addRectangle (0.0f, 0.0f, (float) srcClip.getWidth(), (float) srcClip.getHeight()); - EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); - et.clipToEdgeTable (edgeTable->edgeTable); + ClipRegionBase::Ptr c (clip->clone()); + c = c->clipToPath (p, transform); - if (! et.isEmpty()) - transformedRenderImage (sourceImage, destImage, et, destData, srcData, alpha, transform, betterQuality, false); - } - } - - void clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& t) throw() - { - if (! image.hasAlphaChannel()) - { - Path p; - p.addRectangle (srcClip); - clipToPath (p, t); - return; - } - - dupeEdgeTableIfMultiplyReferenced(); - - const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); - const Image::BitmapData srcData (image, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight()); - const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); - EdgeTable& et = edgeTable->edgeTable; - - if (transform.isOnlyTranslation()) - { - // If our translation doesn't involve any distortion, just use a simple blit.. - const int tx = (int) (transform.getTranslationX() * 256.0f); - const int ty = (int) (transform.getTranslationY() * 256.0f); - - if ((! betterQuality) || ((tx | ty) & 224) == 0) - { - const int imageX = ((tx + 128) >> 8); - const int imageY = ((ty + 128) >> 8); - - if (image.getFormat() == Image::ARGB) - straightClipImage (et, srcData, imageX, imageY, (PixelARGB*)0); - else - straightClipImage (et, srcData, imageX, imageY, (PixelAlpha*)0); - - return; - } - } - - if (transform.isSingularity()) - { - et.clipToRectangle (Rectangle()); - return; - } - - { - Path p; - p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height); - EdgeTable et2 (et.getMaximumBounds(), p, transform); - et.clipToEdgeTable (et2); - } - - if (! et.isEmpty()) - { - if (image.getFormat() == Image::ARGB) - transformedClipImage (et, srcData, transform, betterQuality, (PixelARGB*)0); - else - transformedClipImage (et, srcData, transform, betterQuality, (PixelAlpha*)0); + if (c != 0) + c->renderImageTransformed (destData, srcData, srcClip, alpha, transform, betterQuality, true); } } - template - void transformedClipImage (EdgeTable& et, const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality, const SrcPixelType *) throw() - { - TransformedImageFillEdgeTableRenderer renderer (srcData, srcData, transform, 255, betterQuality); - - for (int y = 0; y < et.getMaximumBounds().getHeight(); ++y) - renderer.clipEdgeTableLine (et, et.getMaximumBounds().getX(), y + et.getMaximumBounds().getY(), - et.getMaximumBounds().getWidth()); - } - - template - void straightClipImage (EdgeTable& et, const Image::BitmapData& srcData, int imageX, int imageY, const SrcPixelType *) throw() - { - Rectangle r (imageX, imageY, srcData.width, srcData.height); - et.clipToRectangle (r); - - ImageFillEdgeTableRenderer renderer (srcData, srcData, 255, imageX, imageY); - - for (int y = 0; y < r.getHeight(); ++y) - renderer.clipEdgeTableLine (et, r.getX(), y + r.getY(), r.getWidth()); - } - - class EdgeTableHolder : public ReferenceCountedObject - { - public: - EdgeTableHolder (const EdgeTable& e) throw() : edgeTable (e) {} - - EdgeTable edgeTable; - }; - - ReferenceCountedObjectPtr edgeTable; + ClipRegionBase::Ptr clip; int xOffset, yOffset; Font font; FillType fillType; Graphics::ResamplingQuality interpolationQuality; private: - LLGCSavedState& operator= (const LLGCSavedState&); - - void dupeEdgeTableIfMultiplyReferenced() throw() - { - if (edgeTable->getReferenceCount() > 1) - edgeTable = new EdgeTableHolder (edgeTable->edgeTable); - } - - template - void renderGradient (EdgeTable& et, const Image::BitmapData& destData, const ColourGradient& g, const AffineTransform& transform, - const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity, DestPixelType*) throw() - { - jassert (destData.pixelStride == sizeof (DestPixelType)); - - if (g.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); - et.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - - template - void renderSolidFill (EdgeTable& et, const Image::BitmapData& destData, const PixelARGB& fillColour, const bool replaceContents, DestPixelType*) throw() + void cloneClipIfMultiplyReferenced() { - jassert (destData.pixelStride == sizeof (DestPixelType)); - if (replaceContents) - { - SolidColourEdgeTableRenderer r (destData, fillColour); - et.iterate (r); - } - else - { - SolidColourEdgeTableRenderer r (destData, fillColour); - et.iterate (r); - } + if (clip->getReferenceCount() > 1) + clip = clip->clone(); } - void transformedRenderImage (const Image& srcImage, Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData, - const int alpha, const AffineTransform& transform, const bool betterQuality, const bool repeatPattern) throw() - { - switch (destImage.getFormat()) - { - case Image::ARGB: - switch (srcImage.getFormat()) - { - case Image::ARGB: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - case Image::RGB: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - default: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - } - break; - case Image::RGB: - switch (srcImage.getFormat()) - { - case Image::ARGB: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - case Image::RGB: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - default: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - } - break; - default: - switch (srcImage.getFormat()) - { - case Image::ARGB: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - case Image::RGB: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - default: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - } - break; - } - } - - void blittedRenderImage (const Image& srcImage, Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, - const Image::BitmapData& srcData, const int alpha, int x, int y, const bool repeatPattern) throw() - { - switch (destImage.getFormat()) - { - case Image::ARGB: - switch (srcImage.getFormat()) - { - case Image::ARGB: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - case Image::RGB: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - default: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - } - break; - case Image::RGB: - switch (srcImage.getFormat()) - { - case Image::ARGB: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - case Image::RGB: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - default: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - } - break; - default: - switch (srcImage.getFormat()) - { - case Image::ARGB: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - case Image::RGB: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - default: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - } - break; - } - } + LLGCSavedState& operator= (const LLGCSavedState&); }; LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image_) : image (image_) { - currentState = new LLGCSavedState (image_.getBounds(), 0, 0, Font(), - FillType(), Graphics::mediumResamplingQuality); + currentState = new LLGCSavedState (image_.getBounds(), 0, 0); +} + +LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image_, const int xOffset, const int yOffset, + const RectangleList& initialClip) + : image (image_) +{ + currentState = new LLGCSavedState (initialClip, xOffset, yOffset); } LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() @@ -82529,8 +83162,7 @@ bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y) { - currentState->xOffset += x; - currentState->yOffset += y; + currentState->setOrigin (x, y); } bool LowLevelGraphicsSoftwareRenderer::clipToRectangle (const Rectangle& r) @@ -82560,18 +83192,17 @@ void LowLevelGraphicsSoftwareRenderer::clipToImageAlpha (const Image& sourceImag bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (const Rectangle& r) { - return currentState->edgeTable->edgeTable.getMaximumBounds() - .intersects (r.translated (currentState->xOffset, currentState->yOffset)); + return currentState->clipRegionIntersects (r); } const Rectangle LowLevelGraphicsSoftwareRenderer::getClipBounds() const { - return currentState->edgeTable->edgeTable.getMaximumBounds().translated (-currentState->xOffset, -currentState->yOffset); + return currentState->getClipBounds(); } bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const { - return currentState->edgeTable->edgeTable.isEmpty(); + return currentState->clip == 0; } void LowLevelGraphicsSoftwareRenderer::saveState() @@ -82611,23 +83242,12 @@ void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::Resamp void LowLevelGraphicsSoftwareRenderer::fillRect (const Rectangle& r, const bool replaceExistingContents) { - const Rectangle& totalClip = currentState->edgeTable->edgeTable.getMaximumBounds(); - const Rectangle clipped (totalClip.getIntersection (r.translated (currentState->xOffset, currentState->yOffset))); - - if (! clipped.isEmpty()) - { - EdgeTable et (clipped); - currentState->fillEdgeTable (image, et, replaceExistingContents); - } + currentState->fillRect (image, r, replaceExistingContents); } void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform) { - EdgeTable et (currentState->edgeTable->edgeTable.getMaximumBounds(), - path, transform.translated ((float) currentState->xOffset, - (float) currentState->yOffset)); - - currentState->fillEdgeTable (image, et); + currentState->fillPath (image, path, transform); } void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const Rectangle& srcClip, @@ -82636,7 +83256,7 @@ void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, cons jassert (sourceImage.getBounds().contains (srcClip)); currentState->renderImage (image, sourceImage, srcClip, transform, - fillEntireClipAsTiles ? &(currentState->edgeTable->edgeTable) : 0); + fillEntireClipAsTiles ? currentState->clip : 0); } void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2, double y2) @@ -82649,40 +83269,33 @@ void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2 void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, double top, double bottom) { if (bottom > top) - { - EdgeTable et ((float) (x + currentState->xOffset), (float) (top + currentState->yOffset), 1.0f, (float) (bottom - top)); - currentState->fillEdgeTable (image, et); - } + currentState->fillRect (image, Rectangle ((float) x, (float) top, 1.0f, (float) (bottom - top))); } void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double left, double right) { if (right > left) - { - EdgeTable et ((float) (left + currentState->xOffset), (float) (y + currentState->yOffset), - (float) (right - left), 1.0f); - currentState->fillEdgeTable (image, et); - } + currentState->fillRect (image, Rectangle ((float) left, (float) y, (float) (right - left), 1.0f)); } class GlyphCache : private DeletedAtShutdown { public: - GlyphCache() throw() + GlyphCache() : accessCounter (0), hits (0), misses (0) { for (int i = 120; --i >= 0;) glyphs.add (new CachedGlyph()); } - ~GlyphCache() throw() + ~GlyphCache() { clearSingletonInstance(); } juce_DeclareSingleton_SingleThreaded_Minimal (GlyphCache); - void drawGlyph (LLGCSavedState& state, Image& image, const Font& font, const int glyphNumber, float x, float y) throw() + void drawGlyph (LLGCSavedState& state, Image& image, const Font& font, const int glyphNumber, float x, float y) { ++accessCounter; int oldestCounter = std::numeric_limits::max(); @@ -82731,17 +83344,13 @@ public: CachedGlyph() : glyph (0), lastAccessCount (0) {} ~CachedGlyph() {} - void draw (LLGCSavedState& state, Image& image, const float x, const float y) const throw() + void draw (LLGCSavedState& state, Image& image, const float x, const float y) const { if (edgeTable != 0) - { - EdgeTable et (*edgeTable); - et.translate (x, roundToInt (y)); - state.fillEdgeTable (image, et, false); - } + state.fillEdgeTable (image, *edgeTable, x, roundToInt (y)); } - void generate (const Font& newFont, const int glyphNumber) throw() + void generate (const Font& newFont, const int glyphNumber) { font = newFont; glyph = glyphNumber; @@ -82802,8 +83411,8 @@ void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineT if (transform.isOnlyTranslation()) { GlyphCache::getInstance()->drawGlyph (*currentState, image, f, glyphNumber, - transform.getTranslationX() + (float) currentState->xOffset, - transform.getTranslationY() + (float) currentState->yOffset); + transform.getTranslationX(), + transform.getTranslationY()); } else { @@ -84884,13 +85493,13 @@ private: } else if (t.startsWithIgnoreCase ("skewX")) { - trans = AffineTransform (1.0f, tanf (numbers[0] * (float_Pi / 180.0f)), 0.0f, + trans = AffineTransform (1.0f, std::tan (numbers[0] * (float_Pi / 180.0f)), 0.0f, 0.0f, 1.0f, 0.0f); } else if (t.startsWithIgnoreCase ("skewY")) { trans = AffineTransform (1.0f, 0.0f, 0.0f, - tanf (numbers[0] * (float_Pi / 180.0f)), 1.0f, 0.0f); + std::tan (numbers[0] * (float_Pi / 180.0f)), 1.0f, 0.0f); } result = trans.followedBy (result); @@ -84926,15 +85535,15 @@ private: if (s <= 1.0) { - c = sqrt (jmax (0.0, ((rx2 * ry2) - (rx2 * yp2) - (ry2 * xp2)) - / (( rx2 * yp2) + (ry2 * xp2)))); + c = std::sqrt (jmax (0.0, ((rx2 * ry2) - (rx2 * yp2) - (ry2 * xp2)) + / (( rx2 * yp2) + (ry2 * xp2)))); if (largeArc == sweep) c = -c; } else { - const double s2 = sqrt (s); + const double s2 = std::sqrt (s); rx *= s2; ry *= s2; rx2 = rx * rx; @@ -87135,8 +87744,8 @@ const AffineTransform AffineTransform::translation (const float dx, const AffineTransform AffineTransform::rotated (const float rad) const throw() { - const float cosRad = cosf (rad); - const float sinRad = sinf (rad); + const float cosRad = std::cos (rad); + const float sinRad = std::sin (rad); return followedBy (cosRad, -sinRad, 0, sinRad, cosRad, 0); @@ -87144,8 +87753,8 @@ const AffineTransform AffineTransform::rotated (const float rad) const throw() const AffineTransform AffineTransform::rotation (const float rad) throw() { - const float cosRad = cosf (rad); - const float sinRad = sinf (rad); + const float cosRad = std::cos (rad); + const float sinRad = std::sin (rad); return AffineTransform (cosRad, -sinRad, 0, sinRad, cosRad, 0); @@ -87814,8 +88423,8 @@ void Path::addCentredArc (const float centreX, const float centreY, if (startAsNewSubPath) { - float x = centreX + radiusX * sinf (angle); - float y = centreY - radiusY * cosf (angle); + float x = centreX + radiusX * std::sin (angle); + float y = centreY - radiusY * std::cos (angle); if (rotationOfEllipse != 0) rotation.transformPoint (x, y); @@ -87830,8 +88439,8 @@ void Path::addCentredArc (const float centreX, const float centreY, while (angle < toRadians) { - float x = centreX + radiusX * sinf (angle); - float y = centreY - radiusY * cosf (angle); + float x = centreX + radiusX * std::sin (angle); + float y = centreY - radiusY * std::cos (angle); if (rotationOfEllipse != 0) rotation.transformPoint (x, y); @@ -87848,8 +88457,8 @@ void Path::addCentredArc (const float centreX, const float centreY, while (angle > toRadians) { - float x = centreX + radiusX * sinf (angle); - float y = centreY - radiusY * cosf (angle); + float x = centreX + radiusX * std::sin (angle); + float y = centreY - radiusY * std::cos (angle); if (rotationOfEllipse != 0) rotation.transformPoint (x, y); @@ -87860,8 +88469,8 @@ void Path::addCentredArc (const float centreX, const float centreY, } } - float x = centreX + radiusX * sinf (toRadians); - float y = centreY - radiusY * cosf (toRadians); + float x = centreX + radiusX * std::sin (toRadians); + float y = centreY - radiusY * std::cos (toRadians); if (rotationOfEllipse != 0) rotation.transformPoint (x, y); @@ -87881,12 +88490,12 @@ void Path::addPieSegment (const float x, const float y, const float centreX = x + hw; const float centreY = y + hh; - startNewSubPath (centreX + hw * sinf (fromRadians), - centreY - hh * cosf (fromRadians)); + startNewSubPath (centreX + hw * std::sin (fromRadians), + centreY - hh * std::cos (fromRadians)); addArc (x, y, width, height, fromRadians, toRadians); - if (fabs (fromRadians - toRadians) > float_Pi * 1.999f) + if (std::abs (fromRadians - toRadians) > float_Pi * 1.999f) { closeSubPath(); @@ -87895,8 +88504,8 @@ void Path::addPieSegment (const float x, const float y, hw *= innerCircleProportionalSize; hh *= innerCircleProportionalSize; - startNewSubPath (centreX + hw * sinf (toRadians), - centreY - hh * cosf (toRadians)); + startNewSubPath (centreX + hw * std::sin (toRadians), + centreY - hh * std::cos (toRadians)); addArc (centreX - hw, centreY - hh, hw * 2.0f, hh * 2.0f, toRadians, fromRadians); @@ -88009,8 +88618,8 @@ void Path::addStar (const float centreX, { float angle = startAngle + i * angleBetweenPoints; - const float x = centreX + outerRadius * sinf (angle); - const float y = centreY - outerRadius * cosf (angle); + const float x = centreX + outerRadius * std::sin (angle); + const float y = centreY - outerRadius * std::cos (angle); if (i == 0) startNewSubPath (x, y); @@ -88019,8 +88628,8 @@ void Path::addStar (const float centreX, angle += angleBetweenPoints * 0.5f; - lineTo (centreX + innerRadius * sinf (angle), - centreY - innerRadius * cosf (angle)); + lineTo (centreX + innerRadius * std::sin (angle), + centreY - innerRadius * std::cos (angle)); } closeSubPath(); @@ -89389,13 +89998,13 @@ namespace PathFunctions else { // curved joints - float angle1 = atan2f (x2 - midX, y2 - midY); - float angle2 = atan2f (x3 - midX, y3 - midY); + float angle1 = std::atan2 (x2 - midX, y2 - midY); + float angle2 = std::atan2 (x3 - midX, y3 - midY); const float angleIncrement = 0.1f; destPath.lineTo (x2, y2); - if (fabs (angle1 - angle2) > angleIncrement) + if (std::abs (angle1 - angle2) > angleIncrement) { if (angle2 > angle1 + float_Pi || (angle2 < angle1 && angle2 >= angle1 - float_Pi)) @@ -89408,8 +90017,8 @@ namespace PathFunctions angle1 -= angleIncrement; while (angle1 > angle2) { - destPath.lineTo (midX + width * sinf (angle1), - midY + width * cosf (angle1)); + destPath.lineTo (midX + width * std::sin (angle1), + midY + width * std::cos (angle1)); angle1 -= angleIncrement; } @@ -89424,8 +90033,8 @@ namespace PathFunctions angle1 += angleIncrement; while (angle1 < angle2) { - destPath.lineTo (midX + width * sinf (angle1), - midY + width * cosf (angle1)); + destPath.lineTo (midX + width * std::sin (angle1), + midY + width * std::cos (angle1)); angle1 += angleIncrement; } @@ -89681,7 +90290,7 @@ void PathStrokeType::createStrokedPath (Path& destPath, if (it.closesSubPath || hypotSquared > minSegmentLength || it.isLastInSubpath()) { - const float len = sqrtf (hypotSquared); + const float len = std::sqrt (hypotSquared); if (len == 0) { @@ -90276,7 +90885,8 @@ void RectangleList::add (const Rectangle& rect) void RectangleList::addWithoutMerging (const Rectangle& rect) { - rects.add (rect); + if (! rect.isEmpty()) + rects.add (rect); } void RectangleList::add (const int x, const int y, const int w, const int h) @@ -90708,8 +91318,9 @@ LowLevelGraphicsContext* Image::createLowLevelContext() return new LowLevelGraphicsSoftwareRenderer (*this); } -Image::BitmapData::BitmapData (Image& image, int x, int y, int w, int h, const bool /*makeWritable*/) +Image::BitmapData::BitmapData (Image& image, const int x, const int y, const int w, const int h, const bool /*makeWritable*/) : data (image.imageData + image.lineStride * y + image.pixelStride * x), + pixelFormat (image.getFormat()), lineStride (image.lineStride), pixelStride (image.pixelStride), width (w), @@ -90718,8 +91329,9 @@ Image::BitmapData::BitmapData (Image& image, int x, int y, int w, int h, const b jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= image.getWidth() && y + h <= image.getHeight()); } -Image::BitmapData::BitmapData (const Image& image, int x, int y, int w, int h) +Image::BitmapData::BitmapData (const Image& image, const int x, const int y, const int w, const int h) : data (image.imageData + image.lineStride * y + image.pixelStride * x), + pixelFormat (image.getFormat()), lineStride (image.lineStride), pixelStride (image.pixelStride), width (w), @@ -90733,7 +91345,7 @@ Image::BitmapData::~BitmapData() } void Image::setPixelData (int x, int y, int w, int h, - const uint8* sourcePixelData, int sourceLineStride) + const uint8* const sourcePixelData, const int sourceLineStride) { jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= imageWidth && y + h <= imageHeight); @@ -90750,14 +91362,17 @@ void Image::setPixelData (int x, int y, int w, int h, } } -void Image::clear (int dx, int dy, int dw, int dh, const Colour& colourToClearTo) +void Image::clear (const Rectangle& area, const Colour& colourToClearTo) { - if (Rectangle::intersectRectangles (dx, dy, dw, dh, 0, 0, imageWidth, imageHeight)) + const Rectangle clipped (area.getIntersection (getBounds())); + + if (! clipped.isEmpty()) { const PixelARGB col (colourToClearTo.getPixelARGB()); - const BitmapData destData (*this, dx, dy, dw, dh, true); + const BitmapData destData (*this, clipped.getX(), clipped.getY(), clipped.getWidth(), clipped.getHeight(), true); uint8* dest = destData.data; + int dh = clipped.getHeight(); while (--dh >= 0) { @@ -90766,7 +91381,7 @@ void Image::clear (int dx, int dy, int dw, int dh, const Colour& colourToClearTo if (isARGB()) { - for (int x = dw; --x >= 0;) + for (int x = clipped.getWidth(); --x >= 0;) { ((PixelARGB*) line)->set (col); line += destData.pixelStride; @@ -90774,7 +91389,7 @@ void Image::clear (int dx, int dy, int dw, int dh, const Colour& colourToClearTo } else if (isRGB()) { - for (int x = dw; --x >= 0;) + for (int x = clipped.getWidth(); --x >= 0;) { ((PixelRGB*) line)->set (col); line += destData.pixelStride; @@ -90782,7 +91397,7 @@ void Image::clear (int dx, int dy, int dw, int dh, const Colour& colourToClearTo } else { - for (int x = dw; --x >= 0;) + for (int x = clipped.getWidth(); --x >= 0;) { *line = col.getAlpha(); line += destData.pixelStride; @@ -90822,7 +91437,7 @@ Image* Image::createCopyOfAlphaChannel() const if (! hasAlphaChannel()) { - newImage->clear (0, 0, imageWidth, imageHeight, Colours::black); + newImage->clear (getBounds(), Colours::black); } else { @@ -233142,7 +233757,7 @@ void* DynamicLibraryLoader::findProcAddress (const String& functionName) extern void juce_initialiseThreadEvents(); -void Logger::outputDebugString (const String& text) throw() +void Logger::outputDebugString (const String& text) { OutputDebugString (text + "\n"); } @@ -233157,7 +233772,7 @@ static double hiResTicksScaleFactor; #pragma intrinsic (__cpuid) #pragma intrinsic (__rdtsc) -const String SystemStats::getCpuVendor() throw() +const String SystemStats::getCpuVendor() { int info [4]; __cpuid (info, 0); @@ -233210,7 +233825,7 @@ static void juce_getCpuVendor (char* const v) memcpy (v, vendor, 16); } -const String SystemStats::getCpuVendor() throw() +const String SystemStats::getCpuVendor() { char v [16]; juce_getCpuVendor (v); @@ -233228,27 +233843,27 @@ struct CPUFlags static CPUFlags cpuFlags; -bool SystemStats::hasMMX() throw() +bool SystemStats::hasMMX() { return cpuFlags.hasMMX; } -bool SystemStats::hasSSE() throw() +bool SystemStats::hasSSE() { return cpuFlags.hasSSE; } -bool SystemStats::hasSSE2() throw() +bool SystemStats::hasSSE2() { return cpuFlags.hasSSE2; } -bool SystemStats::has3DNow() throw() +bool SystemStats::has3DNow() { return cpuFlags.has3DNow; } -void SystemStats::initialiseStats() throw() +void SystemStats::initialiseStats() { juce_initialiseThreadEvents(); @@ -233280,7 +233895,7 @@ void SystemStats::initialiseStats() throw() #endif } -SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() throw() +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() { OSVERSIONINFO info; info.dwOSVersionInfoSize = sizeof (info); @@ -233304,7 +233919,7 @@ SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() throw() return UnknownOS; } -const String SystemStats::getOperatingSystemName() throw() +const String SystemStats::getOperatingSystemName() { const char* name = "Unknown OS"; @@ -233321,7 +233936,7 @@ const String SystemStats::getOperatingSystemName() throw() return name; } -bool SystemStats::isOperatingSystem64Bit() throw() +bool SystemStats::isOperatingSystem64Bit() { #ifdef _WIN64 return true; @@ -233338,7 +233953,7 @@ bool SystemStats::isOperatingSystem64Bit() throw() #endif } -int SystemStats::getMemorySizeInMegabytes() throw() +int SystemStats::getMemorySizeInMegabytes() { MEMORYSTATUSEX mem; mem.dwLength = sizeof (mem); @@ -233346,7 +233961,7 @@ int SystemStats::getMemorySizeInMegabytes() throw() return (int) (mem.ullTotalPhys / (1024 * 1024)) + 1; } -int SystemStats::getNumCpus() throw() +int SystemStats::getNumCpus() { SYSTEM_INFO systemInfo; GetSystemInfo (&systemInfo); @@ -233388,7 +234003,7 @@ int64 Time::getHighResolutionTicksPerSecond() throw() return hiResTicksPerSecond; } -int64 SystemStats::getClockCycleCounter() throw() +static int64 juce_getClockCycleCounter() throw() { #if JUCE_USE_INTRINSICS // MS intrinsics version... @@ -233427,9 +234042,9 @@ int64 SystemStats::getClockCycleCounter() throw() #endif } -int SystemStats::getCpuSpeedInMegaherz() throw() +int SystemStats::getCpuSpeedInMegaherz() { - const int64 cycles = SystemStats::getClockCycleCounter(); + const int64 cycles = juce_getClockCycleCounter(); const uint32 millis = Time::getMillisecondCounter(); int lastResult = 0; @@ -233439,7 +234054,7 @@ int SystemStats::getCpuSpeedInMegaherz() throw() while (--n > 0) {} const uint32 millisElapsed = Time::getMillisecondCounter() - millis; - const int64 cyclesNow = SystemStats::getClockCycleCounter(); + const int64 cyclesNow = juce_getClockCycleCounter(); if (millisElapsed > 80) { @@ -233453,7 +234068,7 @@ int SystemStats::getCpuSpeedInMegaherz() throw() } } -bool Time::setSystemTimeToThisTime() const throw() +bool Time::setSystemTimeToThisTime() const { SYSTEMTIME st; @@ -233472,7 +234087,7 @@ bool Time::setSystemTimeToThisTime() const throw() && SetLocalTime (&st) != 0; } -int SystemStats::getPageSize() throw() +int SystemStats::getPageSize() { SYSTEM_INFO systemInfo; GetSystemInfo (&systemInfo); @@ -236695,6 +237310,7 @@ private: WindowsBitmapImage* const offscreenImage = offscreenImageGenerator.getImage (transparent, w, h); RectangleList contextClip; + const Rectangle clipBounds (0, 0, w, h); bool needToPaintAll = true; @@ -236722,15 +237338,12 @@ private: while (--num >= 0) { - // (need to move this one pixel to the left because of a win32 bug) - const int cx = jmax (x, (int) rects->left - 1); - const int cy = rects->top; - const int cw = rects->right - cx; - const int ch = rects->bottom - rects->top; - - if (cx + cw - x <= w && cy + ch - y <= h) + if (rects->right <= x + w && rects->bottom <= y + h) { - contextClip.addWithoutMerging (Rectangle (cx - x, cy - y, cw, ch)); + // (need to move this one pixel to the left because of a win32 bug) + const int cx = jmax (x, (int) rects->left - 1); + contextClip.addWithoutMerging (Rectangle (cx - x, rects->top - y, rects->right - cx, rects->bottom - rects->top) + .getIntersection (clipBounds)); } else { @@ -236755,10 +237368,7 @@ private: RectangleList::Iterator i (contextClip); while (i.next()) - { - const Rectangle& r = *i.getRectangle(); - offscreenImage->clear (r.getX(), r.getY(), r.getWidth(), r.getHeight()); - } + offscreenImage->clear (*i.getRectangle()); } // if the component's not opaque, this won't draw properly unless the platform can support this @@ -236766,10 +237376,7 @@ private: updateCurrentModifiers(); - LowLevelGraphicsSoftwareRenderer context (*offscreenImage); - context.clipToRectangleList (contextClip); - context.setOrigin (-x, -y); - + LowLevelGraphicsSoftwareRenderer context (*offscreenImage, -x, -y, contextClip); handlePaint (context); if (! dontRepaint) @@ -250775,22 +251382,22 @@ int juce_seekInInternetFile (void* handle, int newPosition) // compiled on its own). #if JUCE_INCLUDED_FILE -void Logger::outputDebugString (const String& text) throw() +void Logger::outputDebugString (const String& text) { std::cerr << text << std::endl; } -SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() throw() +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() { return Linux; } -const String SystemStats::getOperatingSystemName() throw() +const String SystemStats::getOperatingSystemName() { return "Linux"; } -bool SystemStats::isOperatingSystem64Bit() throw() +bool SystemStats::isOperatingSystem64Bit() { #if JUCE_64BIT return true; @@ -250800,82 +251407,78 @@ bool SystemStats::isOperatingSystem64Bit() throw() #endif } -static const String getCpuInfo (const char* key, bool lastOne = false) throw() +static const String juce_getCpuInfo (const char* const key) { - String info; - char buf [256]; + StringArray lines; + lines.addLines (File ("/proc/cpuinfo").loadFileAsString()); - FILE* f = fopen ("/proc/cpuinfo", "r"); + for (int i = lines.size(); --i >= 0;) // (NB - it's important that this runs in reverse order) + if (lines[i].startsWithIgnoreCase (key)) + return lines[i].fromFirstOccurrenceOf (":", false, false).trim(); - while (f != 0 && fgets (buf, sizeof(buf), f)) - { - if (strncmp (buf, key, strlen (key)) == 0) - { - char* p = buf; - - while (*p && *p != '\n') - ++p; + return String::empty; +} - if (*p != 0) - *p = 0; +bool SystemStats::hasMMX() { return juce_getCpuInfo ("flags").contains ("mmx"); } +bool SystemStats::hasSSE() { return juce_getCpuInfo ("flags").contains ("sse"); } +bool SystemStats::hasSSE2() { return juce_getCpuInfo ("flags").contains ("sse2"); } +bool SystemStats::has3DNow() { return juce_getCpuInfo ("flags").contains ("3dnow"); } - p = buf; +const String SystemStats::getCpuVendor() +{ + return juce_getCpuInfo ("vendor_id"); +} - while (*p != 0 && *p != ':') - ++p; +int SystemStats::getCpuSpeedInMegaherz() +{ + return roundToInt (juce_getCpuInfo ("cpu MHz").getFloatValue()); +} - if (*p != 0 && *(p + 1) != 0) - info = p + 2; +int SystemStats::getMemorySizeInMegabytes() +{ + struct sysinfo sysi; - if (! lastOne) - break; - } - } + if (sysinfo (&sysi) == 0) + return (sysi.totalram * sysi.mem_unit / (1024 * 1024)); - fclose (f); - return info; + return 0; } -bool SystemStats::hasMMX() throw() +int SystemStats::getPageSize() { - return getCpuInfo ("flags").contains ("mmx"); + return sysconf (_SC_PAGESIZE); } -bool SystemStats::hasSSE() throw() +int SystemStats::getNumCpus() { - return getCpuInfo ("flags").contains ("sse"); + return juce_getCpuInfo ("processor").getIntValue() + 1; } -bool SystemStats::hasSSE2() throw() +const String SystemStats::getLogonName() { - return getCpuInfo ("flags").contains ("sse2"); -} + const char* user = getenv ("USER"); -bool SystemStats::has3DNow() throw() -{ - return getCpuInfo ("flags").contains ("3dnow"); + if (user == 0) + { + struct passwd* const pw = getpwuid (getuid()); + if (pw != 0) + user = pw->pw_name; + } + + return String::fromUTF8 (user); } -const String SystemStats::getCpuVendor() throw() +const String SystemStats::getFullUserName() { - return getCpuInfo ("vendor_id"); + return getLogonName(); } -int SystemStats::getCpuSpeedInMegaherz() throw() +void SystemStats::initialiseStats() { - const String speed (getCpuInfo ("cpu MHz")); - - return (int) (speed.getFloatValue() + 0.5f); } -int SystemStats::getMemorySizeInMegabytes() throw() +void PlatformUtilities::fpuReset() { - struct sysinfo sysi; - - if (sysinfo (&sysi) == 0) - return (sysi.totalram * sysi.mem_unit / (1024 * 1024)); - - return 0; } uint32 juce_millisecondsSinceStartup() throw() @@ -250920,65 +251523,16 @@ int64 Time::getHighResolutionTicks() throw() int64 Time::getHighResolutionTicksPerSecond() throw() { - // Microseconds - return 1000000; + return 1000000; // (microseconds) } -bool Time::setSystemTimeToThisTime() const throw() +bool Time::setSystemTimeToThisTime() const { timeval t; t.tv_sec = millisSinceEpoch % 1000000; t.tv_usec = millisSinceEpoch - t.tv_sec; - return settimeofday (&t, NULL) ? false : true; -} - -int SystemStats::getPageSize() throw() -{ - static int systemPageSize = 0; - - if (systemPageSize == 0) - systemPageSize = sysconf (_SC_PAGESIZE); - - return systemPageSize; -} - -int SystemStats::getNumCpus() throw() -{ - const int lastCpu = getCpuInfo ("processor", true).getIntValue(); - - return lastCpu + 1; -} - -const String SystemStats::getLogonName() -{ - const char* user = getenv ("USER"); - - if (user == 0) - { - struct passwd* const pw = getpwuid (getuid()); - if (pw != 0) - user = pw->pw_name; - } - - return String::fromUTF8 (user); -} - -const String SystemStats::getFullUserName() -{ - return getLogonName(); -} - -void SystemStats::initialiseStats() throw() -{ - // Process starts off as root when running suid - Process::lowerPrivilege(); - - String s (SystemStats::getJUCEVersion()); -} - -void PlatformUtilities::fpuReset() -{ + return settimeofday (&t, 0) ? false : true; } #endif @@ -250999,9 +251553,6 @@ void JUCE_API juce_threadEntryPoint (void*); void* threadEntryProc (void* value) { - // New threads start off as root when running suid - Process::lowerPrivilege(); - juce_threadEntryPoint (value); return 0; } @@ -251022,7 +251573,7 @@ void* juce_createThread (void* userData) void juce_killThread (void* handle) { if (handle != 0) - pthread_cancel ((pthread_t)handle); + pthread_cancel ((pthread_t) handle); } void juce_setCurrentThreadName (const String& /*name*/) @@ -251034,26 +251585,25 @@ Thread::ThreadID Thread::getCurrentThreadId() return (ThreadID) pthread_self(); } -/* - * This is all a bit non-ideal... the trouble is that on Linux you - * need to call setpriority to affect the dynamic priority for - * non-realtime processes, but this requires the pid, which is not - * accessible from the pthread_t. We could get it by calling getpid - * once each thread has started, but then we would need a list of - * running threads etc etc. - * Also there is no such thing as IDLE priority on Linux. - * For the moment, map idle, low and normal process priorities to - * SCHED_OTHER, with the thread priority ignored for these classes. - * Map high priority processes to the lower half of the SCHED_RR - * range, and realtime to the upper half - */ - -// priority 1 to 10 where 5=normal, 1=low. If the handle is 0, sets the -// priority of the current thread +/* This is all a bit non-ideal... the trouble is that on Linux you + need to call setpriority to affect the dynamic priority for + non-realtime processes, but this requires the pid, which is not + accessible from the pthread_t. We could get it by calling getpid + once each thread has started, but then we would need a list of + running threads etc etc. + Also there is no such thing as IDLE priority on Linux. + For the moment, map idle, low and normal process priorities to + SCHED_OTHER, with the thread priority ignored for these classes. + Map high priority processes to the lower half of the SCHED_RR + range, and realtime to the upper half. + + priority 1 to 10 where 5=normal, 1=low. If the handle is 0, sets the + priority of the current thread +*/ bool juce_setThreadPriority (void* handle, int priority) { struct sched_param param; - int policy, maxp, minp, pri; + int policy; if (handle == 0) handle = (void*) pthread_self(); @@ -251061,20 +251611,17 @@ bool juce_setThreadPriority (void* handle, int priority) if (pthread_getschedparam ((pthread_t) handle, &policy, ¶m) == 0 && policy != SCHED_OTHER) { - minp = sched_get_priority_min(policy); - maxp = sched_get_priority_max(policy); + int minp = sched_get_priority_min (policy); + int maxp = sched_get_priority_max (policy); - pri = ((maxp - minp) / 2) * (priority - 1) / 9; + int pri = ((maxp - minp) / 2) * (priority - 1) / 9; if (param.__sched_priority >= (minp + (maxp - minp) / 2)) - // Realtime process priority - param.__sched_priority = minp + ((maxp - minp) / 2) + pri; + param.__sched_priority = minp + ((maxp - minp) / 2) + pri; // (realtime) else - // High process priority - param.__sched_priority = minp + pri; + param.__sched_priority = minp + pri; // (high) param.sched_priority = jlimit (1, 127, 1 + (priority * 126) / 11); - return pthread_setschedparam ((pthread_t) handle, policy, ¶m) == 0; } @@ -251236,110 +251783,101 @@ void* PlatformUtilities::getProcedureEntryPoint (void* libraryHandle, const Stri extern Display* display; extern Window juce_messageWindowHandle; -static String localClipboardContent; -static Atom atom_UTF8_STRING; -static Atom atom_CLIPBOARD; -static Atom atom_TARGETS; - -static void initSelectionAtoms() -{ - static bool isInitialised = false; - if (! isInitialised) - { - atom_UTF8_STRING = XInternAtom (display, "UTF8_STRING", False); - atom_CLIPBOARD = XInternAtom (display, "CLIPBOARD", False); - atom_TARGETS = XInternAtom (display, "TARGETS", False); - } -} - -// Read the content of a window property as either a locale-dependent string or an utf8 string -// works only for strings shorter than 1000000 bytes -static String juce_readWindowProperty (Window window, Atom prop, - Atom fmt, // XA_STRING or UTF8_STRING - bool deleteAfterReading) +namespace ClipboardHelpers { - String returnData; - char* clipData; - Atom actualType; - int actualFormat; - unsigned long numItems, bytesLeft; + static String localClipboardContent; + static Atom atom_UTF8_STRING; + static Atom atom_CLIPBOARD; + static Atom atom_TARGETS; - if (XGetWindowProperty (display, window, prop, - 0L /* offset */, 1000000 /* length (max) */, False, - AnyPropertyType /* format */, - &actualType, &actualFormat, &numItems, &bytesLeft, - (unsigned char**) &clipData) == Success) + static void initSelectionAtoms() { - if (actualType == atom_UTF8_STRING && actualFormat == 8) - { - returnData = String::fromUTF8 (clipData, numItems); - } - else if (actualType == XA_STRING && actualFormat == 8) + static bool isInitialised = false; + if (! isInitialised) { - returnData = String (clipData, numItems); + atom_UTF8_STRING = XInternAtom (display, "UTF8_STRING", False); + atom_CLIPBOARD = XInternAtom (display, "CLIPBOARD", False); + atom_TARGETS = XInternAtom (display, "TARGETS", False); } - - if (clipData != 0) - XFree (clipData); - - jassert (bytesLeft == 0 || numItems == 1000000); } - if (deleteAfterReading) - XDeleteProperty (display, window, prop); + // Read the content of a window property as either a locale-dependent string or an utf8 string + // works only for strings shorter than 1000000 bytes + static String readWindowProperty (Window window, Atom prop, Atom fmt) + { + String returnData; + char* clipData; + Atom actualType; + int actualFormat; + unsigned long numItems, bytesLeft; - return returnData; -} + if (XGetWindowProperty (display, window, prop, + 0L /* offset */, 1000000 /* length (max) */, False, + AnyPropertyType /* format */, + &actualType, &actualFormat, &numItems, &bytesLeft, + (unsigned char**) &clipData) == Success) + { + if (actualType == atom_UTF8_STRING && actualFormat == 8) + returnData = String::fromUTF8 (clipData, numItems); + else if (actualType == XA_STRING && actualFormat == 8) + returnData = String (clipData, numItems); -// Send a SelectionRequest to the window owning the selection and waits for its answer (with a timeout) */ -static bool juce_requestSelectionContent (String &selection_content, Atom selection, Atom requested_format) -{ - Atom property_name = XInternAtom (display, "JUCE_SEL", false); + if (clipData != 0) + XFree (clipData); - // The selection owner will be asked to set the JUCE_SEL property on the - // juce_messageWindowHandle with the selection content - XConvertSelection (display, selection, requested_format, property_name, - juce_messageWindowHandle, CurrentTime); + jassert (bytesLeft == 0 || numItems == 1000000); + } - int timeoutMs = 200; // will wait at most for 200 ms + XDeleteProperty (display, window, prop); + return returnData; + } - do + // Send a SelectionRequest to the window owning the selection and waits for its answer (with a timeout) */ + static bool requestSelectionContent (String& selectionContent, Atom selection, Atom requestedFormat) { - XEvent event; + Atom property_name = XInternAtom (display, "JUCE_SEL", false); + + // The selection owner will be asked to set the JUCE_SEL property on the + // juce_messageWindowHandle with the selection content + XConvertSelection (display, selection, requestedFormat, property_name, + juce_messageWindowHandle, CurrentTime); - if (XCheckTypedWindowEvent (display, juce_messageWindowHandle, SelectionNotify, &event)) + int count = 50; // will wait at most for 200 ms + + while (--count >= 0) { - if (event.xselection.property == property_name) + XEvent event; + if (XCheckTypedWindowEvent (display, juce_messageWindowHandle, SelectionNotify, &event)) { - jassert (event.xselection.requestor == juce_messageWindowHandle); + if (event.xselection.property == property_name) + { + jassert (event.xselection.requestor == juce_messageWindowHandle); - selection_content = juce_readWindowProperty (event.xselection.requestor, - event.xselection.property, - requested_format, true); - return true; - } - else - { - return false; // the format we asked for was denied.. (event.xselection.property == None) + selectionContent = readWindowProperty (event.xselection.requestor, + event.xselection.property, + requestedFormat); + return true; + } + else + { + return false; // the format we asked for was denied.. (event.xselection.property == None) + } } + + // not very elegant.. we could do a select() or something like that... + // however clipboard content requesting is inherently slow on x11, it + // often takes 50ms or more so... + Thread::sleep (4); } - // not very elegant.. we could do a select() or something like that... - // however clipboard content requesting is inherently slow on x11, it - // often takes 50ms or more so... - Thread::sleep (4); - timeoutMs -= 4; + return false; } - while (timeoutMs > 0); - - DBG("timeout for juce_requestSelectionContent"); - return false; } // Called from the event loop in juce_linux_Messaging in response to SelectionRequest events void juce_handleSelectionRequest (XSelectionRequestEvent &evt) { - initSelectionAtoms(); + ClipboardHelpers::initSelectionAtoms(); // the selection content is sent to the target window as a window property XSelectionEvent reply; @@ -251354,32 +251892,32 @@ void juce_handleSelectionRequest (XSelectionRequestEvent &evt) HeapBlock data; int propertyFormat = 0, numDataItems = 0; - if (evt.selection == XA_PRIMARY || evt.selection == atom_CLIPBOARD) + if (evt.selection == XA_PRIMARY || evt.selection == ClipboardHelpers::atom_CLIPBOARD) { if (evt.target == XA_STRING) { // format data according to system locale - numDataItems = localClipboardContent.getNumBytesAsCString() + 1; + numDataItems = ClipboardHelpers::localClipboardContent.getNumBytesAsCString() + 1; data.calloc (numDataItems + 1); - localClipboardContent.copyToCString (data, numDataItems); + ClipboardHelpers::localClipboardContent.copyToCString (data, numDataItems); propertyFormat = 8; // bits/item } - else if (evt.target == atom_UTF8_STRING) + else if (evt.target == ClipboardHelpers::atom_UTF8_STRING) { // translate to utf8 - numDataItems = localClipboardContent.getNumBytesAsUTF8() + 1; + numDataItems = ClipboardHelpers::localClipboardContent.getNumBytesAsUTF8() + 1; data.calloc (numDataItems + 1); - localClipboardContent.copyToUTF8 (data, numDataItems); + ClipboardHelpers::localClipboardContent.copyToUTF8 (data, numDataItems); propertyFormat = 8; // bits/item } - else if (evt.target == atom_TARGETS) + else if (evt.target == ClipboardHelpers::atom_TARGETS) { // another application wants to know what we are able to send numDataItems = 2; propertyFormat = 32; // atoms are 32-bit data.calloc (numDataItems * 4); Atom* atoms = reinterpret_cast (data.getData()); - atoms[0] = atom_UTF8_STRING; + atoms[0] = ClipboardHelpers::atom_UTF8_STRING; atoms[1] = XA_STRING; } } @@ -251408,16 +251946,16 @@ void juce_handleSelectionRequest (XSelectionRequestEvent &evt) void SystemClipboard::copyTextToClipboard (const String& clipText) { - initSelectionAtoms(); - localClipboardContent = clipText; + ClipboardHelpers::initSelectionAtoms(); + ClipboardHelpers::localClipboardContent = clipText; XSetSelectionOwner (display, XA_PRIMARY, juce_messageWindowHandle, CurrentTime); - XSetSelectionOwner (display, atom_CLIPBOARD, juce_messageWindowHandle, CurrentTime); + XSetSelectionOwner (display, ClipboardHelpers::atom_CLIPBOARD, juce_messageWindowHandle, CurrentTime); } const String SystemClipboard::getTextFromClipboard() { - initSelectionAtoms(); + ClipboardHelpers::initSelectionAtoms(); /* 1) try to read from the "CLIPBOARD" selection first (the "high level" clipboard that is supposed to be filled by ctrl-C @@ -251434,7 +251972,7 @@ const String SystemClipboard::getTextFromClipboard() if ((selectionOwner = XGetSelectionOwner (display, selection)) == None) { - selection = atom_CLIPBOARD; + selection = ClipboardHelpers::atom_CLIPBOARD; selectionOwner = XGetSelectionOwner (display, selection); } @@ -251442,17 +251980,17 @@ const String SystemClipboard::getTextFromClipboard() { if (selectionOwner == juce_messageWindowHandle) { - content = localClipboardContent; + content = ClipboardHelpers::localClipboardContent; } else { // first try: we want an utf8 string - bool ok = juce_requestSelectionContent (content, selection, atom_UTF8_STRING); + bool ok = ClipboardHelpers::requestSelectionContent (content, selection, ClipboardHelpers::atom_UTF8_STRING); if (! ok) { // second chance, ask for a good old locale-dependent string .. - ok = juce_requestSelectionContent (content, selection, XA_STRING); + ok = ClipboardHelpers::requestSelectionContent (content, selection, XA_STRING); } } } @@ -251474,9 +252012,8 @@ const String SystemClipboard::getTextFromClipboard() #define JUCE_DEBUG_XERRORS 1 #endif -Display* display = 0; // This is also referenced from WindowDriver.cpp +Display* display = 0; Window juce_messageWindowHandle = None; - XContext windowHandleXContext; // This is referenced from Windowing.cpp extern void juce_windowMessageReceive (XEvent* event); // Defined in Windowing.cpp @@ -251683,15 +252220,15 @@ void MessageManager::doPlatformSpecificInitialisation() saction.sa_handler = signalHandler; saction.sa_mask = maskSet; saction.sa_flags = 0; - sigaction (SIGINT, &saction, NULL); + sigaction (SIGINT, &saction, 0); #ifndef _DEBUG // Setup signal handlers for various fatal errors - sigaction (SIGILL, &saction, NULL); - sigaction (SIGBUS, &saction, NULL); - sigaction (SIGFPE, &saction, NULL); - sigaction (SIGSEGV, &saction, NULL); - sigaction (SIGSYS, &saction, NULL); + sigaction (SIGILL, &saction, 0); + sigaction (SIGBUS, &saction, 0); + sigaction (SIGFPE, &saction, 0); + sigaction (SIGSEGV, &saction, 0); + sigaction (SIGSYS, &saction, 0); #endif // Create the internal message queue @@ -252048,7 +252585,7 @@ public: return faces[i]; if (! create) - return NULL; + return 0; FreeTypeFontFace* newFace = new FreeTypeFontFace (familyName); faces.add (newFace); @@ -254320,26 +254857,20 @@ private: startTimer (repaintTimerPeriod); - LowLevelGraphicsSoftwareRenderer context (*image); - - context.setOrigin (-totalArea.getX(), -totalArea.getY()); + RectangleList adjustedList (originalRepaintRegion); + adjustedList.offsetAll (-totalArea.getX(), -totalArea.getY()); + LowLevelGraphicsSoftwareRenderer context (*image, -totalArea.getX(), -totalArea.getY(), adjustedList); - if (context.clipToRectangleList (originalRepaintRegion)) + if (peer->depth == 32) { - if (peer->depth == 32) - { - RectangleList::Iterator i (originalRepaintRegion); - - while (i.next()) - { - const Rectangle& r = *i.getRectangle(); - image->clear (r.getX() - totalArea.getX(), r.getY() - totalArea.getY(), r.getWidth(), r.getHeight()); - } - } + RectangleList::Iterator i (originalRepaintRegion); - peer->handlePaint (context); + while (i.next()) + image->clear (*i.getRectangle() - totalArea.getPosition()); } + peer->handlePaint (context); + if (! peer->maskedRegion.isEmpty()) originalRepaintRegion.subtract (peer->maskedRegion); @@ -258366,7 +258897,7 @@ namespace SystemStatsHelpers #endif } -void SystemStats::initialiseStats() throw() +void SystemStats::initialiseStats() { using namespace SystemStatsHelpers; static bool initialised = false; @@ -258409,17 +258940,17 @@ void SystemStats::initialiseStats() throw() } } -SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() throw() +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() { return MacOSX; } -const String SystemStats::getOperatingSystemName() throw() +const String SystemStats::getOperatingSystemName() { return "Mac OS X"; } -bool SystemStats::isOperatingSystem64Bit() throw() +bool SystemStats::isOperatingSystem64Bit() { #if JUCE_64BIT return true; @@ -258429,7 +258960,7 @@ bool SystemStats::isOperatingSystem64Bit() throw() #endif } -int SystemStats::getMemorySizeInMegabytes() throw() +int SystemStats::getMemorySizeInMegabytes() { uint64 mem = 0; size_t memSize = sizeof (mem); @@ -258438,7 +258969,7 @@ int SystemStats::getMemorySizeInMegabytes() throw() return (int) (mem / (1024 * 1024)); } -bool SystemStats::hasMMX() throw() +bool SystemStats::hasMMX() { #if JUCE_INTEL return SystemStatsHelpers::cpuFlags.hasMMX; @@ -258447,7 +258978,7 @@ bool SystemStats::hasMMX() throw() #endif } -bool SystemStats::hasSSE() throw() +bool SystemStats::hasSSE() { #if JUCE_INTEL return SystemStatsHelpers::cpuFlags.hasSSE; @@ -258456,7 +258987,7 @@ bool SystemStats::hasSSE() throw() #endif } -bool SystemStats::hasSSE2() throw() +bool SystemStats::hasSSE2() { #if JUCE_INTEL return SystemStatsHelpers::cpuFlags.hasSSE2; @@ -258465,7 +258996,7 @@ bool SystemStats::hasSSE2() throw() #endif } -bool SystemStats::has3DNow() throw() +bool SystemStats::has3DNow() { #if JUCE_INTEL return SystemStatsHelpers::cpuFlags.has3DNow; @@ -258474,7 +259005,7 @@ bool SystemStats::has3DNow() throw() #endif } -const String SystemStats::getCpuVendor() throw() +const String SystemStats::getCpuVendor() { #if JUCE_INTEL char v [16]; @@ -258485,7 +259016,7 @@ const String SystemStats::getCpuVendor() throw() #endif } -int SystemStats::getCpuSpeedInMegaherz() throw() +int SystemStats::getCpuSpeedInMegaherz() { uint64 speedHz = 0; size_t speedSize = sizeof (speedHz); @@ -258499,7 +259030,7 @@ int SystemStats::getCpuSpeedInMegaherz() throw() return (int) (speedHz / 1000000); } -int SystemStats::getNumCpus() throw() +int SystemStats::getNumCpus() { #if JUCE_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) return (int) [[NSProcessInfo processInfo] activeProcessorCount]; @@ -258538,18 +259069,13 @@ int64 Time::getHighResolutionTicksPerSecond() throw() return SystemStatsHelpers::highResTimerFrequency; } -int64 SystemStats::getClockCycleCounter() throw() -{ - return (int64) mach_absolute_time(); -} - -bool Time::setSystemTimeToThisTime() const throw() +bool Time::setSystemTimeToThisTime() const { jassertfalse return false; } -int SystemStats::getPageSize() throw() +int SystemStats::getPageSize() { return (int) NSPageSize(); } @@ -260705,7 +261231,7 @@ void juce_updateMultiMonitorInfo (Array >& monitorCoords, const // compiled on its own). #if JUCE_INCLUDED_FILE -void Logger::outputDebugString (const String& text) throw() +void Logger::outputDebugString (const String& text) { std::cerr << text << std::endl; } @@ -260831,8 +261357,8 @@ public: [nsFont retain]; - ascent = fabsf ((float) [nsFont ascender]); - float totalSize = ascent + fabsf ((float) [nsFont descender]); + ascent = std::abs ((float) [nsFont ascender]); + float totalSize = ascent + std::abs ((float) [nsFont descender]); ascent /= totalSize; pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize); @@ -260851,7 +261377,7 @@ public: fontRef = CGFontCreateWithPlatformFont (&atsFont); - const float totalHeight = fabsf ([nsFont ascender]) + fabsf([nsFont descender]); + const float totalHeight = std::abs ([nsFont ascender]) + std::abs ([nsFont descender]); unitsToHeightScaleFactor = 1.0f / totalHeight; fontHeightToCGSizeFactor = 1024.0f / totalHeight; #else @@ -260865,7 +261391,7 @@ public: fontRef = CGFontCreateWithPlatformFont (&atsFont); - const float totalHeight = fabsf ([nsFont ascender]) + fabsf([nsFont descender]); + const float totalHeight = std::abs ([nsFont ascender]) + std::abs ([nsFont descender]); unitsToHeightScaleFactor = 1.0f / totalHeight; fontHeightToCGSizeFactor = 1024.0f / totalHeight; } @@ -265505,8 +266031,8 @@ public: [nsFont retain]; - ascent = fabsf ((float) [nsFont ascender]); - float totalSize = ascent + fabsf ((float) [nsFont descender]); + ascent = std::abs ((float) [nsFont ascender]); + float totalSize = ascent + std::abs ((float) [nsFont descender]); ascent /= totalSize; pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize); @@ -265525,7 +266051,7 @@ public: fontRef = CGFontCreateWithPlatformFont (&atsFont); - const float totalHeight = fabsf ([nsFont ascender]) + fabsf([nsFont descender]); + const float totalHeight = std::abs ([nsFont ascender]) + std::abs ([nsFont descender]); unitsToHeightScaleFactor = 1.0f / totalHeight; fontHeightToCGSizeFactor = 1024.0f / totalHeight; #else @@ -265539,7 +266065,7 @@ public: fontRef = CGFontCreateWithPlatformFont (&atsFont); - const float totalHeight = fabsf ([nsFont ascender]) + fabsf([nsFont descender]); + const float totalHeight = std::abs ([nsFont ascender]) + std::abs ([nsFont descender]); unitsToHeightScaleFactor = 1.0f / totalHeight; fontHeightToCGSizeFactor = 1024.0f / totalHeight; } @@ -268075,25 +268601,28 @@ void NSViewComponentPeer::drawRect (NSRect r) (int) (r.size.height + 0.5f), ! getComponent()->isOpaque()); - LowLevelGraphicsSoftwareRenderer context (temp); - context.setOrigin (-roundToInt (r.origin.x), - -roundToInt ([view frame].size.height - (r.origin.y + r.size.height))); + const int xOffset = -roundToInt (r.origin.x); + const int yOffset = -roundToInt ([view frame].size.height - (r.origin.y + r.size.height)); const NSRect* rects = 0; NSInteger numRects = 0; [view getRectsBeingDrawn: &rects count: &numRects]; + const Rectangle clipBounds (temp.getBounds()); + RectangleList clip; for (int i = 0; i < numRects; ++i) { - clip.addWithoutMerging (Rectangle (roundToInt (rects[i].origin.x), - roundToInt ([view frame].size.height - (rects[i].origin.y + rects[i].size.height)), - roundToInt (rects[i].size.width), - roundToInt (rects[i].size.height))); + clip.addWithoutMerging (clipBounds.getIntersection (Rectangle (roundToInt (rects[i].origin.x) + xOffset, + roundToInt ([view frame].size.height - (rects[i].origin.y + rects[i].size.height)) + yOffset, + roundToInt (rects[i].size.width), + roundToInt (rects[i].size.height)))); } - if (context.clipToRectangleList (clip)) + if (! clip.isEmpty()) { + LowLevelGraphicsSoftwareRenderer context (temp, xOffset, yOffset, clip); + insideDrawRect = true; handlePaint (context); insideDrawRect = false; diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 3fb7206be1..6909a65619 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -1139,7 +1139,10 @@ inline int64 abs64 (const int64 n) throw() template inline Type juce_negate (Type n) throw() { - return -n; + return sizeof (Type) == 1 ? (Type) -(char) n + : (sizeof (Type) == 2 ? (Type) -(short) n + : (sizeof (Type) == 4 ? (Type) -(int) n + : ((Type) -(int64) n))); } /** This templated negate function will negate pointers as well as integers */ @@ -2682,7 +2685,7 @@ public: This can be called directly, or by using the DBG() macro in juce_PlatformDefs.h (which will avoid calling the method in non-debug builds). */ - static void JUCE_CALLTYPE outputDebugString (const String& text) throw(); + static void JUCE_CALLTYPE outputDebugString (const String& text); protected: @@ -7931,7 +7934,7 @@ public: @returns true if this succeeds, although depending on the system, the application might not have sufficient privileges to do this. */ - bool setSystemTimeToThisTime() const throw(); + bool setSystemTimeToThisTime() const; /** Returns the name of a day of the week. @@ -14258,7 +14261,7 @@ public: See also the JUCE_VERSION, JUCE_MAJOR_VERSION and JUCE_MINOR_VERSION macros. */ - static const String getJUCEVersion() throw(); + static const String getJUCEVersion(); /** The set of possible results of the getOperatingSystemType() method. */ @@ -14289,18 +14292,18 @@ public: @returns one of the values from the OperatingSystemType enum. @see getOperatingSystemName */ - static OperatingSystemType getOperatingSystemType() throw(); + static OperatingSystemType getOperatingSystemType(); /** Returns the name of the type of operating system we're running on. @returns a string describing the OS type. @see getOperatingSystemType */ - static const String getOperatingSystemName() throw(); + static const String getOperatingSystemName(); /** Returns true if the OS is 64-bit, or false for a 32-bit OS. */ - static bool isOperatingSystem64Bit() throw(); + static bool isOperatingSystem64Bit(); /** Returns the current user's name, if available. @see getFullUserName() @@ -14320,52 +14323,42 @@ public: @returns the speed in megahertz, e.g. 1500, 2500, 32000 (depending on what year you're reading this...) */ - static int getCpuSpeedInMegaherz() throw(); + static int getCpuSpeedInMegaherz(); /** Returns a string to indicate the CPU vendor. Might not be known on some systems. */ - static const String getCpuVendor() throw(); + static const String getCpuVendor(); /** Checks whether Intel MMX instructions are available. */ - static bool hasMMX() throw(); + static bool hasMMX(); /** Checks whether Intel SSE instructions are available. */ - static bool hasSSE() throw(); + static bool hasSSE(); /** Checks whether Intel SSE2 instructions are available. */ - static bool hasSSE2() throw(); + static bool hasSSE2(); /** Checks whether AMD 3DNOW instructions are available. */ - static bool has3DNow() throw(); + static bool has3DNow(); /** Returns the number of CPUs. */ - static int getNumCpus() throw(); - - /** Returns a clock-cycle tick counter, if available. - - If the machine can do it, this will return a tick-count - where each tick is one cpu clock cycle - used for profiling - code. - - @returns the tick count, or zero if not available. - */ - static int64 getClockCycleCounter() throw(); + static int getNumCpus(); /** Finds out how much RAM is in the machine. @returns the approximate number of megabytes of memory, or zero if something goes wrong when finding out. */ - static int getMemorySizeInMegabytes() throw(); + static int getMemorySizeInMegabytes(); /** Returns the system page-size. This is only used by programmers with beards. */ - static int getPageSize() throw(); + static int getPageSize(); /** Returns a list of MAC addresses found on this machine. @@ -14396,7 +14389,7 @@ public: static const StringArray getMACAddressStrings(); // not-for-public-use platform-specific method gets called at startup to initialise things. - static void initialiseStats() throw(); + static void initialiseStats(); private: SystemStats(); @@ -18873,7 +18866,7 @@ public: The return value is the number of radians clockwise from the 3 o'clock direction, where this point is the centre and the other point is on the radius. */ - ValueType getAngleToPoint (const Point& other) const throw() { return (ValueType) atan2 (other.x - x, other.y - y); } + ValueType getAngleToPoint (const Point& other) const throw() { return (ValueType) std::atan2 (other.x - x, other.y - y); } /** Uses a transform to change the point's co-ordinates. This will only compile if ValueType = float! @@ -20427,10 +20420,10 @@ public: */ const Rectangle getSmallestIntegerContainer() const throw() { - const int x1 = (int) floorf ((float) x); - const int y1 = (int) floorf ((float) y); - const int x2 = (int) floorf ((float) (x + w + 0.9999f)); - const int y2 = (int) floorf ((float) (y + h + 0.9999f)); + const int x1 = (int) std::floor (static_cast (x)); + const int y1 = (int) std::floor (static_cast (y)); + const int x2 = (int) std::floor (static_cast (x + w + 0.9999f)); + const int y2 = (int) std::floor (static_cast (y + h + 0.9999f)); return Rectangle (x1, y1, x2 - x1, y2 - y1); } @@ -20661,186 +20654,6 @@ private: #endif // __JUCE_JUSTIFICATION_JUCEHEADER__ /*** End of inlined file: juce_Justification.h ***/ - -/*** Start of inlined file: juce_EdgeTable.h ***/ -#ifndef __JUCE_EDGETABLE_JUCEHEADER__ -#define __JUCE_EDGETABLE_JUCEHEADER__ - -class Path; -class RectangleList; -class Image; - -/** - A table of horizontal scan-line segments - used for rasterising Paths. - - @see Path, Graphics -*/ -class JUCE_API EdgeTable -{ -public: - - /** Creates an edge table containing a path. - - A table is created with a fixed vertical range, and only sections of the path - which lie within this range will be added to the table. - - @param clipLimits only the region of the path that lies within this area will be added - @param pathToAdd the path to add to the table - @param transform a transform to apply to the path being added - */ - EdgeTable (const Rectangle& clipLimits, - const Path& pathToAdd, - const AffineTransform& transform); - - /** Creates an edge table containing a rectangle. - */ - EdgeTable (const Rectangle& rectangleToAdd); - - /** Creates an edge table containing a rectangle list. - */ - EdgeTable (const RectangleList& rectanglesToAdd); - - /** Creates an edge table containing a rectangle. - */ - EdgeTable (float x, float y, float w, float h); - - /** Creates a copy of another edge table. */ - EdgeTable (const EdgeTable& other); - - /** Copies from another edge table. */ - EdgeTable& operator= (const EdgeTable& other); - - /** Destructor. */ - ~EdgeTable(); - - void clipToRectangle (const Rectangle& r) throw(); - void excludeRectangle (const Rectangle& r) throw(); - void clipToEdgeTable (const EdgeTable& other); - void clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels) throw(); - bool isEmpty() throw(); - const Rectangle& getMaximumBounds() const throw() { return bounds; } - void translate (float dx, int dy) throw(); - - /** Reduces the amount of space the table has allocated. - - This will shrink the table down to use as little memory as possible - useful for - read-only tables that get stored and re-used for rendering. - */ - void optimiseTable() throw(); - - /** Iterates the lines in the table, for rendering. - - This function will iterate each line in the table, and call a user-defined class - to render each pixel or continuous line of pixels that the table contains. - - @param iterationCallback this templated class must contain the following methods: - @code - inline void setEdgeTableYPos (int y); - inline void handleEdgeTablePixel (int x, int alphaLevel) const; - inline void handleEdgeTableLine (int x, int width, int alphaLevel) const; - @endcode - (these don't necessarily have to be 'const', but it might help it go faster) - */ - template - void iterate (EdgeTableIterationCallback& iterationCallback) const throw() - { - const int* lineStart = table; - - for (int y = 0; y < bounds.getHeight(); ++y) - { - const int* line = lineStart; - lineStart += lineStrideElements; - int numPoints = line[0]; - - if (--numPoints > 0) - { - int x = *++line; - jassert ((x >> 8) >= bounds.getX() && (x >> 8) < bounds.getRight()); - int levelAccumulator = 0; - - iterationCallback.setEdgeTableYPos (bounds.getY() + y); - - while (--numPoints >= 0) - { - const int level = *++line; - jassert (((unsigned int) level) < (unsigned int) 256); - const int endX = *++line; - jassert (endX >= x); - const int endOfRun = (endX >> 8); - - if (endOfRun == (x >> 8)) - { - // small segment within the same pixel, so just save it for the next - // time round.. - levelAccumulator += (endX - x) * level; - } - else - { - // plot the fist pixel of this segment, including any accumulated - // levels from smaller segments that haven't been drawn yet - levelAccumulator += (0xff - (x & 0xff)) * level; - levelAccumulator >>= 8; - x >>= 8; - - if (levelAccumulator > 0) - { - if (levelAccumulator >> 8) - levelAccumulator = 0xff; - - iterationCallback.handleEdgeTablePixel (x, levelAccumulator); - } - - // if there's a run of similar pixels, do it all in one go.. - if (level > 0) - { - jassert (endOfRun <= bounds.getRight()); - const int numPix = endOfRun - ++x; - - if (numPix > 0) - iterationCallback.handleEdgeTableLine (x, numPix, level); - } - - // save the bit at the end to be drawn next time round the loop. - levelAccumulator = (endX & 0xff) * level; - } - - x = endX; - } - - if (levelAccumulator > 0) - { - levelAccumulator >>= 8; - if (levelAccumulator >> 8) - levelAccumulator = 0xff; - - x >>= 8; - jassert (x >= bounds.getX() && x < bounds.getRight()); - iterationCallback.handleEdgeTablePixel (x, levelAccumulator); - } - } - } - } - - juce_UseDebuggingNewOperator - -private: - // table line format: number of points; point0 x, point0 levelDelta, point1 x, point1 levelDelta, etc - HeapBlock table; - Rectangle bounds; - int maxEdgesPerLine, lineStrideElements; - bool needToCheckEmptinesss; - - void addEdgePoint (int x, int y, int winding) throw(); - void remapTableForNumEdges (int newNumEdgesPerLine) throw(); - void intersectWithEdgeTableLine (int y, const int* otherLine) throw(); - void clipEdgeTableLineToRange (int* line, int x1, int x2) throw(); - void sanitiseLevels (bool useNonZeroWinding) throw(); - static void copyEdgeTableData (int* dest, int destLineStride, const int* src, int srcLineStride, int numLines) throw(); -}; - -#endif // __JUCE_EDGETABLE_JUCEHEADER__ -/*** End of inlined file: juce_EdgeTable.h ***/ - class Image; /** @@ -44113,6 +43926,9 @@ public: const StringArray getTokenTypes(); const Colour getDefaultColour (int tokenType); + /** This is a handy method for checking whether a string is a c++ reserved keyword. */ + static bool isReservedKeyword (const String& token) throw(); + juce_UseDebuggingNewOperator }; @@ -46857,8 +46673,7 @@ public: This won't do any alpha-blending - it just sets all pixels in the image to the given colour (which may be non-opaque if the image has an alpha channel). */ - virtual void clear (int x, int y, int w, int h, - const Colour& colourToClearTo = Colour (0x00000000)); + virtual void clear (const Rectangle& area, const Colour& colourToClearTo = Colour (0x00000000)); /** Returns a new image that's a copy of this one. @@ -46963,6 +46778,7 @@ public: inline uint8* getPixelPointer (int x, int y) const { return data + y * lineStride + x * pixelStride; } uint8* data; + const PixelFormat pixelFormat; int lineStride, pixelStride, width, height; private: @@ -56975,6 +56791,185 @@ private: #endif #ifndef __JUCE_EDGETABLE_JUCEHEADER__ +/*** Start of inlined file: juce_EdgeTable.h ***/ +#ifndef __JUCE_EDGETABLE_JUCEHEADER__ +#define __JUCE_EDGETABLE_JUCEHEADER__ + +class Path; +class Image; + +/** + A table of horizontal scan-line segments - used for rasterising Paths. + + @see Path, Graphics +*/ +class JUCE_API EdgeTable +{ +public: + + /** Creates an edge table containing a path. + + A table is created with a fixed vertical range, and only sections of the path + which lie within this range will be added to the table. + + @param clipLimits only the region of the path that lies within this area will be added + @param pathToAdd the path to add to the table + @param transform a transform to apply to the path being added + */ + EdgeTable (const Rectangle& clipLimits, + const Path& pathToAdd, + const AffineTransform& transform); + + /** Creates an edge table containing a rectangle. + */ + EdgeTable (const Rectangle& rectangleToAdd); + + /** Creates an edge table containing a rectangle list. + */ + EdgeTable (const RectangleList& rectanglesToAdd); + + /** Creates an edge table containing a rectangle. + */ + EdgeTable (const Rectangle& rectangleToAdd); + + /** Creates a copy of another edge table. */ + EdgeTable (const EdgeTable& other); + + /** Copies from another edge table. */ + EdgeTable& operator= (const EdgeTable& other); + + /** Destructor. */ + ~EdgeTable(); + + void clipToRectangle (const Rectangle& r) throw(); + void excludeRectangle (const Rectangle& r) throw(); + void clipToEdgeTable (const EdgeTable& other); + void clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels) throw(); + bool isEmpty() throw(); + const Rectangle& getMaximumBounds() const throw() { return bounds; } + void translate (float dx, int dy) throw(); + + /** Reduces the amount of space the table has allocated. + + This will shrink the table down to use as little memory as possible - useful for + read-only tables that get stored and re-used for rendering. + */ + void optimiseTable() throw(); + + /** Iterates the lines in the table, for rendering. + + This function will iterate each line in the table, and call a user-defined class + to render each pixel or continuous line of pixels that the table contains. + + @param iterationCallback this templated class must contain the following methods: + @code + inline void setEdgeTableYPos (int y); + inline void handleEdgeTablePixel (int x, int alphaLevel) const; + inline void handleEdgeTableLine (int x, int width, int alphaLevel) const; + @endcode + (these don't necessarily have to be 'const', but it might help it go faster) + */ + template + void iterate (EdgeTableIterationCallback& iterationCallback) const throw() + { + const int* lineStart = table; + + for (int y = 0; y < bounds.getHeight(); ++y) + { + const int* line = lineStart; + lineStart += lineStrideElements; + int numPoints = line[0]; + + if (--numPoints > 0) + { + int x = *++line; + jassert ((x >> 8) >= bounds.getX() && (x >> 8) < bounds.getRight()); + int levelAccumulator = 0; + + iterationCallback.setEdgeTableYPos (bounds.getY() + y); + + while (--numPoints >= 0) + { + const int level = *++line; + jassert (((unsigned int) level) < (unsigned int) 256); + const int endX = *++line; + jassert (endX >= x); + const int endOfRun = (endX >> 8); + + if (endOfRun == (x >> 8)) + { + // small segment within the same pixel, so just save it for the next + // time round.. + levelAccumulator += (endX - x) * level; + } + else + { + // plot the fist pixel of this segment, including any accumulated + // levels from smaller segments that haven't been drawn yet + levelAccumulator += (0xff - (x & 0xff)) * level; + levelAccumulator >>= 8; + x >>= 8; + + if (levelAccumulator > 0) + { + if (levelAccumulator >> 8) + levelAccumulator = 0xff; + + iterationCallback.handleEdgeTablePixel (x, levelAccumulator); + } + + // if there's a run of similar pixels, do it all in one go.. + if (level > 0) + { + jassert (endOfRun <= bounds.getRight()); + const int numPix = endOfRun - ++x; + + if (numPix > 0) + iterationCallback.handleEdgeTableLine (x, numPix, level); + } + + // save the bit at the end to be drawn next time round the loop. + levelAccumulator = (endX & 0xff) * level; + } + + x = endX; + } + + if (levelAccumulator > 0) + { + levelAccumulator >>= 8; + if (levelAccumulator >> 8) + levelAccumulator = 0xff; + + x >>= 8; + jassert (x >= bounds.getX() && x < bounds.getRight()); + iterationCallback.handleEdgeTablePixel (x, levelAccumulator); + } + } + } + } + + juce_UseDebuggingNewOperator + +private: + // table line format: number of points; point0 x, point0 levelDelta, point1 x, point1 levelDelta, etc + HeapBlock table; + Rectangle bounds; + int maxEdgesPerLine, lineStrideElements; + bool needToCheckEmptinesss; + + void addEdgePoint (int x, int y, int winding) throw(); + void remapTableForNumEdges (int newNumEdgesPerLine) throw(); + void intersectWithEdgeTableLine (int y, const int* otherLine) throw(); + void clipEdgeTableLineToRange (int* line, int x1, int x2) throw(); + void sanitiseLevels (bool useNonZeroWinding) throw(); + static void copyEdgeTableData (int* dest, int destLineStride, const int* src, int srcLineStride, int numLines) throw(); +}; + +#endif // __JUCE_EDGETABLE_JUCEHEADER__ +/*** End of inlined file: juce_EdgeTable.h ***/ + + #endif #ifndef __JUCE_FILLTYPE_JUCEHEADER__ @@ -57178,6 +57173,7 @@ class JUCE_API LowLevelGraphicsSoftwareRenderer : public LowLevelGraphicsContex public: LowLevelGraphicsSoftwareRenderer (Image& imageToRenderOn); + LowLevelGraphicsSoftwareRenderer (Image& imageToRenderOn, int xOffset, int yOffset, const RectangleList& initialClip); ~LowLevelGraphicsSoftwareRenderer(); bool isVectorDevice() const; diff --git a/src/core/juce_MathsFunctions.h b/src/core/juce_MathsFunctions.h index 66b453ff24..41dd99f185 100644 --- a/src/core/juce_MathsFunctions.h +++ b/src/core/juce_MathsFunctions.h @@ -210,7 +210,10 @@ inline int64 abs64 (const int64 n) throw() template inline Type juce_negate (Type n) throw() { - return -n; + return sizeof (Type) == 1 ? (Type) -(char) n + : (sizeof (Type) == 2 ? (Type) -(short) n + : (sizeof (Type) == 4 ? (Type) -(int) n + : ((Type) -(int64) n))); } /** This templated negate function will negate pointers as well as integers */ diff --git a/src/gui/components/special/juce_MagnifierComponent.cpp b/src/gui/components/special/juce_MagnifierComponent.cpp index 38e0b81db9..dcfb15bdd8 100644 --- a/src/gui/components/special/juce_MagnifierComponent.cpp +++ b/src/gui/components/special/juce_MagnifierComponent.cpp @@ -266,7 +266,7 @@ void MagnifierComponent::paint (Graphics& g) } Image temp (Image::ARGB, jmax (w, srcX + srcW), jmax (h, srcY + srcH), false); - temp.clear (srcX, srcY, srcW, srcH); + temp.clear (Rectangle (srcX, srcY, srcW, srcH)); { Graphics g2 (temp); diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index c3ef974fd5..3c9c41e38d 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -56,7 +56,7 @@ template class SolidColourEdgeTableRenderer { public: - SolidColourEdgeTableRenderer (const Image::BitmapData& data_, const PixelARGB& colour) throw() + SolidColourEdgeTableRenderer (const Image::BitmapData& data_, const PixelARGB& colour) : data (data_), sourceColour (colour) { @@ -247,7 +247,7 @@ class RadialGradientPixelGenerator { public: RadialGradientPixelGenerator (const ColourGradient& gradient, const AffineTransform&, - const PixelARGB* const lookupTable_, const int numEntries_) throw() + const PixelARGB* const lookupTable_, const int numEntries_) : lookupTable (lookupTable_), numEntries (numEntries_), gx1 (gradient.x1), @@ -291,7 +291,7 @@ class TransformedRadialGradientPixelGenerator : public RadialGradientPixelGene { public: TransformedRadialGradientPixelGenerator (const ColourGradient& gradient, const AffineTransform& transform, - const PixelARGB* const lookupTable_, const int numEntries_) throw() + const PixelARGB* const lookupTable_, const int numEntries_) : RadialGradientPixelGenerator (gradient, transform, lookupTable_, numEntries_), inverseTransform (transform.inverted()) { @@ -333,7 +333,7 @@ class GradientEdgeTableRenderer : public GradientType { public: GradientEdgeTableRenderer (const Image::BitmapData& destData_, const ColourGradient& gradient, const AffineTransform& transform, - const PixelARGB* const lookupTable_, const int numEntries_) throw() + const PixelARGB* const lookupTable_, const int numEntries_) : GradientType (gradient, transform, lookupTable_, numEntries_ - 1), destData (destData_) { @@ -394,7 +394,7 @@ public: ImageFillEdgeTableRenderer (const Image::BitmapData& destData_, const Image::BitmapData& srcData_, const int extraAlpha_, - const int x, const int y) throw() + const int x, const int y) : destData (destData_), srcData (srcData_), extraAlpha (extraAlpha_ + 1), @@ -455,7 +455,7 @@ public: } } - void clipEdgeTableLine (EdgeTable& et, int x, int y, int width) throw() + void clipEdgeTableLine (EdgeTable& et, int x, int y, int width) { jassert (x - xOffset >= 0 && x + width - xOffset <= srcData.width); SrcPixelType* s = (SrcPixelType*) srcData.getLinePointer (y - yOffset); @@ -501,7 +501,7 @@ public: const Image::BitmapData& srcData_, const AffineTransform& transform, const int extraAlpha_, - const bool betterQuality_) throw() + const bool betterQuality_) : interpolator (transform), destData (destData_), srcData (srcData_), @@ -516,7 +516,7 @@ public: scratchBuffer.malloc (scratchSize); } - ~TransformedImageFillEdgeTableRenderer() throw() + ~TransformedImageFillEdgeTableRenderer() { } @@ -568,7 +568,7 @@ public: } } - void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width) throw() + void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width) { if (width > scratchSize) { @@ -888,69 +888,938 @@ private: TransformedImageFillEdgeTableRenderer& operator= (const TransformedImageFillEdgeTableRenderer&); }; +//============================================================================== +class ClipRegionBase : public ReferenceCountedObject +{ +public: + ClipRegionBase() {} + virtual ~ClipRegionBase() {} + + typedef ReferenceCountedObjectPtr Ptr; + + virtual const Ptr clone() const = 0; + + const Ptr clipTo (ClipRegionBase* other); + virtual const Ptr clipToRectangle (const Rectangle& r) = 0; + virtual const Ptr clipToRectangleList (const RectangleList& r) = 0; + virtual const Ptr excludeClipRectangle (const Rectangle& r) = 0; + virtual const Ptr clipToPath (const Path& p, const AffineTransform& transform) = 0; + virtual const Ptr clipToEdgeTable (const EdgeTable& et) = 0; + virtual const Ptr clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& t, const bool betterQuality) = 0; + + virtual bool clipRegionIntersects (const Rectangle& r) const = 0; + virtual const Rectangle getClipBounds() const = 0; + + virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour, bool replaceContents) const = 0; + virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour) const = 0; + virtual void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const = 0; + virtual void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const = 0; + virtual void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, const AffineTransform& t, bool betterQuality, bool tiledFill) const = 0; + virtual void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, int x, int y, bool tiledFill) const = 0; + +protected: + //============================================================================== + template + static void renderImageTransformedInternal (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, + const Rectangle& srcClip, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) + { + switch (destData.pixelFormat) + { + case Image::ARGB: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + default: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + } + break; + + case Image::RGB: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + default: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + } + break; + + default: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + default: + if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } + break; + } + break; + } + } + + template + static void renderImageUntransformedInternal (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, int x, int y, bool tiledFill) + { + switch (destData.pixelFormat) + { + case Image::ARGB: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + default: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + } + break; + + case Image::RGB: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + default: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + } + break; + + default: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + default: + if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + } + break; + } + } + + template + static void renderSolidFill (Iterator& iter, const Image::BitmapData& destData, const PixelARGB& fillColour, const bool replaceContents, DestPixelType*) + { + jassert (destData.pixelStride == sizeof (DestPixelType)); + if (replaceContents) + { + SolidColourEdgeTableRenderer r (destData, fillColour); + iter.iterate (r); + } + else + { + SolidColourEdgeTableRenderer r (destData, fillColour); + iter.iterate (r); + } + } + + template + static void renderGradient (Iterator& iter, const Image::BitmapData& destData, const ColourGradient& g, const AffineTransform& transform, + const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity, DestPixelType*) + { + jassert (destData.pixelStride == sizeof (DestPixelType)); + + if (g.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); + iter.iterate (renderer); + } + else + { + GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); + iter.iterate (renderer); + } + } + else + { + GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); + iter.iterate (renderer); + } + } +}; + +//============================================================================== +class ClipRegion_EdgeTable : public ClipRegionBase +{ +public: + ClipRegion_EdgeTable (const EdgeTable& e) : edgeTable (e) {} + ClipRegion_EdgeTable (const Rectangle& r) : edgeTable (r) {} + ClipRegion_EdgeTable (const Rectangle& r) : edgeTable (r) {} + ClipRegion_EdgeTable (const RectangleList& r) : edgeTable (r) {} + ClipRegion_EdgeTable (const Rectangle& bounds, const Path& p, const AffineTransform& t) : edgeTable (bounds, p, t) {} + ClipRegion_EdgeTable (const ClipRegion_EdgeTable& other) : edgeTable (other.edgeTable) {} + ~ClipRegion_EdgeTable() {} + + const Ptr clone() const + { + return new ClipRegion_EdgeTable (*this); + } + + const Ptr clipToRectangle (const Rectangle& r) + { + edgeTable.clipToRectangle (r); + return edgeTable.isEmpty() ? 0 : this; + } + + const Ptr clipToRectangleList (const RectangleList& r) + { + EdgeTable et (r); + edgeTable.clipToEdgeTable (et); + return edgeTable.isEmpty() ? 0 : this; + } + + const Ptr excludeClipRectangle (const Rectangle& r) + { + edgeTable.excludeRectangle (r); + return edgeTable.isEmpty() ? 0 : this; + } + + const Ptr clipToPath (const Path& p, const AffineTransform& transform) + { + EdgeTable et (edgeTable.getMaximumBounds(), p, transform); + edgeTable.clipToEdgeTable (et); + return edgeTable.isEmpty() ? 0 : this; + } + + const Ptr clipToEdgeTable (const EdgeTable& et) + { + edgeTable.clipToEdgeTable (et); + return edgeTable.isEmpty() ? 0 : this; + } + + const Ptr clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& transform, const bool betterQuality) + { + const Image::BitmapData srcData (image, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight()); + + if (transform.isOnlyTranslation()) + { + // If our translation doesn't involve any distortion, just use a simple blit.. + const int tx = (int) (transform.getTranslationX() * 256.0f); + const int ty = (int) (transform.getTranslationY() * 256.0f); + + if ((! betterQuality) || ((tx | ty) & 224) == 0) + { + const int imageX = ((tx + 128) >> 8); + const int imageY = ((ty + 128) >> 8); + + if (image.getFormat() == Image::ARGB) + straightClipImage (srcData, imageX, imageY, (PixelARGB*) 0); + else + straightClipImage (srcData, imageX, imageY, (PixelAlpha*) 0); + + return edgeTable.isEmpty() ? 0 : this; + } + } + + if (transform.isSingularity()) + return 0; + + { + Path p; + p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height); + EdgeTable et2 (edgeTable.getMaximumBounds(), p, transform); + edgeTable.clipToEdgeTable (et2); + } + + if (! edgeTable.isEmpty()) + { + if (image.getFormat() == Image::ARGB) + transformedClipImage (srcData, transform, betterQuality, (PixelARGB*) 0); + else + transformedClipImage (srcData, transform, betterQuality, (PixelAlpha*) 0); + } + + return edgeTable.isEmpty() ? 0 : this; + } + + bool clipRegionIntersects (const Rectangle& r) const + { + return edgeTable.getMaximumBounds().intersects (r); + } + + const Rectangle getClipBounds() const + { + return edgeTable.getMaximumBounds(); + } + + void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour, bool replaceContents) const + { + const Rectangle totalClip (edgeTable.getMaximumBounds()); + const Rectangle clipped (totalClip.getIntersection (area)); + + if (! clipped.isEmpty()) + { + ClipRegion_EdgeTable et (clipped); + et.edgeTable.clipToEdgeTable (edgeTable); + et.fillAllWithColour (destData, colour, replaceContents); + } + } + + void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour) const + { + const Rectangle totalClip (edgeTable.getMaximumBounds().toFloat()); + const Rectangle clipped (totalClip.getIntersection (area)); + + if (! clipped.isEmpty()) + { + ClipRegion_EdgeTable et (clipped); + et.edgeTable.clipToEdgeTable (edgeTable); + et.fillAllWithColour (destData, colour, false); + } + } + + void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const + { + switch (destData.pixelFormat) + { + case Image::ARGB: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelARGB*) 0); break; + case Image::RGB: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelRGB*) 0); break; + default: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelAlpha*) 0); break; + } + } + + void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const + { + HeapBlock lookupTable; + const int numLookupEntries = gradient.createLookupTable (transform, lookupTable); + jassert (numLookupEntries > 0); + + switch (destData.pixelFormat) + { + case Image::ARGB: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; + case Image::RGB: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; + default: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; + } + } + + void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) const + { + renderImageTransformedInternal (edgeTable, destData, srcData, srcClip, alpha, transform, betterQuality, tiledFill); + } + + void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, int x, int y, bool tiledFill) const + { + renderImageUntransformedInternal (edgeTable, destData, srcData, srcClip, alpha, x, y, tiledFill); + } + + EdgeTable edgeTable; + +private: + //============================================================================== + template + void transformedClipImage (const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality, const SrcPixelType*) + { + TransformedImageFillEdgeTableRenderer renderer (srcData, srcData, transform, 255, betterQuality); + + for (int y = 0; y < edgeTable.getMaximumBounds().getHeight(); ++y) + renderer.clipEdgeTableLine (edgeTable, edgeTable.getMaximumBounds().getX(), y + edgeTable.getMaximumBounds().getY(), + edgeTable.getMaximumBounds().getWidth()); + } + + template + void straightClipImage (const Image::BitmapData& srcData, int imageX, int imageY, const SrcPixelType*) + { + Rectangle r (imageX, imageY, srcData.width, srcData.height); + edgeTable.clipToRectangle (r); + + ImageFillEdgeTableRenderer renderer (srcData, srcData, 255, imageX, imageY); + + for (int y = 0; y < r.getHeight(); ++y) + renderer.clipEdgeTableLine (edgeTable, r.getX(), y + r.getY(), r.getWidth()); + } + + ClipRegion_EdgeTable& operator= (const ClipRegion_EdgeTable&); +}; + + +//============================================================================== +class ClipRegion_RectangleList : public ClipRegionBase +{ +public: + ClipRegion_RectangleList (const Rectangle& r) : clip (r) {} + ClipRegion_RectangleList (const RectangleList& r) : clip (r) {} + ClipRegion_RectangleList (const ClipRegion_RectangleList& other) : clip (other.clip) {} + ~ClipRegion_RectangleList() {} + + const Ptr clone() const + { + return new ClipRegion_RectangleList (*this); + } + + const Ptr clipToRectangle (const Rectangle& r) + { + clip.clipTo (r); + return clip.isEmpty() ? 0 : this; + } + + const Ptr clipToRectangleList (const RectangleList& r) + { + clip.clipTo (r); + return clip.isEmpty() ? 0 : this; + } + + const Ptr excludeClipRectangle (const Rectangle& r) + { + clip.subtract (r); + return clip.isEmpty() ? 0 : this; + } + + const Ptr clipToPath (const Path& p, const AffineTransform& transform) + { + return Ptr (new ClipRegion_EdgeTable (clip))->clipToPath (p, transform); + } + + const Ptr clipToEdgeTable (const EdgeTable& et) + { + return Ptr (new ClipRegion_EdgeTable (clip))->clipToEdgeTable (et); + } + + const Ptr clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& transform, const bool betterQuality) + { + return Ptr (new ClipRegion_EdgeTable (clip))->clipToImageAlpha (image, srcClip, transform, betterQuality); + } + + bool clipRegionIntersects (const Rectangle& r) const + { + return clip.intersects (r); + } + + const Rectangle getClipBounds() const + { + return clip.getBounds(); + } + + void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour, bool replaceContents) const + { + SubRectangleIterator iter (clip, area); + + switch (destData.pixelFormat) + { + case Image::ARGB: renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) 0); break; + case Image::RGB: renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) 0); break; + default: renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) 0); break; + } + } + + void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour) const + { + SubRectangleIteratorFloat iter (clip, area); + + switch (destData.pixelFormat) + { + case Image::ARGB: renderSolidFill (iter, destData, colour, false, (PixelARGB*) 0); break; + case Image::RGB: renderSolidFill (iter, destData, colour, false, (PixelRGB*) 0); break; + default: renderSolidFill (iter, destData, colour, false, (PixelAlpha*) 0); break; + } + } + + void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const + { + switch (destData.pixelFormat) + { + case Image::ARGB: renderSolidFill (*this, destData, colour, replaceContents, (PixelARGB*) 0); break; + case Image::RGB: renderSolidFill (*this, destData, colour, replaceContents, (PixelRGB*) 0); break; + default: renderSolidFill (*this, destData, colour, replaceContents, (PixelAlpha*) 0); break; + } + } + + void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const + { + HeapBlock lookupTable; + const int numLookupEntries = gradient.createLookupTable (transform, lookupTable); + jassert (numLookupEntries > 0); + + switch (destData.pixelFormat) + { + case Image::ARGB: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; + case Image::RGB: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; + default: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; + } + } + + void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) const + { + renderImageTransformedInternal (*this, destData, srcData, srcClip, alpha, transform, betterQuality, tiledFill); + } + + void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const Rectangle& srcClip, const int alpha, int x, int y, bool tiledFill) const + { + renderImageUntransformedInternal (*this, destData, srcData, srcClip, alpha, x, y, tiledFill); + } + + RectangleList clip; + + //============================================================================== + template + void iterate (Renderer& r) const throw() + { + RectangleList::Iterator iter (clip); + + while (iter.next()) + { + const Rectangle rect (*iter.getRectangle()); + const int x = rect.getX(); + const int w = rect.getWidth(); + jassert (w > 0); + const int bottom = rect.getBottom(); + + for (int y = rect.getY(); y < bottom; ++y) + { + r.setEdgeTableYPos (y); + r.handleEdgeTableLine (x, w, 255); + } + } + } + +private: + //============================================================================== + class SubRectangleIterator + { + public: + SubRectangleIterator (const RectangleList& clip_, const Rectangle& area_) + : clip (clip_), area (area_) + { + } + + template + void iterate (Renderer& r) const throw() + { + RectangleList::Iterator iter (clip); + + while (iter.next()) + { + const Rectangle rect (iter.getRectangle()->getIntersection (area)); + + if (! rect.isEmpty()) + { + const int x = rect.getX(); + const int w = rect.getWidth(); + const int bottom = rect.getBottom(); + + for (int y = rect.getY(); y < bottom; ++y) + { + r.setEdgeTableYPos (y); + r.handleEdgeTableLine (x, w, 255); + } + } + } + } + + private: + const RectangleList& clip; + const Rectangle area; + }; + + //============================================================================== + class SubRectangleIteratorFloat + { + public: + SubRectangleIteratorFloat (const RectangleList& clip_, const Rectangle& area_) + : clip (clip_), area (area_) + { + } + + template + void iterate (Renderer& r) const throw() + { + int left = roundToInt (area.getX() * 256.0f); + int top = roundToInt (area.getY() * 256.0f); + int right = roundToInt (area.getRight() * 256.0f); + int bottom = roundToInt (area.getBottom() * 256.0f); + + int totalTop, totalLeft, totalBottom, totalRight; + int topAlpha, leftAlpha, bottomAlpha, rightAlpha; + + if ((top >> 8) == (bottom >> 8)) + { + topAlpha = bottom - top; + bottomAlpha = 0; + totalTop = top >> 8; + totalBottom = bottom = top = totalTop + 1; + } + else + { + if ((top & 255) == 0) + { + topAlpha = 0; + top = totalTop = (top >> 8); + } + else + { + topAlpha = 255 - (top & 255); + totalTop = (top >> 8); + top = totalTop + 1; + } + + bottomAlpha = bottom & 255; + bottom >>= 8; + totalBottom = bottom + (bottomAlpha != 0 ? 1 : 0); + } + + if ((left >> 8) == (right >> 8)) + { + leftAlpha = right - left; + rightAlpha = 0; + totalLeft = (left >> 8); + totalRight = right = left = totalLeft + 1; + } + else + { + if ((left & 255) == 0) + { + leftAlpha = 0; + left = totalLeft = (left >> 8); + } + else + { + leftAlpha = 255 - (left & 255); + totalLeft = (left >> 8); + left = totalLeft + 1; + } + + rightAlpha = right & 255; + right >>= 8; + totalRight = right + (rightAlpha != 0 ? 1 : 0); + } + + RectangleList::Iterator iter (clip); + + while (iter.next()) + { + const int clipLeft = iter.getRectangle()->getX(); + const int clipRight = iter.getRectangle()->getRight(); + const int clipTop = iter.getRectangle()->getY(); + const int clipBottom = iter.getRectangle()->getBottom(); + + if (totalBottom > clipTop && totalTop < clipBottom && totalRight > clipLeft && totalLeft < clipRight) + { + if (right - left == 1 && leftAlpha + rightAlpha == 0) // special case for 1-pix vertical lines + { + if (topAlpha != 0 && totalTop >= clipTop) + { + r.setEdgeTableYPos (totalTop); + r.handleEdgeTablePixel (left, topAlpha); + } + + const int endY = jmin (bottom, clipBottom); + for (int y = jmax (clipTop, top); y < endY; ++y) + { + r.setEdgeTableYPos (y); + r.handleEdgeTablePixel (left, 255); + } + + if (bottomAlpha != 0 && bottom < clipBottom) + { + r.setEdgeTableYPos (bottom); + r.handleEdgeTablePixel (left, bottomAlpha); + } + } + else + { + const int clippedLeft = jmax (left, clipLeft); + const int clippedWidth = jmin (right, clipRight) - clippedLeft; + const bool doLeftAlpha = leftAlpha != 0 && totalLeft >= clipLeft; + const bool doRightAlpha = rightAlpha != 0 && right < clipRight; + + if (topAlpha != 0 && totalTop >= clipTop) + { + r.setEdgeTableYPos (totalTop); + + if (doLeftAlpha) + r.handleEdgeTablePixel (totalLeft, (leftAlpha * topAlpha) >> 8); + + if (clippedWidth > 0) + r.handleEdgeTableLine (clippedLeft, clippedWidth, topAlpha); + + if (doRightAlpha) + r.handleEdgeTablePixel (right, (rightAlpha * topAlpha) >> 8); + } + + const int endY = jmin (bottom, clipBottom); + for (int y = jmax (clipTop, top); y < endY; ++y) + { + r.setEdgeTableYPos (y); + + if (doLeftAlpha) + r.handleEdgeTablePixel (totalLeft, leftAlpha); + + if (clippedWidth > 0) + r.handleEdgeTableLine (clippedLeft, clippedWidth, 255); + + if (doRightAlpha) + r.handleEdgeTablePixel (right, rightAlpha); + } + + if (bottomAlpha != 0 && bottom < clipBottom) + { + r.setEdgeTableYPos (bottom); + + if (doLeftAlpha) + r.handleEdgeTablePixel (totalLeft, (leftAlpha * bottomAlpha) >> 8); + + if (clippedWidth > 0) + r.handleEdgeTableLine (clippedLeft, clippedWidth, bottomAlpha); + + if (doRightAlpha) + r.handleEdgeTablePixel (right, (rightAlpha * bottomAlpha) >> 8); + } + } + } + } + } + + private: + const RectangleList& clip; + const Rectangle& area; + }; + + ClipRegion_RectangleList& operator= (const ClipRegion_RectangleList&); +}; + +//============================================================================== +const ClipRegionBase::Ptr ClipRegionBase::clipTo (ClipRegionBase* const other) +{ + ClipRegion_EdgeTable* et = dynamic_cast (other); + + if (et != 0) + return clipToEdgeTable (et->edgeTable); + + ClipRegion_RectangleList* rl = dynamic_cast (other); + + if (rl != 0) + return clipToRectangleList (rl->clip); + + jassertfalse + return 0; +} + //============================================================================== class LLGCSavedState { public: - LLGCSavedState (const Rectangle& clip_, const int xOffset_, const int yOffset_, - const Font& font_, const FillType& fillType_, - const Graphics::ResamplingQuality interpolationQuality_) throw() - : edgeTable (new EdgeTableHolder (EdgeTable (clip_))), + LLGCSavedState (const Rectangle& clip_, const int xOffset_, const int yOffset_) + : clip (new ClipRegion_RectangleList (clip_)), + xOffset (xOffset_), yOffset (yOffset_), + interpolationQuality (Graphics::mediumResamplingQuality) + { + } + + LLGCSavedState (const RectangleList& clip_, const int xOffset_, const int yOffset_) + : clip (new ClipRegion_RectangleList (clip_)), xOffset (xOffset_), yOffset (yOffset_), - font (font_), fillType (fillType_), - interpolationQuality (interpolationQuality_) + interpolationQuality (Graphics::mediumResamplingQuality) { } - LLGCSavedState (const LLGCSavedState& other) throw() - : edgeTable (other.edgeTable), xOffset (other.xOffset), + LLGCSavedState (const LLGCSavedState& other) + : clip (other.clip), xOffset (other.xOffset), yOffset (other.yOffset), font (other.font), fillType (other.fillType), interpolationQuality (other.interpolationQuality) { } - ~LLGCSavedState() throw() + ~LLGCSavedState() + { + } + + void setOrigin (const int x, const int y) throw() + { + xOffset += x; + yOffset += y; + } + + bool clipToRectangle (const Rectangle& r) + { + if (clip != 0) + { + cloneClipIfMultiplyReferenced(); + clip = clip->clipToRectangle (r.translated (xOffset, yOffset)); + } + + return clip != 0; + } + + bool clipToRectangleList (const RectangleList& r) { + if (clip != 0) + { + cloneClipIfMultiplyReferenced(); + + RectangleList offsetList (r); + offsetList.offsetAll (xOffset, yOffset); + clip = clip->clipToRectangleList (offsetList); + } + + return clip != 0; } - bool clipToRectangle (const Rectangle& r) throw() + bool excludeClipRectangle (const Rectangle& r) { - dupeEdgeTableIfMultiplyReferenced(); - edgeTable->edgeTable.clipToRectangle (r.translated (xOffset, yOffset)); - return ! edgeTable->edgeTable.isEmpty(); + if (clip != 0) + { + cloneClipIfMultiplyReferenced(); + clip = clip->excludeClipRectangle (r.translated (xOffset, yOffset)); + } + + return clip != 0; } - bool clipToRectangleList (const RectangleList& r) throw() + void clipToPath (const Path& p, const AffineTransform& transform) { - dupeEdgeTableIfMultiplyReferenced(); - RectangleList offsetList (r); - offsetList.offsetAll (xOffset, yOffset); - EdgeTable e2 (offsetList); - edgeTable->edgeTable.clipToEdgeTable (e2); + if (clip != 0) + { + cloneClipIfMultiplyReferenced(); + clip = clip->clipToPath (p, transform.translated ((float) xOffset, (float) yOffset)); + } + } - return ! edgeTable->edgeTable.isEmpty(); + void clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& t) + { + if (clip != 0) + { + if (image.hasAlphaChannel()) + { + cloneClipIfMultiplyReferenced(); + clip = clip->clipToImageAlpha (image, srcClip, t.translated ((float) xOffset, (float) yOffset), + interpolationQuality != Graphics::lowResamplingQuality); + } + else + { + Path p; + p.addRectangle (srcClip); + clipToPath (p, t); + } + } } - bool excludeClipRectangle (const Rectangle& r) throw() + bool clipRegionIntersects (const Rectangle& r) const { - dupeEdgeTableIfMultiplyReferenced(); - edgeTable->edgeTable.excludeRectangle (r.translated (xOffset, yOffset)); - return ! edgeTable->edgeTable.isEmpty(); + return clip != 0 && clip->clipRegionIntersects (r.translated (xOffset, yOffset)); } - void clipToPath (const Path& p, const AffineTransform& transform) throw() + const Rectangle getClipBounds() const { - dupeEdgeTableIfMultiplyReferenced(); - EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform.translated ((float) xOffset, (float) yOffset)); - edgeTable->edgeTable.clipToEdgeTable (et); + return clip == 0 ? Rectangle() : clip->getClipBounds().translated (-xOffset, -yOffset); } //============================================================================== - void fillEdgeTable (Image& image, EdgeTable& et, const bool replaceContents = false) throw() + void fillRect (Image& image, const Rectangle& r, const bool replaceContents) + { + if (clip != 0) + { + if (fillType.isColour()) + { + Image::BitmapData destData (image, 0, 0, image.getWidth(), image.getHeight(), true); + clip->fillRectWithColour (destData, r.translated (xOffset, yOffset), fillType.colour.getPixelARGB(), replaceContents); + } + else + { + const Rectangle totalClip (clip->getClipBounds()); + const Rectangle clipped (totalClip.getIntersection (r.translated (xOffset, yOffset))); + + if (! clipped.isEmpty()) + fillShape (image, new ClipRegion_RectangleList (clipped), false); + } + } + } + + void fillRect (Image& image, const Rectangle& r) + { + if (clip != 0) + { + if (fillType.isColour()) + { + Image::BitmapData destData (image, 0, 0, image.getWidth(), image.getHeight(), true); + clip->fillRectWithColour (destData, r.translated ((float) xOffset, (float) yOffset), fillType.colour.getPixelARGB()); + } + else + { + const Rectangle totalClip (clip->getClipBounds().toFloat()); + const Rectangle clipped (totalClip.getIntersection (r.translated ((float) xOffset, (float) yOffset))); + + if (! clipped.isEmpty()) + fillShape (image, new ClipRegion_EdgeTable (clipped), false); + } + } + } + + void fillPath (Image& image, const Path& path, const AffineTransform& transform) + { + if (clip != 0) + fillShape (image, new ClipRegion_EdgeTable (clip->getClipBounds(), path, transform.translated ((float) xOffset, (float) yOffset)), false); + } + + void fillEdgeTable (Image& image, const EdgeTable& edgeTable, const float x, const int y) + { + if (clip != 0) + { + ClipRegion_EdgeTable* edgeTableClip = new ClipRegion_EdgeTable (edgeTable); + ClipRegionBase::Ptr shapeToFill (edgeTableClip); + edgeTableClip->edgeTable.translate (x + xOffset, y + yOffset); + fillShape (image, shapeToFill, false); + } + } + + void fillShape (Image& image, ClipRegionBase::Ptr shapeToFill, const bool replaceContents) { - et.clipToEdgeTable (edgeTable->edgeTable); + jassert (clip != 0); + + shapeToFill = shapeToFill->clipTo (clip); + + if (shapeToFill != 0) + fillShapeWithoutClipping (image, shapeToFill, replaceContents); + } + void fillShapeWithoutClipping (Image& image, const ClipRegionBase::Ptr& shapeToFill, const bool replaceContents) + { Image::BitmapData destData (image, 0, 0, image.getWidth(), image.getHeight(), true); if (fillType.isGradient()) @@ -972,37 +1841,21 @@ public: transform = AffineTransform::identity; } - HeapBlock lookupTable; - const int numLookupEntries = g2.createLookupTable (transform, lookupTable); - jassert (numLookupEntries > 0); - - switch (image.getFormat()) - { - case Image::ARGB: renderGradient (et, destData, g2, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; - case Image::RGB: renderGradient (et, destData, g2, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; - default: renderGradient (et, destData, g2, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; - } + shapeToFill->fillAllWithGradient (destData, g2, transform, isIdentity); } else if (fillType.isTiledImage()) { - renderImage (image, *(fillType.image), fillType.image->getBounds(), fillType.transform, &et); + renderImage (image, *(fillType.image), fillType.image->getBounds(), fillType.transform, shapeToFill); } else { - const PixelARGB fillColour (fillType.colour.getPixelARGB()); - - switch (image.getFormat()) - { - case Image::ARGB: renderSolidFill (et, destData, fillColour, replaceContents, (PixelARGB*) 0); break; - case Image::RGB: renderSolidFill (et, destData, fillColour, replaceContents, (PixelRGB*) 0); break; - default: renderSolidFill (et, destData, fillColour, replaceContents, (PixelAlpha*) 0); break; - } + shapeToFill->fillAllWithColour (destData, fillType.colour.getPixelARGB(), replaceContents); } } //============================================================================== void renderImage (Image& destImage, const Image& sourceImage, const Rectangle& srcClip, - const AffineTransform& t, const EdgeTable* const tiledFillClipRegion) throw() + const AffineTransform& t, const ClipRegionBase* const tiledFillClipRegion) { const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); @@ -1024,15 +1877,15 @@ public: if (tiledFillClipRegion != 0) { - blittedRenderImage (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, tx, ty, true); + tiledFillClipRegion->renderImageUntransformed (destData, srcData, srcClip, alpha, tx, ty, true); } else { - EdgeTable et (Rectangle (tx, ty, srcClip.getWidth(), srcClip.getHeight()).getIntersection (destImage.getBounds())); - et.clipToEdgeTable (edgeTable->edgeTable); + ClipRegionBase::Ptr c (new ClipRegion_EdgeTable (Rectangle (tx, ty, srcClip.getWidth(), srcClip.getHeight()).getIntersection (destImage.getBounds()))); + c = c->clipTo (clip); - if (! et.isEmpty()) - blittedRenderImage (sourceImage, destImage, et, destData, srcData, alpha, tx, ty, false); + if (c != 0) + c->renderImageUntransformed (destData, srcData, srcClip, alpha, tx, ty, false); } return; @@ -1044,290 +1897,36 @@ public: if (tiledFillClipRegion != 0) { - transformedRenderImage (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, transform, betterQuality, true); + tiledFillClipRegion->renderImageTransformed (destData, srcData, srcClip, alpha, transform, betterQuality, true); } else { Path p; p.addRectangle (0.0f, 0.0f, (float) srcClip.getWidth(), (float) srcClip.getHeight()); - EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); - et.clipToEdgeTable (edgeTable->edgeTable); - - if (! et.isEmpty()) - transformedRenderImage (sourceImage, destImage, et, destData, srcData, alpha, transform, betterQuality, false); - } - } - - //============================================================================== - void clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& t) throw() - { - if (! image.hasAlphaChannel()) - { - Path p; - p.addRectangle (srcClip); - clipToPath (p, t); - return; - } - - dupeEdgeTableIfMultiplyReferenced(); - - const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); - const Image::BitmapData srcData (image, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight()); - const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); - EdgeTable& et = edgeTable->edgeTable; - - if (transform.isOnlyTranslation()) - { - // If our translation doesn't involve any distortion, just use a simple blit.. - const int tx = (int) (transform.getTranslationX() * 256.0f); - const int ty = (int) (transform.getTranslationY() * 256.0f); - - if ((! betterQuality) || ((tx | ty) & 224) == 0) - { - const int imageX = ((tx + 128) >> 8); - const int imageY = ((ty + 128) >> 8); - - if (image.getFormat() == Image::ARGB) - straightClipImage (et, srcData, imageX, imageY, (PixelARGB*)0); - else - straightClipImage (et, srcData, imageX, imageY, (PixelAlpha*)0); - - return; - } - } - - if (transform.isSingularity()) - { - et.clipToRectangle (Rectangle()); - return; - } - - { - Path p; - p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height); - EdgeTable et2 (et.getMaximumBounds(), p, transform); - et.clipToEdgeTable (et2); - } + ClipRegionBase::Ptr c (clip->clone()); + c = c->clipToPath (p, transform); - if (! et.isEmpty()) - { - if (image.getFormat() == Image::ARGB) - transformedClipImage (et, srcData, transform, betterQuality, (PixelARGB*)0); - else - transformedClipImage (et, srcData, transform, betterQuality, (PixelAlpha*)0); + if (c != 0) + c->renderImageTransformed (destData, srcData, srcClip, alpha, transform, betterQuality, true); } } - template - void transformedClipImage (EdgeTable& et, const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality, const SrcPixelType *) throw() - { - TransformedImageFillEdgeTableRenderer renderer (srcData, srcData, transform, 255, betterQuality); - - for (int y = 0; y < et.getMaximumBounds().getHeight(); ++y) - renderer.clipEdgeTableLine (et, et.getMaximumBounds().getX(), y + et.getMaximumBounds().getY(), - et.getMaximumBounds().getWidth()); - } - - template - void straightClipImage (EdgeTable& et, const Image::BitmapData& srcData, int imageX, int imageY, const SrcPixelType *) throw() - { - Rectangle r (imageX, imageY, srcData.width, srcData.height); - et.clipToRectangle (r); - - ImageFillEdgeTableRenderer renderer (srcData, srcData, 255, imageX, imageY); - - for (int y = 0; y < r.getHeight(); ++y) - renderer.clipEdgeTableLine (et, r.getX(), y + r.getY(), r.getWidth()); - } - //============================================================================== - class EdgeTableHolder : public ReferenceCountedObject - { - public: - EdgeTableHolder (const EdgeTable& e) throw() : edgeTable (e) {} - - EdgeTable edgeTable; - }; - - ReferenceCountedObjectPtr edgeTable; + ClipRegionBase::Ptr clip; int xOffset, yOffset; Font font; FillType fillType; Graphics::ResamplingQuality interpolationQuality; private: - LLGCSavedState& operator= (const LLGCSavedState&); - - void dupeEdgeTableIfMultiplyReferenced() throw() - { - if (edgeTable->getReferenceCount() > 1) - edgeTable = new EdgeTableHolder (edgeTable->edgeTable); - } - - //============================================================================== - template - void renderGradient (EdgeTable& et, const Image::BitmapData& destData, const ColourGradient& g, const AffineTransform& transform, - const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity, DestPixelType*) throw() - { - jassert (destData.pixelStride == sizeof (DestPixelType)); - - if (g.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); - et.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - - //============================================================================== - template - void renderSolidFill (EdgeTable& et, const Image::BitmapData& destData, const PixelARGB& fillColour, const bool replaceContents, DestPixelType*) throw() - { - jassert (destData.pixelStride == sizeof (DestPixelType)); - if (replaceContents) - { - SolidColourEdgeTableRenderer r (destData, fillColour); - et.iterate (r); - } - else - { - SolidColourEdgeTableRenderer r (destData, fillColour); - et.iterate (r); - } - } - - //============================================================================== - void transformedRenderImage (const Image& srcImage, Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData, - const int alpha, const AffineTransform& transform, const bool betterQuality, const bool repeatPattern) throw() + void cloneClipIfMultiplyReferenced() { - switch (destImage.getFormat()) - { - case Image::ARGB: - switch (srcImage.getFormat()) - { - case Image::ARGB: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - case Image::RGB: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - default: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - } - break; - case Image::RGB: - switch (srcImage.getFormat()) - { - case Image::ARGB: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - case Image::RGB: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - default: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - } - break; - default: - switch (srcImage.getFormat()) - { - case Image::ARGB: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - case Image::RGB: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - default: - if (repeatPattern) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); et.iterate (r); } - break; - } - break; - } + if (clip->getReferenceCount() > 1) + clip = clip->clone(); } - //============================================================================== - void blittedRenderImage (const Image& srcImage, Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, - const Image::BitmapData& srcData, const int alpha, int x, int y, const bool repeatPattern) throw() - { - switch (destImage.getFormat()) - { - case Image::ARGB: - switch (srcImage.getFormat()) - { - case Image::ARGB: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - case Image::RGB: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - default: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - } - break; - case Image::RGB: - switch (srcImage.getFormat()) - { - case Image::ARGB: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - case Image::RGB: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - default: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - } - break; - default: - switch (srcImage.getFormat()) - { - case Image::ARGB: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - case Image::RGB: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - default: - if (repeatPattern) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); et.iterate (r); } - break; - } - break; - } - } + LLGCSavedState& operator= (const LLGCSavedState&); }; @@ -1335,8 +1934,14 @@ private: LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image_) : image (image_) { - currentState = new LLGCSavedState (image_.getBounds(), 0, 0, Font(), - FillType(), Graphics::mediumResamplingQuality); + currentState = new LLGCSavedState (image_.getBounds(), 0, 0); +} + +LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image_, const int xOffset, const int yOffset, + const RectangleList& initialClip) + : image (image_) +{ + currentState = new LLGCSavedState (initialClip, xOffset, yOffset); } LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() @@ -1351,8 +1956,7 @@ bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const //============================================================================== void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y) { - currentState->xOffset += x; - currentState->yOffset += y; + currentState->setOrigin (x, y); } bool LowLevelGraphicsSoftwareRenderer::clipToRectangle (const Rectangle& r) @@ -1382,18 +1986,17 @@ void LowLevelGraphicsSoftwareRenderer::clipToImageAlpha (const Image& sourceImag bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (const Rectangle& r) { - return currentState->edgeTable->edgeTable.getMaximumBounds() - .intersects (r.translated (currentState->xOffset, currentState->yOffset)); + return currentState->clipRegionIntersects (r); } const Rectangle LowLevelGraphicsSoftwareRenderer::getClipBounds() const { - return currentState->edgeTable->edgeTable.getMaximumBounds().translated (-currentState->xOffset, -currentState->yOffset); + return currentState->getClipBounds(); } bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const { - return currentState->edgeTable->edgeTable.isEmpty(); + return currentState->clip == 0; } //============================================================================== @@ -1436,23 +2039,12 @@ void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::Resamp //============================================================================== void LowLevelGraphicsSoftwareRenderer::fillRect (const Rectangle& r, const bool replaceExistingContents) { - const Rectangle& totalClip = currentState->edgeTable->edgeTable.getMaximumBounds(); - const Rectangle clipped (totalClip.getIntersection (r.translated (currentState->xOffset, currentState->yOffset))); - - if (! clipped.isEmpty()) - { - EdgeTable et (clipped); - currentState->fillEdgeTable (image, et, replaceExistingContents); - } + currentState->fillRect (image, r, replaceExistingContents); } void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform) { - EdgeTable et (currentState->edgeTable->edgeTable.getMaximumBounds(), - path, transform.translated ((float) currentState->xOffset, - (float) currentState->yOffset)); - - currentState->fillEdgeTable (image, et); + currentState->fillPath (image, path, transform); } void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const Rectangle& srcClip, @@ -1461,7 +2053,7 @@ void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, cons jassert (sourceImage.getBounds().contains (srcClip)); currentState->renderImage (image, sourceImage, srcClip, transform, - fillEntireClipAsTiles ? &(currentState->edgeTable->edgeTable) : 0); + fillEntireClipAsTiles ? currentState->clip : 0); } //============================================================================== @@ -1475,39 +2067,27 @@ void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2 void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, double top, double bottom) { if (bottom > top) - { - EdgeTable et (Rectangle ((float) (x + currentState->xOffset), - (float) (top + currentState->yOffset), - 1.0f, (float) (bottom - top))); - - currentState->fillEdgeTable (image, et); - } + currentState->fillRect (image, Rectangle ((float) x, (float) top, 1.0f, (float) (bottom - top))); } void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double left, double right) { if (right > left) - { - EdgeTable et (Rectangle ((float) (left + currentState->xOffset), - (float) (y + currentState->yOffset), - (float) (right - left), 1.0f)); - - currentState->fillEdgeTable (image, et); - } + currentState->fillRect (image, Rectangle ((float) left, (float) y, (float) (right - left), 1.0f)); } //============================================================================== class GlyphCache : private DeletedAtShutdown { public: - GlyphCache() throw() + GlyphCache() : accessCounter (0), hits (0), misses (0) { for (int i = 120; --i >= 0;) glyphs.add (new CachedGlyph()); } - ~GlyphCache() throw() + ~GlyphCache() { clearSingletonInstance(); } @@ -1515,7 +2095,7 @@ public: juce_DeclareSingleton_SingleThreaded_Minimal (GlyphCache); //============================================================================== - void drawGlyph (LLGCSavedState& state, Image& image, const Font& font, const int glyphNumber, float x, float y) throw() + void drawGlyph (LLGCSavedState& state, Image& image, const Font& font, const int glyphNumber, float x, float y) { ++accessCounter; int oldestCounter = std::numeric_limits::max(); @@ -1565,17 +2145,13 @@ public: CachedGlyph() : glyph (0), lastAccessCount (0) {} ~CachedGlyph() {} - void draw (LLGCSavedState& state, Image& image, const float x, const float y) const throw() + void draw (LLGCSavedState& state, Image& image, const float x, const float y) const { if (edgeTable != 0) - { - EdgeTable et (*edgeTable); - et.translate (x, roundToInt (y)); - state.fillEdgeTable (image, et, false); - } + state.fillEdgeTable (image, *edgeTable, x, roundToInt (y)); } - void generate (const Font& newFont, const int glyphNumber) throw() + void generate (const Font& newFont, const int glyphNumber) { font = newFont; glyph = glyphNumber; @@ -1639,8 +2215,8 @@ void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineT if (transform.isOnlyTranslation()) { GlyphCache::getInstance()->drawGlyph (*currentState, image, f, glyphNumber, - transform.getTranslationX() + (float) currentState->xOffset, - transform.getTranslationY() + (float) currentState->yOffset); + transform.getTranslationX(), + transform.getTranslationY()); } else { diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h index 9208b14988..3baccc4c9a 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h @@ -42,6 +42,7 @@ class JUCE_API LowLevelGraphicsSoftwareRenderer : public LowLevelGraphicsCon public: //============================================================================== LowLevelGraphicsSoftwareRenderer (Image& imageToRenderOn); + LowLevelGraphicsSoftwareRenderer (Image& imageToRenderOn, int xOffset, int yOffset, const RectangleList& initialClip); ~LowLevelGraphicsSoftwareRenderer(); bool isVectorDevice() const; diff --git a/src/gui/graphics/geometry/juce_RectangleList.cpp b/src/gui/graphics/geometry/juce_RectangleList.cpp index ee492e8087..80feb78e6d 100644 --- a/src/gui/graphics/geometry/juce_RectangleList.cpp +++ b/src/gui/graphics/geometry/juce_RectangleList.cpp @@ -157,7 +157,8 @@ void RectangleList::add (const Rectangle& rect) void RectangleList::addWithoutMerging (const Rectangle& rect) { - rects.add (rect); + if (! rect.isEmpty()) + rects.add (rect); } void RectangleList::add (const int x, const int y, const int w, const int h) diff --git a/src/gui/graphics/imaging/juce_Image.cpp b/src/gui/graphics/imaging/juce_Image.cpp index aa77798f9c..5e951f2606 100644 --- a/src/gui/graphics/imaging/juce_Image.cpp +++ b/src/gui/graphics/imaging/juce_Image.cpp @@ -96,8 +96,9 @@ LowLevelGraphicsContext* Image::createLowLevelContext() } //============================================================================== -Image::BitmapData::BitmapData (Image& image, int x, int y, int w, int h, const bool /*makeWritable*/) +Image::BitmapData::BitmapData (Image& image, const int x, const int y, const int w, const int h, const bool /*makeWritable*/) : data (image.imageData + image.lineStride * y + image.pixelStride * x), + pixelFormat (image.getFormat()), lineStride (image.lineStride), pixelStride (image.pixelStride), width (w), @@ -106,8 +107,9 @@ Image::BitmapData::BitmapData (Image& image, int x, int y, int w, int h, const b jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= image.getWidth() && y + h <= image.getHeight()); } -Image::BitmapData::BitmapData (const Image& image, int x, int y, int w, int h) +Image::BitmapData::BitmapData (const Image& image, const int x, const int y, const int w, const int h) : data (image.imageData + image.lineStride * y + image.pixelStride * x), + pixelFormat (image.getFormat()), lineStride (image.lineStride), pixelStride (image.pixelStride), width (w), @@ -121,7 +123,7 @@ Image::BitmapData::~BitmapData() } void Image::setPixelData (int x, int y, int w, int h, - const uint8* sourcePixelData, int sourceLineStride) + const uint8* const sourcePixelData, const int sourceLineStride) { jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= imageWidth && y + h <= imageHeight); @@ -139,14 +141,17 @@ void Image::setPixelData (int x, int y, int w, int h, } //============================================================================== -void Image::clear (int dx, int dy, int dw, int dh, const Colour& colourToClearTo) +void Image::clear (const Rectangle& area, const Colour& colourToClearTo) { - if (Rectangle::intersectRectangles (dx, dy, dw, dh, 0, 0, imageWidth, imageHeight)) + const Rectangle clipped (area.getIntersection (getBounds())); + + if (! clipped.isEmpty()) { const PixelARGB col (colourToClearTo.getPixelARGB()); - const BitmapData destData (*this, dx, dy, dw, dh, true); + const BitmapData destData (*this, clipped.getX(), clipped.getY(), clipped.getWidth(), clipped.getHeight(), true); uint8* dest = destData.data; + int dh = clipped.getHeight(); while (--dh >= 0) { @@ -155,7 +160,7 @@ void Image::clear (int dx, int dy, int dw, int dh, const Colour& colourToClearTo if (isARGB()) { - for (int x = dw; --x >= 0;) + for (int x = clipped.getWidth(); --x >= 0;) { ((PixelARGB*) line)->set (col); line += destData.pixelStride; @@ -163,7 +168,7 @@ void Image::clear (int dx, int dy, int dw, int dh, const Colour& colourToClearTo } else if (isRGB()) { - for (int x = dw; --x >= 0;) + for (int x = clipped.getWidth(); --x >= 0;) { ((PixelRGB*) line)->set (col); line += destData.pixelStride; @@ -171,7 +176,7 @@ void Image::clear (int dx, int dy, int dw, int dh, const Colour& colourToClearTo } else { - for (int x = dw; --x >= 0;) + for (int x = clipped.getWidth(); --x >= 0;) { *line = col.getAlpha(); line += destData.pixelStride; @@ -211,7 +216,7 @@ Image* Image::createCopyOfAlphaChannel() const if (! hasAlphaChannel()) { - newImage->clear (0, 0, imageWidth, imageHeight, Colours::black); + newImage->clear (getBounds(), Colours::black); } else { diff --git a/src/gui/graphics/imaging/juce_Image.h b/src/gui/graphics/imaging/juce_Image.h index c0375d0bb1..7d60e2cc96 100644 --- a/src/gui/graphics/imaging/juce_Image.h +++ b/src/gui/graphics/imaging/juce_Image.h @@ -132,8 +132,7 @@ public: This won't do any alpha-blending - it just sets all pixels in the image to the given colour (which may be non-opaque if the image has an alpha channel). */ - virtual void clear (int x, int y, int w, int h, - const Colour& colourToClearTo = Colour (0x00000000)); + virtual void clear (const Rectangle& area, const Colour& colourToClearTo = Colour (0x00000000)); /** Returns a new image that's a copy of this one. @@ -240,6 +239,7 @@ public: inline uint8* getPixelPointer (int x, int y) const { return data + y * lineStride + x * pixelStride; } uint8* data; + const PixelFormat pixelFormat; int lineStride, pixelStride, width, height; private: diff --git a/src/native/linux/juce_linux_Windowing.cpp b/src/native/linux/juce_linux_Windowing.cpp index 8e1b19056e..0939a71ad4 100644 --- a/src/native/linux/juce_linux_Windowing.cpp +++ b/src/native/linux/juce_linux_Windowing.cpp @@ -1888,26 +1888,20 @@ private: startTimer (repaintTimerPeriod); - LowLevelGraphicsSoftwareRenderer context (*image); + RectangleList adjustedList (originalRepaintRegion); + adjustedList.offsetAll (-totalArea.getX(), -totalArea.getY()); + LowLevelGraphicsSoftwareRenderer context (*image, -totalArea.getX(), -totalArea.getY(), adjustedList); - context.setOrigin (-totalArea.getX(), -totalArea.getY()); - - if (context.clipToRectangleList (originalRepaintRegion)) + if (peer->depth == 32) { - if (peer->depth == 32) - { - RectangleList::Iterator i (originalRepaintRegion); + RectangleList::Iterator i (originalRepaintRegion); - while (i.next()) - { - const Rectangle& r = *i.getRectangle(); - image->clear (r.getX() - totalArea.getX(), r.getY() - totalArea.getY(), r.getWidth(), r.getHeight()); - } - } - - peer->handlePaint (context); + while (i.next()) + image->clear (*i.getRectangle() - totalArea.getPosition()); } + peer->handlePaint (context); + if (! peer->maskedRegion.isEmpty()) originalRepaintRegion.subtract (peer->maskedRegion); diff --git a/src/native/mac/juce_mac_NSViewComponentPeer.mm b/src/native/mac/juce_mac_NSViewComponentPeer.mm index f2ead064e5..fc185f3852 100644 --- a/src/native/mac/juce_mac_NSViewComponentPeer.mm +++ b/src/native/mac/juce_mac_NSViewComponentPeer.mm @@ -1505,25 +1505,28 @@ void NSViewComponentPeer::drawRect (NSRect r) (int) (r.size.height + 0.5f), ! getComponent()->isOpaque()); - LowLevelGraphicsSoftwareRenderer context (temp); - context.setOrigin (-roundToInt (r.origin.x), - -roundToInt ([view frame].size.height - (r.origin.y + r.size.height))); + const int xOffset = -roundToInt (r.origin.x); + const int yOffset = -roundToInt ([view frame].size.height - (r.origin.y + r.size.height)); const NSRect* rects = 0; NSInteger numRects = 0; [view getRectsBeingDrawn: &rects count: &numRects]; + const Rectangle clipBounds (temp.getBounds()); + RectangleList clip; for (int i = 0; i < numRects; ++i) { - clip.addWithoutMerging (Rectangle (roundToInt (rects[i].origin.x), - roundToInt ([view frame].size.height - (rects[i].origin.y + rects[i].size.height)), - roundToInt (rects[i].size.width), - roundToInt (rects[i].size.height))); + clip.addWithoutMerging (clipBounds.getIntersection (Rectangle (roundToInt (rects[i].origin.x) + xOffset, + roundToInt ([view frame].size.height - (rects[i].origin.y + rects[i].size.height)) + yOffset, + roundToInt (rects[i].size.width), + roundToInt (rects[i].size.height)))); } - if (context.clipToRectangleList (clip)) + if (! clip.isEmpty()) { + LowLevelGraphicsSoftwareRenderer context (temp, xOffset, yOffset, clip); + insideDrawRect = true; handlePaint (context); insideDrawRect = false; diff --git a/src/native/windows/juce_win32_Windowing.cpp b/src/native/windows/juce_win32_Windowing.cpp index 781e7576bd..9c35d9ee69 100644 --- a/src/native/windows/juce_win32_Windowing.cpp +++ b/src/native/windows/juce_win32_Windowing.cpp @@ -1122,6 +1122,7 @@ private: WindowsBitmapImage* const offscreenImage = offscreenImageGenerator.getImage (transparent, w, h); RectangleList contextClip; + const Rectangle clipBounds (0, 0, w, h); bool needToPaintAll = true; @@ -1149,15 +1150,12 @@ private: while (--num >= 0) { - // (need to move this one pixel to the left because of a win32 bug) - const int cx = jmax (x, (int) rects->left - 1); - const int cy = rects->top; - const int cw = rects->right - cx; - const int ch = rects->bottom - rects->top; - - if (cx + cw - x <= w && cy + ch - y <= h) + if (rects->right <= x + w && rects->bottom <= y + h) { - contextClip.addWithoutMerging (Rectangle (cx - x, cy - y, cw, ch)); + // (need to move this one pixel to the left because of a win32 bug) + const int cx = jmax (x, (int) rects->left - 1); + contextClip.addWithoutMerging (Rectangle (cx - x, rects->top - y, rects->right - cx, rects->bottom - rects->top) + .getIntersection (clipBounds)); } else { @@ -1182,10 +1180,7 @@ private: RectangleList::Iterator i (contextClip); while (i.next()) - { - const Rectangle& r = *i.getRectangle(); - offscreenImage->clear (r.getX(), r.getY(), r.getWidth(), r.getHeight()); - } + offscreenImage->clear (*i.getRectangle()); } // if the component's not opaque, this won't draw properly unless the platform can support this @@ -1193,10 +1188,7 @@ private: updateCurrentModifiers(); - LowLevelGraphicsSoftwareRenderer context (*offscreenImage); - context.clipToRectangleList (contextClip); - context.setOrigin (-x, -y); - + LowLevelGraphicsSoftwareRenderer context (*offscreenImage, -x, -y, contextClip); handlePaint (context); if (! dontRepaint)