glMatrixMode(GL_MODELVIEW);
glPushMatrix();
- glTranslated(f_p[0], f_p[1], 0);
- glScaled(f_s[0], f_s[1], 1);
+ glTranslated(f_p[0], f_p[1], 0.0);
+ glScaled(f_s[0], f_s[1], 1.0);
+
+ switch(params.geometry.type())
+ {
+ case core::frame_geometry::quad:
+ {
+ const std::vector<float>& data = params.geometry.data();
+ float v_left = data[0], v_top = data[1], t_left = data[2], t_top = data[3];
+ float v_right = data[4], v_bottom = data[5], t_right = data[6], t_bottom = data[7];
+
+ glBegin(GL_QUADS);
+ glMultiTexCoord2d(GL_TEXTURE0, t_left, t_top); glVertex2d(v_left, v_top);
+ glMultiTexCoord2d(GL_TEXTURE0, t_right, t_top); glVertex2d(v_right, v_top);
+ glMultiTexCoord2d(GL_TEXTURE0, t_right, t_bottom); glVertex2d(v_right, v_bottom);
+ glMultiTexCoord2d(GL_TEXTURE0, t_left, t_bottom); glVertex2d(v_left, v_bottom);
+ glEnd();
+ }
+ break;
+
+ case core::frame_geometry::quad_list:
+ {
+ glClientActiveTexture(GL_TEXTURE0);
+
+ glDisableClientState(GL_EDGE_FLAG_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_INDEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glTexCoordPointer(2, GL_FLOAT, 4*sizeof(float), &(params.geometry.data().data()[2]));
+ glVertexPointer(2, GL_FLOAT, 4*sizeof(float), params.geometry.data().data());
+
+ glDrawArrays(GL_QUADS, 0, (GLsizei)params.geometry.data().size()/4); //each vertex is four floats.
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ }
+ break;
+
+ default:
+ break;
+ }
+
// Draw
- glBegin(GL_QUADS);
+ /*glBegin(GL_QUADS);
glMultiTexCoord2d(GL_TEXTURE0, 0.0, 0.0); glVertex2d(0, 0);
glMultiTexCoord2d(GL_TEXTURE0, 1.0, 0.0); glVertex2d(1, 0);
glMultiTexCoord2d(GL_TEXTURE0, 1.0, 1.0); glVertex2d(1, 1);
glMultiTexCoord2d(GL_TEXTURE0, 0.0, 1.0); glVertex2d(0, 1);
- glEnd();
+ glEnd();*/
glPopMatrix();
// Cleanup
#include <core/frame/pixel_format.h>
#include <core/frame/frame_transform.h>
+#include <core/frame/geometry.h>
namespace caspar { namespace accelerator { namespace ogl {
core::pixel_format_desc pix_desc;
std::vector<spl::shared_ptr<class texture>> textures;
core::image_transform transform;
+ core::frame_geometry geometry;
core::blend_mode blend_mode;
keyer keyer;
std::shared_ptr<class texture> background;
#include <core/frame/frame_transform.h>
#include <core/frame/pixel_format.h>
#include <core/video_format.h>
+#include <core/frame/geometry.h>
#include <asmlib.h>
core::pixel_format_desc pix_desc;
std::vector<future_texture> textures;
core::image_transform transform;
+ core::frame_geometry geometry;
item()
: pix_desc(core::pixel_format::invalid)
draw_params draw_params;
draw_params.pix_desc = std::move(item.pix_desc);
draw_params.transform = std::move(item.transform);
+ draw_params.geometry = item.geometry;
BOOST_FOREACH(auto& future_texture, item.textures)
draw_params.textures.push_back(future_texture.get());
draw_params.transform = core::image_transform();
draw_params.blend_mode = blend_mode;
draw_params.background = target_texture;
+ draw_params.geometry = core::frame_geometry::default();
kernel_.draw(std::move(draw_params));
}
item item;
item.pix_desc = frame.pixel_format_desc();
item.transform = transform_stack_.back();
+ item.geometry = frame.geometry();
// NOTE: Once we have copied the arrays they are no longer valid for reading!!! Check for alternative solution e.g. transfer with AMD_pinned_memory.
for(int n = 0; n < static_cast<int>(item.pix_desc.planes.size()); ++n)
// " gl_TexCoord[1] = gl_MultiTexCoord1; \n"
" vec4 pos = ftransform(); \n"
" gl_TexCoord[1] = vec4(pos.xy, 0.0, 0.0); \n"
- " pos.x = pos.x*2 - 1; \n"
- " pos.y = pos.y*2 - 1; \n"
+ " pos.x = pos.x*2.0 - 1.0; \n"
+ " pos.y = pos.y*2.0 - 1.0; \n"
" gl_Position = pos; \n"
"} \n";
}
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)tmp\$(Configuration)\</IntDir>\r
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)tmp\$(Configuration)\</IntDir>\r
- <IncludePath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\;..\dependencies64\boost\;..\dependencies64\tbb\include\;..\dependencies64\sfml\include\;..\dependencies64\glew\include\;..\dependencies64\asmlib\;$(IncludePath)</IncludePath>\r
- <IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\;..\dependencies64\boost\;..\dependencies64\tbb\include\;..\dependencies64\sfml\include\;..\dependencies64\glew\include\;..\dependencies64\asmlib\;$(IncludePath)</IncludePath>\r
+ <IncludePath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\;..\dependencies64\boost\;..\dependencies64\tbb\include\;..\dependencies64\sfml\include\;..\dependencies64\glew\include\;..\dependencies64\asmlib\;..\dependencies64\freetype\include;..\dependencies64\freeimage\include\;$(IncludePath)</IncludePath>\r
+ <IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\;..\dependencies64\boost\;..\dependencies64\tbb\include\;..\dependencies64\sfml\include\;..\dependencies64\glew\include\;..\dependencies64\asmlib\;..\dependencies64\freetype\include;..\dependencies64\freeimage\include\;$(IncludePath)</IncludePath>\r
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)bin\$(Configuration)\</OutDir>\r
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)bin\$(Configuration)\</OutDir>\r
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)</TargetName>\r
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectName)</TargetName>\r
+ <LibraryPath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(LibraryPath)</LibraryPath>\r
+ <LibraryPath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(LibraryPath)</LibraryPath>\r
</PropertyGroup>\r
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
<PreBuildEvent>\r
</Command>\r
</PostBuildEvent>\r
<Lib />\r
+ <Lib>\r
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>\r
+ </Lib>\r
</ItemDefinitionGroup>\r
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
<PreBuildEvent>\r
</PostBuildEvent>\r
<Lib>\r
<LinkTimeCodeGeneration>false</LinkTimeCodeGeneration>\r
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>\r
</Lib>\r
</ItemDefinitionGroup>\r
<ItemGroup>\r
<ClInclude Include="frame\frame_factory.h" />\r
<ClInclude Include="frame\frame_transform.h" />\r
<ClInclude Include="frame\frame_visitor.h" />\r
+ <ClInclude Include="frame\geometry.h" />\r
<ClInclude Include="frame\pixel_format.h" />\r
<ClInclude Include="interaction\interaction_aggregator.h" />\r
<ClInclude Include="interaction\interaction_event.h" />\r
<ClInclude Include="producer\draw\freehand_producer.h" />\r
<ClInclude Include="producer\scene\const_producer.h" />\r
<ClInclude Include="producer\scene\scene_producer.h" />\r
+ <ClInclude Include="producer\text\text_producer.h" />\r
+ <ClInclude Include="producer\text\utils\color.h" />\r
+ <ClInclude Include="producer\text\utils\string_metrics.h" />\r
+ <ClInclude Include="producer\text\utils\texture_atlas.h" />\r
+ <ClInclude Include="producer\text\utils\texture_font.h" />\r
<ClInclude Include="video_channel.h" />\r
<ClInclude Include="consumer\output.h" />\r
<ClInclude Include="consumer\frame_consumer.h" />\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
</ClCompile>\r
+ <ClCompile Include="frame\geometry.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
<ClCompile Include="mixer\image\blend_modes.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
</ClCompile>\r
+ <ClCompile Include="producer\text\text_producer.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
+ <ClCompile Include="producer\text\utils\texture_atlas.cpp">\r
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>\r
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>\r
+ </ClCompile>\r
+ <ClCompile Include="producer\text\utils\texture_font.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\..\StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\..\StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
<ClCompile Include="video_channel.cpp" />\r
<ClCompile Include="consumer\frame_consumer.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
<Filter Include="source\producer\draw">\r
<UniqueIdentifier>{0138e754-c089-46e6-9540-cf5cf4818381}</UniqueIdentifier>\r
</Filter>\r
+ <Filter Include="source\producer\text">\r
+ <UniqueIdentifier>{3fcfe649-213d-4ecd-a2d4-95ec24a6b152}</UniqueIdentifier>\r
+ </Filter>\r
<Filter Include="source\producer\scene">\r
<UniqueIdentifier>{a949a4cd-ede1-41fb-89bb-2fc1c4dc386a}</UniqueIdentifier>\r
</Filter>\r
+ <Filter Include="source\producer\text\utils">\r
+ <UniqueIdentifier>{fc435676-34ca-46fe-bbcf-2fbad8e3cd21}</UniqueIdentifier>\r
+ </Filter>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="producer\transition\transition_producer.h">\r
<ClInclude Include="producer\scene\const_producer.h">\r
<Filter>source\producer\scene</Filter>\r
</ClInclude>\r
+ <ClInclude Include="frame\geometry.h">\r
+ <Filter>source\frame</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="producer\text\text_producer.h">\r
+ <Filter>source\producer\text</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="producer\text\utils\texture_font.h">\r
+ <Filter>source\producer\text\utils</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="producer\text\utils\texture_atlas.h">\r
+ <Filter>source\producer\text\utils</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="producer\text\utils\color.h">\r
+ <Filter>source\producer\text\utils</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="producer\text\utils\string_metrics.h">\r
+ <Filter>source\producer\text\utils</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="producer\transition\transition_producer.cpp">\r
<ClCompile Include="producer\scene\const_producer.cpp">\r
<Filter>source\producer\scene</Filter>\r
</ClCompile>\r
+ <ClCompile Include="frame\geometry.cpp">\r
+ <Filter>source\frame</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="producer\text\text_producer.cpp">\r
+ <Filter>source\producer\text</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="producer\text\utils\texture_font.cpp">\r
+ <Filter>source\producer\text\utils</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="producer\text\utils\texture_atlas.cpp">\r
+ <Filter>source\producer\text\utils</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
// Properties
const core::frame_transform& transform() const;
- core::frame_transform& transform();
+ core::frame_transform& transform();
+
private:
struct impl;
spl::unique_ptr<impl> impl_;
#include <core/frame/frame_visitor.h>
#include <core/frame/pixel_format.h>
+#include <core/frame/geometry.h>
#include <boost/lexical_cast.hpp>
#include <boost/thread/future.hpp>
core::audio_buffer audio_data_;
const core::pixel_format_desc desc_;
const void* tag_;
+ core::frame_geometry geometry_;
impl(std::vector<array<std::uint8_t>> buffers, audio_buffer audio_buffer, const void* tag, const core::pixel_format_desc& desc)
: buffers_(std::move(buffers))
, audio_data_(std::move(audio_buffer))
, desc_(desc)
, tag_(tag)
+ , geometry_(frame_geometry::default())
{
BOOST_FOREACH(auto& buffer, buffers_)
if(!buffer.data())
std::size_t mutable_frame::height() const{return impl_->desc_.planes.at(0).height;}
const void* mutable_frame::stream_tag()const{return impl_->tag_;}
const void* mutable_frame::data_tag()const{return impl_.get();}
+const frame_geometry& mutable_frame::geometry() const { return impl_->geometry_; }
+void mutable_frame::set_geometry(const frame_geometry& g) { impl_->geometry_ = g; }
const const_frame& const_frame::empty()
{
core::audio_buffer audio_data_;
const core::pixel_format_desc desc_;
const void* tag_;
+ core::frame_geometry geometry_;
impl(const void* tag)
: desc_(core::pixel_format::invalid)
, tag_(tag)
, id_(0)
+ , geometry_(frame_geometry::default())
{
}
, desc_(desc)
, tag_(tag)
, id_(reinterpret_cast<int>(this))
+ , geometry_(frame_geometry::default())
{
if(desc.format != core::pixel_format::bgra)
CASPAR_THROW_EXCEPTION(not_implemented());
, desc_(other.pixel_format_desc())
, tag_(other.stream_tag())
, id_(reinterpret_cast<int>(this))
+ , geometry_(other.geometry())
{
for(std::size_t n = 0; n < desc_.planes.size(); ++n)
{
std::size_t const_frame::size()const{return impl_->size();}
const void* const_frame::stream_tag()const{return impl_->tag_;}
const void* const_frame::data_tag()const{return impl_.get();}
+const frame_geometry& const_frame::geometry() const { return impl_->geometry_; }
+void const_frame::set_geometry(const frame_geometry& g) { impl_->geometry_ = g; }
}}
\ No newline at end of file
namespace caspar { namespace core {
typedef std::vector<int32_t, tbb::cache_aligned_allocator<int32_t>> audio_buffer;
+class frame_geometry;
class mutable_frame sealed
{
const void* stream_tag() const;
const void* data_tag() const;
+
+ const core::frame_geometry& geometry() const;
+ void set_geometry(const frame_geometry& g);
private:
struct impl;
const void* stream_tag() const;
const void* data_tag() const;
+ const core::frame_geometry& geometry() const;
+ void set_geometry(const frame_geometry& g);
+
bool operator==(const const_frame& other);
bool operator!=(const const_frame& other);
bool operator<(const const_frame& other);
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Niklas P Andersson, niklas.p.andersson@svt.se
+*/
+
+
+#include "..\StdAfx.h"
+
+#include "geometry.h"
+
+namespace caspar { namespace core {
+
+struct frame_geometry::impl
+{
+ impl() : type_(frame_geometry::none) {}
+ impl(frame_geometry::geometry_type t, std::vector<float> d) : type_(t), data_(std::move(d)) {}
+
+ frame_geometry::geometry_type type_;
+ std::vector<float> data_;
+};
+
+frame_geometry::frame_geometry() : impl_(new impl()) {}
+frame_geometry::frame_geometry(const frame_geometry& rhs) : impl_(rhs.impl_) {}
+frame_geometry::frame_geometry(geometry_type t, std::vector<float> d) : impl_(new impl(t, std::move(d))) {}
+
+const frame_geometry& frame_geometry::operator=(const frame_geometry& rhs) { impl_ = rhs.impl_; return *this; }
+
+frame_geometry::geometry_type frame_geometry::type() { return impl_->type_; }
+const std::vector<float>& frame_geometry::data() { return impl_->data_; }
+
+const frame_geometry& frame_geometry::default()
+{
+ const float d[] = {0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f};
+ static frame_geometry g(frame_geometry::quad, std::move(std::vector<float>(d, d+8)));
+
+ return g;
+}
+
+}}
\ No newline at end of file
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Niklas P Andersson, niklas.p.andersson@svt.se
+*/
+
+#pragma once
+
+#include <common/memory.h>
+#include <vector>
+
+namespace caspar { namespace core {
+
+class frame_geometry
+{
+public:
+ enum geometry_type
+ {
+ none,
+ quad,
+ quad_list
+ };
+
+ frame_geometry();
+ frame_geometry(const frame_geometry&);
+ frame_geometry(geometry_type, std::vector<float>);
+ const frame_geometry& operator=(const frame_geometry&);
+
+ geometry_type type();
+ const std::vector<float>& data();
+
+ static const frame_geometry& default();
+
+private:
+ struct impl;
+ spl::shared_ptr<impl> impl_;
+};
+
+}}
\ No newline at end of file
#include "../../stdafx.h"
+#include <common/future.h>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string.hpp>
+
#include "scene_producer.h"
#include "../../frame/draw_frame.h"
: producer(producer)
{
}
+layer::layer(const std::wstring& name, const spl::shared_ptr<frame_producer>& producer)
+ : name(name), producer(producer)
+{
+}
adjustments::adjustments()
: opacity(1.0)
{
}
+ layer& create_layer(
+ const spl::shared_ptr<frame_producer>& producer, int x, int y, const std::wstring& name)
+ {
+ layer& layer = create_layer(producer, x, y);
+ layer.name.set(name);
+
+ return layer;
+ }
+
layer& create_layer(
const spl::shared_ptr<frame_producer>& producer, int x, int y)
{
return boost::optional<interaction_target>();
}
+ boost::unique_future<std::wstring> call(const std::wstring& params)
+ {
+ std::wstring result;
+
+ std::vector<std::wstring> words;
+ boost::split(words, params, [](wchar_t c) { return c == L' '; }, boost::token_compress_on);
+
+ if(words.size() >= 2)
+ {
+ struct layer_comparer
+ {
+ const std::wstring& str;
+ explicit layer_comparer(const std::wstring& s) : str(s) {}
+ bool operator()(const layer& val) { return boost::iequals(val.name.get(), str); }
+ };
+
+ auto it = std::find_if(layers_.begin(), layers_.end(), layer_comparer(words[0]));
+ if(it != layers_.end())
+ (*it).producer.get()->call(words[1]);
+ }
+
+ return async(launch::deferred, [=]{return result;});
+ }
+
std::wstring print() const
{
return L"scene[]";
return impl_->create_layer(producer, x, y);
}
+layer& scene_producer::create_layer(
+ const spl::shared_ptr<frame_producer>& producer, int x, int y, const std::wstring& name)
+{
+ return impl_->create_layer(producer, x, y, name);
+}
+
layer& scene_producer::create_layer(
const spl::shared_ptr<frame_producer>& producer)
{
return impl_->info();
}
+boost::unique_future<std::wstring> scene_producer::call(const std::wstring& params)
+{
+ return impl_->call(params);
+}
+
void scene_producer::subscribe(const monitor::observable::observer_ptr& o)
{
impl_->subscribe(o);
if (params.size() < 1 || params.at(0) != L"[SCENE]")
return core::frame_producer::empty();
- auto scene = spl::make_shared<scene_producer>(1280, 720);
+ auto scene = spl::make_shared<scene_producer>(format_desc.width, format_desc.height);
binding<double> text_width(10);
binding<double> padding(1);
binding<bool> hidden;
binding<bool> is_key;
- layer(const spl::shared_ptr<frame_producer>& producer);
+ explicit layer(const spl::shared_ptr<frame_producer>& producer);
+ layer(const std::wstring& name, const spl::shared_ptr<frame_producer>& producer);
};
class scene_producer : public frame_producer_base
bool collides(double x, double y) const override;
std::wstring print() const override;
std::wstring name() const override;
+ boost::unique_future<std::wstring> call(const std::wstring& params) override;
boost::property_tree::wptree info() const override;
void subscribe(const monitor::observable::observer_ptr& o) override;
void unsubscribe(const monitor::observable::observer_ptr& o) override;
layer& create_layer(
const spl::shared_ptr<frame_producer>& producer, int x, int y);
+ layer& create_layer(
+ const spl::shared_ptr<frame_producer>& producer, int x, int y, const std::wstring& name);
layer& create_layer(const spl::shared_ptr<frame_producer>& producer);
binding<int64_t> frame();
private:
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Niklas P Andersson, niklas.p.andersson@svt.se
+*/
+
+#include "../../stdafx.h"
+
+#include "text_producer.h"
+
+#include <core/producer/frame_producer.h>
+#include <core/frame/geometry.h>
+#include <core/frame/frame.h>
+#include <core/frame/draw_frame.h>
+#include <core/frame/frame_factory.h>
+#include <core/frame/pixel_format.h>
+#include <core/monitor/monitor.h>
+
+#include <core/consumer/frame_consumer.h>
+#include <modules/image/consumer/image_consumer.h>
+
+#include <common/except.h>
+#include <common/array.h>
+#include <common/env.h>
+#include <common/future.h>
+#include <memory>
+
+#include <asmlib.h>
+#include <FreeImage.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/filesystem.hpp>
+
+#include "utils\texture_atlas.h"
+#include "utils\texture_font.h"
+
+namespace caspar { namespace core {
+
+std::wstring find_font_file(const std::wstring& font_name)
+{
+ std::wstring filename = L"c:\\windows\\fonts\\" + font_name; //TODO: move font-folder setting to settings
+ if(boost::filesystem::exists(filename + L".otf"))
+ return filename + L".otf";
+ if(boost::filesystem::exists(filename + L".ttf"))
+ return filename + L".ttf";
+ if(boost::filesystem::exists(filename + L".fon"))
+ return filename + L".fon";
+
+ //TODO: Searching by filename is not enough to identify some fonts. we need to extract the font-names somehow
+ return L"";
+}
+
+struct text_producer::impl
+{
+ spl::shared_ptr<core::frame_factory> frame_factory_;
+ constraints constraints_;
+ int x_, y_, parent_width_, parent_height_;
+ std::wstring text_;
+ draw_frame frame_;
+ text::texture_atlas atlas_;
+ text::texture_font font_;
+
+public:
+ explicit impl(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, const text::text_info& text_info, long parent_width, long parent_height)
+ : frame_factory_(frame_factory)
+ , constraints_(parent_width, parent_height)
+ , x_(x), y_(y), parent_width_(parent_width), parent_height_(parent_height)
+ , atlas_(512,512,4)
+ , font_(atlas_, find_font_file(text_info.font), text_info.size)
+ {
+ font_.load_glyphs(text::Basic_Latin, text_info.color);
+
+ //generate frame
+ generate_frame(str);
+
+ CASPAR_LOG(info) << print() << L" Initialized";
+ }
+
+ void generate_frame(const std::wstring& str)
+ {
+ core::pixel_format_desc pfd(core::pixel_format::bgra);
+ pfd.planes.push_back(core::pixel_format_desc::plane((int)atlas_.width(), (int)atlas_.height(), (int)atlas_.depth()));
+
+ std::vector<float> vertex_stream(std::move(font_.create_vertex_stream(str, x_, y_, parent_width_, parent_height_)));
+ auto frame = frame_factory_->create_frame(vertex_stream.data(), pfd);
+ memcpy(frame.image_data().data(), atlas_.data(), frame.image_data().size());
+ frame.set_geometry(frame_geometry(frame_geometry::quad_list, std::move(vertex_stream)));
+
+ frame_ = core::draw_frame(std::move(frame));
+ }
+
+ text::string_metrics measure_string(const std::wstring& str)
+ {
+ return font_.measure_string(str);
+ }
+
+ // frame_producer
+
+ draw_frame receive_impl()
+ {
+ return frame_;
+ }
+
+ boost::unique_future<std::wstring> call(const std::wstring& param)
+ {
+ std::wstring result;
+ generate_frame(param);
+
+ return async(launch::deferred, [=]{return result;});
+ }
+
+ constraints& pixel_constraints()
+ {
+ return constraints_;
+ }
+
+ std::wstring print() const
+ {
+ return L"text[" + text_ + L"]";
+ }
+
+ std::wstring name() const
+ {
+ return L"text";
+ }
+
+ boost::property_tree::wptree info() const
+ {
+ boost::property_tree::wptree info;
+ info.add(L"type", L"text");
+ info.add(L"text", text_);
+ return info;
+ }
+};
+
+text_producer::text_producer(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, const text::text_info& text_info, long parent_width, long parent_height)
+ : impl_(new impl(frame_factory, x, y, str, text_info, parent_width, parent_height))
+{}
+
+draw_frame text_producer::receive_impl() { return impl_->receive_impl(); }
+boost::unique_future<std::wstring> text_producer::call(const std::wstring& param) { return impl_->call(param); }
+text::string_metrics text_producer::measure_string(const std::wstring& str) { return impl_->measure_string(str); }
+
+constraints& text_producer::pixel_constraints() { return impl_->pixel_constraints(); }
+std::wstring text_producer::print() const { return impl_->print(); }
+std::wstring text_producer::name() const { return impl_->name(); }
+boost::property_tree::wptree text_producer::info() const { return impl_->info(); }
+void text_producer::subscribe(const monitor::observable::observer_ptr& o) {}
+void text_producer::unsubscribe(const monitor::observable::observer_ptr& o) {}
+
+
+
+spl::shared_ptr<frame_producer> do_create_text_producer(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, const text::text_info& text_info, long parent_width, long parent_height)
+{
+ return spl::make_shared<text_producer>(frame_factory, x, y, str, text_info, parent_width, parent_height);
+}
+
+spl::shared_ptr<frame_producer> create_text_producer(const spl::shared_ptr<frame_factory>& frame_factory, const video_format_desc& format_desc, const std::vector<std::wstring>& params)
+{
+ if(params.size() < 2 || params.at(0) != L"[TEXT]")
+ return core::frame_producer::empty();
+
+ int x = 0, y = 0;
+ if(params.size() >= 4)
+ {
+ x = boost::lexical_cast<int>(params.at(2));
+ y = boost::lexical_cast<int>(params.at(3));
+ }
+
+ text::text_info text_info;
+ text_info.font = L"verdana";
+ text_info.size = 30;
+ text_info.color = text::color<float>(1.0f, 0, 0, 1.0f);
+ return do_create_text_producer(frame_factory, x, y, params.at(1), text_info, format_desc.width, format_desc.height);
+}
+
+}}
\ No newline at end of file
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Niklas P Andersson, niklas.p.andersson@svt.se
+*/
+
+#pragma once
+
+#include "../frame_producer.h"
+
+#include <common/memory.h>
+
+#include <boost/property_tree/ptree_fwd.hpp>
+#include <string>
+#include <vector>
+
+#include "utils/color.h"
+#include "utils/string_metrics.h"
+
+namespace caspar { namespace core {
+ namespace text
+ {
+ struct text_info
+ {
+ std::wstring font;
+ float size;
+ color<float> color;
+ };
+ }
+
+class text_producer : public frame_producer_base
+{
+public:
+ text_producer(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, const text::text_info& text_info, long parent_width, long parent_height);
+ draw_frame receive_impl() override;
+ boost::unique_future<std::wstring> call(const std::wstring& param) override;
+
+ text::string_metrics measure_string(const std::wstring& str);
+
+ constraints& pixel_constraints() override;
+ std::wstring print() const override;
+ std::wstring name() const override;
+ boost::property_tree::wptree info() const override;
+ void subscribe(const monitor::observable::observer_ptr& o) override;
+ void unsubscribe(const monitor::observable::observer_ptr& o) override;
+
+private:
+ struct impl;
+ spl::unique_ptr<impl> impl_;
+};
+
+spl::shared_ptr<frame_producer> do_create_text_producer(const spl::shared_ptr<class frame_factory>& frame_factory, int x, int y, const std::wstring& str, const text::text_info& text_info, long parent_width, long parent_height);
+spl::shared_ptr<frame_producer> create_text_producer(const spl::shared_ptr<class frame_factory>& frame_factory, const video_format_desc& format_desc, const std::vector<std::wstring>& params);
+
+
+}}
--- /dev/null
+#pragma once
+
+namespace caspar { namespace core { namespace text {
+
+template<typename T>
+struct color
+{
+ color() {}
+ color(const color& other) : r(other.r), g(other.g), b(other.b), a(other.a) {}
+ color(T red, T green, T blue, T alpha) : r(red), g(green), b(blue), a(alpha) {}
+
+ const color&operator=(const color& other)
+ {
+ r = other.r;
+ g = other.g;
+ b = other.b;
+ a = other.a;
+
+ return *this;
+ }
+
+ T r;
+ T g;
+ T b;
+ T a;
+};
+
+}}}
\ No newline at end of file
--- /dev/null
+#pragma once
+
+namespace caspar { namespace core { namespace text {
+
+ struct string_metrics
+ {
+ string_metrics() : width(0), bearingY(0), height(0) {}
+ int width;
+ int bearingY;
+ int height;
+ };
+}}}
\ No newline at end of file
--- /dev/null
+/* ============================================================================
+ * Freetype GL - A C OpenGL Freetype engine
+ * Platform: Any
+ * WWW: http://code.google.com/p/freetype-gl/
+ * ----------------------------------------------------------------------------
+ * Copyright 2011,2012 Nicolas P. Rougier. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NICOLAS P. ROUGIER ''AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL NICOLAS P. ROUGIER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of Nicolas P. Rougier.
+ * ============================================================================
+ */
+
+#include <list>
+#include <vector>
+#include "texture_atlas.h"
+
+namespace caspar { namespace core { namespace text {
+
+
+struct texture_atlas::impl
+{
+private:
+ struct node
+ {
+ int x;
+ int y;
+ int width;
+ };
+
+ typedef std::list<node> node_list;
+ typedef std::list<node>::iterator node_iterator;
+
+public:
+ impl(const size_t width, const size_t height, const size_t depth) : width_(width), height_(height), depth_(depth), used_(0), data_(width*height*depth, 0)
+ {
+ // We want a one pixel border around the whole atlas to avoid any artefact when sampling texture
+ node n = {1, 1, (int)width_ - 2};
+ nodes_.push_back(n);
+ }
+
+ rect get_region(int width, int height)
+ {
+ int y, best_height, best_width;
+
+ rect region = {0,0,(int)width,(int)height};
+
+ best_height = INT_MAX;
+ best_width = INT_MAX;
+ node_iterator best_it = nodes_.end();
+
+ auto it = nodes_.begin();
+ for(; it != nodes_.end(); ++it)
+ {
+ y = fit(it, width, height);
+ if( y >= 0 )
+ {
+ if( ( (y + height) < best_height ) ||
+ ( ((y + height) == best_height) && ((*it).width < best_width)) )
+ {
+ best_height = y + height;
+ best_it = it;
+ best_width = (*it).width;
+ region.x = (*it).x;
+ region.y = (int)y;
+ }
+ }
+ }
+
+ if(best_it == nodes_.end())
+ {
+ region.x = -1;
+ region.y = -1;
+ region.width = 0;
+ region.height = 0;
+ return region;
+ }
+
+ node new_node;
+ new_node.x = region.x;
+ new_node.y = region.y + (int)height;
+ new_node.width = (int)width;
+
+ best_it = nodes_.insert(best_it, new_node);
+
+ for(auto it = ++best_it; it != nodes_.end(); ++it)
+ {
+ auto prev = it; --prev;
+
+ if ((*it).x < ((*prev).x + (*prev).width) )
+ {
+ int shrink = (*prev).x + (*prev).width - (*it).x;
+ (*it).x += shrink;
+ (*it).width -= shrink;
+ if ((*it).width <= 0)
+ {
+ nodes_.erase(it);
+ it = prev;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+
+ merge();
+ used_ += width * height;
+ return region;
+ }
+
+ //the data parameter points to bitmap-data that is 8-bit grayscale.
+ void set_region(const size_t x, const size_t y, const size_t width, const size_t height, const unsigned char *src, const size_t stride, const color<float>& col)
+ {
+ //this assumes depth_ is set to 4
+ for(size_t i=0; i<height; ++i)
+ {
+ for(size_t j=0; j<width; ++j)
+ {
+ unsigned char* pixel = &data_[((y+i)*width_ + x + j)*depth_];
+ unsigned char value = src[i*stride + j];
+ pixel[0] = (unsigned char)(value*col.b);
+ pixel[1] = (unsigned char)(value*col.g);
+ pixel[2] = (unsigned char)(value*col.r);
+ pixel[3] = (unsigned char)(value*col.a);
+ }
+ }
+ }
+
+ void clear()
+ {
+ nodes_.clear();
+ used_ = 0;
+
+ node n = {1,1,(int)width_-2};
+ nodes_.push_back(n);
+ data_.assign(width_*height_*depth_, 0);
+ }
+
+ size_t depth() { return depth_; }
+ size_t width() { return width_; }
+ size_t height() { return height_; }
+ unsigned char* data() { return data_.data(); }
+
+private:
+ int fit(node_iterator it, const size_t width, const size_t height)
+ {
+ int x, y, width_left;
+
+ x = (*it).x;
+ y = (*it).y;
+ width_left = (int)width;
+
+ if ((x + width) > (width_ - 1))
+ return -1;
+
+ while( width_left > 0 && it != nodes_.end())
+ {
+ if((*it).y > y)
+ y = (*it).y;
+ if((y + height) > (height_ - 1))
+ return -1;
+
+ width_left -= (*it).width;
+ ++it;
+ }
+
+ return y;
+ }
+
+ void merge()
+ {
+ auto it = nodes_.begin();
+ while(true)
+ {
+ auto next = it; ++next;
+ if(next == nodes_.end())
+ break;
+
+ if((*it).y == (*next).y)
+ {
+ (*it).width += (*next).width;
+ nodes_.erase(next);
+ }
+ else
+ ++it;
+ }
+ }
+
+ node_list nodes_;
+
+ size_t width_;
+ size_t height_;
+ size_t depth_;
+
+ std::vector<unsigned char> data_;
+ size_t used_;
+};
+
+texture_atlas::texture_atlas(const size_t w, const size_t h, const size_t d) : impl_(new impl(w, h, d)) {}
+rect texture_atlas::get_region(int width, int height) { return impl_->get_region(width, height); }
+void texture_atlas::set_region(const size_t x, const size_t y, const size_t width, const size_t height, const unsigned char *src, const size_t stride, const color<float>& col)
+ { impl_->set_region(x, y, width, height, src, stride, col); }
+
+void texture_atlas::clear() { impl_->clear(); }
+
+size_t texture_atlas::width() { return impl_->width(); }
+size_t texture_atlas::height() { return impl_->height(); }
+size_t texture_atlas::depth() { return impl_->depth(); }
+unsigned char* texture_atlas::data() { return impl_->data(); }
+
+}}}
\ No newline at end of file
--- /dev/null
+/* ============================================================================
+ * Freetype GL - A C OpenGL Freetype engine
+ * Platform: Any
+ * WWW: http://code.google.com/p/freetype-gl/
+ * ----------------------------------------------------------------------------
+ * Copyright 2011,2012 Nicolas P. Rougier. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NICOLAS P. ROUGIER ''AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL NICOLAS P. ROUGIER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of Nicolas P. Rougier.
+ * ============================================================================
+ *
+ * This source is based on the article by Jukka Jylänki :
+ * "A Thousand Ways to Pack the Bin - A Practical Approach to
+ * Two-Dimensional Rectangle Bin Packing", February 27, 2010.
+ *
+ * More precisely, this is an implementation of the Skyline Bottom-Left
+ * algorithm based on C++ sources provided by Jukka Jylänki at:
+ * http://clb.demon.fi/files/RectangleBinPack/
+ *
+ * ============================================================================
+ */
+#pragma once
+
+
+/**
+ * @file texture-atlas.h
+ * @author Nicolas Rougier (Nicolas.Rougier@inria.fr)
+ *
+ * @defgroup texture-atlas Texture atlas
+ *
+ * A texture atlas is used to pack several small regions into a single texture.
+ *
+ * The actual implementation is based on the article by Jukka Jylänki : "A
+ * Thousand Ways to Pack the Bin - A Practical Approach to Two-Dimensional
+ * Rectangle Bin Packing", February 27, 2010.
+ * More precisely, this is an implementation of the Skyline Bottom-Left
+ * algorithm based on C++ sources provided by Jukka Jylänki at:
+ * http://clb.demon.fi/files/RectangleBinPack/
+ *
+ *
+ */
+
+#include "common/memory.h"
+#include "color.h"
+
+namespace caspar { namespace core { namespace text {
+
+struct rect
+{
+ int x;
+ int y;
+ int width;
+ int height;
+};
+
+class texture_atlas
+{
+public:
+ texture_atlas(const size_t w, const size_t h, const size_t d);
+
+ rect get_region(int width, int height);
+ void set_region(const size_t x, const size_t y, const size_t width, const size_t height, const unsigned char *data, const size_t stride, const color<float>& col);
+ void clear();
+
+ size_t depth();
+ size_t width();
+ size_t height();
+
+ unsigned char* data();
+
+private:
+ struct impl;
+ caspar::spl::shared_ptr<impl> impl_;
+};
+
+}}}
--- /dev/null
+#include "..\..\..\StdAfx.h"
+
+#include "texture_atlas.h"
+#include "texture_font.h"
+
+#include <map>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
+namespace caspar { namespace core { namespace text {
+
+struct freetype_exception : virtual caspar_exception
+{
+ freetype_exception() {}
+ explicit freetype_exception(const char* msg) : caspar_exception(msg) {}
+};
+
+struct unicode_range
+{
+ int first;
+ int last;
+};
+
+unicode_range get_range(unicode_block block);
+
+
+struct texture_font::impl
+{
+private:
+ struct glyph_info
+ {
+ glyph_info(int w, int h, float l, float t, float r, float b) : width(w), height(h), left(l), top(t), right(r), bottom(b)
+ {}
+
+ float left, top, right, bottom;
+ int width, height;
+ };
+
+ FT_Library lib_;
+ FT_Face face_;
+ texture_atlas atlas_;
+ float size_;
+ std::map<int, glyph_info> glyphs_;
+
+public:
+ impl::impl(texture_atlas& atlas, const std::wstring& filename, float size) : lib_(nullptr), face_(nullptr), atlas_(atlas), size_(size)
+ {
+ try
+ {
+ FT_Error err;
+ err = FT_Init_FreeType(&lib_);
+ if(err) throw freetype_exception("Failed to initialize freetype");
+
+ err = FT_New_Face(lib_, u8(filename).c_str(), 0, &face_);
+ if(err) throw freetype_exception("Failed to load font");
+
+ err = FT_Set_Char_Size(face_, (FT_F26Dot6)(size*64), 0, 72, 72);
+ if(err) throw freetype_exception("Failed to set font size");
+ }
+ catch(std::exception& ex)
+ {
+ if(face_ != nullptr)
+ FT_Done_Face(face_);
+ if(lib_ != nullptr)
+ FT_Done_FreeType(lib_);
+
+ throw ex;
+ }
+ }
+
+ ~impl()
+ {
+ if(face_ != nullptr)
+ FT_Done_Face(face_);
+ if(lib_ != nullptr)
+ FT_Done_FreeType(lib_);
+ }
+
+ int count_glyphs_in_range(unicode_block block)
+ {
+ unicode_range range = get_range(block);
+
+ //TODO: extract info from freetype
+
+ //very pesimistic, assumes a glyph for each charcode
+ return range.last - range.first;
+ }
+
+ void impl::load_glyphs(unicode_block block, const color<float>& col)
+ {
+ FT_Error err;
+ int flags = FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
+ unicode_range range = get_range(block);
+
+ for(int i = range.first; i <= range.last; ++i)
+ {
+ FT_UInt glyph_index = FT_Get_Char_Index(face_, i);
+ if(!glyph_index) //ignore codes that doesn't have a glyph for now. Might want to map these to a special glyph later.
+ continue;
+
+ err = FT_Load_Glyph(face_, glyph_index, flags);
+ if(err) continue; //igonore glyphs that fail to load
+
+ const FT_Bitmap& bitmap = face_->glyph->bitmap; //shorthand notation
+
+ auto region = atlas_.get_region(bitmap.width+1, bitmap.rows+1);
+ if(region.x < 0)
+ {
+ //the glyph doesn't fit in the texture-atlas. ignore it for now.
+ //we might want to restart with a bigger atlas in the future
+ continue;
+ }
+
+ atlas_.set_region(region.x, region.y, bitmap.width, bitmap.rows, bitmap.buffer, bitmap.pitch, col);
+ glyphs_.insert(std::pair<int, glyph_info>(i, glyph_info(bitmap.width, bitmap.rows,
+ region.x / (float)atlas_.width(),
+ region.y / (float)atlas_.height(),
+ (region.x + bitmap.width) / (float)atlas_.width(),
+ (region.y + bitmap.rows) / (float)atlas_.height())));
+ }
+ }
+
+ std::vector<float> create_vertex_stream(const std::wstring& str, int x, int y, int parent_width, int parent_height)
+ {
+ //TODO: detect glyphs that aren't in the atlas and load them (and maybe that entire unicode_block on the fly
+
+ std::vector<float> result(16*str.length(), 0);
+
+ bool use_kerning = (face_->face_flags & FT_FACE_FLAG_KERNING) == FT_FACE_FLAG_KERNING;
+ int index = 0;
+ FT_UInt previous = 0;
+ float pos_x = (float)x;
+ float pos_y = (float)y;
+
+ auto end = str.end();
+ for(auto it = str.begin(); it != end; ++it, ++index)
+ {
+ auto glyph_it = glyphs_.find(*it);
+ if(glyph_it != glyphs_.end())
+ {
+ const glyph_info& coords = glyph_it->second;
+
+ FT_UInt glyph_index = FT_Get_Char_Index(face_, (*it));
+
+ if(use_kerning && previous && glyph_index)
+ {
+ FT_Vector delta;
+ FT_Get_Kerning(face_, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
+
+ pos_x += delta.x / 64.0f;
+ }
+
+ FT_Load_Glyph(face_, glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL);
+
+ float left = (pos_x + face_->glyph->metrics.horiBearingX/64.0f) / parent_width ;
+ float right = ((pos_x + face_->glyph->metrics.horiBearingX/64.0f) + coords.width) / parent_width;
+
+ float top = (pos_y - face_->glyph->metrics.horiBearingY/64.0f) / parent_height;
+ float bottom = ((pos_y - face_->glyph->metrics.horiBearingY/64.0f) + coords.height) / parent_height;
+
+ //vertex 1 top left
+ result[index*16 + 0] = left; //vertex.x
+ result[index*16 + 1] = top; //vertex.y
+ result[index*16 + 2] = coords.left; //texcoord.r
+ result[index*16 + 3] = coords.top; //texcoord.s
+
+ //vertex 2 top right
+ result[index*16 + 4] = right; //vertex.x
+ result[index*16 + 5] = top; //vertex.y
+ result[index*16 + 6] = coords.right; //texcoord.r
+ result[index*16 + 7] = coords.top; //texcoord.s
+
+ //vertex 3 bottom right
+ result[index*16 + 8] = right; //vertex.x
+ result[index*16 + 9] = bottom; //vertex.y
+ result[index*16 + 10] = coords.right; //texcoord.r
+ result[index*16 + 11] = coords.bottom; //texcoord.s
+
+ //vertex 4 bottom left
+ result[index*16 + 12] = left; //vertex.x
+ result[index*16 + 13] = bottom; //vertex.y
+ result[index*16 + 14] = coords.left; //texcoord.r
+ result[index*16 + 15] = coords.bottom; //texcoord.s
+
+ pos_x += face_->glyph->advance.x / 64.0f;
+ previous = glyph_index;
+ }
+ }
+ return result;
+ }
+
+ string_metrics measure_string(const std::wstring& str)
+ {
+ string_metrics result;
+
+ bool use_kerning = (face_->face_flags & FT_FACE_FLAG_KERNING) == FT_FACE_FLAG_KERNING;
+ int index = 0;
+ FT_UInt previous = 0;
+ float pos_x = 0;
+// float pos_y = 0;
+
+ auto end = str.end();
+ for(auto it = str.begin(); it != end; ++it, ++index)
+ {
+ auto glyph_it = glyphs_.find(*it);
+ if(glyph_it != glyphs_.end())
+ {
+ const glyph_info& coords = glyph_it->second;
+
+ FT_UInt glyph_index = FT_Get_Char_Index(face_, (*it));
+
+ if(use_kerning && previous && glyph_index)
+ {
+ FT_Vector delta;
+ FT_Get_Kerning(face_, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
+
+ pos_x += delta.x / 64.0f;
+ }
+
+ FT_Load_Glyph(face_, glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL);
+
+ int bearingY = face_->glyph->metrics.horiBearingY >> 6;
+ if(bearingY > result.bearingY)
+ result.bearingY = bearingY;
+
+ if(coords.height > result.height)
+ result.height = coords.height;
+
+ pos_x += face_->glyph->advance.x / 64.0f;
+ previous = glyph_index;
+ }
+ }
+
+ result.width = (int)(pos_x+.5f);
+ return result;
+ }
+};
+
+texture_font::texture_font(texture_atlas& atlas, const std::wstring& filename, float size) : impl_(new impl(atlas, filename, size)) {}
+void texture_font::load_glyphs(unicode_block range, const color<float>& col) { impl_->load_glyphs(range, col); }
+std::vector<float> texture_font::create_vertex_stream(const std::wstring& str, int x, int y, int parent_width, int parent_height) { return impl_->create_vertex_stream(str, x, y, parent_width, parent_height); }
+string_metrics texture_font::measure_string(const std::wstring& str) { return impl_->measure_string(str); }
+
+unicode_range get_range(unicode_block block)
+{
+ //TODO: implement
+ unicode_range range = {0x0000, 0x007F};
+ return range;
+}
+
+}}}
+
+//unicode blocks
+/*
+ range(0x0000, 0x007F);
+ range(0x0080, 0x00FF);
+ range(0x0100, 0x017F);
+ range(0x0180, 0x024F);
+ range(0x0250, 0x02AF);
+ range(0x02B0, 0x02FF);
+ range(0x0300, 0x036F);
+ range(0x0370, 0x03FF);
+ range(0x0400, 0x04FF);
+ range(0x0500, 0x052F);
+ range(0x0530, 0x058F);
+ range(0x0590, 0x05FF);
+ range(0x0600, 0x06FF);
+ range(0x0700, 0x074F);
+ range(0x0750, 0x077F);
+ range(0x0780, 0x07BF);
+ range(0x07C0, 0x07FF);
+ range(0x0800, 0x083F);
+ range(0x0840, 0x085F);
+ range(0x08A0, 0x08FF);
+ range(0x0900, 0x097F);
+ range(0x0980, 0x09FF);
+ range(0x0A00, 0x0A7F);
+ range(0x0A80, 0x0AFF);
+ range(0x0B00, 0x0B7F);
+ range(0x0B80, 0x0BFF);
+ range(0x0C00, 0x0C7F);
+ range(0x0C80, 0x0CFF);
+ range(0x0D00, 0x0D7F);
+ range(0x0D80, 0x0DFF);
+ range(0x0E00, 0x0E7F);
+ range(0x0E80, 0x0EFF);
+ range(0x0F00, 0x0FFF);
+ range(0x1000, 0x109F);
+ range(0x10A0, 0x10FF);
+ range(0x1100, 0x11FF);
+ range(0x1200, 0x137F);
+ range(0x1380, 0x139F);
+ range(0x13A0, 0x13FF);
+ range(0x1400, 0x167F);
+ range(0x1680, 0x169F);
+ range(0x16A0, 0x16FF);
+ range(0x1700, 0x171F);
+ range(0x1720, 0x173F);
+ range(0x1740, 0x175F);
+ range(0x1760, 0x177F);
+ range(0x1780, 0x17FF);
+ range(0x1800, 0x18AF);
+ range(0x18B0, 0x18FF);
+ range(0x1900, 0x194F);
+ range(0x1950, 0x197F);
+ range(0x1980, 0x19DF);
+ range(0x19E0, 0x19FF);
+ range(0x1A00, 0x1A1F);
+ range(0x1A20, 0x1AAF);
+ range(0x1B00, 0x1B7F);
+ range(0x1B80, 0x1BBF);
+ range(0x1BC0, 0x1BFF);
+ range(0x1C00, 0x1C4F);
+ range(0x1C50, 0x1C7F);
+ range(0x1CC0, 0x1CCF);
+ range(0x1CD0, 0x1CFF);
+ range(0x1D00, 0x1D7F);
+ range(0x1D80, 0x1DBF);
+ range(0x1DC0, 0x1DFF);
+ range(0x1E00, 0x1EFF);
+ range(0x1F00, 0x1FFF);
+ range(0x2000, 0x206F);
+ range(0x2070, 0x209F);
+ range(0x20A0, 0x20CF);
+ range(0x20D0, 0x20FF);
+ range(0x2100, 0x214F);
+ range(0x2150, 0x218F);
+ range(0x2190, 0x21FF);
+ range(0x2200, 0x22FF);
+ range(0x2300, 0x23FF);
+ range(0x2400, 0x243F);
+ range(0x2440, 0x245F);
+ range(0x2460, 0x24FF);
+ range(0x2500, 0x257F);
+ range(0x2580, 0x259F);
+ range(0x25A0, 0x25FF);
+ range(0x2600, 0x26FF);
+ range(0x2700, 0x27BF);
+ range(0x27C0, 0x27EF);
+ range(0x27F0, 0x27FF);
+ range(0x2800, 0x28FF);
+ range(0x2900, 0x297F);
+ range(0x2980, 0x29FF);
+ range(0x2A00, 0x2AFF);
+ range(0x2B00, 0x2BFF);
+ range(0x2C00, 0x2C5F);
+ range(0x2C60, 0x2C7F);
+ range(0x2C80, 0x2CFF);
+ range(0x2D00, 0x2D2F);
+ range(0x2D30, 0x2D7F);
+ range(0x2D80, 0x2DDF);
+ range(0x2DE0, 0x2DFF);
+ range(0x2E00, 0x2E7F);
+ range(0x2E80, 0x2EFF);
+ range(0x2F00, 0x2FDF);
+ range(0x2FF0, 0x2FFF);
+ range(0x3000, 0x303F);
+ range(0x3040, 0x309F);
+ range(0x30A0, 0x30FF);
+ range(0x3100, 0x312F);
+ range(0x3130, 0x318F);
+ range(0x3190, 0x319F);
+ range(0x31A0, 0x31BF);
+ range(0x31C0, 0x31EF);
+ range(0x31F0, 0x31FF);
+ range(0x3200, 0x32FF);
+ range(0x3300, 0x33FF);
+ range(0x3400, 0x4DBF);
+ range(0x4DC0, 0x4DFF);
+ range(0x4E00, 0x9FFF);
+ range(0xA000, 0xA48F);
+ range(0xA490, 0xA4CF);
+ range(0xA4D0, 0xA4FF);
+ range(0xA500, 0xA63F);
+ range(0xA640, 0xA69F);
+ range(0xA6A0, 0xA6FF);
+ range(0xA700, 0xA71F);
+ range(0xA720, 0xA7FF);
+ range(0xA800, 0xA82F);
+ range(0xA830, 0xA83F);
+ range(0xA840, 0xA87F);
+ range(0xA880, 0xA8DF);
+ range(0xA8E0, 0xA8FF);
+ range(0xA900, 0xA92F);
+ range(0xA930, 0xA95F);
+ range(0xA960, 0xA97F);
+ range(0xA980, 0xA9DF);
+ range(0xAA00, 0xAA5F);
+ range(0xAA60, 0xAA7F);
+ range(0xAA80, 0xAADF);
+ range(0xAAE0, 0xAAFF);
+ range(0xAB00, 0xAB2F);
+ range(0xABC0, 0xABFF);
+ range(0xAC00, 0xD7AF);
+ range(0xD7B0, 0xD7FF);
+ range(0xD800, 0xDB7F);
+ range(0xDB80, 0xDBFF);
+ range(0xDC00, 0xDFFF);
+ range(0xE000, 0xF8FF);
+ range(0xF900, 0xFAFF);
+ range(0xFB00, 0xFB4F);
+ range(0xFB50, 0xFDFF);
+ range(0xFE00, 0xFE0F);
+ range(0xFE10, 0xFE1F);
+ range(0xFE20, 0xFE2F);
+ range(0xFE30, 0xFE4F);
+ range(0xFE50, 0xFE6F);
+ range(0xFE70, 0xFEFF);
+ range(0xFF00, 0xFFEF);
+ range(0xFFF0, 0xFFFF);
+ range(0x10000, 0x1007F);
+ range(0x10080, 0x100FF);
+ range(0x10100, 0x1013F);
+ range(0x10140, 0x1018F);
+ range(0x10190, 0x101CF);
+ range(0x101D0, 0x101FF);
+ range(0x10280, 0x1029F);
+ range(0x102A0, 0x102DF);
+ range(0x10300, 0x1032F);
+ range(0x10330, 0x1034F);
+ range(0x10380, 0x1039F);
+ range(0x103A0, 0x103DF);
+ range(0x10400, 0x1044F);
+ range(0x10450, 0x1047F);
+ range(0x10480, 0x104AF);
+ range(0x10800, 0x1083F);
+ range(0x10840, 0x1085F);
+ range(0x10900, 0x1091F);
+ range(0x10920, 0x1093F);
+ range(0x10980, 0x1099F);
+ range(0x109A0, 0x109FF);
+ range(0x10A00, 0x10A5F);
+ range(0x10A60, 0x10A7F);
+ range(0x10B00, 0x10B3F);
+ range(0x10B40, 0x10B5F);
+ range(0x10B60, 0x10B7F);
+ range(0x10C00, 0x10C4F);
+ range(0x10E60, 0x10E7F);
+ range(0x11000, 0x1107F);
+ range(0x11080, 0x110CF);
+ range(0x110D0, 0x110FF);
+ range(0x11100, 0x1114F);
+ range(0x11180, 0x111DF);
+ range(0x11680, 0x116CF);
+ range(0x12000, 0x123FF);
+ range(0x12400, 0x1247F);
+ range(0x13000, 0x1342F);
+ range(0x16800, 0x16A3F);
+ range(0x16F00, 0x16F9F);
+ range(0x1B000, 0x1B0FF);
+ range(0x1D000, 0x1D0FF);
+ range(0x1D100, 0x1D1FF);
+ range(0x1D200, 0x1D24F);
+ range(0x1D300, 0x1D35F);
+ range(0x1D360, 0x1D37F);
+ range(0x1D400, 0x1D7FF);
+ range(0x1EE00, 0x1EEFF);
+ range(0x1F000, 0x1F02F);
+ range(0x1F030, 0x1F09F);
+ range(0x1F0A0, 0x1F0FF);
+ range(0x1F100, 0x1F1FF);
+ range(0x1F200, 0x1F2FF);
+ range(0x1F300, 0x1F5FF);
+ range(0x1F600, 0x1F64F);
+ range(0x1F680, 0x1F6FF);
+ range(0x1F700, 0x1F77F);
+ range(0x20000, 0x2A6DF);
+ range(0x2A700, 0x2B73F);
+ range(0x2B740, 0x2B81F);
+ range(0x2F800, 0x2FA1F);
+ range(0xE0000, 0xE007F);
+ range(0xE0100, 0xE01EF);
+ range(0xF0000, 0xFFFFF);
+ range(0x100000, 0x10FFFF);
+*/
\ No newline at end of file
--- /dev/null
+#pragma once
+
+#include <string>
+#include <vector>
+#include <common/memory.h>
+
+#include "string_metrics.h"
+
+namespace caspar { namespace core { namespace text {
+
+class texture_atlas;
+enum unicode_block;
+
+class texture_font
+{
+ texture_font();
+ texture_font(const texture_font&);
+ const texture_font& operator=(const texture_font&);
+
+public:
+ texture_font(texture_atlas&, const std::wstring& filename, float size);
+ void load_glyphs(unicode_block block, const color<float>& col);
+ std::vector<float> create_vertex_stream(const std::wstring& str, int x, int y, int parent_width, int parent_height);
+ string_metrics measure_string(const std::wstring& str);
+
+private:
+ struct impl;
+ spl::shared_ptr<impl> impl_;
+};
+
+enum unicode_block
+{
+ Basic_Latin,
+ Latin_1_Supplement,
+ Latin_Extended_A,
+ Latin_Extended_B,
+ IPA_Extensions,
+ Spacing_Modifier_Letters,
+ Combining_Diacritical_Marks,
+ Greek_and_Coptic,
+ Cyrillic,
+ Cyrillic_Supplement,
+ Armenian,
+ Hebrew,
+ Arabic,
+ Syriac,
+ Arabic_Supplement,
+ Thaana,
+ NKo,
+ Samaritan,
+ Mandaic,
+ Arabic_Extended_A,
+ Devanagari,
+ Bengali,
+ Gurmukhi,
+ Gujarati,
+ Oriya,
+ Tamil,
+ Telugu,
+ Kannada,
+ Malayalam,
+ Sinhala,
+ Thai,
+ Lao,
+ Tibetan,
+ Myanmar,
+ Georgian,
+ Hangul_Jamo,
+ Ethiopic,
+ Ethiopic_Supplement,
+ Cherokee,
+ Unified_Canadian_Aboriginal_Syllabics,
+ Ogham,
+ Runic,
+ Tagalog,
+ Hanunoo,
+ Buhid,
+ Tagbanwa,
+ Khmer,
+ Mongolian,
+ Unified_Canadian_Aboriginal_Syllabics_Extended,
+ Limbu,
+ Tai_Le,
+ New_Tai_Lue,
+ Khmer_Symbols,
+ Buginese,
+ Tai_Tham,
+ Balinese,
+ Sundanese,
+ Batak,
+ Lepcha,
+ Ol_Chiki,
+ Sundanese_Supplement,
+ Vedic_Extensions,
+ Phonetic_Extensions,
+ Phonetic_Extensions_Supplement,
+ Combining_Diacritical_Marks_Supplement,
+ Latin_Extended_Additional,
+ Greek_Extended,
+ General_Punctuation,
+ Superscripts_and_Subscripts,
+ Currency_Symbols,
+ Combining_Diacritical_Marks_for_Symbols,
+ Letterlike_Symbols,
+ Number_Forms,
+ Arrows,
+ Mathematical_Operators,
+ Miscellaneous_Technical,
+ Control_Pictures,
+ Optical_Character_Recognition,
+ Enclosed_Alphanumerics,
+ Box_Drawing,
+ Block_Elements,
+ Geometric_Shapes,
+ Miscellaneous_Symbols,
+ Dingbats,
+ Miscellaneous_Mathematical_Symbols_A,
+ Supplemental_Arrows_A,
+ Braille_Patterns,
+ Supplemental_Arrows_B,
+ Miscellaneous_Mathematical_Symbols_B,
+ Supplemental_Mathematical_Operators,
+ Miscellaneous_Symbols_and_Arrows,
+ Glagolitic,
+ Latin_Extended_C,
+ Coptic,
+ Georgian_Supplement,
+ Tifinagh,
+ Ethiopic_Extended,
+ Cyrillic_Extended_A,
+ Supplemental_Punctuation,
+ CJK_Radicals_Supplement,
+ Kangxi_Radicals,
+ Ideographic_Description_Characters,
+ CJK_Symbols_and_Punctuation,
+ Hiragana,
+ Katakana,
+ Bopomofo,
+ Hangul_Compatibility_Jamo,
+ Kanbun,
+ Bopomofo_Extended,
+ CJK_Strokes,
+ Katakana_Phonetic_Extensions,
+ Enclosed_CJK_Letters_and_Months,
+ CJK_Compatibility,
+ CJK_Unified_Ideographs_Extension_A,
+ Yijing_Hexagram_Symbols,
+ CJK_Unified_Ideographs,
+ Yi_Syllables,
+ Yi_Radicals,
+ Lisu,
+ Vai,
+ Cyrillic_Extended_B,
+ Bamum,
+ Modifier_Tone_Letters,
+ Latin_Extended_D,
+ Syloti_Nagri,
+ Common_Indic_Number_Forms,
+ Phags_pa,
+ Saurashtra,
+ Devanagari_Extended,
+ Kayah_Li,
+ Rejang,
+ Hangul_Jamo_Extended_A,
+ Javanese,
+ Cham,
+ Myanmar_Extended_A,
+ Tai_Viet,
+ Meetei_Mayek_Extensions,
+ Ethiopic_Extended_A,
+ Meetei_Mayek,
+ Hangul_Syllables,
+ Hangul_Jamo_Extended_B,
+ High_Surrogates,
+ High_Private_Use_Surrogates,
+ Low_Surrogates,
+ Private_Use_Area,
+ CJK_Compatibility_Ideographs,
+ Alphabetic_Presentation_Forms,
+ Arabic_Presentation_Forms_A,
+ Variation_Selectors,
+ Vertical_Forms,
+ Combining_Half_Marks,
+ CJK_Compatibility_Forms,
+ Small_Form_Variants,
+ Arabic_Presentation_Forms_B,
+ Halfwidth_and_Fullwidth_Forms,
+ Specials,
+ Linear_B_Syllabary,
+ Linear_B_Ideograms,
+ Aegean_Numbers,
+ Ancient_Greek_Numbers,
+ Ancient_Symbols,
+ Phaistos_Disc,
+ Lycian,
+ Carian,
+ Old_Italic,
+ Gothic,
+ Ugaritic,
+ Old_Persian,
+ Deseret,
+ Shavian,
+ Osmanya,
+ Cypriot_Syllabary,
+ Imperial_Aramaic,
+ Phoenician,
+ Lydian,
+ Meroitic_Hieroglyphs,
+ Meroitic_Cursive,
+ Kharoshthi,
+ Old_South_Arabian,
+ Avestan,
+ Inscriptional_Parthian,
+ Inscriptional_Pahlavi,
+ Old_Turkic,
+ Rumi_Numeral_Symbols,
+ Brahmi,
+ Kaithi,
+ Sora_Sompeng,
+ Chakma,
+ Sharada,
+ Takri,
+ Cuneiform,
+ Cuneiform_Numbers_and_Punctuation,
+ Egyptian_Hieroglyphs,
+ Bamum_Supplement,
+ Miao,
+ Kana_Supplement,
+ Byzantine_Musical_Symbols,
+ Musical_Symbols,
+ Ancient_Greek_Musical_Notation,
+ Tai_Xuan_Jing_Symbols,
+ Counting_Rod_Numerals,
+ Mathematical_Alphanumeric_Symbols,
+ Arabic_Mathematical_Alphabetic_Symbols,
+ Mahjong_Tiles,
+ Domino_Tiles,
+ Playing_Cards,
+ Enclosed_Alphanumeric_Supplement,
+ Enclosed_Ideographic_Supplement,
+ Miscellaneous_Symbols_And_Pictographs,
+ Emoticons,
+ Transport_And_Map_Symbols,
+ Alchemical_Symbols,
+ CJK_Unified_Ideographs_Extension_B,
+ CJK_Unified_Ideographs_Extension_C,
+ CJK_Unified_Ideographs_Extension_D,
+ CJK_Compatibility_Ideographs_Supplement,
+ Tags,
+ Variation_Selectors_Supplement,
+ Supplementary_Private_Use_Area_A,
+ Supplementary_Private_Use_Area_B
+};
+
+}}}
\ No newline at end of file
if(rect_.width() > 0 && rect_.height() > 0)
{
- img = std::make_shared<image8bit>(rect_.width(), rect_.height(), std::min<unsigned char>(channels_.size() - masks_, 4));
+ img = std::make_shared<image8bit>(rect_.width(), rect_.height(), 4);
//std::clog << std::dec << "has image: [width: " << rect_.width() << " height: " << rect_.height() << "]" << std::endl;
if(!has_transparency)
- std::memset(img->data(), (unsigned long)(255<<24), rect_.width()*rect_.height());
+ std::memset(img->data(), 255, img->width()*img->height()*img->channel_count());
}
if(masks_ > 0 && mask_.rect_.width() > 0 && mask_.rect_.height() > 0)
#include <core/frame/pixel_format.h>
#include <core/frame/frame_factory.h>
#include <core/producer/frame_producer.h>
+#include <core/producer/text/text_producer.h>
#include <core/producer/scene/scene_producer.h>
#include <core/producer/scene/const_producer.h>
#include <core/frame/draw_frame.h>
#include <common/env.h>
+#include <common/memory.h>
#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
namespace caspar { namespace psd {
core::register_producer_factory(create_producer);
}
+core::text::text_info get_text_info(const boost::property_tree::wptree& ptree)
+{
+ core::text::text_info result;
+ int font_index = ptree.get(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.Font", 0);
+ result.size = ptree.get(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.FontSize", 30.0f);
+
+ int child_index = 0;
+ auto color_node = ptree.get_child(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.FillColor.Values");
+ for(auto it = color_node.begin(); it != color_node.end(); ++it, ++child_index)
+ {
+ auto& value_node = (*it).second;
+ float value = value_node.get_value(0.0f);
+ switch(child_index)
+ {
+ case 0: result.color.a = value; break;
+ case 1: result.color.r = value; break;
+ case 2: result.color.g = value; break;
+ case 3: result.color.b = value; break;
+ }
+ }
+
+ //find fontname
+ child_index = 0;
+ auto fontset_node = ptree.get_child(L"ResourceDict.FontSet");
+ for(auto it = fontset_node.begin(); it != fontset_node.end(); ++it, ++child_index)
+ {
+ auto& font_node = (*it).second;
+ if(child_index == font_index)
+ {
+ result.font = font_node.get(L"Name", L"");
+ break;
+ }
+ }
+
+ return result;
+}
+
spl::shared_ptr<core::frame_producer> create_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const core::video_format_desc& format_desc, const std::vector<std::wstring>& params)
{
std::wstring filename = env::media_folder() + L"\\" + params[0] + L".psd";
auto layers_end = doc.layers().end();
for(auto it = doc.layers().begin(); it != layers_end; ++it)
{
- if((*it)->is_text())
+ if((*it)->is_text() && (*it)->visible())
{
+ std::wstring str = (*it)->text_data().get(L"EngineDict.Editor.Text", L"");
+
+ core::text::text_info text_info(std::move(get_text_info((*it)->text_data())));
+ auto layer_producer = spl::make_shared<core::text_producer>(frame_factory, 0, 0, str, text_info, doc.width(), doc.height());
+
+ core::text::string_metrics metrics = layer_producer->measure_string(str);
+
+ auto& new_layer = root->create_layer(layer_producer, (*it)->rect().left - 2, (*it)->rect().top + metrics.bearingY, (*it)->name()); //the 2 offset is just a hack for now. don't know why our text is rendered 2 px to the right of that in photoshop
+ new_layer.adjustments.opacity.set((*it)->opacity() / 255.0);
+ new_layer.hidden.set(!(*it)->visible());
}
- else if((*it)->image())
+ else if((*it)->image() && (*it)->visible())
{
core::pixel_format_desc pfd(core::pixel_format::bgra);
pfd.planes.push_back(core::pixel_format_desc::plane((*it)->rect().width(), (*it)->rect().height(), 4));
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
transform.image_transform.fill_translation[1] = y;
transform.image_transform.fill_scale[0] = x_s;
transform.image_transform.fill_scale[1] = y_s;
- transform.image_transform.clip_translation[0] = x;
- transform.image_transform.clip_translation[1] = y;
- transform.image_transform.clip_scale[0] = x_s;
- transform.image_transform.clip_scale[1] = y_s;
return transform;
}, duration, tween));
}
#include <core/producer/stage.h>
#include <core/producer/frame_producer.h>
#include <core/producer/scene/scene_producer.h>
+#include <core/producer/text/text_producer.h>
#include <core/consumer/output.h>
#include <modules/bluefish/bluefish.h>
CASPAR_LOG(info) << L"Initialized psd module.";
register_producer_factory(&core::scene::create_dummy_scene_producer);
+ register_producer_factory(&core::create_text_producer);
setup_channels(env::properties());
CASPAR_LOG(info) << L"Initialized channels.";
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)tmp\$(Configuration)\</IntDir>\r
<IncludePath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\dependencies64\boost\;..\dependencies64\ffmpeg\include\;..\dependencies64\glew\include;..\dependencies64\sfml\include\;..\dependencies64\tbb\include\;$(IncludePath)</IncludePath>\r
<IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\dependencies64\boost\;..\dependencies64\ffmpeg\include\;..\dependencies64\glew\include;..\dependencies64\sfml\include\;..\dependencies64\tbb\include\;$(IncludePath)</IncludePath>\r
- <LibraryPath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\dependencies64\boost\stage\lib;..\dependencies64\ffmpeg\lib\;..\dependencies64\glew\lib;..\dependencies64\sfml\lib\;..\dependencies64\tbb\lib\;..\dependencies64\freeimage\lib\;..\dependencies64\sfml\extlibs\lib\;..\dependencies64\openal\lib\;..\dependencies64\asmlib\;$(LibraryPath)</LibraryPath>\r
- <LibraryPath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\dependencies64\boost\stage\lib;..\dependencies64\ffmpeg\lib\;..\dependencies64\glew\lib;..\dependencies64\sfml\lib\;..\dependencies64\tbb\lib\;..\dependencies64\freeimage\lib\;..\dependencies64\sfml\extlibs\lib\;..\dependencies64\openal\lib\;..\dependencies64\asmlib\;$(LibraryPath)</LibraryPath>\r
+ <LibraryPath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\dependencies64\boost\stage\lib;..\dependencies64\ffmpeg\lib\;..\dependencies64\glew\lib;..\dependencies64\sfml\extlibs\lib;..\dependencies64\sfml\lib\;..\dependencies64\tbb\lib\;..\dependencies64\freeimage\lib\;..\dependencies64\openal\lib\;..\dependencies64\asmlib\;$(LibraryPath)</LibraryPath>\r
+ <LibraryPath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\dependencies64\boost\stage\lib;..\dependencies64\ffmpeg\lib\;..\dependencies64\glew\lib;..\dependencies64\sfml\lib\;..\dependencies64\tbb\lib\;..\dependencies64\freeimage\lib\;..\dependencies64\openal\lib\;..\dependencies64\asmlib\;..\dependencies64\freetype\objs\win32\vc2010;$(LibraryPath)</LibraryPath>\r
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)bin\$(Configuration)\</OutDir>\r
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)bin\$(Configuration)\</OutDir>\r
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionName)</TargetName>\r
<ForcedIncludeFiles>common/compiler/vs/disable_silly_warnings.h</ForcedIncludeFiles>\r
</ClCompile>\r
<Link>\r
- <AdditionalDependencies>alibcof64.lib;sfml-system-d.lib;sfml-window-d.lib;sfml-graphics-d.lib;sfml-audio-d.lib;Winmm.lib;Ws2_32.lib;avformat.lib;avcodec.lib;avutil.lib;avfilter.lib;swscale.lib;swresample.lib;tbb.lib;OpenGL32.lib;FreeImaged.lib;glew32.lib;freetype248_D.lib;openal32.lib</AdditionalDependencies>\r
+ <AdditionalDependencies>alibcof64.lib;freetype248_d.lib;sfml-system-d.lib;sfml-window-d.lib;sfml-graphics-d.lib;sfml-audio-d.lib;Winmm.lib;Ws2_32.lib;avformat.lib;avcodec.lib;avutil.lib;avfilter.lib;swscale.lib;swresample.lib;tbb.lib;OpenGL32.lib;FreeImaged.lib;glew32.lib;openal32.lib</AdditionalDependencies>\r
<Version>\r
</Version>\r
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
</ClCompile>\r
<Link>\r
<OptimizeReferences>true</OptimizeReferences>\r
- <AdditionalDependencies>alibcof64.lib;sfml-system.lib;sfml-window.lib;sfml-graphics.lib;Winmm.lib;Ws2_32.lib;avformat.lib;avcodec.lib;avutil.lib;avfilter.lib;swscale.lib;swresample.lib;tbb.lib;OpenGL32.lib;glew32.lib;freetype248.lib;openal32.lib;freeimage.lib</AdditionalDependencies>\r
+ <AdditionalDependencies>alibcof64.lib;freetype250.lib;sfml-system.lib;sfml-window.lib;sfml-graphics.lib;Winmm.lib;Ws2_32.lib;avformat.lib;avcodec.lib;avutil.lib;avfilter.lib;swscale.lib;swresample.lib;tbb.lib;OpenGL32.lib;glew32.lib;openal32.lib;freeimage.lib</AdditionalDependencies>\r
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
<GenerateDebugInformation>true</GenerateDebugInformation>\r
<ProgramDatabaseFile>$(TargetDir)$(TargetName).pdb</ProgramDatabaseFile>\r
#include <iostream>
#include <boost/property_tree/ptree.hpp>
-#include <boost/property_tree/json_parser.hpp>
+#include <boost/property_tree/detail/file_parser_error.hpp>
+#include <boost/property_tree/xml_parser.hpp>
int _tmain(int argc, _TCHAR* argv[])
{
caspar::psd::psd_document doc;
- doc.parse(L"C:\\Lokala Filer\\Utveckling\\CasparCG\\Server 2.1\\test\\data\\test1.psd");
-
- int a = 42;
-
- //std::wstringstream trace;
-
- //trace << L"<doc filename='" << doc.filename() << L"' color_mode='" << caspar::psd::color_mode_to_string(doc.color_mode()) << L"' color_depth='" << doc.color_depth() << L"' channel_count='" << doc.channels_count() << L"' width='" << doc.width() << L"' height='" << doc.height() << L"'>" << std::endl;
-
- //
- //auto end = doc.layers().end();
- //for(auto it = doc.layers().begin(); it != end; ++it)
- //{
- // caspar::psd::layer_ptr layer = (*it);
- // trace << L" <layer name='" << layer->name() << L"' opacity='" << layer->opacity() << L"'>" << std::endl;
- // if(layer->image())
- // trace << L" <bounding-box left='" << layer->rect().left << "' top='" << layer->rect().top << "' right='" << layer->rect().right << "' bottom='" << layer->rect().bottom << "' />" << std::endl;
- // if(layer->mask())
- // trace << L" <mask default-value='" << layer->default_mask_value() << "' left='" << layer->mask_rect().left << "' top='" << layer->mask_rect().top << "' right='" << layer->mask_rect().right << "' bottom='" << layer->mask_rect().bottom << "' />" << std::endl;
- // trace << L" </layer>" << std::endl;
- //}
-
- //trace << L"</doc>" << std::endl;
-
- //std::cout << caspar::u8(trace.str());
+ doc.parse(L"C:\\Lokala Filer\\Utveckling\\CasparCG\\Server 2.1\\shell\\media\\test1.psd");
+
+ std::wstringstream trace;
+
+ trace << L"<doc filename='" << doc.filename() << L"' color_mode='" << caspar::psd::color_mode_to_string(doc.color_mode()) << L"' color_depth='" << doc.color_depth() << L"' channel_count='" << doc.channels_count() << L"' width='" << doc.width() << L"' height='" << doc.height() << L"'>" << std::endl;
+
+ auto end = doc.layers().end();
+ for(auto it = doc.layers().begin(); it != end; ++it)
+ {
+ caspar::psd::layer_ptr layer = (*it);
+ trace << L" <layer name='" << layer->name() << L"' opacity='" << layer->opacity() << L"' flags='" << std::hex << layer->flags() << std::dec << "'>" << std::endl;
+ if(layer->image())
+ trace << L" <bounding-box left='" << layer->rect().left << "' top='" << layer->rect().top << "' right='" << layer->rect().right << "' bottom='" << layer->rect().bottom << "' />" << std::endl;
+ if(layer->mask())
+ trace << L" <mask default-value='" << layer->mask_info().default_value_ << "' flags='" << std::hex << (unsigned short) layer->mask_info().flags_ << std::dec << "' left='" << layer->mask_info().rect_.left << "' top='" << layer->mask_info().rect_.top << "' right='" << layer->mask_info().rect_.right << "' bottom='" << layer->mask_info().rect_.bottom << "' />" << std::endl;
+ if(layer->is_text())
+ {
+ boost::property_tree::xml_writer_settings<wchar_t> w(' ', 3);
+ boost::property_tree::write_xml(trace, (*it)->text_data(), w);
+ }
+ trace << L" </layer>" << std::endl;
+ }
+
+ trace << L"</doc>" << std::endl;
+
+ std::ofstream log("psd-log.txt");
+ log << caspar::u8(trace.str());
+ std::cout << caspar::u8(trace.str());
+
return 0;
}
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
- <LibraryPath>C:\Lokala Filer\Utveckling\CasparCG\Server 2.1\dependencies64\boost\stage\lib;$(LibraryPath)</LibraryPath>
- <IncludePath>C:\Lokala Filer\Utveckling\CasparCG\Server 2.1\dependencies64\boost;$(IncludePath)</IncludePath>
+ <LibraryPath>..\..\dependencies64\boost\stage\lib;$(LibraryPath)</LibraryPath>
+ <IncludePath>..\..\dependencies64\boost;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
+ <IncludePath>..\..\dependencies64\boost;$(IncludePath)</IncludePath>
+ <LibraryPath>..\..\dependencies64\boost\stage\lib;$(VCInstallDir)lib\amd64;$(VCInstallDir)atlmfc\lib\amd64;$(WindowsSdkDir)lib\x64;</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>