// Copyright (c) 2018 The Foundry Visionmongers Ltd. All Rights Reserved. static const char* const CLASS = "SimpleBlurCached"; static const char* const HELP = "Does a simple box blur"; #include "DDImage/Iop.h" #include "DDImage/NukeWrapper.h" #include "DDImage/Row.h" #include "DDImage/Tile.h" #include "DDImage/Knobs.h" #include "DDImage/ImageCache.h" #include "DDImage/ChannelSet.h" using namespace std; using namespace DD::Image; class SimpleBlurCached : public Iop { int _param_blur; float _param_constant; bool _isFirstTime; std::vector vec_dst_image; Lock _lock; bool fetchRGBAImage(std::vector&, const int height, const int width); /* Return available RGBA channels */ ChannelSet getNeededChannels() const; public: int maximum_inputs() const { return 1; } int minimum_inputs() const { return 1; } //! Constructor. Initialize user controls to their default values. SimpleBlurCached (Node* node) : Iop (node) { _param_blur = 1; _param_constant = 0; _isFirstTime = true; } ~SimpleBlurCached () {} void _validate(bool); void _request(int x, int y, int r, int t, ChannelMask channels, int count); void knobs(Knob_Callback f); //! This function does all the work. void engine ( int y, int x, int r, ChannelMask channels, Row& out ); //! Return the name of the class. const char* Class() const { return CLASS; } const char* node_help() const { return HELP; } //! Information to the plug-in manager of DDNewImage/Nuke. static const Iop::Description description; }; /*! This is a function that creates an instance of the operator, and is needed for the Iop::Description to work. */ static Iop* SimpleBlurCreate(Node* node) { return new SimpleBlurCached(node); } /*! The Iop::Description is how NUKE knows what the name of the operator is, how to create one, and the menu item to show the user. The menu item may be 0 if you do not want the operator to be visible. */ const Iop::Description SimpleBlurCached::description ( CLASS, "Merge/SimpleBlurCached", SimpleBlurCreate ); ChannelSet SimpleBlurCached::getNeededChannels() const { ChannelSet rgbaChannels = Mask_RGBA; rgbaChannels &= info_.channels(); return rgbaChannels; } void SimpleBlurCached::_validate(bool for_real) { copy_info(); // copy bbox channels etc from input0, which will validate it. _isFirstTime = true; } void SimpleBlurCached::_request(int x, int y, int r, int t, ChannelMask channels, int count) { // Add available RGBA channels so that we can pull them when setting up the cache ChannelSet requiredChannels = channels + getNeededChannels(); input(0)->request(0, 0, info_.w(), info_.h(), requiredChannels, count); } void SimpleBlurCached::knobs(Knob_Callback f) { Int_knob(f, &_param_blur, "size", "size"); Float_knob(f, &_param_constant, "constant", "constant"); } bool SimpleBlurCached::fetchRGBAImage(std::vector& vec, const int height, const int width) { ChannelSet rgbaChannels = getNeededChannels(); Tile tile(input0(), 0, 0, width, height, rgbaChannels); if (aborted()) { return false; } vec.resize(width * height * 4); foreach(z, rgbaChannels) { int channel_skip = 0; if(z == Chan_Red) channel_skip = 0; else if(z == Chan_Green) channel_skip = 1; else if(z == Chan_Blue) channel_skip = 2; else if(z == Chan_Alpha) channel_skip = 3; for (int py = 0; py < height; ++py) { for (int px = 0; px < width; ++px) { const size_t index = (py*width + px)*4 + channel_skip; float value = tile[z][py][px]; vec[index] = value; } } } return true; } void SimpleBlurCached::engine ( int y, int x, int r, ChannelMask channels, Row& row ) { const int width = info_.w(); const int height = info_.h(); // engine calls are multi-threaded so any processing must be locked if (_isFirstTime) { Guard guard(_lock); if (_isFirstTime) { const size_t totalPixels = width * height * 4; std::vector vec_src_image; vec_dst_image.resize(totalPixels); /* try fetch input image */ if (fetchRGBAImage(vec_src_image, height, width) ) { /* now going to see if the input is in the cache */ Image_Cache *i_cache = &Image_Cache::mainCache(); printf("Checking active cache: %d.\n", i_cache->is_active()); size_t desired_read_bytes = (vec_src_image.size())*sizeof(float); DD::Image::Hash hash; hash.reset(); hash.append(input0().hash()); hash.append(_param_blur); printf("Printing hash value: %d.\n", (int)hash.value()); printf("Has file: %d.\n", i_cache->has_file(hash)); /* is our blurred image already in the cache? */ bool cache_read_success = true; if (i_cache->is_active() && i_cache->has_file(hash) ) { DD::Image::ImageCacheReadI* cache_read = i_cache->open( hash ); size_t read_bytes = cache_read->read(vec_dst_image.data(), desired_read_bytes); if (!i_cache->is_read() || read_bytes != desired_read_bytes) { cache_read_success = false; } cache_read->close(); } else { cache_read_success = false; } /* did we get the cached blur image or do we need to calculate it? */ if (!cache_read_success) { /* blur the image, very naively, do not use this code for anything useful! */ float blur_sum; int blur_counter; for(int i=0;i 0) ? blur_sum / (float)blur_counter : 0; } /* write result to cache */ DD::Image::ImageCacheWriteI* cache_write = i_cache->create( hash ); size_t desired_write_bytes = (vec_dst_image.size())*sizeof(float); cache_write->write(vec_dst_image.data(), desired_write_bytes); if (!i_cache->is_written()) printf("Error saving blurred image to cache (is written: %d).\n", (int)i_cache->is_written()); cache_write->close(); } /* now just going to add the constant to the image */ for(size_t i = 0; i