From d6e3576234584c96ce0850636a346a669f79ff1f Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Wed, 2 Dec 2009 14:21:33 +0000 Subject: [PATCH] Fixes for iPhone build and mac/iPhone CoreGraphics. Added ability to remove specified jobs from a ThreadPool --- .../iphone/JuceDemo.xcodeproj/project.pbxproj | 18 +-- juce_amalgamated.cpp | 104 +++++++++++------- juce_amalgamated.h | 31 +++++- .../juce_LowLevelGraphicsSoftwareRenderer.cpp | 12 +- src/gui/graphics/imaging/juce_Image.h | 5 + .../mac/juce_mac_CoreGraphicsContext.mm | 23 ++-- src/native/mac/juce_mac_SystemStats.mm | 6 +- src/threads/juce_ThreadPool.cpp | 42 ++++--- src/threads/juce_ThreadPool.h | 27 ++++- 9 files changed, 182 insertions(+), 86 deletions(-) diff --git a/extras/juce demo/build/iphone/JuceDemo.xcodeproj/project.pbxproj b/extras/juce demo/build/iphone/JuceDemo.xcodeproj/project.pbxproj index 4a50b0a51a..c441948c69 100644 --- a/extras/juce demo/build/iphone/JuceDemo.xcodeproj/project.pbxproj +++ b/extras/juce demo/build/iphone/JuceDemo.xcodeproj/project.pbxproj @@ -18,7 +18,6 @@ 848168AD107E7DA6008FEC33 /* DragAndDropDemo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84816891107E7DA6008FEC33 /* DragAndDropDemo.cpp */; }; 848168AE107E7DA6008FEC33 /* InterprocessCommsDemo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84816892107E7DA6008FEC33 /* InterprocessCommsDemo.cpp */; }; 848168AF107E7DA6008FEC33 /* OpenGLDemo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84816893107E7DA6008FEC33 /* OpenGLDemo.cpp */; }; - 848168B0107E7DA6008FEC33 /* PathsAndTransformsDemo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84816894107E7DA6008FEC33 /* PathsAndTransformsDemo.cpp */; }; 848168B1107E7DA6008FEC33 /* QuickTimeDemo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84816895107E7DA6008FEC33 /* QuickTimeDemo.cpp */; }; 848168B2107E7DA6008FEC33 /* TableDemo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84816896107E7DA6008FEC33 /* TableDemo.cpp */; }; 848168B3107E7DA6008FEC33 /* ThreadingDemo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84816897107E7DA6008FEC33 /* ThreadingDemo.cpp */; }; @@ -34,6 +33,7 @@ 848168BD107E7DA6008FEC33 /* AudioDemoLatencyPage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 848168A7107E7DA6008FEC33 /* AudioDemoLatencyPage.cpp */; }; 848168BE107E7DA6008FEC33 /* MainDemoWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 848168A9107E7DA6008FEC33 /* MainDemoWindow.cpp */; }; 8481714D1080CCA5008FEC33 /* libjucedebug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 848171011080CB2A008FEC33 /* libjucedebug.a */; }; + 848789C910C67ABA009A1E59 /* RenderingTestComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 848789C710C67ABA009A1E59 /* RenderingTestComponent.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -68,7 +68,6 @@ 84816891107E7DA6008FEC33 /* DragAndDropDemo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DragAndDropDemo.cpp; sourceTree = ""; }; 84816892107E7DA6008FEC33 /* InterprocessCommsDemo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InterprocessCommsDemo.cpp; sourceTree = ""; }; 84816893107E7DA6008FEC33 /* OpenGLDemo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OpenGLDemo.cpp; sourceTree = ""; }; - 84816894107E7DA6008FEC33 /* PathsAndTransformsDemo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PathsAndTransformsDemo.cpp; sourceTree = ""; }; 84816895107E7DA6008FEC33 /* QuickTimeDemo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = QuickTimeDemo.cpp; sourceTree = ""; }; 84816896107E7DA6008FEC33 /* TableDemo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TableDemo.cpp; sourceTree = ""; }; 84816897107E7DA6008FEC33 /* ThreadingDemo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadingDemo.cpp; sourceTree = ""; }; @@ -92,6 +91,8 @@ 848168A9107E7DA6008FEC33 /* MainDemoWindow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MainDemoWindow.cpp; path = ../../src/MainDemoWindow.cpp; sourceTree = SOURCE_ROOT; }; 848168AA107E7DA6008FEC33 /* MainDemoWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MainDemoWindow.h; path = ../../src/MainDemoWindow.h; sourceTree = SOURCE_ROOT; }; 848170FB1080CB2A008FEC33 /* Juce.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Juce.xcodeproj; path = ../../../../build/macosx/Juce.xcodeproj; sourceTree = SOURCE_ROOT; }; + 848789C710C67ABA009A1E59 /* RenderingTestComponent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderingTestComponent.cpp; sourceTree = ""; }; + 848789C810C67ABA009A1E59 /* RenderingTestComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderingTestComponent.h; sourceTree = ""; }; 8D1107310486CEB800E47090 /* JuceDemo-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "JuceDemo-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -171,16 +172,15 @@ 840F1D9B10AD6701002A03A6 /* CodeEditorDemo.cpp */, 84816890107E7DA6008FEC33 /* CameraDemo.cpp */, 84816891107E7DA6008FEC33 /* DragAndDropDemo.cpp */, + 848168A6107E7DA6008FEC33 /* FontsAndTextDemo.cpp */, 84816892107E7DA6008FEC33 /* InterprocessCommsDemo.cpp */, 84816893107E7DA6008FEC33 /* OpenGLDemo.cpp */, - 84816894107E7DA6008FEC33 /* PathsAndTransformsDemo.cpp */, 84816895107E7DA6008FEC33 /* QuickTimeDemo.cpp */, 84816896107E7DA6008FEC33 /* TableDemo.cpp */, 84816897107E7DA6008FEC33 /* ThreadingDemo.cpp */, 84816898107E7DA6008FEC33 /* AudioDemoTabComponent.cpp */, - 84816899107E7DA6008FEC33 /* WebBrowserDemo.cpp */, - 8481689A107E7DA6008FEC33 /* WidgetsDemo.cpp */, 8481689B107E7DA6008FEC33 /* AudioDemoSetupPage.h */, + 848168A7107E7DA6008FEC33 /* AudioDemoLatencyPage.cpp */, 8481689C107E7DA6008FEC33 /* AudioDemoLatencyPage.h */, 8481689D107E7DA6008FEC33 /* AudioDemoRecordPage.h */, 8481689E107E7DA6008FEC33 /* AudioDemoPlaybackPage.h */, @@ -190,9 +190,11 @@ 848168A2107E7DA6008FEC33 /* AudioDemoTabComponent.h */, 848168A3107E7DA6008FEC33 /* AudioDemoSynthPage.cpp */, 848168A4107E7DA6008FEC33 /* AudioDemoRecordPage.cpp */, + 848789C710C67ABA009A1E59 /* RenderingTestComponent.cpp */, + 848789C810C67ABA009A1E59 /* RenderingTestComponent.h */, 848168A5107E7DA6008FEC33 /* TreeViewDemo.cpp */, - 848168A6107E7DA6008FEC33 /* FontsAndTextDemo.cpp */, - 848168A7107E7DA6008FEC33 /* AudioDemoLatencyPage.cpp */, + 84816899107E7DA6008FEC33 /* WebBrowserDemo.cpp */, + 8481689A107E7DA6008FEC33 /* WidgetsDemo.cpp */, ); name = demos; path = ../../src/demos; @@ -288,7 +290,6 @@ 848168AD107E7DA6008FEC33 /* DragAndDropDemo.cpp in Sources */, 848168AE107E7DA6008FEC33 /* InterprocessCommsDemo.cpp in Sources */, 848168AF107E7DA6008FEC33 /* OpenGLDemo.cpp in Sources */, - 848168B0107E7DA6008FEC33 /* PathsAndTransformsDemo.cpp in Sources */, 848168B1107E7DA6008FEC33 /* QuickTimeDemo.cpp in Sources */, 848168B2107E7DA6008FEC33 /* TableDemo.cpp in Sources */, 848168B3107E7DA6008FEC33 /* ThreadingDemo.cpp in Sources */, @@ -304,6 +305,7 @@ 848168BD107E7DA6008FEC33 /* AudioDemoLatencyPage.cpp in Sources */, 848168BE107E7DA6008FEC33 /* MainDemoWindow.cpp in Sources */, 840F1D9C10AD6701002A03A6 /* CodeEditorDemo.cpp in Sources */, + 848789C910C67ABA009A1E59 /* RenderingTestComponent.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 8268fbc44b..9a981a6f10 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -15890,6 +15890,7 @@ ThreadPool::~ThreadPool() void ThreadPool::addJob (ThreadPoolJob* const job) { + jassert (job != 0); jassert (job->pool == 0); if (job->pool == 0) @@ -16012,25 +16013,33 @@ bool ThreadPool::removeJob (ThreadPoolJob* const job, bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const int timeOutMs, - const bool deleteInactiveJobs) + const bool deleteInactiveJobs, + ThreadPool::JobSelector* selectedJobsToRemove) { + Array jobsToWaitFor; + lock.enter(); for (int i = jobs.size(); --i >= 0;) { ThreadPoolJob* const job = (ThreadPoolJob*) jobs.getUnchecked(i); - if (job->isActive) + if (selectedJobsToRemove == 0 || selectedJobsToRemove->isJobSuitable (job)) { - if (interruptRunningJobs) - job->signalJobShouldExit(); - } - else - { - jobs.remove (i); + if (job->isActive) + { + jobsToWaitFor.add (job); - if (deleteInactiveJobs) - delete job; + if (interruptRunningJobs) + job->signalJobShouldExit(); + } + else + { + jobs.remove (i); + + if (deleteInactiveJobs) + delete job; + } } } @@ -16038,12 +16047,19 @@ bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const uint32 start = Time::getMillisecondCounter(); - while (jobs.size() > 0) + for (;;) { + for (int i = jobsToWaitFor.size(); --i >= 0;) + if (! isJobRunning (jobsToWaitFor.getUnchecked (i))) + jobsToWaitFor.remove (i); + + if (jobsToWaitFor.size() == 0) + break; + if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + timeOutMs) return false; - jobFinishedSignal.wait (2); + jobFinishedSignal.wait (20); } return true; @@ -81523,8 +81539,7 @@ public: } else if (fillType.isTiledImage()) { - renderImage (image, *(fillType.image), Rectangle (0, 0, fillType.image->getWidth(), fillType.image->getHeight()), - fillType.transform, &et); + renderImage (image, *(fillType.image), fillType.image->getBounds(), fillType.transform, &et); } else { @@ -81544,8 +81559,6 @@ public: { const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); - jassert (Rectangle (0, 0, sourceImage.getWidth(), sourceImage.getHeight()).contains (srcClip)); - const Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true); const Image::BitmapData srcData (sourceImage, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight()); const int alpha = fillType.colour.getAlpha(); @@ -81567,7 +81580,7 @@ public: } else { - EdgeTable et (srcRect.getIntersection (Rectangle (0, 0, destImage.getWidth(), destImage.getHeight()))); + EdgeTable et (srcRect.getIntersection (destImage.getBounds())); et.clipToEdgeTable (edgeTable->edgeTable); if (! et.isEmpty()) @@ -81814,8 +81827,7 @@ LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image : image (image_), stateStack (20) { - currentState = new LLGCSavedState (Rectangle (0, 0, image_.getWidth(), image_.getHeight()), - 0, 0, Font(), FillType(), Graphics::mediumResamplingQuality); + currentState = new LLGCSavedState (image_.getBounds(), 0, 0, Font(), FillType(), Graphics::mediumResamplingQuality); } LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() @@ -81935,6 +81947,8 @@ void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineT void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform, const bool fillEntireClipAsTiles) { + jassert (sourceImage.getBounds().contains (srcClip)); + currentState->renderImage (image, sourceImage, srcClip, transform, fillEntireClipAsTiles ? &(currentState->edgeTable->edgeTable) : 0); } @@ -258658,10 +258672,10 @@ int SystemStats::getCpuSpeedInMegaherz() throw() int SystemStats::getNumCpus() throw() { -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - return MPProcessors(); -#else +#if JUCE_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) return [[NSProcessInfo processInfo] activeProcessorCount]; +#else + return MPProcessors(); #endif } @@ -261649,8 +261663,7 @@ public: { CGContextSaveGState (context); CGContextClipToRect (context, cgRect); - drawImage (*(state->fillType.image), Rectangle (0, 0, state->fillType.image->getWidth(), state->fillType.image->getHeight()), - state->fillType.transform, true); + drawImage (*(state->fillType.image), state->fillType.image->getBounds(), state->fillType.transform, true); CGContextRestoreGState (context); } } @@ -261683,8 +261696,7 @@ public: if (state->fillType.isGradient()) drawGradient(); else - drawImage (*(state->fillType.image), Rectangle (0, 0, state->fillType.image->getWidth(), state->fillType.image->getHeight()), - state->fillType.transform, true); + drawImage (*(state->fillType.image), state->fillType.image->getBounds(), state->fillType.transform, true); } CGContextRestoreGState (context); @@ -261693,10 +261705,17 @@ public: void drawImage (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform, const bool fillEntireClipAsTiles) { + jassert (sourceImage.getBounds().contains (srcClip)); + CGImageRef fullImage = CoreGraphicsImage::createImage (sourceImage, false, rgbColourSpace); - CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), sourceImage.getHeight() - srcClip.getBottom(), - srcClip.getWidth(), srcClip.getHeight())); - CGImageRelease (fullImage); + CGImageRef image = fullImage; + + if (srcClip != sourceImage.getBounds()) + { + image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), sourceImage.getHeight() - srcClip.getBottom(), + srcClip.getWidth(), srcClip.getHeight())); + CGImageRelease (fullImage); + } CGContextSaveGState (context); CGContextSetAlpha (context, state->fillType.getOpacity()); @@ -261743,7 +261762,7 @@ public: CGContextDrawImage (context, imageRect, image); } - CGImageRelease (image); + CGImageRelease (image); // (This causes a memory bug in iPhone sim 3.0 - try upgrading to a later version if you hit this) CGContextRestoreGState (context); } @@ -261886,7 +261905,6 @@ private: CGShadingRef createGradient (const AffineTransform& transform, ColourGradient gradient) throw() { -// gradient.multiplyOpacity (state->fillType.getOpacity()); delete gradientLookupTable; gradientLookupTable = gradient.createLookupTable (transform, numGradientLookupEntries); --numGradientLookupEntries; @@ -261920,6 +261938,7 @@ private: CGContextSetInterpolationQuality (context, kCGInterpolationDefault); // (This is required for 10.4, where there's a crash if // you draw a gradient with high quality interp enabled). CGShadingRef shading = createGradient (state->fillType.transform, *(state->fillType.gradient)); + CGContextSetAlpha (context, state->fillType.getOpacity()); CGContextDrawShading (context, shading); CGShadingRelease (shading); } @@ -266101,8 +266120,7 @@ public: { CGContextSaveGState (context); CGContextClipToRect (context, cgRect); - drawImage (*(state->fillType.image), Rectangle (0, 0, state->fillType.image->getWidth(), state->fillType.image->getHeight()), - state->fillType.transform, true); + drawImage (*(state->fillType.image), state->fillType.image->getBounds(), state->fillType.transform, true); CGContextRestoreGState (context); } } @@ -266135,8 +266153,7 @@ public: if (state->fillType.isGradient()) drawGradient(); else - drawImage (*(state->fillType.image), Rectangle (0, 0, state->fillType.image->getWidth(), state->fillType.image->getHeight()), - state->fillType.transform, true); + drawImage (*(state->fillType.image), state->fillType.image->getBounds(), state->fillType.transform, true); } CGContextRestoreGState (context); @@ -266145,10 +266162,17 @@ public: void drawImage (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform, const bool fillEntireClipAsTiles) { + jassert (sourceImage.getBounds().contains (srcClip)); + CGImageRef fullImage = CoreGraphicsImage::createImage (sourceImage, false, rgbColourSpace); - CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), sourceImage.getHeight() - srcClip.getBottom(), - srcClip.getWidth(), srcClip.getHeight())); - CGImageRelease (fullImage); + CGImageRef image = fullImage; + + if (srcClip != sourceImage.getBounds()) + { + image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), sourceImage.getHeight() - srcClip.getBottom(), + srcClip.getWidth(), srcClip.getHeight())); + CGImageRelease (fullImage); + } CGContextSaveGState (context); CGContextSetAlpha (context, state->fillType.getOpacity()); @@ -266195,7 +266219,7 @@ public: CGContextDrawImage (context, imageRect, image); } - CGImageRelease (image); + CGImageRelease (image); // (This causes a memory bug in iPhone sim 3.0 - try upgrading to a later version if you hit this) CGContextRestoreGState (context); } @@ -266338,7 +266362,6 @@ private: CGShadingRef createGradient (const AffineTransform& transform, ColourGradient gradient) throw() { -// gradient.multiplyOpacity (state->fillType.getOpacity()); delete gradientLookupTable; gradientLookupTable = gradient.createLookupTable (transform, numGradientLookupEntries); --numGradientLookupEntries; @@ -266372,6 +266395,7 @@ private: CGContextSetInterpolationQuality (context, kCGInterpolationDefault); // (This is required for 10.4, where there's a crash if // you draw a gradient with high quality interp enabled). CGShadingRef shading = createGradient (state->fillType.transform, *(state->fillType.gradient)); + CGContextSetAlpha (context, state->fillType.getOpacity()); CGContextDrawShading (context, shading); CGShadingRelease (shading); } diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 4af4b1ea49..2c8278f1cc 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -15282,6 +15282,23 @@ public: */ ~ThreadPool(); + /** A callback class used when you need to select which ThreadPoolJob objects are suitable + for some kind of operation. + @see ThreadPool::removeAllJobs + */ + class JUCE_API JobSelector + { + public: + virtual ~JobSelector() {} + + /** Should return true if the specified thread matches your criteria for whatever + operation that this object is being used for. + + Any implementation of this method must be extremely fast and thread-safe! + */ + virtual bool isJobSuitable (ThreadPoolJob* job) = 0; + }; + /** Adds a job to the queue. Once a job has been added, then the next time a thread is free, it will run @@ -15312,7 +15329,7 @@ public: const bool interruptIfRunning, const int timeOutMilliseconds); - /** Tries clear all jobs from the pool. + /** Tries to remove all jobs from the pool. @param interruptRunningJobs if true, then all running jobs will have their ThreadPoolJob::signalJobShouldExit() methods called to try to interrupt them @@ -15322,12 +15339,15 @@ public: they will simply be removed from the pool. Jobs that are already running when this method is called can choose whether they should be deleted by returning jobHasFinishedAndShouldBeDeleted from their runJob() method. + @param selectedJobsToRemove if this is non-zero, the JobSelector object is asked to decide which + jobs should be removed. If it is zero, all jobs are removed @returns true if all jobs are successfully stopped and removed; false if the timeout period - expires while waiting for them to stop + expires while waiting for one or more jobs to stop */ bool removeAllJobs (const bool interruptRunningJobs, const int timeOutMilliseconds, - const bool deleteInactiveJobs = false); + const bool deleteInactiveJobs = false, + JobSelector* selectedJobsToRemove = 0); /** Returns the number of jobs currently running or queued. */ @@ -39605,6 +39625,11 @@ public: /** Returns the image's height (in pixels). */ int getHeight() const throw() { return imageHeight; } + /** Returns a rectangle with the same size as this image. + The rectangle is always at position (0, 0). + */ + const Rectangle getBounds() const throw() { return Rectangle (0, 0, imageWidth, imageHeight); } + /** Returns the image's pixel format. */ PixelFormat getFormat() const throw() { return format; } diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index b5c1b8885e..8d57cc3daf 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -976,8 +976,7 @@ public: } else if (fillType.isTiledImage()) { - renderImage (image, *(fillType.image), Rectangle (0, 0, fillType.image->getWidth(), fillType.image->getHeight()), - fillType.transform, &et); + renderImage (image, *(fillType.image), fillType.image->getBounds(), fillType.transform, &et); } else { @@ -998,8 +997,6 @@ public: { const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); - jassert (Rectangle (0, 0, sourceImage.getWidth(), sourceImage.getHeight()).contains (srcClip)); - const Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true); const Image::BitmapData srcData (sourceImage, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight()); const int alpha = fillType.colour.getAlpha(); @@ -1021,7 +1018,7 @@ public: } else { - EdgeTable et (srcRect.getIntersection (Rectangle (0, 0, destImage.getWidth(), destImage.getHeight()))); + EdgeTable et (srcRect.getIntersection (destImage.getBounds())); et.clipToEdgeTable (edgeTable->edgeTable); if (! et.isEmpty()) @@ -1276,8 +1273,7 @@ LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image : image (image_), stateStack (20) { - currentState = new LLGCSavedState (Rectangle (0, 0, image_.getWidth(), image_.getHeight()), - 0, 0, Font(), FillType(), Graphics::mediumResamplingQuality); + currentState = new LLGCSavedState (image_.getBounds(), 0, 0, Font(), FillType(), Graphics::mediumResamplingQuality); } LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() @@ -1401,6 +1397,8 @@ void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineT void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform, const bool fillEntireClipAsTiles) { + jassert (sourceImage.getBounds().contains (srcClip)); + currentState->renderImage (image, sourceImage, srcClip, transform, fillEntireClipAsTiles ? &(currentState->edgeTable->edgeTable) : 0); } diff --git a/src/gui/graphics/imaging/juce_Image.h b/src/gui/graphics/imaging/juce_Image.h index cb78b077a4..492a37546f 100644 --- a/src/gui/graphics/imaging/juce_Image.h +++ b/src/gui/graphics/imaging/juce_Image.h @@ -109,6 +109,11 @@ public: /** Returns the image's height (in pixels). */ int getHeight() const throw() { return imageHeight; } + /** Returns a rectangle with the same size as this image. + The rectangle is always at position (0, 0). + */ + const Rectangle getBounds() const throw() { return Rectangle (0, 0, imageWidth, imageHeight); } + /** Returns the image's pixel format. */ PixelFormat getFormat() const throw() { return format; } diff --git a/src/native/mac/juce_mac_CoreGraphicsContext.mm b/src/native/mac/juce_mac_CoreGraphicsContext.mm index 856d528665..f5ab79fa03 100644 --- a/src/native/mac/juce_mac_CoreGraphicsContext.mm +++ b/src/native/mac/juce_mac_CoreGraphicsContext.mm @@ -330,8 +330,7 @@ public: { CGContextSaveGState (context); CGContextClipToRect (context, cgRect); - drawImage (*(state->fillType.image), Rectangle (0, 0, state->fillType.image->getWidth(), state->fillType.image->getHeight()), - state->fillType.transform, true); + drawImage (*(state->fillType.image), state->fillType.image->getBounds(), state->fillType.transform, true); CGContextRestoreGState (context); } } @@ -364,8 +363,7 @@ public: if (state->fillType.isGradient()) drawGradient(); else - drawImage (*(state->fillType.image), Rectangle (0, 0, state->fillType.image->getWidth(), state->fillType.image->getHeight()), - state->fillType.transform, true); + drawImage (*(state->fillType.image), state->fillType.image->getBounds(), state->fillType.transform, true); } CGContextRestoreGState (context); @@ -374,10 +372,17 @@ public: void drawImage (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform, const bool fillEntireClipAsTiles) { + jassert (sourceImage.getBounds().contains (srcClip)); + CGImageRef fullImage = CoreGraphicsImage::createImage (sourceImage, false, rgbColourSpace); - CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), sourceImage.getHeight() - srcClip.getBottom(), - srcClip.getWidth(), srcClip.getHeight())); - CGImageRelease (fullImage); + CGImageRef image = fullImage; + + if (srcClip != sourceImage.getBounds()) + { + image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), sourceImage.getHeight() - srcClip.getBottom(), + srcClip.getWidth(), srcClip.getHeight())); + CGImageRelease (fullImage); + } CGContextSaveGState (context); CGContextSetAlpha (context, state->fillType.getOpacity()); @@ -424,7 +429,7 @@ public: CGContextDrawImage (context, imageRect, image); } - CGImageRelease (image); + CGImageRelease (image); // (This causes a memory bug in iPhone sim 3.0 - try upgrading to a later version if you hit this) CGContextRestoreGState (context); } @@ -568,7 +573,6 @@ private: CGShadingRef createGradient (const AffineTransform& transform, ColourGradient gradient) throw() { -// gradient.multiplyOpacity (state->fillType.getOpacity()); delete gradientLookupTable; gradientLookupTable = gradient.createLookupTable (transform, numGradientLookupEntries); --numGradientLookupEntries; @@ -602,6 +606,7 @@ private: CGContextSetInterpolationQuality (context, kCGInterpolationDefault); // (This is required for 10.4, where there's a crash if // you draw a gradient with high quality interp enabled). CGShadingRef shading = createGradient (state->fillType.transform, *(state->fillType.gradient)); + CGContextSetAlpha (context, state->fillType.getOpacity()); CGContextDrawShading (context, shading); CGShadingRelease (shading); } diff --git a/src/native/mac/juce_mac_SystemStats.mm b/src/native/mac/juce_mac_SystemStats.mm index 59c78259e5..b7264fff39 100644 --- a/src/native/mac/juce_mac_SystemStats.mm +++ b/src/native/mac/juce_mac_SystemStats.mm @@ -211,10 +211,10 @@ int SystemStats::getCpuSpeedInMegaherz() throw() int SystemStats::getNumCpus() throw() { -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - return MPProcessors(); -#else +#if JUCE_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) return [[NSProcessInfo processInfo] activeProcessorCount]; +#else + return MPProcessors(); #endif } diff --git a/src/threads/juce_ThreadPool.cpp b/src/threads/juce_ThreadPool.cpp index c9c59aaf02..1adea9f5a6 100644 --- a/src/threads/juce_ThreadPool.cpp +++ b/src/threads/juce_ThreadPool.cpp @@ -135,6 +135,7 @@ ThreadPool::~ThreadPool() void ThreadPool::addJob (ThreadPoolJob* const job) { + jassert (job != 0); jassert (job->pool == 0); if (job->pool == 0) @@ -257,25 +258,33 @@ bool ThreadPool::removeJob (ThreadPoolJob* const job, bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const int timeOutMs, - const bool deleteInactiveJobs) + const bool deleteInactiveJobs, + ThreadPool::JobSelector* selectedJobsToRemove) { + Array jobsToWaitFor; + lock.enter(); for (int i = jobs.size(); --i >= 0;) { ThreadPoolJob* const job = (ThreadPoolJob*) jobs.getUnchecked(i); - - if (job->isActive) + + if (selectedJobsToRemove == 0 || selectedJobsToRemove->isJobSuitable (job)) { - if (interruptRunningJobs) - job->signalJobShouldExit(); - } - else - { - jobs.remove (i); + if (job->isActive) + { + jobsToWaitFor.add (job); - if (deleteInactiveJobs) - delete job; + if (interruptRunningJobs) + job->signalJobShouldExit(); + } + else + { + jobs.remove (i); + + if (deleteInactiveJobs) + delete job; + } } } @@ -283,12 +292,19 @@ bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const uint32 start = Time::getMillisecondCounter(); - while (jobs.size() > 0) + for (;;) { + for (int i = jobsToWaitFor.size(); --i >= 0;) + if (! isJobRunning (jobsToWaitFor.getUnchecked (i))) + jobsToWaitFor.remove (i); + + if (jobsToWaitFor.size() == 0) + break; + if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + timeOutMs) return false; - jobFinishedSignal.wait (2); + jobFinishedSignal.wait (20); } return true; diff --git a/src/threads/juce_ThreadPool.h b/src/threads/juce_ThreadPool.h index 8bfbcfc31c..a42c76ee01 100644 --- a/src/threads/juce_ThreadPool.h +++ b/src/threads/juce_ThreadPool.h @@ -178,6 +178,24 @@ public: */ ~ThreadPool(); + //============================================================================== + /** A callback class used when you need to select which ThreadPoolJob objects are suitable + for some kind of operation. + @see ThreadPool::removeAllJobs + */ + class JUCE_API JobSelector + { + public: + virtual ~JobSelector() {} + + /** Should return true if the specified thread matches your criteria for whatever + operation that this object is being used for. + + Any implementation of this method must be extremely fast and thread-safe! + */ + virtual bool isJobSuitable (ThreadPoolJob* job) = 0; + }; + //============================================================================== /** Adds a job to the queue. @@ -209,7 +227,7 @@ public: const bool interruptIfRunning, const int timeOutMilliseconds); - /** Tries clear all jobs from the pool. + /** Tries to remove all jobs from the pool. @param interruptRunningJobs if true, then all running jobs will have their ThreadPoolJob::signalJobShouldExit() methods called to try to interrupt them @@ -219,12 +237,15 @@ public: they will simply be removed from the pool. Jobs that are already running when this method is called can choose whether they should be deleted by returning jobHasFinishedAndShouldBeDeleted from their runJob() method. + @param selectedJobsToRemove if this is non-zero, the JobSelector object is asked to decide which + jobs should be removed. If it is zero, all jobs are removed @returns true if all jobs are successfully stopped and removed; false if the timeout period - expires while waiting for them to stop + expires while waiting for one or more jobs to stop */ bool removeAllJobs (const bool interruptRunningJobs, const int timeOutMilliseconds, - const bool deleteInactiveJobs = false); + const bool deleteInactiveJobs = false, + JobSelector* selectedJobsToRemove = 0); /** Returns the number of jobs currently running or queued. */