]> git.sesse.net Git - casparcg/blobdiff - modules/psd/layer.cpp
Use namespace for isnan() in PSD module
[casparcg] / modules / psd / layer.cpp
index acc595e33f886964116e857139058a893fc19796..d62cf5119b83e9ff98c4383a5490af5cfb0cd2d5 100644 (file)
@@ -46,7 +46,7 @@ void layer::mask_info::read_mask_data(bigendian_file_input_stream& stream)
                break;\r
 \r
        case 20:\r
-       case 36:        //discard total user mask data\r
+       case 36:\r
                rect_.location.y = stream.read_long();\r
                rect_.location.x = stream.read_long();\r
                rect_.size.height = stream.read_long() - rect_.location.y;\r
@@ -78,8 +78,8 @@ void layer::mask_info::read_mask_data(bigendian_file_input_stream& stream)
 \r
 bool layer::vector_mask_info::populate(int length, bigendian_file_input_stream& stream, int doc_width, int doc_height)\r
 {\r
-       typedef std::pair<int, int> path_point;\r
-       bool bFail = false;\r
+       std::vector<point<int>> knots;\r
+       bool smooth_curve = false;\r
 \r
        stream.read_long(); // version\r
        this->flags_ = static_cast<std::uint8_t>(stream.read_long()); // flags\r
@@ -87,8 +87,6 @@ bool layer::vector_mask_info::populate(int length, bigendian_file_input_stream&
 \r
        auto position = stream.current_position();\r
 \r
-       std::vector<path_point> knots;\r
-\r
        const int SELECTOR_SIZE = 2;\r
        const int PATH_POINT_SIZE = 4 + 4;\r
        const int PATH_POINT_RECORD_SIZE = SELECTOR_SIZE + (3 * PATH_POINT_SIZE);\r
@@ -100,21 +98,22 @@ bool layer::vector_mask_info::populate(int length, bigendian_file_input_stream&
                {\r
                        auto p_y = stream.read_long();\r
                        auto p_x = stream.read_long();\r
-                       path_point cp_prev(p_x, p_y);\r
+                       point<int> prev{ static_cast<int>(p_x), static_cast<int>(p_y) };\r
 \r
                        auto a_y = stream.read_long();\r
                        auto a_x = stream.read_long();\r
-                       path_point anchor(a_x, a_y);\r
+                       point<int> anchor{ static_cast<int>(a_x), static_cast<int>(a_y) };\r
 \r
                        auto n_y = stream.read_long();\r
                        auto n_x = stream.read_long();\r
-                       path_point cp_next(n_x, n_y);\r
+                       point<int> next{ static_cast<int>(n_x), static_cast<int>(n_y) };\r
 \r
-                       if (anchor == cp_prev && anchor == cp_next)\r
+                       if (anchor == prev && anchor == next)\r
                                knots.push_back(anchor);\r
-                       else\r
-                       {       //we can't handle smooth curves yet\r
-                               bFail = true;\r
+                       else \r
+                       {\r
+                               //note that we've got a smooth curve, but continue to iterate through the data\r
+                               smooth_curve = true;\r
                        }\r
                }\r
 \r
@@ -122,37 +121,46 @@ bool layer::vector_mask_info::populate(int length, bigendian_file_input_stream&
                stream.set_position(position + offset);\r
        }\r
 \r
-       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)))\r
-               bFail = true;\r
-\r
-       if (bFail) {\r
+       if (smooth_curve || knots.size() != 4)  //we can't handle smooth-curves yet and we only support quad-gons\r
+       {\r
                rect_.clear();\r
-               flags_ = static_cast<std::uint8_t>(flags::unsupported);\r
+               flags_ = static_cast<std::uint8_t>(flags::unsupported | flags::disabled);\r
+               return false;\r
        }\r
-       else \r
+\r
+       //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.\r
+       float x_ratio = doc_width / 16777215.0f;\r
+       float y_ratio = doc_height / 16777215.0f;\r
+       rect_.clear();\r
+       knots_.clear();\r
+\r
+       //is it an orthogonal rectangle\r
+       if (knots[0].x == knots[3].x && knots[1].x == knots[2].x && knots[0].y == knots[1].y && knots[2].y == knots[3].y)\r
        {\r
-               //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.\r
-               float x_ratio = doc_width / 16777215.0f;\r
-               float y_ratio = doc_height / 16777215.0f;\r
-               rect_.location.x = static_cast<int>(knots[0].first * x_ratio + 0.5f);                                                           //add .5 to get propper rounding when converting to integer\r
-               rect_.location.y = static_cast<int>(knots[0].second * y_ratio + 0.5f);                                                          //add .5 to get propper rounding when converting to integer\r
-               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\r
-               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\r
+               rect_.location.x = static_cast<int>(knots[0].x * x_ratio + 0.5f);                                               //add .5 to get propper rounding when converting to integer\r
+               rect_.location.y = static_cast<int>(knots[0].y * y_ratio + 0.5f);                                               //add .5 to get propper rounding when converting to integer\r
+               rect_.size.width = static_cast<int>(knots[1].x * x_ratio + 0.5f) - rect_.location.x;    //add .5 to get propper rounding when converting to integer\r
+               rect_.size.height = static_cast<int>(knots[2].y * y_ratio + 0.5f) - rect_.location.y;   //add .5 to get propper rounding when converting to integer\r
+       }\r
+       else //it's could be any kind of quad-gon\r
+       {\r
+               for (auto& k : knots)\r
+                       knots_.push_back(psd::point<int>{static_cast<int>(k.x * x_ratio + 0.5f), static_cast<int>(k.y * y_ratio + 0.5f)});\r
        }\r
 \r
-       return !bFail;\r
+       return true;\r
 }\r
 \r
 struct layer::impl\r
 {\r
        friend class layer;\r
 \r
-       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), tags_(layer_tag::none)\r
+       impl() : blend_mode_(caspar::core::blend_mode::normal), layer_type_(layer_type::content), link_group_id_(0), opacity_(255), sheet_color_(0), baseClipping_(false), flags_(0), protection_flags_(0), masks_count_(0), scale_{ 1.0, 1.0 }, angle_(0), shear_(0), tags_(layer_tag::none)\r
        {}\r
 \r
 private:\r
        std::vector<channel>                    channels_;\r
-       blend_mode                                              blend_mode_;\r
+       caspar::core::blend_mode                blend_mode_;\r
        layer_type                                              layer_type_;\r
        int                                                             link_group_id_;\r
        int                                                             opacity_;\r
@@ -162,7 +170,10 @@ private:
        std::uint32_t                                   protection_flags_;\r
        std::wstring                                    name_;\r
        int                                                             masks_count_;\r
-       double                                                  text_scale_;\r
+       psd::point<double>                              text_pos_;\r
+       psd::point<double>                              scale_;\r
+       double                                                  angle_;\r
+       double                                                  shear_;\r
 \r
        layer::mask_info                                mask_;\r
 \r
@@ -285,6 +296,7 @@ public:
                        case 'lnk3':    //linked layer\r
                                break;\r
 \r
+                       case 'vsms':\r
                        case 'vmsk':\r
                                mask_.read_vector_mask_data(length, stream, doc.width(), doc.height());\r
                                break;\r
@@ -388,12 +400,11 @@ public:
                auto xy = stream.read_double();\r
                auto yx = stream.read_double();\r
                auto yy = stream.read_double();\r
-               stream.read_double(); // tx\r
-               stream.read_double(); // ty\r
-               if(xx != yy || (xy != 0 && yx != 0))\r
-                       CASPAR_THROW_EXCEPTION(psd_file_format_exception() << msg_info("Rotation and non-uniform scaling of dynamic textfields is not supported yet"));\r
+               auto tx = stream.read_double(); // tx\r
+               auto ty = stream.read_double(); // ty\r
 \r
-               text_scale_ = xx;\r
+               text_pos_.x = tx;\r
+               text_pos_.y = ty;\r
 \r
                if(stream.read_short() != 50)   //"text version" should be 50\r
                        CASPAR_THROW_EXCEPTION(psd_file_format_exception() << msg_info("invalid text version"));\r
@@ -420,10 +431,45 @@ public:
 \r
                descriptor warp_descriptor(L"warp");\r
                warp_descriptor.populate(stream);\r
-               stream.read_double(); // w_top\r
-               stream.read_double();  // w_left\r
+               stream.read_double(); // w_left\r
+               stream.read_double();  // w_top\r
                stream.read_double();  // w_right\r
                stream.read_double();  // w_bottom\r
+\r
+\r
+               //extract scale, angle and shear factor from transformation matrix \r
+               const double PI = 3.141592653589793;\r
+               auto angle = atan2(xy, xx);\r
+\r
+               auto c = cos(angle);\r
+               auto s = sin(angle);\r
+               auto scale_x = (abs(c) > 0.1) ? xx / c : xy / s;\r
+\r
+               if (xx / scale_x < 0) { //fel kvadrant\r
+                       angle += PI;\r
+                       c = cos(angle);\r
+                       s = sin(angle);\r
+                       scale_x = (abs(c) > 0.1) ? xx / c : xy / s;\r
+               }\r
+\r
+               auto shear_factor = (yx*c + yy*s) / (yy*c - yx * s);\r
+               auto scale_y = 1.0;\r
+               if (abs(shear_factor) < 0.0001 || std::isnan(shear_factor)) {\r
+                       shear_factor = 0;\r
+                       scale_y = (abs(c) > 0.1) ? yy / c : yx / -s;\r
+               }\r
+               else {\r
+                       scale_y = yx / (c*shear_factor - s);\r
+               }\r
+\r
+               scale_.x = scale_x;\r
+               scale_.y = scale_y;\r
+               angle_ = angle * 180 / PI;\r
+               shear_ = shear_factor;\r
+\r
+               \r
+\r
+\r
        }\r
 \r
        //TODO: implement\r
