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
\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
\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
{\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
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
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
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
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
\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
\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
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