Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
darbyjohnston committed Sep 12, 2024
1 parent 3c626d2 commit 316e380
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 5 deletions.
4 changes: 2 additions & 2 deletions bin/toucan-render/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ int main(int argc, char** argv)

// Initialize the filmstrip.
OIIO::ImageBuf filmstripBuf;
const int thumbnailWidth = 120;
const int thumbnailSpacing = 20;
const int thumbnailWidth = 360;
const int thumbnailSpacing = 0;
IMATH_NAMESPACE::V2d thumbnailSize;
if (filmstrip && imageSize.x > 0 && imageSize.y > 0)
{
Expand Down
7 changes: 6 additions & 1 deletion bin/toucan-view/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ namespace toucan
context,
std::dynamic_pointer_cast<App>(shared_from_this()),
"toucan-view",
dtk::Size2I(1920, 1080));
dtk::Size2I(1820 * 2, 910 * 2));
addWindow(_window);

if (!_path.empty())
Expand Down Expand Up @@ -89,6 +89,11 @@ namespace toucan
return _timeUnitsModel;
}

const std::shared_ptr<ImageEffectHost>& App::getHost() const
{
return _host;
}

const std::shared_ptr<DocumentsModel>& App::getDocumentsModel() const
{
return _documentsModel;
Expand Down
1 change: 1 addition & 0 deletions bin/toucan-view/App.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace toucan
std::vector<std::string>&);

const std::shared_ptr<TimeUnitsModel>& getTimeUnitsModel() const;
const std::shared_ptr<ImageEffectHost>& getHost() const;
const std::shared_ptr<DocumentsModel>& getDocumentsModel() const;
const std::shared_ptr<WindowModel>& getWindowModel() const;

