diff --git a/MMCore/CoreCallback.cpp b/MMCore/CoreCallback.cpp index 572f9a0ce..15c9ab225 100644 --- a/MMCore/CoreCallback.cpp +++ b/MMCore/CoreCallback.cpp @@ -251,6 +251,18 @@ int CoreCallback::InsertImage(const MM::Device* caller, const unsigned char* buf int CoreCallback::InsertImage(const MM::Device* caller, const unsigned char* buf, unsigned width, unsigned height, unsigned byteDepth, const Metadata* pMd, bool doProcess) { + // Check if the camera is currently snapping an image, if its calling InsertImage, + // then its circumventing having its own buffer. This allows camera device adapters to + // not have to implement their own buffers, and can instead just copy data in directly. + { + std::shared_ptr camera = std::static_pointer_cast(core_->deviceManager_->GetDevice(caller)); + if (camera->IsSnapping()) { + // Don't do any metadata or image processing, for consistency with the existing SnapImage()/GetImage() methods + camera->StoreSnappedImage(buf, width, height, byteDepth); + return DEVICE_OK; + } + } + try { Metadata md = AddCameraMetadata(caller, pMd); @@ -283,6 +295,18 @@ int CoreCallback::InsertImage(const MM::Device* caller, const unsigned char* buf int CoreCallback::InsertImage(const MM::Device* caller, const unsigned char* buf, unsigned width, unsigned height, unsigned byteDepth, unsigned nComponents, const Metadata* pMd, bool doProcess) { + // Check if the camera is currently snapping an image, if its calling InsertImage, + // then its circumventing having its own buffer. This allows camera device adapters to + // not have to implement their own buffers, and can instead just copy data in directly. + { + std::shared_ptr camera = std::static_pointer_cast(core_->deviceManager_->GetDevice(caller)); + if (camera->IsSnapping()) { + // Don't do any metadata or image processing, for consistency with the existing SnapImage()/GetImage() methods + camera->StoreSnappedImage(buf, width, height, byteDepth); + return DEVICE_OK; + } + } + try { Metadata md = AddCameraMetadata(caller, pMd); diff --git a/MMCore/Devices/CameraInstance.cpp b/MMCore/Devices/CameraInstance.cpp index 8633d1b5d..b35b89bf3 100644 --- a/MMCore/Devices/CameraInstance.cpp +++ b/MMCore/Devices/CameraInstance.cpp @@ -22,10 +22,42 @@ #include "CameraInstance.h" -int CameraInstance::SnapImage() { RequireInitialized(__func__); return GetImpl()->SnapImage(); } -const unsigned char* CameraInstance::GetImageBuffer() { RequireInitialized(__func__); return GetImpl()->GetImageBuffer(); } -const unsigned char* CameraInstance::GetImageBuffer(unsigned channelNr) { RequireInitialized(__func__); return GetImpl()->GetImageBuffer(channelNr); } -const unsigned int* CameraInstance::GetImageBufferAsRGB32() { RequireInitialized(__func__); return GetImpl()->GetImageBufferAsRGB32(); } +int CameraInstance::SnapImage() { + RequireInitialized(__func__); + isSnapping_.store(true); + multiChannelImageCounter_.store(0); + int ret = GetImpl()->SnapImage(); + isSnapping_.store(false); + return ret; +} + +const unsigned char* CameraInstance::GetImageBuffer() { + RequireInitialized(__func__); + const unsigned char* snappedPixels = snappedImage_.GetPixels(0); + if (snappedPixels != nullptr) { + return snappedPixels; + } + return GetImpl()->GetImageBuffer(); +} + +const unsigned char* CameraInstance::GetImageBuffer(unsigned channelNr) { + RequireInitialized(__func__); + const unsigned char* snappedPixels = snappedImage_.GetPixels(channelNr); + if (snappedPixels != nullptr) { + return snappedPixels; + } + return GetImpl()->GetImageBuffer(channelNr); +} + +const unsigned int* CameraInstance::GetImageBufferAsRGB32() { + RequireInitialized(__func__); + const unsigned int* snappedPixels = reinterpret_cast(snappedImage_.GetPixels(0)); + if (snappedPixels != nullptr) { + return snappedPixels; + } + return GetImpl()->GetImageBufferAsRGB32(); +} + unsigned CameraInstance::GetNumberOfComponents() const { RequireInitialized(__func__); return GetImpl()->GetNumberOfComponents(); } std::string CameraInstance::GetComponentName(unsigned component) @@ -154,3 +186,22 @@ int CameraInstance::StopExposureSequence() { RequireInitialized(__func__); retur int CameraInstance::ClearExposureSequence() { RequireInitialized(__func__); return GetImpl()->ClearExposureSequence(); } int CameraInstance::AddToExposureSequence(double exposureTime_ms) { RequireInitialized(__func__); return GetImpl()->AddToExposureSequence(exposureTime_ms); } int CameraInstance::SendExposureSequence() const { RequireInitialized(__func__); return GetImpl()->SendExposureSequence(); } + +bool CameraInstance::IsSnapping() const { + return isSnapping_.load(); +} + +void CameraInstance::StoreSnappedImage(const unsigned char* buf, unsigned width, unsigned height, + unsigned byteDepth) { + // If the buffer doesn't exist or has wrong dimensions, create/resize it + if (snappedImage_.Width() != width || + snappedImage_.Height() != height || + snappedImage_.Depth() != byteDepth) { + snappedImage_.Resize(width, height, byteDepth); + } + + // For multi-channel cameras, insertImage will be called once for each channel + snappedImage_.SetPixels(multiChannelImageCounter_.load(), buf); + multiChannelImageCounter_.fetch_add(1); + +} diff --git a/MMCore/Devices/CameraInstance.h b/MMCore/Devices/CameraInstance.h index f228a37e6..5ce2a9aad 100644 --- a/MMCore/Devices/CameraInstance.h +++ b/MMCore/Devices/CameraInstance.h @@ -20,6 +20,8 @@ #pragma once #include "DeviceInstanceBase.h" +#include +#include "../FrameBuffer.h" class CameraInstance : public DeviceInstanceBase @@ -82,4 +84,20 @@ class CameraInstance : public DeviceInstanceBase int ClearExposureSequence(); int AddToExposureSequence(double exposureTime_ms); int SendExposureSequence() const; + + /** + * Checks if the camera is currently snapping an image. + * Thread-safe method that can be called from any thread. + * @return true if the camera is currently snapping an image, false otherwise + */ + bool IsSnapping() const; + void StoreSnappedImage(const unsigned char* buf, unsigned width, unsigned height, unsigned byteDepth); + +private: + // Atomic flag to track if the camera is currently snapping an image + std::atomic isSnapping_{false}; + + // Frame buffer to store captured images + mm::FrameBuffer snappedImage_; + std::atomic multiChannelImageCounter_{0}; };