diff --git a/Support/Plugins/GPUGain/GPUGain.cpp b/Support/Plugins/GPUGain/GPUGain.cpp index 10a6d057..0c2832fe 100644 --- a/Support/Plugins/GPUGain/GPUGain.cpp +++ b/Support/Plugins/GPUGain/GPUGain.cpp @@ -414,7 +414,6 @@ void GPUGainFactory::describeInContext(OFX::ImageEffectDescriptor& p_Desc, OFX:: // Create the mandated output clip ClipDescriptor* dstClip = p_Desc.defineClip(kOfxImageEffectOutputClipName); dstClip->addSupportedComponent(ePixelComponentRGBA); - dstClip->addSupportedComponent(ePixelComponentAlpha); dstClip->setSupportsTiles(kSupportsTiles); // Make some pages and to things in diff --git a/Support/Plugins/Generator/noise.cpp b/Support/Plugins/Generator/noise.cpp index 249d1839..e4592395 100644 --- a/Support/Plugins/Generator/noise.cpp +++ b/Support/Plugins/Generator/noise.cpp @@ -8,7 +8,16 @@ #include "../include/ofxsProcessing.H" -#include +#include + +/** @brief mix the bits of v thoroughly (the splitmix64 finaliser) */ +static inline uint64_t hash64(uint64_t v) +{ + v += 0x9e3779b97f4a7c15ULL; + v = (v ^ (v >> 30)) * 0xbf58476d1ce4e5b9ULL; + v = (v ^ (v >> 27)) * 0x94d049bb133111ebULL; + return v ^ (v >> 31); +} //////////////////////////////////////////////////////////////////////////////// // base class for the noise @@ -46,24 +55,25 @@ public : // and do some processing void multiThreadProcessImages(OfxRectI procWindow) { - float noiseLevel = _noiseLevel; - - // set up a random number generator // Distribution is from 0 to pixel max level times noise level - std::random_device rd; - std::mt19937_64 mt(rd()); - mt.seed(_seed + procWindow.y1); - std::uniform_real_distribution dist(0.0, max * noiseLevel); + double scale = max * _noiseLevel; + + // Each pixel's noise is a hash of the seed and its position, so a frame + // comes out the same however it is split into tiles and threads + uint64_t seedHash = hash64(_seed); // push pixels for(int y = procWindow.y1; y < procWindow.y2; y++) { if(_effect.abort()) break; PIX *dstPix = (PIX *) _dstImg->getPixelAddress(procWindow.x1, y); + uint64_t rowHash = hash64(seedHash ^ uint32_t(y)); for(int x = procWindow.x1; x < procWindow.x2; x++) { + uint64_t pixelHash = hash64(rowHash ^ uint32_t(x)); for(int c = 0; c < nComponents; c++) { - double randValue = dist(mt); + // the top 32 bits of the hash, as a uniform random number in [0, 1) + double randValue = scale * (double(hash64(pixelHash + c) >> 32) / 4294967296.0); if(max == 1) // implies floating point, so don't clamp dstPix[c] = PIX(randValue); diff --git a/Support/Plugins/MultiBundle/multibundle1.cpp b/Support/Plugins/MultiBundle/multibundle1.cpp index bc1e9444..dddf355c 100755 --- a/Support/Plugins/MultiBundle/multibundle1.cpp +++ b/Support/Plugins/MultiBundle/multibundle1.cpp @@ -120,7 +120,9 @@ public : { for(int c = 0; c < nComponents; c++) { - float v = (float)(pow((double)srcPix[c], (double)scales[c])) * maskScale + (1.0f - maskScale) * srcPix[c]; + // apply the gamma to the magnitude and keep the sign, since pow of a negative value is NaN for a fractional exponent + double s = srcPix[c]; + float v = (float)(copysign(pow(fabs(s), (double)scales[c]), s)) * maskScale + (1.0f - maskScale) * srcPix[c]; if(max == 1) dstPix[c] = PIX(v); else diff --git a/Support/Plugins/Retimer/retimer.cpp b/Support/Plugins/Retimer/retimer.cpp index 59f113dd..7b3a502f 100644 --- a/Support/Plugins/Retimer/retimer.cpp +++ b/Support/Plugins/Retimer/retimer.cpp @@ -61,6 +61,9 @@ public : /* override the time domain action, only for the general context */ virtual bool getTimeDomain(OfxRangeD &range); + /* the time on the source clip that the output at time comes from */ + double getSourceTime(double time); + /* set up and run a processor */ void setupAndProcess(OFX::ImageBlenderBase &, const OFX::RenderArguments &args); @@ -120,6 +123,20 @@ static void framesNeeded(double sourceTime, OFX::FieldEnum fieldToRender, double *blendp = blend; } +/* figure the frame we should be retiming from */ +double +RetimerPlugin::getSourceTime(double time) +{ + if(getContext() == OFX::eContextRetimer) { + // the host is specifying it, so fetch it from the kOfxImageEffectRetimerParamName pseudo-param + return sourceTime_->getValueAtTime(time); + } + else { + // we have our own param, which is a speed, so we integrate it to get the time we want + return speed_->integrate(0, time); + } +} + /* set up and run a processor */ void RetimerPlugin::setupAndProcess(OFX::ImageBlenderBase &processor, const OFX::RenderArguments &args) @@ -129,22 +146,10 @@ RetimerPlugin::setupAndProcess(OFX::ImageBlenderBase &processor, const OFX::Rend OFX::BitDepthEnum dstBitDepth = dst->getPixelDepth(); OFX::PixelComponentEnum dstComponents = dst->getPixelComponents(); - // figure the frame we should be retiming from - double sourceTime; - - if(getContext() == OFX::eContextRetimer) { - // the host is specifying it, so fetch it from the kOfxImageEffectRetimerParamName pseudo-param - sourceTime = sourceTime_->getValueAtTime(args.time); - } - else { - // we have our own param, which is a speed, so we integrate it to get the time we want - sourceTime = speed_->integrate(0, args.time); - } - // figure the two images we are blending between double fromTime, toTime; double blend; - framesNeeded(sourceTime, args.fieldToRender, &fromTime, &toTime, &blend); + framesNeeded(getSourceTime(args.time), args.fieldToRender, &fromTime, &toTime, &blend); // fetch the two source images std::unique_ptr fromImg(srcClip_->fetchImage(fromTime)); @@ -177,7 +182,7 @@ RetimerPlugin::getFramesNeeded(const OFX::FramesNeededArguments &args, double fromTime, toTime; double blend; // whatever the rendered field is, the frames are the same - framesNeeded(args.time, OFX::eFieldNone, &fromTime, &toTime, &blend); + framesNeeded(getSourceTime(args.time), OFX::eFieldNone, &fromTime, &toTime, &blend); OfxRangeD range; range.min = fromTime; range.max = toTime; diff --git a/release-notes-next.md b/release-notes-next.md index 4b8ceac3..8abb5011 100644 --- a/release-notes-next.md +++ b/release-notes-next.md @@ -27,6 +27,10 @@ This is version NEXT of the OpenFX API. - Fixed the ColourSpace example's `OfxSetHost` to have the proper signature so it actually gets called. - Fixed the `@propdef` metadata of `kOfxParamPropChoiceEnum` (a string array, not a bool) and `kOfxParamPropDimensionLabel` (one label per dimension, not one). - Fixed the Invert example never releasing its output image (a shadowed handle variable). +- Fixed the Support Noise example giving a different image when a frame is rendered in tiles: it seeded its noise per render call, and now each pixel's noise depends only on its position, the time and the noise level. +- Fixed the Support GPUGain example failing to render when the host chose Alpha for its output: it declared Alpha output but processes only RGBA, so its output clip now supports only RGBA. +- Fixed the Support Gamma example producing NaNs from negative float input: it now applies the gamma to a value's magnitude and keeps its sign. +- Fixed the Support Retimer example asking for source frames around the output time rather than the retimed source time it then fetches. ## Deprecations