Expand Down
2 changes: 2 additions & 0 deletions bin/toucan-view/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ set(HEADERS
PlaybackModel.h
SelectionModel.h
StackItem.h
ThumbnailGenerator.h
TimeUnitsModel.h
TimeWidgets.h
TimelineItem.h
Expand Down Expand Up @@ -41,6 +42,7 @@ set(SOURCE
PlaybackModel.cpp
SelectionModel.cpp
StackItem.cpp
ThumbnailGenerator.cpp
TimeUnitsModel.cpp
TimeWidgets.cpp
TimelineItem.cpp
Expand Down
11 changes: 11 additions & 0 deletions bin/toucan-view/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "PlaybackModel.h"
#include "SelectionModel.h"
#include "ThumbnailGenerator.h"
#include "ViewModel.h"

#include <dtk/core/Math.h>
Expand Down Expand Up @@ -41,6 +42,11 @@ namespace toucan

_selectionModel = std::make_shared<SelectionModel>();

_thumbnailGenerator = std::make_shared<ThumbnailGenerator>(
_path.parent_path(),
_timeline,
_host);

_currentImage = dtk::ObservableValue<std::shared_ptr<dtk::Image> >::create();

_rootNode = dtk::ObservableValue<std::shared_ptr<IImageNode> >::create();
Expand Down Expand Up @@ -93,6 +99,11 @@ namespace toucan
return _selectionModel;
}

const std::shared_ptr<ThumbnailGenerator>& Document::getThumbnailGenerator() const
{
return _thumbnailGenerator;
}

std::shared_ptr<dtk::IObservableValue<std::shared_ptr<dtk::Image> > > Document::observeCurrentImage() const
{
return _currentImage;
Expand Down
3 changes: 3 additions & 0 deletions bin/toucan-view/Document.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace toucan
{
class PlaybackModel;
class SelectionModel;
class ThumbnailGenerator;
class ViewModel;

class Document : std::enable_shared_from_this<Document>
Expand All @@ -38,6 +39,7 @@ namespace toucan
const std::shared_ptr<PlaybackModel>& getPlaybackModel() const;
const std::shared_ptr<ViewModel>& getViewModel() const;
const std::shared_ptr<SelectionModel>& getSelectionModel() const;
const std::shared_ptr<ThumbnailGenerator>& getThumbnailGenerator() const;

std::shared_ptr<dtk::IObservableValue<std::shared_ptr<dtk::Image> > > observeCurrentImage() const;

Expand All @@ -54,6 +56,7 @@ namespace toucan
std::shared_ptr<PlaybackModel> _playbackModel;
std::shared_ptr<ViewModel> _viewModel;
std::shared_ptr<SelectionModel> _selectionModel;
std::shared_ptr<ThumbnailGenerator> _thumbnailGenerator;
std::shared_ptr<dtk::ObservableValue<std::shared_ptr<dtk::Image> > > _currentImage;
OTIO_NS::RationalTime _currentTime;

Expand Down
2 changes: 2 additions & 0 deletions bin/toucan-view/StackItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ namespace toucan
_text = !stack->name().empty() ? stack->name() : "Stack";
_color = dtk::Color4F(.2F, .2F, .2F);

setTooltip(_text);

for (const auto& child : stack->children())
{
if (auto track = OTIO_NS::dynamic_retainer_cast<OTIO_NS::Track>(child))
Expand Down
115 changes: 115 additions & 0 deletions bin/toucan-view/ThumbnailGenerator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2024 Darby Johnston
// All rights reserved.

#include "ThumbnailGenerator.h"

#include <toucan/ImageGraph.h>

#include <OpenImageIO/imagebufalgo.h>

#include <sstream>

namespace toucan
{
ThumbnailGenerator::ThumbnailGenerator(
const std::filesystem::path& path,
const OTIO_NS::SerializableObject::Retainer<OTIO_NS::Timeline>& timeline,
const std::shared_ptr<ImageEffectHost>& host) :
_path(path),
_timeline(timeline),
_host(host)
{
_graph = std::make_shared<ImageGraph>(_path, _timeline);
const IMATH_NAMESPACE::V2i& imageSize = _graph->getImageSize();
_aspect = imageSize.y > 0 ?
(imageSize.x / static_cast<float>(imageSize.y)) :
0.F;
}

ThumbnailGenerator::~ThumbnailGenerator()
{}

float ThumbnailGenerator::getAspect() const
{
return _aspect;
}

std::future<Thumbnail> ThumbnailGenerator::getThumbnail(
const OTIO_NS::RationalTime& time,
int height)
{
auto graph = _graph->exec(_host, time);
return std::async(
std::launch::async,
[this, graph, time, height]
{
const auto sourceBuf = graph->exec(time);
const auto& sourceSpec = sourceBuf.spec();

const dtk::Size2I thumbnailSize(height * _aspect, height);
dtk::ImageInfo info;
info.size = thumbnailSize;
switch (sourceSpec.nchannels)
{
case 1:
switch (sourceSpec.format.basetype)
{
case OIIO::TypeDesc::UINT8: info.type = dtk::ImageType::L_U8; break;
case OIIO::TypeDesc::UINT16: info.type = dtk::ImageType::L_U16; break;
case OIIO::TypeDesc::HALF: info.type = dtk::ImageType::L_F16; break;
case OIIO::TypeDesc::FLOAT: info.type = dtk::ImageType::L_F32; break;
}
break;
case 2:
switch (sourceSpec.format.basetype)
{
case OIIO::TypeDesc::UINT8: info.type = dtk::ImageType::LA_U8; break;
case OIIO::TypeDesc::UINT16: info.type = dtk::ImageType::LA_U16; break;
case OIIO::TypeDesc::HALF: info.type = dtk::ImageType::LA_F16; break;
case OIIO::TypeDesc::FLOAT: info.type = dtk::ImageType::LA_F32; break;
}
break;
case 3:
switch (sourceSpec.format.basetype)
{
case OIIO::TypeDesc::UINT8: info.type = dtk::ImageType::RGB_U8; break;
case OIIO::TypeDesc::UINT16: info.type = dtk::ImageType::RGB_U16; break;
case OIIO::TypeDesc::HALF: info.type = dtk::ImageType::RGB_F16; break;
case OIIO::TypeDesc::FLOAT: info.type = dtk::ImageType::RGB_F32; break;
}
break;
default:
switch (sourceSpec.format.basetype)
{
case OIIO::TypeDesc::UINT8: info.type = dtk::ImageType::RGBA_U8; break;
case OIIO::TypeDesc::UINT16: info.type = dtk::ImageType::RGBA_U16; break;
case OIIO::TypeDesc::HALF: info.type = dtk::ImageType::RGBA_F16; break;
case OIIO::TypeDesc::FLOAT: info.type = dtk::ImageType::RGBA_F32; break;
}
break;
}
info.layout.mirror.y = true;

std::shared_ptr<dtk::Image> thumbnail;
if (info.isValid())
{
thumbnail = dtk::Image::create(info);
auto resizedBuf = OIIO::ImageBufAlgo::resize(
sourceBuf,
"",
0.F,
OIIO::ROI(
0, info.size.w,
0, info.size.h,
0, 1,
0, std::min(4, sourceSpec.nchannels)));
memcpy(
thumbnail->getData(),
resizedBuf.localpixels(),
thumbnail->getByteCount());
}
return Thumbnail{ time, thumbnail };
});
}
}
48 changes: 48 additions & 0 deletions bin/toucan-view/ThumbnailGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2024 Darby Johnston
// All rights reserved.

#pragma once

#include <toucan/ImageEffectHost.h>
#include <toucan/ImageGraph.h>

#include <dtk/core/Image.h>

#include <opentimelineio/timeline.h>

#include <filesystem>
#include <future>

namespace toucan
{
struct Thumbnail
{
OTIO_NS::RationalTime time;
std::shared_ptr<dtk::Image> image;
};

class ThumbnailGenerator : public std::enable_shared_from_this<ThumbnailGenerator>
{
public:
ThumbnailGenerator(
const std::filesystem::path&,
const OTIO_NS::SerializableObject::Retainer<OTIO_NS::Timeline>&,
const std::shared_ptr<ImageEffectHost>&);

~ThumbnailGenerator();

float getAspect() const;

std::future<Thumbnail> getThumbnail(
const OTIO_NS::RationalTime&,
int height);

private:
std::filesystem::path _path;
OTIO_NS::SerializableObject::Retainer<OTIO_NS::Timeline> _timeline;
std::shared_ptr<ImageEffectHost> _host;
std::shared_ptr<ImageGraph> _graph;
float _aspect = 1.F;
};
}
55 changes: 53 additions & 2 deletions bin/toucan-view/TimelineItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ namespace toucan
_timeline = timeline;
_timeUnitsModel = app->getTimeUnitsModel();
_selectionModel = document->getSelectionModel();
_thumbnailGenerator = document->getThumbnailGenerator();

StackItem::create(context, app, _timeline->tracks(), shared_from_this());

Expand Down Expand Up @@ -133,7 +134,7 @@ namespace toucan
const dtk::Size2I& sizeHint = child->getSizeHint();
child->setGeometry(dtk::Box2I(
g.min.x,
g.min.y + timeHeight,
g.min.y + timeHeight + _size.thumbnailSize.h,
sizeHint.w,
sizeHint.h));
}
Expand All @@ -143,6 +144,21 @@ namespace toucan
}
}

void TimelineItem::tickEvent(
bool parentsVisible,
bool parentsEnabled,
const dtk::TickEvent& event)
{
IItem::tickEvent(parentsVisible, parentsEnabled, event);
if (_thumbnailFuture.valid() &&
_thumbnailFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
{
const auto thumbnail = _thumbnailFuture.get();
_thumbnails[thumbnail.time] = thumbnail.image;
_setDrawUpdate();
}
}

void TimelineItem::sizeHintEvent(const dtk::SizeHintEvent& event)
{
IItem::sizeHintEvent(event);
Expand All @@ -154,8 +170,12 @@ namespace toucan
_size.margin = event.style->getSizeRole(dtk::SizeRole::MarginInside, event.displayScale);
_size.border = event.style->getSizeRole(dtk::SizeRole::Border, event.displayScale);
_size.handle = event.style->getSizeRole(dtk::SizeRole::Handle, event.displayScale);
_size.thumbnailSize.h = 2 * event.style->getSizeRole(dtk::SizeRole::SwatchLarge, event.displayScale);
_size.thumbnailSize.w = _size.thumbnailSize.h * _thumbnailGenerator->getAspect();
_size.fontInfo = event.style->getFontRole(dtk::FontRole::Label, event.displayScale);
_size.fontMetrics = event.fontSystem->getMetrics(_size.fontInfo);
_thumbnailFuture = std::future<Thumbnail>();
_thumbnails.clear();
}
int childSizeHint = 0;
for (const auto& child : getChildren())
Expand All @@ -164,10 +184,41 @@ namespace toucan
}
dtk::Size2I sizeHint(
_timeRange.duration().rescaled_to(1.0).value() * _scale,
_size.fontMetrics.lineHeight + _size.margin * 2 + childSizeHint);
_size.fontMetrics.lineHeight + _size.margin * 2);
sizeHint.h += _size.thumbnailSize.h;
sizeHint.h += childSizeHint;
_setSizeHint(sizeHint);
}

