]> git.sesse.net Git - casparcg/commitdiff
* don't throw on not finding a font file, just ignore that font
authorNiklas P Andersson <niklas.p.andersson@svt.se>
Thu, 26 Nov 2015 09:38:01 +0000 (10:38 +0100)
committerNiklas P Andersson <niklas.p.andersson@svt.se>
Tue, 1 Dec 2015 14:39:22 +0000 (15:39 +0100)
* handle groups in psd
* improved handling of masks in psd

core/producer/scene/scene_producer.cpp
core/producer/scene/scene_producer.h
core/producer/text/text_producer.cpp
core/producer/text/utils/texture_atlas.cpp
modules/psd/layer.cpp
modules/psd/layer.h
modules/psd/misc.cpp
modules/psd/misc.h
modules/psd/psd_document.cpp
modules/psd/psd_scene_producer.cpp

index 6fc01cc231f0235f627d5c852093ac0246cb1088..b684f372abf918973487da346052c3fce56313d6 100644 (file)
@@ -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<int64_t> scene_producer::frame()
 {
        return impl_->frame();
index d86cbe1e8f0c24fd93005bb2287cf2c831cade00..074bc97a9f965be5ab34441dc2386d4b9b657b82 100644 (file)
@@ -128,6 +128,8 @@ public:
                        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();
 
index 8b1b0a7079a9bae8c69a2fe62fd6f1774645c5ec..58bfa55988ad6a6c587f8122956b82b73408b4e6 100644 (file)
@@ -77,17 +77,21 @@ std::map<std::wstring, std::wstring> 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<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());
index f56dbb91f95b324e3cfb6460427ab49572532f62..0622d58581a81ffd615f4b1e1d77061c106b2900 100644 (file)
@@ -57,13 +57,14 @@ private:
        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);
index 7ea63afd83a9c0899ca9e1f7301bbcb4fab40951..c866927f66f55e40f0c3fb1b39faa238df508857 100644 (file)
@@ -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<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_;
@@ -94,8 +163,7 @@ private:
        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_;
@@ -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<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)
@@ -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<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)));
                                }
@@ -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<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);
 
@@ -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<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; }
 
index 33a3422e678900f6ede390344162c7e3e37b90ab..74edc0cd1da6db519828c7f43b3e3887f52855ba 100644 (file)
@@ -44,24 +44,76 @@ class layer
        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_; }
        };
 
@@ -87,8 +139,11 @@ public:
        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);
 };
index 54c3923368cb8615a78305c66a9f1c189c11abe5..5ea9a8f45da8ed21ecd19047baf44993089fe9a8 100644 (file)
 
 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);
index f451fef300fcc0081eea80655c235eba5e2471f7..86496bc6ff935c8489f03387f77d6b30e5008f50 100644 (file)
@@ -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<typename T>
@@ -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<typename T>
@@ -67,6 +71,7 @@ struct rect
        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 {};
@@ -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,
index 0dac03eca119a2b1299236bc903a081742b38ca3..bc467299e9e208cdca54fac3f245c4bb2c0ec4e6 100644 (file)
@@ -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<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)
index df956699c81a2a07626a239d0b5897f19ced015c..704017ba5f02acb7b28b40fbeffc60113d06ddfe 100644 (file)
@@ -369,83 +369,114 @@ spl::shared_ptr<core::frame_producer> create_psd_scene_producer(const core::fram
 
        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();