return layers_.back();
}
+ void reverse_layers() {
+ layers_.reverse();
+ }
+
void store_keyframe(void* timeline_identity, const keyframe& k)
{
timelines_[timeline_identity].keyframes.insert(std::make_pair(k.destination_frame, k));
return impl_->create_layer(producer, 0, 0, name);
}
+void scene_producer::reverse_layers() {
+ impl_->reverse_layers();
+}
+
binding<int64_t> scene_producer::frame()
{
return impl_->frame();
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, const std::wstring& name);
+ void reverse_layers();
+
binding<int64_t> frame();
binding<double> speed();
for(auto iter = directory_iterator(env::font_folder()), end = directory_iterator(); iter != end; ++iter)
{
- auto file = (*iter);
- if(is_regular_file(file.path()))
+ try
{
- auto face = get_new_face(u8(file.path().native()));
- const char* fontname = FT_Get_Postscript_Name(face.get()); //this doesn't work for .fon fonts. Ignoring those for now
- if(fontname != nullptr)
+ auto file = (*iter);
+ if (is_regular_file(file.path()))
{
- std::string fontname_str(fontname);
- result.insert(std::make_pair(u16(fontname_str), u16(file.path().native())));
+ auto face = get_new_face(u8(file.path().native()));
+ const char* fontname = FT_Get_Postscript_Name(face.get()); //this doesn't work for .fon fonts. Ignoring those for now
+ if (fontname != nullptr)
+ {
+ std::string fontname_str(fontname);
+ result.insert(std::make_pair(u16(fontname_str), u16(file.path().native())));
+ }
}
}
+ catch(...) { }
}
return result;
text::string_metrics metrics;
font_.set_tracking(static_cast<int>(tracking_.value().get()));
+
auto vertex_stream = font_.create_vertex_stream(text_.value().get(), x_, y_, parent_width_, parent_height_, &metrics);
auto frame = frame_factory_->create_frame(vertex_stream.data(), pfd, core::audio_channel_layout::invalid());
memcpy(frame.image_data().data(), atlas_.data(), frame.image_data().size());
size_t width_;
size_t height_;
size_t depth_;
-
+ const unsigned char* dataptr_;
std::vector<unsigned char> data_;
size_t used_ = 0;
public:
impl(const size_t width, const size_t height, const size_t depth) : width_(width), height_(height), depth_(depth), data_(width*height*depth, 0)
{
+ dataptr_ = data_.data();
// We want a one pixel border around the whole atlas to avoid any artefact when sampling texture
node n = {1, 1, static_cast<int>(width_) - 2};
nodes_.push_back(n);
namespace caspar { namespace psd {
-void layer::layer_mask_info::read_mask_data(bigendian_file_input_stream& stream)
+void layer::mask_info::read_mask_data(bigendian_file_input_stream& stream)
{
auto length = stream.read_long();
switch(length)
if(length == 36)
{
- stream.discard_bytes(18); //discard "total user mask" data
- //flags_ = stream.read_byte();
- //default_value_ = stream.read_byte();
- //rect_.location.y = stream.read_long();
- //rect_.location.x = stream.read_long();
- //rect_.size.height = stream.read_long() - rect_.location.y;
- //rect_.size.width = stream.read_long() - rect_.location.x;
+ //we save the information about the total mask in case the vector-mask has an unsupported shape
+ total_mask_.reset(new layer::mask_info);
+ total_mask_->flags_ = stream.read_byte();
+ total_mask_->default_value_ = stream.read_byte();
+ total_mask_->rect_.location.y = stream.read_long();
+ total_mask_->rect_.location.x = stream.read_long();
+ total_mask_->rect_.size.height = stream.read_long() - rect_.location.y;
+ total_mask_->rect_.size.width = stream.read_long() - rect_.location.x;
}
break;
};
}
+bool layer::vector_mask_info::populate(int length, bigendian_file_input_stream& stream, int doc_width, int doc_height)
+{
+ typedef std::pair<int, int> path_point;
+ bool bFail = false;
+
+ stream.read_long(); // version
+ this->flags_ = static_cast<std::uint8_t>(stream.read_long()); // flags
+ int path_records = (length - 8) / 26;
+
+ auto position = stream.current_position();
+
+ std::vector<path_point> knots;
+
+ const int SELECTOR_SIZE = 2;
+ const int PATH_POINT_SIZE = 4 + 4;
+ const int PATH_POINT_RECORD_SIZE = SELECTOR_SIZE + (3 * PATH_POINT_SIZE);
+
+ for (int i = 1; i <= path_records; ++i)
+ {
+ auto selector = stream.read_short();
+ if (selector == 2) //we only concern ourselves with closed paths
+ {
+ auto p_y = stream.read_long();
+ auto p_x = stream.read_long();
+ path_point cp_prev(p_x, p_y);
+
+ auto a_y = stream.read_long();
+ auto a_x = stream.read_long();
+ path_point anchor(a_x, a_y);
+
+ auto n_y = stream.read_long();
+ auto n_x = stream.read_long();
+ path_point cp_next(n_x, n_y);
+
+ if (anchor == cp_prev && anchor == cp_next)
+ knots.push_back(anchor);
+ else
+ { //we can't handle smooth curves yet
+ bFail = true;
+ }
+ }
+
+ auto offset = PATH_POINT_RECORD_SIZE * i;
+ stream.set_position(position + offset);
+ }
+
+ if ((knots.size() != 4) || (!(knots[0].first == knots[3].first && knots[1].first == knots[2].first && knots[0].second == knots[1].second && knots[2].second == knots[3].second)))
+ bFail = true;
+
+ if (bFail) {
+ rect_.clear();
+ flags_ = static_cast<std::uint8_t>(flags::unsupported);
+ }
+ else
+ {
+ //the path_points are given in fixed-point 8.24 as a ratio with regards to the width/height of the document. we need to divide by 16777215.0f to get the real ratio.
+ float x_ratio = doc_width / 16777215.0f;
+ float y_ratio = doc_height / 16777215.0f;
+ rect_.location.x = static_cast<int>(knots[0].first * x_ratio + 0.5f); //add .5 to get propper rounding when converting to integer
+ rect_.location.y = static_cast<int>(knots[0].second * y_ratio + 0.5f); //add .5 to get propper rounding when converting to integer
+ rect_.size.width = static_cast<int>(knots[1].first * x_ratio + 0.5f) - rect_.location.x; //add .5 to get propper rounding when converting to integer
+ rect_.size.height = static_cast<int>(knots[2].second * y_ratio + 0.5f) - rect_.location.y; //add .5 to get propper rounding when converting to integer
+ }
+
+ return !bFail;
+}
+
struct layer::impl
{
friend class layer;
- impl() : blend_mode_(blend_mode::InvalidBlendMode), link_group_id_(0), opacity_(255), sheet_color_(0), baseClipping_(false), flags_(0), protection_flags_(0), masks_count_(0), text_scale_(1.0f)
+ impl() : blend_mode_(blend_mode::InvalidBlendMode), layer_type_(layer_type::content), link_group_id_(0), opacity_(255), sheet_color_(0), baseClipping_(false), flags_(0), protection_flags_(0), masks_count_(0), text_scale_(1.0f)
{}
private:
std::vector<channel> channels_;
blend_mode blend_mode_;
+ layer_type layer_type_;
int link_group_id_;
int opacity_;
int sheet_color_;
int masks_count_;
double text_scale_;
- rect<int> vector_mask_;
- layer::layer_mask_info mask_;
+ layer::mask_info mask_;
rect<int> bitmap_rect_;
image8bit_ptr bitmap_;
read_solid_color(stream);
break;
+ case 'lsct': //group settings (folders)
+ read_group_settings(stream, length);
+
case 'lspf': //protection settings
protection_flags_ = stream.read_long();
break;
break;
case 'vmsk':
- read_vector_mask(length, stream, doc.width(), doc.height());
+ mask_.read_vector_mask_data(length, stream, doc.width(), doc.height());
break;
case 'tmln':
solid_color_.alpha = 255;
}
- void read_vector_mask(int length, bigendian_file_input_stream& stream, int doc_width, int doc_height)
+ void read_group_settings(bigendian_file_input_stream& stream, unsigned int length)
{
- typedef std::pair<int, int> path_point;
-
- stream.read_long(); // version
- stream.read_long(); // flags
- int path_records = (length-8) / 26;
-
- auto position = stream.current_position();
+ auto type = stream.read_long();
+ unsigned int sub_type = 0;
- std::vector<path_point> knots;
-
- const int SELECTOR_SIZE = 2;
- const int PATH_POINT_SIZE = 4 + 4;
- const int PATH_POINT_RECORD_SIZE = SELECTOR_SIZE + (3 * PATH_POINT_SIZE);
-
- for (int i = 1; i <= path_records; ++i)
+ if (length >= 12)
{
- auto selector = stream.read_short();
- if(selector == 2) //we only concern ourselves with closed paths
- {
- auto p_y = stream.read_long();
- auto p_x = stream.read_long();
- path_point cp_prev(p_x, p_y);
-
- auto a_y = stream.read_long();
- auto a_x = stream.read_long();
- path_point anchor(a_x, a_y);
-
- auto n_y = stream.read_long();
- auto n_x = stream.read_long();
- path_point cp_next(n_x, n_y);
-
- if(anchor == cp_prev && anchor == cp_next)
- knots.push_back(anchor);
- else
- { //we can't handle smooth curves yet
- CASPAR_THROW_EXCEPTION(psd_file_format_exception() << msg_info("we can't handle smooth curves yet"));
- }
- }
-
- auto offset = PATH_POINT_RECORD_SIZE * i;
- stream.set_position(position + offset);
+ auto signature = stream.read_long();
+ if (signature != '8BIM')
+ CASPAR_THROW_EXCEPTION(psd_file_format_exception() << msg_info("signature != '8BIM'"));
+
+ blend_mode_ = int_to_blend_mode(stream.read_long());
+
+ if (length >= 16)
+ sub_type = stream.read_long();
}
- if(knots.size() != 4) //we only support rectangular vector masks
- CASPAR_THROW_EXCEPTION(psd_file_format_exception() << msg_info("we only support rectangular vector masks"));
-
- //we only support rectangular vector masks
- if(!(knots[0].first == knots[3].first && knots[1].first == knots[2].first && knots[0].second == knots[1].second && knots[2].second == knots[3].second))
- CASPAR_THROW_EXCEPTION(psd_file_format_exception() << msg_info("we only support rectangular vector masks"));
-
- //the path_points are given in fixed-point 8.24 as a ratio with regards to the width/height of the document. we need to divide by 16777215.0f to get the real ratio.
- float x_ratio = doc_width / 16777215.0f;
- float y_ratio = doc_height / 16777215.0f;
- vector_mask_.location.x = static_cast<int>(knots[0].first * x_ratio +0.5f); //add .5 to get propper rounding when converting to integer
- vector_mask_.location.y = static_cast<int>(knots[0].second * y_ratio +0.5f); //add .5 to get propper rounding when converting to integer
- vector_mask_.size.width = static_cast<int>(knots[1].first * x_ratio +0.5f) - vector_mask_.location.x; //add .5 to get propper rounding when converting to integer
- vector_mask_.size.height = static_cast<int>(knots[2].second * y_ratio +0.5f) - vector_mask_.location.y; //add .5 to get propper rounding when converting to integer
+ layer_type_ = int_to_layer_type(type, sub_type);
}
void read_metadata(bigendian_file_input_stream& stream, const psd_document& doc)
void read_channel_data(bigendian_file_input_stream& stream)
{
image8bit_ptr bitmap;
- image8bit_ptr mask;
bool has_transparency = has_channel(channel_type::transparency);
- rect<int> clip_rect;
if(!bitmap_rect_.empty())
{
- clip_rect = bitmap_rect_;
-
- if(!vector_mask_.empty())
- clip_rect = vector_mask_;
-
- bitmap = std::make_shared<image8bit>(clip_rect.size.width, clip_rect.size.height, 4);
-
+ bitmap = std::make_shared<image8bit>(bitmap_rect_.size.width, bitmap_rect_.size.height, 4);
if(!has_transparency)
std::memset(bitmap->data(), 255, bitmap->width()*bitmap->height()*bitmap->channel_count());
}
- if(masks_count_ > 0 && !mask_.rect_.empty())
- {
- mask = std::make_shared<image8bit>(mask_.rect_.size.width, mask_.rect_.size.height, 1);
- }
-
for(auto it = channels_.begin(); it != channels_.end(); ++it)
{
- psd::rect<int> src_rect;
+ auto channel = (*it);
image8bit_ptr target;
int offset = 0;
bool discard_channel = false;
//determine target bitmap and offset
- if((*it).id >= 3)
+ if(channel.id >= 3)
discard_channel = true; //discard channels that doesn't contribute to the final image
- else if((*it).id >= -1) //BGRA-data
+ else if(channel.id >= -1) //BGRA-data
{
target = bitmap;
- offset = ((*it).id >= 0) ? 2 - (*it).id : 3;
- src_rect = bitmap_rect_;
+ offset = (channel.id >= 0) ? 2 - channel.id : 3;
}
- else if(mask) //mask
+ else //mask
{
- if((*it).id == -3) //discard the "total user mask"
- discard_channel = true;
- else
+ offset = 0;
+ if (channel.id == -2)
{
- target = mask;
+ mask_.create_bitmap();
+ target = mask_.bitmap_;
+ }
+ else if (channel.id == -3) //total_mask
+ {
+ mask_.total_mask_->create_bitmap();
+ target = mask_.total_mask_->bitmap_;
offset = 0;
- src_rect = mask_.rect_;
}
}
- if(!target || src_rect.empty())
+ if(!target)
discard_channel = true;
- auto end_of_data = stream.current_position() + (*it).data_length;
+ auto end_of_data = stream.current_position() + channel.data_length;
if(!discard_channel)
{
auto encoding = stream.read_short();
if(target)
{
if(encoding == 0)
- read_raw_image_data(stream, (*it).data_length-2, target, offset);
+ read_raw_image_data(stream, channel.data_length-2, target, offset);
else if(encoding == 1)
- read_rle_image_data(stream, src_rect, (target == bitmap) ? clip_rect : mask_.rect_, target, offset);
+ read_rle_image_data(stream, target, offset);
else
CASPAR_THROW_EXCEPTION(psd_file_format_exception() << msg_info("Unhandled image data encoding: " + boost::lexical_cast<std::string>(encoding)));
}
}
bitmap_ = bitmap;
- bitmap_rect_ = clip_rect;
- mask_.bitmap_ = mask;
}
void read_raw_image_data(bigendian_file_input_stream& stream, int data_length, image8bit_ptr target, int offset)
}
}
- void read_rle_image_data(bigendian_file_input_stream& stream, const rect<int>&src_rect, const rect<int>&clip_rect, image8bit_ptr target, int offset)
+ void read_rle_image_data(bigendian_file_input_stream& stream, image8bit_ptr target, int offset)
{
- auto width = src_rect.size.width;
- auto height = src_rect.size.height;
+ auto width = target->width();
+ auto height = target->height();
auto stride = target->channel_count();
- int offset_x = clip_rect.location.x - src_rect.location.x;
- int offset_y = clip_rect.location.y - src_rect.location.y;
-
std::vector<int> scanline_lengths;
scanline_lengths.reserve(height);
for(int scanlineIndex=0; scanlineIndex < height; ++scanlineIndex)
{
- if(scanlineIndex >= target->height()+offset_y)
- break;
-
int colIndex = 0;
do
while(colIndex < width);
//use line to populate target
- if(scanlineIndex >= offset_y)
- for(int index = offset_x; index < target->width(); ++index)
- {
- target_data[((scanlineIndex-offset_y)*target->width()+index-offset_x) * stride + offset] = line[index];
- }
+ for(int index = 0; index < width; ++index)
+ {
+ target_data[(scanlineIndex*width+index) * stride + offset] = line[index];
+ }
}
}
};
color<std::uint8_t> layer::solid_color() const { return impl_->solid_color_; }
const point<int>& layer::location() const { return impl_->bitmap_rect_.location; }
+const size<int>& layer::size() const { return impl_->bitmap_rect_.size; }
const image8bit_ptr& layer::bitmap() const { return impl_->bitmap_; }
+layer_type layer::group_mode() const { return impl_->layer_type_; }
int layer::link_group_id() const { return impl_->link_group_id_; }
void layer::set_link_group_id(int id) { impl_->link_group_id_ = id; }
spl::shared_ptr<impl> impl_;
public:
- class layer_mask_info
+ class mask_info;
+
+ class vector_mask_info
+ {
+ enum class flags {
+ inverted = 1,
+ unlinked = 2,
+ disabled = 4,
+ unsupported = 128
+ };
+
+ std::uint8_t flags_;
+ rect<int> rect_;
+
+ friend class layer::mask_info;
+ bool populate(int length, bigendian_file_input_stream& stream, int doc_width, int doc_height);
+
+ public:
+ vector_mask_info() : flags_(0)
+ {}
+
+ bool enabled() const { return (flags_ & static_cast<std::uint8_t>(flags::disabled)) == 0; }
+ bool linked() const { return (flags_ & static_cast<std::uint8_t>(flags::unlinked)) == 0; }
+ bool inverted() const { return (flags_ & static_cast<std::uint8_t>(flags::inverted)) == static_cast<std::uint8_t>(flags::inverted); }
+ bool unsupported() const { return (flags_ & static_cast<std::uint8_t>(flags::unsupported)) == static_cast<std::uint8_t>(flags::unsupported); }
+
+ bool empty() { return rect_.empty(); }
+
+ const rect<int>& rect() const { return rect_; }
+ };
+
+ class mask_info
{
friend struct layer::impl;
+ friend class layer::vector_mask_info;
void read_mask_data(bigendian_file_input_stream&);
+ void read_vector_mask_data(int length, bigendian_file_input_stream& stream, int doc_width, int doc_height)
+ {
+ vector_mask_.reset(new vector_mask_info);
+ vector_mask_->populate(length, stream, doc_width, doc_height);
+ }
image8bit_ptr bitmap_;
std::uint8_t default_value_;
std::uint8_t flags_;
- char mask_id_;
rect<int> rect_;
+ std::unique_ptr<vector_mask_info> vector_mask_;
+ std::unique_ptr<mask_info> total_mask_;
+
+ void create_bitmap() {
+ bitmap_ = std::make_shared<image8bit>(rect_.size.width, rect_.size.height, 1);
+ }
+
public:
+ mask_info() : default_value_(0), flags_(0)
+ {}
+
bool enabled() const { return (flags_ & 2) == 0; }
bool linked() const { return (flags_ & 1) == 0; }
bool inverted() const { return (flags_ & 4) == 4; }
- const point<int>& location() const { return rect_.location; }
+ bool empty() const { return rect_.empty(); }
+
+ bool has_vector() const { return (vector_mask_ && !vector_mask_->empty() && vector_mask_->enabled()); }
+ bool has_bitmap() const { return (!vector_mask_ && !empty()) || (vector_mask_ && total_mask_); }
+ const std::unique_ptr<vector_mask_info>& vector() { return vector_mask_; }
+
+ const rect<int>& rect() const { return rect_; }
const image8bit_ptr& bitmap() const { return bitmap_; }
};
const boost::property_tree::wptree& timeline_data() const;
const point<int>& location() const;
+ const size<int>& size() const;
const image8bit_ptr& bitmap() const;
+ layer_type group_mode() const;
+
int link_group_id() const;
void set_link_group_id(int id);
};
namespace caspar { namespace psd {
+layer_type int_to_layer_type(std::uint32_t x, std::uint32_t y)
+{
+ if (x == 1 || x == 2)
+ return (y == 1) ? layer_type::timeline_group : layer_type::group;
+ else if (x == 3)
+ return layer_type::group_delimiter;
+
+ return layer_type::content;
+}
+
+std::wstring layer_type_to_string(layer_type b)
+{
+ switch (b)
+ {
+ case layer_type::content: return L"Content";
+ case layer_type::group: return L"Group";
+ case layer_type::timeline_group: return L"TimelineGroup";
+ case layer_type::group_delimiter: return L"GroupDelimiter";
+ default: return L"Invalid";
+ }
+}
+
+
blend_mode int_to_blend_mode(std::uint32_t x)
{
blend_mode mode = static_cast<blend_mode>(x);
point(T x1, T y1) : x(x1), y(y1) {}
T x;
T y;
+
+ void clear() { x = 0; y = 0; }
};
template<typename T>
size(T w, T h) : width(w), height(h) {}
T width;
T height;
+
+ void clear() { width = 0; height = 0; }
};
template<typename T>
psd::size<T> size;
bool empty() const { return size.width == 0 || size.height == 0; }
+ void clear() { location.clear(); size.clear(); }
};
struct psd_file_format_exception : virtual caspar_exception {};
color_blue = 2
};
+enum class layer_type
+{
+ content = 0,
+ group,
+ timeline_group,
+ group_delimiter
+};
+layer_type int_to_layer_type(std::uint32_t x, std::uint32_t y);
+std::wstring layer_type_to_string(layer_type b);
+
enum class blend_mode
{
InvalidBlendMode = -1,
std::wstring name = input_.read_pascal_string(2);
auto resource_length = input_.read_long();
- auto end_of_chunk = input_.current_position() + resource_length;
+ auto end_of_chunk = input_.current_position() + resource_length + resource_length%2; //the actual data is padded to even bytes
try
{
timeline_desc_.swap(timeline_descriptor.items());
}
break;
+ case 1072: //layer group(s) enabled id
+ {
+
+ }
+ break;
case 1005:
{
case 1060: //XMP metadata
case 1065: //layer comps
case 1069: //layer selection ID(s)
- case 1072: //layer group(s) enabled id
case 1077: //DisplayInfo
case 2999: //name of clipping path
default:
auto layer_info_length = input_.read_long(); //length of "Layer info" section
auto end_of_layers_info = input_.current_position() + layer_info_length;
- int layers_count = std::abs(static_cast<std::int16_t>(input_.read_short()));
+ int layers_count = static_cast<std::int16_t>(input_.read_short());
+ //bool has_merged_alpha = (layers_count < 0);
+ layers_count = std::abs(layers_count);
//std::clog << "Expecting " << layers_count << " layers" << std::endl;
for(int layer_index = 0; layer_index < layers_count; ++layer_index)
std::vector<std::pair<std::wstring, spl::shared_ptr<core::text_producer>>> text_producers_by_layer_name;
- auto layers_end = doc.layers().end();
- for(auto it = doc.layers().begin(); it != layers_end; ++it)
+ std::stack<spl::shared_ptr<core::scene::scene_producer>> current_scene_stack;
+ current_scene_stack.push(root);
+
+ auto layers_end = doc.layers().rend();
+ for(auto it = doc.layers().rbegin(); it != layers_end; ++it)
{
- if((*it)->is_visible())
+ auto psd_layer = (*it);
+ auto current_scene = current_scene_stack.top();
+
+ if (psd_layer->group_mode() == layer_type::group) {
+ auto group = spl::make_shared<core::scene::scene_producer>(psd_layer->name(), doc.width(), doc.height(), dependencies.format_desc);
+
+ auto& scene_layer = current_scene->create_layer(group, psd_layer->location().x, psd_layer->location().y, psd_layer->name());
+ scene_layer.adjustments.opacity.set(psd_layer->opacity() / 255.0);
+ scene_layer.hidden.set(!psd_layer->is_visible());
+
+ if (psd_layer->has_timeline())
+ create_timelines(current_scene, dependencies.format_desc, scene_layer, psd_layer, 0, 0);
+
+ if (psd_layer->link_group_id() != 0)
+ link_constructor.add(&scene_layer, psd_layer->link_group_id(), psd_layer->is_position_protected(), 0, 0);
+
+ current_scene_stack.push(group);
+ continue;
+ }
+ else if (psd_layer->group_mode() == layer_type::group_delimiter) {
+ current_scene->reverse_layers();
+ current_scene_stack.pop();
+ continue;
+ }
+
+ if(psd_layer->is_visible())
{
- bool is_text = (*it)->is_text();
- bool is_marked_as_dynamic = (*it)->sheet_color() == 4;
- std::wstring layer_name = (*it)->name();
+ bool is_text = psd_layer->is_text();
+ bool is_marked_as_dynamic = psd_layer->sheet_color() != 1;
+ std::wstring layer_name = psd_layer->name();
+
if(is_text && is_marked_as_dynamic)
{
- std::wstring str = (*it)->text_data().get(L"EngineDict.Editor.Text", L"");
+ std::wstring str = psd_layer->text_data().get(L"EngineDict.Editor.Text", L"");
- core::text::text_info text_info(std::move(get_text_info((*it)->text_data())));
- text_info.size *= (*it)->text_scale();
+ core::text::text_info text_info(std::move(get_text_info(psd_layer->text_data())));
+ text_info.size *= psd_layer->text_scale();
auto text_producer = core::text_producer::create(dependencies.frame_factory, 0, 0, str, text_info, doc.width(), doc.height());
-
+ text_producer->pixel_constraints().width.set(psd_layer->size().width);
+ text_producer->pixel_constraints().height.set(psd_layer->size().height);
core::text::string_metrics metrics = text_producer->measure_string(str);
auto adjustment_x = -2; //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
auto adjustment_y = metrics.bearingY;
- auto& new_layer = root->create_layer(text_producer, (*it)->location().x + adjustment_x, (*it)->location().y + adjustment_y, (*it)->name());
- new_layer.adjustments.opacity.set((*it)->opacity() / 255.0);
- new_layer.hidden.set(!(*it)->is_visible());
+ auto& scene_layer = current_scene->create_layer(text_producer, psd_layer->location().x + adjustment_x, psd_layer->location().y + adjustment_y, psd_layer->name());
+ scene_layer.adjustments.opacity.set(psd_layer->opacity() / 255.0);
+ scene_layer.hidden.set(!psd_layer->is_visible());
- if ((*it)->has_timeline())
- create_timelines(root, dependencies.format_desc, new_layer, (*it), adjustment_x, adjustment_y);
+ if (psd_layer->has_timeline())
+ create_timelines(current_scene, dependencies.format_desc, scene_layer, psd_layer, adjustment_x, adjustment_y);
- if((*it)->link_group_id() != 0)
- link_constructor.add(&new_layer, (*it)->link_group_id(), (*it)->is_position_protected(), -adjustment_x, -adjustment_y);
+ if(psd_layer->link_group_id() != 0)
+ link_constructor.add(&scene_layer, psd_layer->link_group_id(), psd_layer->is_position_protected(), -adjustment_x, -adjustment_y);
text_producers_by_layer_name.push_back(std::make_pair(layer_name, text_producer));
}
else
{
std::shared_ptr<core::frame_producer> layer_producer;
- if((*it)->is_solid())
+ if(psd_layer->is_solid())
{
- layer_producer = core::create_const_producer(core::create_color_frame(it->get(), dependencies.frame_factory, (*it)->solid_color().to_uint32()), (*it)->bitmap()->width(), (*it)->bitmap()->height());
+ layer_producer = core::create_const_producer(core::create_color_frame(it->get(), dependencies.frame_factory, psd_layer->solid_color().to_uint32()), psd_layer->bitmap()->width(), psd_layer->bitmap()->height());
}
- else if((*it)->bitmap())
+ else if(psd_layer->bitmap())
{
if (boost::algorithm::istarts_with(layer_name, L"[producer]"))
{
- auto hotswap = std::make_shared<core::hotswap_producer>((*it)->bitmap()->width(), (*it)->bitmap()->height());
+ auto hotswap = std::make_shared<core::hotswap_producer>(psd_layer->bitmap()->width(), psd_layer->bitmap()->height());
hotswap->producer().set(dependencies.producer_registry->create_producer(dependencies, layer_name.substr(10)));
layer_producer = hotswap;
}
else
{
core::pixel_format_desc pfd(core::pixel_format::bgra);
- pfd.planes.push_back(core::pixel_format_desc::plane((*it)->bitmap()->width(), (*it)->bitmap()->height(), 4));
+ pfd.planes.push_back(core::pixel_format_desc::plane(psd_layer->bitmap()->width(), psd_layer->bitmap()->height(), 4));
auto frame = dependencies.frame_factory->create_frame(it->get(), pfd, core::audio_channel_layout::invalid());
auto destination = frame.image_data().data();
- auto source = (*it)->bitmap()->data();
+ auto source = psd_layer->bitmap()->data();
memcpy(destination, source, frame.image_data().size());
- layer_producer = core::create_const_producer(core::draw_frame(std::move(frame)), (*it)->bitmap()->width(), (*it)->bitmap()->height());
+ layer_producer = core::create_const_producer(core::draw_frame(std::move(frame)), psd_layer->bitmap()->width(), psd_layer->bitmap()->height());
}
}
if(layer_producer)
{
- auto& new_layer = root->create_layer(spl::make_shared_ptr(layer_producer), (*it)->location().x, (*it)->location().y, (*it)->name());
- new_layer.adjustments.opacity.set((*it)->opacity() / 255.0);
- new_layer.hidden.set(!(*it)->is_visible());
+ auto& scene_layer = current_scene->create_layer(spl::make_shared_ptr(layer_producer), psd_layer->location().x, psd_layer->location().y, psd_layer->name());
+ scene_layer.adjustments.opacity.set(psd_layer->opacity() / 255.0);
+ scene_layer.hidden.set(!psd_layer->is_visible());
- if ((*it)->has_timeline())
- create_timelines(root, dependencies.format_desc, new_layer, (*it), 0, 0);
+ if (psd_layer->has_timeline())
+ create_timelines(current_scene, dependencies.format_desc, scene_layer, psd_layer, 0, 0);
- if((*it)->link_group_id() != 0)
- link_constructor.add(&new_layer, (*it)->link_group_id(), (*it)->is_position_protected(), 0, 0);
+ if(psd_layer->link_group_id() != 0)
+ link_constructor.add(&scene_layer, psd_layer->link_group_id(), psd_layer->is_position_protected(), 0, 0);
}
}
}
}
+ root->reverse_layers();
link_constructor.calculate();