@@ -591,12 +637,19 @@ void layer::read_channel_data(bigendian_file_input_stream& stream) { impl_->read
 \r
 const std::wstring& layer::name() const { return impl_->name_; }\r
 int layer::opacity() const { return impl_->opacity_; }\r
+caspar::core::blend_mode layer::blend_mode() const { return impl_->blend_mode_; }\r
 int layer::sheet_color() const { return impl_->sheet_color_; }\r
 \r
 bool layer::is_visible() { return (impl_->flags_ & 2) == 0; }  //the (PSD file-format) documentation is is saying the opposite but what the heck\r
 bool layer::is_position_protected() { return (impl_->protection_flags_& 4) == 4; }\r
 \r
-double layer::text_scale() const { return impl_->text_scale_; }\r
+const layer::mask_info& layer::mask() const { return impl_->mask_; }\r
+\r
+const psd::point<double>& layer::text_pos() const { return impl_->text_pos_; }\r
+const psd::point<double>& layer::scale() const { return impl_->scale_; }\r
+const double layer::angle() const { return impl_->angle_; }\r
+const double layer::shear() const { return impl_->shear_; }\r
+\r
 bool layer::is_text() const { return !impl_->text_layer_info_.empty(); }\r
 const boost::property_tree::wptree& layer::text_data() const { return impl_->text_layer_info_; }\r
 \r
@@ -619,6 +672,8 @@ bool layer::is_static() const { return (impl_->tags_ & layer_tag::rasterized) ==
 bool layer::is_movable() const { return (impl_->tags_ & layer_tag::moveable) == layer_tag::moveable; }\r
 bool layer::is_resizable() const { return (impl_->tags_ & layer_tag::resizable) == layer_tag::resizable; }\r
 bool layer::is_placeholder() const { return (impl_->tags_ & layer_tag::placeholder) == layer_tag::placeholder; }\r
+bool layer::is_cornerpin() const { return (impl_->tags_ & layer_tag::cornerpin) == layer_tag::cornerpin; }\r
+\r
 layer_tag layer::tags() const { return impl_->tags_; }\r
 \r
 }      //namespace psd\r