rendering still not correct
This commit is contained in:
@@ -293,6 +293,13 @@ static olive::AVFramePtr ConvertPackedFloatFrame(olive::AVFramePtr src,
|
||||
|
||||
const std::string &olive::plugin::OliveClipInstance::getUnmappedBitDepth() const
|
||||
{
|
||||
// Return the plugin's preferred pixel depth from base class
|
||||
// This is set during getClipPreferences action via setPixelDepth()
|
||||
const std::string &depth = getPixelDepth();
|
||||
if (!depth.empty() && depth != kBitDepthNoneStr) {
|
||||
return depth;
|
||||
}
|
||||
// Fallback to params_ if base class value is not set
|
||||
switch (params_.format()) {
|
||||
case PixelFormat::INVALID:
|
||||
return kBitDepthNoneStr;
|
||||
@@ -311,6 +318,13 @@ const std::string &olive::plugin::OliveClipInstance::getUnmappedBitDepth() const
|
||||
const std::string &
|
||||
olive::plugin::OliveClipInstance::getUnmappedComponents() const
|
||||
{
|
||||
// Return the plugin's preferred components from base class
|
||||
// This is set during getClipPreferences action via setComponents()
|
||||
const std::string &comp = getComponents();
|
||||
if (!comp.empty() && comp != kImageComponentNoneStr) {
|
||||
return comp;
|
||||
}
|
||||
// Fallback to params_ if base class value is not set
|
||||
switch (params_.channel_count()) {
|
||||
case 1:
|
||||
return kImageComponentAlphaStr;
|
||||
@@ -429,13 +443,35 @@ olive::plugin::OliveClipInstance::getImage(OfxTime time,
|
||||
return image;
|
||||
}
|
||||
|
||||
// If this input clip was never populated (no setInputTexture call),
|
||||
// return nullptr so the plugin knows no image is available.
|
||||
// This prevents EXC_BAD_ACCESS when plugins (e.g. ofxsMaskMixPix)
|
||||
// try to read pixel data from an empty/invalid image buffer.
|
||||
if (!getConnected()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Fetch on demand for the input clip.
|
||||
// It does get deleted after the plugin is done with it as we
|
||||
// have not incremented the auto ref
|
||||
//
|
||||
// You should do somewhat more sophisticated image management
|
||||
// than this.
|
||||
Image *image = new Image(*this, params_, bounds, rod, true);
|
||||
// Use plugin-preferred params to ensure the image format matches
|
||||
// what the plugin expects (may differ from input texture format)
|
||||
VideoParams preferred_params = getPluginPreferredParams();
|
||||
if (preferred_params.format() == core::PixelFormat::INVALID) {
|
||||
preferred_params = params_;
|
||||
}
|
||||
// Keep dimensions and other settings from params_
|
||||
preferred_params.set_width(params_.width());
|
||||
preferred_params.set_height(params_.height());
|
||||
preferred_params.set_pixel_aspect_ratio(params_.pixel_aspect_ratio());
|
||||
|
||||
// Guard against zero-size or invalid-format images that would
|
||||
// cause EXC_BAD_ACCESS when the plugin accesses pixel data.
|
||||
if (preferred_params.width() <= 0 || preferred_params.height() <= 0 ||
|
||||
preferred_params.format() == core::PixelFormat::INVALID ||
|
||||
preferred_params.channel_count() <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Image *image = new Image(*this, preferred_params, bounds, rod, true);
|
||||
return image;
|
||||
}
|
||||
}
|
||||
@@ -454,10 +490,54 @@ olive::plugin::OliveClipInstance::getOutputImage(OfxTime time)
|
||||
static_cast<int>(std::ceil(rod_d.y2)) };
|
||||
OfxRectI bounds = rod;
|
||||
|
||||
auto image = new Image(*this, params_, bounds, rod, true);
|
||||
// Use plugin-preferred params instead of params_ to ensure the image
|
||||
// is created with the format the plugin expects
|
||||
VideoParams preferred_params = getPluginPreferredParams();
|
||||
if (preferred_params.format() == core::PixelFormat::INVALID) {
|
||||
preferred_params = params_;
|
||||
}
|
||||
// Keep the dimensions and other settings from params_
|
||||
preferred_params.set_width(params_.width());
|
||||
preferred_params.set_height(params_.height());
|
||||
preferred_params.set_pixel_aspect_ratio(params_.pixel_aspect_ratio());
|
||||
|
||||
auto image = new Image(*this, preferred_params, bounds, rod, true);
|
||||
images_.insert(time, image);
|
||||
return image;
|
||||
}
|
||||
|
||||
olive::VideoParams olive::plugin::OliveClipInstance::getPluginPreferredParams() const
|
||||
{
|
||||
VideoParams result = params_;
|
||||
|
||||
// Get format from base class _pixelDepth (set by getClipPreferences)
|
||||
const std::string &depth = getPixelDepth();
|
||||
if (!depth.empty()) {
|
||||
if (depth == kOfxBitDepthByte) {
|
||||
result.set_format(core::PixelFormat::U8);
|
||||
} else if (depth == kOfxBitDepthShort) {
|
||||
result.set_format(core::PixelFormat::U16);
|
||||
} else if (depth == kOfxBitDepthHalf) {
|
||||
result.set_format(core::PixelFormat::F16);
|
||||
} else if (depth == kOfxBitDepthFloat) {
|
||||
result.set_format(core::PixelFormat::F32);
|
||||
}
|
||||
}
|
||||
|
||||
// Get channel count from base class _components (set by getClipPreferences)
|
||||
const std::string &comp = getComponents();
|
||||
if (!comp.empty()) {
|
||||
if (comp == kOfxImageComponentRGBA) {
|
||||
result.set_channel_count(4);
|
||||
} else if (comp == kOfxImageComponentRGB) {
|
||||
result.set_channel_count(3);
|
||||
} else if (comp == kOfxImageComponentAlpha) {
|
||||
result.set_channel_count(1);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
OfxRectD
|
||||
olive::plugin::OliveClipInstance::getRegionOfDefinition(OfxTime time) const
|
||||
{
|
||||
@@ -497,9 +577,11 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
|
||||
VideoParams incoming = texture->params();
|
||||
|
||||
this->params_ = incoming;
|
||||
// Sync with OpenFX Host Support's _pixelDepth and _components
|
||||
setPixelDepth(getUnmappedBitDepth());
|
||||
setComponents(getUnmappedComponents());
|
||||
// Note: We do NOT call setPixelDepth/setComponents here because
|
||||
// those should be set by getClipPreferences to reflect the PLUGIN's
|
||||
// preferred format, not the input texture's format.
|
||||
// The base class values are used by getUnmappedBitDepth/Components
|
||||
// to report plugin capabilities to the plugin itself.
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
input_textures_.insert(time, texture);
|
||||
#endif
|
||||
|
||||
@@ -72,6 +72,9 @@ public:
|
||||
void setInputTexture(TexturePtr texture, OfxTime time);
|
||||
void setOutputTexture(TexturePtr texture, OfxTime time);
|
||||
|
||||
// Get the plugin-preferred VideoParams based on base class _pixelDepth/_components
|
||||
VideoParams getPluginPreferredParams() const;
|
||||
|
||||
private:
|
||||
VideoParams params_;
|
||||
|
||||
|
||||
@@ -703,8 +703,13 @@ static olive::AVFramePtr create_avframe_from_ofx_image(OFX::Host::ImageEffect::I
|
||||
return frame;
|
||||
}
|
||||
|
||||
// 作用:按指定 VideoParams 复制 OFX Image 到 AVFrame。
|
||||
// Purpose: Copy OFX Image data into an AVFrame using target VideoParams.
|
||||
// 前置声明:必要时将 AVFrame 转换为目标 VideoParams 对应格式。
|
||||
// Forward declaration: Convert AVFrame to match destination VideoParams when needed.
|
||||
static olive::AVFramePtr ConvertFrameIfNeeded(olive::AVFramePtr src,
|
||||
const olive::VideoParams &dst_params);
|
||||
|
||||
// 作用:按指定 VideoParams 复制 OFX Image 到 AVFrame,必要时做格式转换。
|
||||
// Purpose: Copy OFX Image data into an AVFrame using target VideoParams with format conversion.
|
||||
static olive::AVFramePtr create_avframe_from_ofx_image_with_params(
|
||||
OFX::Host::ImageEffect::Image &image,
|
||||
const olive::VideoParams ¶ms)
|
||||
@@ -722,25 +727,74 @@ static olive::AVFramePtr create_avframe_from_ofx_image_with_params(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get ACTUAL source format from image properties
|
||||
std::string image_depth = image.getStringProperty(kOfxImageEffectPropPixelDepth);
|
||||
std::string image_comp = image.getStringProperty(kOfxImageEffectPropComponents);
|
||||
|
||||
int src_channel_count = 4;
|
||||
if (image_comp == kOfxImageComponentRGB) src_channel_count = 3;
|
||||
else if (image_comp == kOfxImageComponentAlpha) src_channel_count = 1;
|
||||
|
||||
int src_bytes_per_component = 1;
|
||||
if (image_depth == kOfxBitDepthShort) src_bytes_per_component = 2;
|
||||
else if (image_depth == kOfxBitDepthHalf) src_bytes_per_component = 2;
|
||||
else if (image_depth == kOfxBitDepthFloat) src_bytes_per_component = 4;
|
||||
|
||||
const int src_bytes_per_pixel = src_channel_count * src_bytes_per_component;
|
||||
const int dst_bytes_per_pixel = params.channel_count() * params.format().byte_count();
|
||||
|
||||
int row_bytes = image.getIntProperty(kOfxImagePropRowBytes);
|
||||
if (row_bytes <= 0) {
|
||||
row_bytes = width * src_bytes_per_pixel;
|
||||
}
|
||||
|
||||
uint8_t *src = static_cast<uint8_t *>(data_ptr);
|
||||
src += bounds[1] * row_bytes + bounds[0] * src_bytes_per_pixel;
|
||||
|
||||
// Check if format conversion is needed
|
||||
bool needs_conversion = (src_bytes_per_pixel != dst_bytes_per_pixel) ||
|
||||
(src_channel_count != params.channel_count());
|
||||
|
||||
if (needs_conversion) {
|
||||
// Create source frame with actual format
|
||||
AVPixelFormat src_fmt = AV_PIX_FMT_NONE;
|
||||
if (src_channel_count == 4) {
|
||||
if (src_bytes_per_component == 1) src_fmt = AV_PIX_FMT_RGBA;
|
||||
else if (src_bytes_per_component == 2) src_fmt = AV_PIX_FMT_RGBA64LE;
|
||||
else if (src_bytes_per_component == 4) src_fmt = AV_PIX_FMT_RGBAF32LE;
|
||||
} else if (src_channel_count == 3) {
|
||||
if (src_bytes_per_component == 1) src_fmt = AV_PIX_FMT_RGB24;
|
||||
else if (src_bytes_per_component == 2) src_fmt = AV_PIX_FMT_RGB48LE;
|
||||
else if (src_bytes_per_component == 4) src_fmt = AV_PIX_FMT_RGBF32LE;
|
||||
} else if (src_channel_count == 1) {
|
||||
if (src_bytes_per_component == 1) src_fmt = AV_PIX_FMT_GRAY8;
|
||||
else if (src_bytes_per_component == 2) src_fmt = AV_PIX_FMT_GRAY16LE;
|
||||
else if (src_bytes_per_component == 4) src_fmt = AV_PIX_FMT_GRAYF32LE;
|
||||
}
|
||||
|
||||
if (src_fmt != AV_PIX_FMT_NONE) {
|
||||
olive::AVFramePtr src_frame = olive::CreateAVFramePtr();
|
||||
src_frame->width = width;
|
||||
src_frame->height = height;
|
||||
src_frame->format = src_fmt;
|
||||
if (av_frame_get_buffer(src_frame.get(), 0) >= 0) {
|
||||
// Copy source data row by row
|
||||
for (int y = 0; y < height; ++y) {
|
||||
memcpy(src_frame->data[0] + y * src_frame->linesize[0],
|
||||
src + y * row_bytes, width * src_bytes_per_pixel);
|
||||
}
|
||||
// Convert to destination format
|
||||
return ConvertFrameIfNeeded(src_frame, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same format - direct copy
|
||||
AVPixelFormat pix_fmt = GetDestinationAVPixelFormat(params);
|
||||
if (pix_fmt == AV_PIX_FMT_NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int bytes_per_pixel =
|
||||
params.channel_count() * params.format().byte_count();
|
||||
if (bytes_per_pixel <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int row_bytes = image.getIntProperty(kOfxImagePropRowBytes);
|
||||
if (row_bytes <= 0) {
|
||||
row_bytes = width * bytes_per_pixel;
|
||||
}
|
||||
|
||||
uint8_t *src = static_cast<uint8_t *>(data_ptr);
|
||||
src += bounds[1] * row_bytes + bounds[0] * bytes_per_pixel;
|
||||
|
||||
olive::AVFramePtr frame = olive::CreateAVFramePtr();
|
||||
frame->width = width;
|
||||
frame->height = height;
|
||||
@@ -750,11 +804,10 @@ static olive::AVFramePtr create_avframe_from_ofx_image_with_params(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int copy_bytes = width * bytes_per_pixel;
|
||||
const int copy_bytes = width * std::min(src_bytes_per_pixel, dst_bytes_per_pixel);
|
||||
for (int y = 0; y < height; ++y) {
|
||||
std::memcpy(frame->data[0] + y * frame->linesize[0],
|
||||
src + y * row_bytes,
|
||||
copy_bytes);
|
||||
memcpy(frame->data[0] + y * frame->linesize[0],
|
||||
src + y * row_bytes, copy_bytes);
|
||||
}
|
||||
|
||||
return frame;
|
||||
@@ -1496,10 +1549,7 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
input_tex->handleFrame(ptr);
|
||||
}
|
||||
if (is_usable_input(input_tex)) {
|
||||
input_textures[entry.first] = input_tex;
|
||||
// First set the input texture (updates params_)
|
||||
input_clip->setInputTexture(input_tex, frame);
|
||||
// Then get the bitdepth/component from the instance
|
||||
// Determine the plugin's preferred format for this clip
|
||||
std::string bitdepth = input_clip->getUnmappedBitDepth();
|
||||
std::string component = input_clip->getUnmappedComponents();
|
||||
VideoParams params = input_tex->params();
|
||||
@@ -1510,7 +1560,17 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
if (!component.empty() && component != kOfxImageComponentNone) {
|
||||
params.set_channel_count(component);
|
||||
}
|
||||
ConvertTextureForParams(input_tex, params);
|
||||
// Convert the texture to the plugin's preferred format BEFORE
|
||||
// setting it on the clip, so the OFX Image receives correctly
|
||||
// formatted pixel data.
|
||||
TexturePtr converted_tex =
|
||||
ConvertTextureForParams(input_tex, params);
|
||||
if (converted_tex && is_usable_input(converted_tex)) {
|
||||
input_tex = converted_tex;
|
||||
}
|
||||
input_textures[entry.first] = input_tex;
|
||||
// Now set the (possibly converted) texture on the clip
|
||||
input_clip->setInputTexture(input_tex, frame);
|
||||
OfxRectD rod;
|
||||
rod.x1 = 0;
|
||||
rod.y1 = 0;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user