void TimelineItem::drawEvent(const dtk::Box2I& drawRect, const dtk::DrawEvent& event)
{
IItem::drawEvent(drawRect, event);
const dtk::Box2I& g = getGeometry();
const int y = g.min.y + _size.fontMetrics.lineHeight + _size.margin * 2;
for (int x = g.min.x; x < g.max.x; x += _size.thumbnailSize.w)
{
const dtk::Box2I g2(x, y, _size.thumbnailSize.w, _size.thumbnailSize.h);
if (dtk::intersects(g2, drawRect))
{
const OTIO_NS::RationalTime t = posToTime(x);
const auto i = _thumbnails.find(t);
if (i != _thumbnails.end())
{
if (i->second)
{
event.render->drawImage(
i->second,
dtk::Box2I(x, y, i->second->getWidth(), i->second->getHeight()));
}
}
else if (!_thumbnailFuture.valid())
{
_thumbnailFuture = _thumbnailGenerator->getThumbnail(t, _size.thumbnailSize.h);
}
}
}
}

void TimelineItem::drawOverlayEvent(const dtk::Box2I& drawRect, const dtk::DrawEvent& event)
{
IItem::drawOverlayEvent(drawRect, event);
Expand Down
Loading

0 comments on commit 316e380

Please sign in to comment.