From: Niklas P Andersson Date: Thu, 26 Nov 2015 09:38:01 +0000 (+0100) Subject: * don't throw on not finding a font file, just ignore that font X-Git-Tag: 2.1.0_Beta1~162 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;ds=sidebyside;h=ee8eff50a67b50440cb35f00e6675457e8ff6d25;p=casparcg * don't throw on not finding a font file, just ignore that font * handle groups in psd * improved handling of masks in psd --- diff --git a/core/producer/scene/scene_producer.cpp b/core/producer/scene/scene_producer.cpp index 6fc01cc23..b684f372a 100644 --- a/core/producer/scene/scene_producer.cpp +++ b/core/producer/scene/scene_producer.cpp @@ -162,6 +162,10 @@ struct scene_producer::impl 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)); @@ -563,6 +567,10 @@ layer& scene_producer::create_layer( return impl_->create_layer(producer, 0, 0, name); } +void scene_producer::reverse_layers() { + impl_->reverse_layers(); +} + binding scene_producer::frame() { return impl_->frame(); diff --git a/core/producer/scene/scene_producer.h b/core/producer/scene/scene_producer.h index d86cbe1e8..074bc97a9 100644 --- a/core/producer/scene/scene_producer.h +++ b/core/producer/scene/scene_producer.h @@ -128,6 +128,8 @@ public: const spl::shared_ptr& producer, int x, int y, const std::wstring& name); layer& create_layer( const spl::shared_ptr& producer, const std::wstring& name); + void reverse_layers(); + binding frame(); binding speed(); diff --git a/core/producer/text/text_producer.cpp b/core/producer/text/text_producer.cpp index 8b1b0a707..58bfa5598 100644 --- a/core/producer/text/text_producer.cpp +++ b/core/producer/text/text_producer.cpp @@ -77,17 +77,21 @@ std::map enumerate_fonts() 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; @@ -181,6 +185,7 @@ public: text::string_metrics metrics; font_.set_tracking(static_cast(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()); diff --git a/core/producer/text/utils/texture_atlas.cpp b/core/producer/text/utils/texture_atlas.cpp index f56dbb91f..0622d5858 100644 --- a/core/producer/text/utils/texture_atlas.cpp +++ b/core/producer/text/utils/texture_atlas.cpp @@ -57,13 +57,14 @@ private: size_t width_; size_t height_; size_t depth_; - + const unsigned char* dataptr_; std::vector 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(width_) - 2}; nodes_.push_back(n); diff --git a/modules/psd/layer.cpp b/modules/psd/layer.cpp index 7ea63afd8..c866927f6 100644 --- a/modules/psd/layer.cpp +++ b/modules/psd/layer.cpp @@ -36,7 +36,7 @@ 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) @@ -57,13 +57,14 @@ void layer::layer_mask_info::read_mask_data(bigendian_file_input_stream& stream) 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; @@ -74,16 +75,84 @@ void layer::layer_mask_info::read_mask_data(bigendian_file_input_stream& stream) }; } +bool layer::vector_mask_info::populate(int length, bigendian_file_input_stream& stream, int doc_width, int doc_height) +{ + typedef std::pair path_point; + bool bFail = false; + + stream.read_long(); // version + this->flags_ = static_cast(stream.read_long()); // flags + int path_records = (length - 8) / 26; + + auto position = stream.current_position(); + + std::vector 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(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(knots[0].first * x_ratio + 0.5f); //add .5 to get propper rounding when converting to integer + rect_.location.y = static_cast(knots[0].second * y_ratio + 0.5f); //add .5 to get propper rounding when converting to integer + rect_.size.width = static_cast(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(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 channels_; blend_mode blend_mode_; + layer_type layer_type_; int link_group_id_; int opacity_; int sheet_color_; @@ -94,8 +163,7 @@ private: int masks_count_; double text_scale_; - rect vector_mask_; - layer::layer_mask_info mask_; + layer::mask_info mask_; rect bitmap_rect_; image8bit_ptr bitmap_; @@ -179,6 +247,9 @@ public: 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; @@ -211,7 +282,7 @@ public: 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': @@ -244,65 +315,24 @@ public: 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 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 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(knots[0].first * x_ratio +0.5f); //add .5 to get propper rounding when converting to integer - vector_mask_.location.y = static_cast(knots[0].second * y_ratio +0.5f); //add .5 to get propper rounding when converting to integer - vector_mask_.size.width = static_cast(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(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) @@ -386,70 +416,60 @@ public: void read_channel_data(bigendian_file_input_stream& stream) { image8bit_ptr bitmap; - image8bit_ptr mask; bool has_transparency = has_channel(channel_type::transparency); - rect clip_rect; if(!bitmap_rect_.empty()) { - clip_rect = bitmap_rect_; - - if(!vector_mask_.empty()) - clip_rect = vector_mask_; - - bitmap = std::make_shared(clip_rect.size.width, clip_rect.size.height, 4); - + bitmap = std::make_shared(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(mask_.rect_.size.width, mask_.rect_.size.height, 1); - } - for(auto it = channels_.begin(); it != channels_.end(); ++it) { - psd::rect 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(encoding))); } @@ -464,8 +484,6 @@ public: } 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) @@ -486,15 +504,12 @@ public: } } - void read_rle_image_data(bigendian_file_input_stream& stream, const rect&src_rect, const rect&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 scanline_lengths; scanline_lengths.reserve(height); @@ -507,9 +522,6 @@ public: for(int scanlineIndex=0; scanlineIndex < height; ++scanlineIndex) { - if(scanlineIndex >= target->height()+offset_y) - break; - int colIndex = 0; do @@ -539,11 +551,10 @@ public: 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]; + } } } }; @@ -571,8 +582,10 @@ bool layer::is_solid() const { return impl_->solid_color_.alpha != 0; } color layer::solid_color() const { return impl_->solid_color_; } const point& layer::location() const { return impl_->bitmap_rect_.location; } +const size& 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; } diff --git a/modules/psd/layer.h b/modules/psd/layer.h index 33a3422e6..74edc0cd1 100644 --- a/modules/psd/layer.h +++ b/modules/psd/layer.h @@ -44,24 +44,76 @@ class layer spl::shared_ptr 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 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(flags::disabled)) == 0; } + bool linked() const { return (flags_ & static_cast(flags::unlinked)) == 0; } + bool inverted() const { return (flags_ & static_cast(flags::inverted)) == static_cast(flags::inverted); } + bool unsupported() const { return (flags_ & static_cast(flags::unsupported)) == static_cast(flags::unsupported); } + + bool empty() { return rect_.empty(); } + + const rect& 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 rect_; + std::unique_ptr vector_mask_; + std::unique_ptr total_mask_; + + void create_bitmap() { + bitmap_ = std::make_shared(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& 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() { return vector_mask_; } + + const rect& rect() const { return rect_; } const image8bit_ptr& bitmap() const { return bitmap_; } }; @@ -87,8 +139,11 @@ public: const boost::property_tree::wptree& timeline_data() const; const point& location() const; + const size& size() const; const image8bit_ptr& bitmap() const; + layer_type group_mode() const; + int link_group_id() const; void set_link_group_id(int id); }; diff --git a/modules/psd/misc.cpp b/modules/psd/misc.cpp index 54c392336..5ea9a8f45 100644 --- a/modules/psd/misc.cpp +++ b/modules/psd/misc.cpp @@ -24,6 +24,29 @@ 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(x); diff --git a/modules/psd/misc.h b/modules/psd/misc.h index f451fef30..86496bc6f 100644 --- a/modules/psd/misc.h +++ b/modules/psd/misc.h @@ -35,6 +35,8 @@ struct point point(T x1, T y1) : x(x1), y(y1) {} T x; T y; + + void clear() { x = 0; y = 0; } }; template @@ -58,6 +60,8 @@ struct size size(T w, T h) : width(w), height(h) {} T width; T height; + + void clear() { width = 0; height = 0; } }; template @@ -67,6 +71,7 @@ struct rect psd::size 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 {}; @@ -81,6 +86,16 @@ enum class channel_type 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, diff --git a/modules/psd/psd_document.cpp b/modules/psd/psd_document.cpp index 0dac03eca..bc467299e 100644 --- a/modules/psd/psd_document.cpp +++ b/modules/psd/psd_document.cpp @@ -85,7 +85,7 @@ void psd_document::read_image_resources() 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 { @@ -117,6 +117,11 @@ void psd_document::read_image_resources() timeline_desc_.swap(timeline_descriptor.items()); } break; + case 1072: //layer group(s) enabled id + { + + } + break; case 1005: { @@ -143,7 +148,6 @@ void psd_document::read_image_resources() 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: @@ -190,7 +194,9 @@ void psd_document::read_layers() 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(input_.read_short())); + int layers_count = static_cast(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) diff --git a/modules/psd/psd_scene_producer.cpp b/modules/psd/psd_scene_producer.cpp index df956699c..704017ba5 100644 --- a/modules/psd/psd_scene_producer.cpp +++ b/modules/psd/psd_scene_producer.cpp @@ -369,83 +369,114 @@ spl::shared_ptr create_psd_scene_producer(const core::fram std::vector>> text_producers_by_layer_name; - auto layers_end = doc.layers().end(); - for(auto it = doc.layers().begin(); it != layers_end; ++it) + std::stack> 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(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 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((*it)->bitmap()->width(), (*it)->bitmap()->height()); + auto hotswap = std::make_shared(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();