]> git.sesse.net Git - casparcg/blob - modules/psd/layer.cpp
* some refactoring i psd + support for rectangular vector-masks
[casparcg] / modules / psd / layer.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Niklas P Andersson, niklas.p.andersson@svt.se
20 */
21
22 #include "layer.h"
23 #include "doc.h"
24 #include "descriptor.h"
25 #include "util\pdf_reader.h"
26
27 #include <algorithm>
28 #include "../image/util/image_algorithms.h"
29 #include "../image/util/image_view.h"
30
31 #include <boost/property_tree/ptree.hpp>
32
33 namespace caspar { namespace psd {
34
35 void layer::layer_mask_info::read_mask_data(BEFileInputStream& stream)
36 {
37         unsigned long length = stream.read_long();
38         switch(length)
39         {
40         case 0:
41                 break;
42
43         case 20:
44         case 36:        //discard total user mask data
45                 rect_.location.y = stream.read_long();
46                 rect_.location.x = stream.read_long();
47                 rect_.size.height = stream.read_long() - rect_.location.y;
48                 rect_.size.width = stream.read_long() - rect_.location.x;
49
50                 default_value_ = stream.read_byte();
51                 flags_ = stream.read_byte();
52                 stream.discard_bytes(2);
53                 
54                 if(length == 36)
55                 {
56                         stream.discard_bytes(18);       //discard "total user mask" data
57                         //flags_ = stream.read_byte();
58                         //default_value_ = stream.read_byte();
59                         //rect_.location.y = stream.read_long();
60                         //rect_.location.x = stream.read_long();
61                         //rect_.size.height = stream.read_long() - rect_.location.y;
62                         //rect_.size.width = stream.read_long() - rect_.location.x;
63                 }
64                 break;
65
66         default:
67                 //TODO: Log that we discard a mask that is not supported
68                 stream.discard_bytes(length);
69                 break;
70         };
71 }
72
73 struct layer::impl
74 {
75         friend class layer;
76
77         impl() : blend_mode_(InvalidBlendMode), link_group_id_(0), opacity_(255), baseClipping_(false), flags_(0), protection_flags_(0), masks_count_(0)
78         {}
79
80 private:
81         std::vector<channel>                    channels_;
82         blend_mode                                              blend_mode_;
83         int                                                             link_group_id_;
84         unsigned char                                   opacity_;
85         bool                                                    baseClipping_;
86         unsigned char                                   flags_;
87         int                                                             protection_flags_;
88         std::wstring                                    name_;
89         char                                                    masks_count_;
90
91         rect<long>                                              vector_mask_;
92         layer::layer_mask_info                  mask_;
93
94         rect<long>                                              bitmap_rect_;
95         image8bit_ptr                                   bitmap_;
96
97         boost::property_tree::wptree    text_layer_info_;
98         boost::property_tree::wptree    timeline_info_;
99
100         color<unsigned char>                    solid_color_;
101
102 public:
103         void populate(BEFileInputStream& stream, const psd_document& doc)
104         {
105                 bitmap_rect_.location.y = stream.read_long();
106                 bitmap_rect_.location.x = stream.read_long();
107                 bitmap_rect_.size.height = stream.read_long() - bitmap_rect_.location.y;
108                 bitmap_rect_.size.width = stream.read_long() - bitmap_rect_.location.x;
109
110                 //Get info about the channels in the layer
111                 unsigned short channelCount = stream.read_short();
112                 for(int channelIndex = 0; channelIndex < channelCount; ++channelIndex)
113                 {
114                         short id = static_cast<short>(stream.read_short());
115                         channel c(id, stream.read_long());
116
117                         if(c.id < -1)
118                                 masks_count_++;
119
120                         channels_.push_back(c);
121                 }
122
123                 unsigned long blendModeSignature = stream.read_long();
124                 if(blendModeSignature != '8BIM')
125                         throw PSDFileFormatException();
126
127                 blend_mode_ = int_to_blend_mode(stream.read_long());
128                 opacity_ = stream.read_byte();
129                 baseClipping_ = stream.read_byte() == 1 ? false : true;
130                 flags_ = stream.read_byte();
131
132                 stream.discard_bytes(1);        //padding
133
134                 unsigned long extras_size = stream.read_long();
135                 long position = stream.current_position();
136                 mask_.read_mask_data(stream);
137                 read_blending_ranges(stream);
138                 name_ = stream.read_pascal_string(4);
139
140                 //Aditional Layer Information
141                 long end_of_layer_info = position + extras_size;
142                 try
143                 {
144                         while(stream.current_position() < end_of_layer_info)
145                         {
146                                 read_chunk(stream, doc);
147                         }
148                 }
149                 catch(PSDFileFormatException&)
150                 {
151                         stream.set_position(end_of_layer_info);
152                 }
153         }
154
155         void read_chunk(BEFileInputStream& stream, const psd_document& doc, bool isMetadata = false)
156         {
157                 unsigned long signature = stream.read_long();
158                 if(signature != '8BIM' && signature != '8B64')
159                         throw PSDFileFormatException();
160
161                 unsigned long key = stream.read_long();
162
163                 if(isMetadata) stream.read_long();
164
165                 unsigned long length = stream.read_long();
166                 unsigned long end_of_chunk = stream.current_position() + length;
167
168                 try
169                 {
170                         switch(key)
171                         {
172                         case 'SoCo':
173                                 read_solid_color(stream);
174                                 break;
175
176                         case 'lspf':    //protection settings
177                                 protection_flags_ = stream.read_long();
178                                 break;
179
180                         case 'Txt2':    //text engine data
181                                 break;
182
183                         case 'TySh':    //type tool object settings
184                                 read_text_data(stream);
185                                 break;
186                                 
187                         case 'shmd':    //metadata
188                                 read_metadata(stream, doc);
189                                 break;
190                                 
191                         case 'lyvr':    //layer version
192                                 break;
193
194                         case 'lnkD':    //linked layer
195                         case 'lnk2':    //linked layer
196                         case 'lnk3':    //linked layer
197                                 break;
198
199                         case 'vmsk':
200                                 read_vector_mask(length, stream, doc.width(), doc.height());
201                                 break;
202                                 
203                         case 'tmln':
204                                 read_timeline_data(stream);
205                                 break;
206
207                         default:
208                                 break;
209                         }
210                 }
211                 catch(PSDFileFormatException&)
212                 {
213                         //ignore failed chunks silently
214                 }
215
216                 stream.set_position(end_of_chunk);
217         }
218
219         void read_solid_color(BEFileInputStream& stream)
220         {
221         }
222
223         void read_vector_mask(unsigned long length, BEFileInputStream& stream, long doc_width, long doc_height)
224         {
225                 typedef std::pair<unsigned long, unsigned long> path_point;
226
227                 unsigned long version = stream.read_long();
228                 unsigned long flags = stream.read_long();
229                 int path_records = (length-8) / 26;
230
231                 long position = stream.current_position();
232
233                 std::vector<path_point> knots;
234                 for(int i=1; i <= path_records; ++i)
235                 {
236                         unsigned short selector = stream.read_short();
237                         if(selector == 2)       //we only concern ourselves with closed paths 
238                         {
239                                 unsigned long p_y = stream.read_long();
240                                 unsigned long p_x = stream.read_long();
241                                 path_point cp_prev(p_x, p_y);
242
243                                 unsigned long a_y = stream.read_long();
244                                 unsigned long a_x = stream.read_long();
245                                 path_point anchor(a_x, a_y);
246
247                                 unsigned long n_y = stream.read_long();
248                                 unsigned long n_x = stream.read_long();
249                                 path_point cp_next(n_x, n_y);
250
251                                 if(anchor == cp_prev && anchor == cp_next)
252                                         knots.push_back(anchor);
253                                 else
254                                 {       //we can't handle smooth curves yet
255                                         throw PSDFileFormatException();
256                                 }
257                         }
258
259                         stream.set_position(position + 26*i);
260                 }
261
262                 if(knots.size() != 4)   //we only support rectangular vector masks
263                         throw PSDFileFormatException();
264
265                 //we only support rectangular vector masks
266                 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))
267                         throw PSDFileFormatException();
268
269                 //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.
270                 float x_ratio = doc_width / 16777215.0f;
271                 float y_ratio = doc_height / 16777215.0f;
272                 vector_mask_.location.x = static_cast<long>(knots[0].first * x_ratio +0.5f);                                                            //add .5 to get propper rounding when converting to integer
273                 vector_mask_.location.y = static_cast<long>(knots[0].second * y_ratio +0.5f);                                                           //add .5 to get propper rounding when converting to integer
274                 vector_mask_.size.width = static_cast<long>(knots[1].first * x_ratio +0.5f)     - vector_mask_.location.x;              //add .5 to get propper rounding when converting to integer
275                 vector_mask_.size.height = static_cast<long>(knots[2].second * y_ratio +0.5f) - vector_mask_.location.y;        //add .5 to get propper rounding when converting to integer
276         }
277
278         void read_metadata(BEFileInputStream& stream, const psd_document& doc)
279         {
280                 unsigned long count = stream.read_long();
281                 for(unsigned long index = 0; index < count; ++index)
282                         read_chunk(stream, doc, true);
283         }
284
285         void read_timeline_data(BEFileInputStream& stream)
286         {
287                 if(stream.read_long() != 16)    //"descriptor version" should be 16
288                         throw PSDFileFormatException();
289
290                 descriptor timeline_descriptor;
291                 if(!timeline_descriptor.populate(stream))
292                         throw PSDFileFormatException();
293                 else
294                 {
295                         timeline_info_.swap(timeline_descriptor.items());
296                 }
297         }
298
299         void read_text_data(BEFileInputStream& stream)
300         {
301                 std::wstring text;      //the text in the layer
302
303                 stream.read_short();    //should be 1
304         
305                 //transformation info
306                 double xx = stream.read_double();
307                 double xy = stream.read_double();
308                 double yx = stream.read_double();
309                 double yy = stream.read_double();
310                 double tx = stream.read_double();
311                 double ty = stream.read_double();
312
313                 if(stream.read_short() != 50)   //"text version" should be 50
314                         throw PSDFileFormatException();
315
316                 if(stream.read_long() != 16)    //"descriptor version" should be 16
317                         throw PSDFileFormatException();
318
319                 descriptor text_descriptor;
320                 if(!text_descriptor.populate(stream))
321                         throw PSDFileFormatException();
322                 else
323                 {
324                         auto text_info = text_descriptor.items().get_optional<std::wstring>(L"EngineData");
325                         if(text_info.is_initialized())
326                         {
327                                 std::string str(text_info.get().begin(), text_info.get().end());
328                                 read_pdf(text_layer_info_, str);
329                         }
330                 }
331
332                 if(stream.read_short() != 1)    //"warp version" should be 1
333                         throw PSDFileFormatException();
334
335                 if(stream.read_long() != 16)    //"descriptor version" should be 16
336                         throw PSDFileFormatException();
337
338                 descriptor warp_descriptor;
339                 if(!warp_descriptor.populate(stream))
340                         throw PSDFileFormatException();
341                 else
342                         stream.discard_bytes(4*8);      //top, left, right, bottom
343         }
344
345         //TODO: implement
346         void read_blending_ranges(BEFileInputStream& stream)
347         {
348                 unsigned long length = stream.read_long();
349                 stream.discard_bytes(length);
350         }
351
352         bool has_channel(channel_type type)
353         {
354                 return std::find_if(channels_.begin(), channels_.end(), [=](const channel& c) { return c.id == type; }) != channels_.end();
355         }
356
357         void read_channel_data(BEFileInputStream& stream)
358         {
359                 image8bit_ptr bitmap;
360                 image8bit_ptr mask;
361
362                 bool has_transparency = has_channel(psd::transparency);
363         
364                 rect<long> clip_rect;
365                 if(!bitmap_rect_.empty())
366                 {
367                         clip_rect = bitmap_rect_;
368
369                         if(!vector_mask_.empty())
370                                 clip_rect = vector_mask_;
371
372                         bitmap = std::make_shared<image8bit>(clip_rect.size.width, clip_rect.size.height, 4);
373
374                         if(!has_transparency)
375                                 std::memset(bitmap->data(), 255, bitmap->width()*bitmap->height()*bitmap->channel_count());
376                 }
377
378                 if(masks_count_ > 0 && !mask_.rect_.empty())
379                 {
380                         mask = std::make_shared<image8bit>(mask_.rect_.size.width, mask_.rect_.size.height, 1);
381                 }
382
383                 for(auto it = channels_.begin(); it != channels_.end(); ++it)
384                 {
385                         psd::rect<long> src_rect;
386                         image8bit_ptr target;
387                         unsigned char offset;
388                         bool discard_channel = false;
389
390                         //determine target bitmap and offset
391                         if((*it).id >= 3)
392                                 discard_channel = true; //discard channels that doesn't contribute to the final image
393                         else if((*it).id >= -1) //BGRA-data
394                         {
395                                 target = bitmap;
396                                 offset = ((*it).id >= 0) ? 2 - (*it).id : 3;
397                                 src_rect = bitmap_rect_;
398                         }
399                         else if(mask)   //mask
400                         {
401                                 if((*it).id == -3)      //discard the "total user mask"
402                                         discard_channel = true;
403                                 else
404                                 {
405                                         target = mask;
406                                         offset = 0;
407                                         src_rect = mask_.rect_;
408                                 }
409                         }
410
411                         if(!target || src_rect.empty())
412                                 discard_channel = true;
413
414                         unsigned long end_of_data = stream.current_position() + (*it).data_length;
415                         if(!discard_channel)
416                         {
417                                 unsigned short encoding = stream.read_short();
418                                 if(target)
419                                 {
420                                         if(encoding == 0)
421                                                 read_raw_image_data(stream, (*it).data_length-2, target, offset);
422                                         else if(encoding == 1)
423                                                 read_rle_image_data(stream, src_rect, (target == bitmap) ? clip_rect : mask_.rect_, target, offset);
424                                         else
425                                                 throw PSDFileFormatException();
426                                 }
427                         }
428                         stream.set_position(end_of_data);
429                 }
430
431                 if(bitmap && has_transparency)
432                 {
433                         caspar::image::image_view<caspar::image::bgra_pixel> view(bitmap->data(), bitmap->width(), bitmap->height());
434                         caspar::image::premultiply(view);
435                 }
436
437                 bitmap_ = bitmap;
438                 bitmap_rect_ = clip_rect;
439                 mask_.bitmap_ = mask;
440         }
441
442         void read_raw_image_data(BEFileInputStream& stream, unsigned long data_length, image8bit_ptr target, unsigned char offset)
443         {
444                 unsigned long total_length = target->width() * target->height();
445                 if(total_length != data_length)
446                         throw PSDFileFormatException();
447
448                 unsigned char* data = target->data();
449
450                 unsigned char stride = target->channel_count();
451                 if(stride == 1)
452                         stream.read(reinterpret_cast<char*>(data + offset), total_length);
453                 else
454                 {
455                         for(unsigned long index=0; index < total_length; ++index)
456                                 data[index*stride+offset] = stream.read_byte();
457                 }
458         }
459
460         void read_rle_image_data(BEFileInputStream& stream, const rect<long>&src_rect, const rect<long>&clip_rect, image8bit_ptr target, unsigned char offset)
461         {
462                 unsigned long width = src_rect.size.width;
463                 unsigned long height = src_rect.size.height;
464                 unsigned char stride = target->channel_count();
465
466                 int offset_x = clip_rect.location.x - src_rect.location.x;
467                 int offset_y = clip_rect.location.y - src_rect.location.y;
468
469                 std::vector<unsigned short> scanline_lengths;
470                 scanline_lengths.reserve(height);
471
472                 for(unsigned long scanlineIndex=0; scanlineIndex < height; ++scanlineIndex)
473                         scanline_lengths.push_back(stream.read_short());
474
475                 unsigned char *target_data = target->data();
476
477                 std::vector<unsigned char> line(width);
478
479                 for(long scanlineIndex=0; scanlineIndex < height; ++scanlineIndex)
480                 {
481                         if(scanlineIndex >= target->height()+offset_y)
482                                 break;
483
484                         unsigned long colIndex = 0;
485                         unsigned char length = 0;
486                         do
487                         {
488                                 length = 0;
489
490                                 //Get controlbyte
491                                 char controlByte = static_cast<char>(stream.read_byte());
492                                 if(controlByte >= 0)
493                                 {
494                                         //Read uncompressed string
495                                         length = controlByte+1;
496                                         for(unsigned long index=0; index < length; ++index)
497                                                 line[colIndex+index] = stream.read_byte();
498                                 }
499                                 else if(controlByte > -128)
500                                 {
501                                         //Repeat next byte
502                                         length = -controlByte+1;
503                                         unsigned char value = stream.read_byte();
504                                         for(unsigned long index=0; index < length; ++index)
505                                                 line[colIndex+index] = value;
506                                 }
507
508                                 colIndex += length;
509                         }
510                         while(colIndex < width);
511
512                         //use line to populate target
513                         if(scanlineIndex >= offset_y)
514                                 for(int index = offset_x; index < target->width(); ++index)
515                                 {
516                                         target_data[((scanlineIndex-offset_y)*target->width()+index-offset_x) * stride + offset] = line[index];
517                                 }
518                 }
519         }
520 };
521
522 layer::layer() : impl_(spl::make_shared<impl>()) {}
523
524 void layer::populate(BEFileInputStream& stream, const psd_document& doc) { impl_->populate(stream, doc); }
525 void layer::read_channel_data(BEFileInputStream& stream) { impl_->read_channel_data(stream); }
526
527 const std::wstring& layer::name() const { return impl_->name_; }
528 unsigned char layer::opacity() const { return impl_->opacity_; }
529
530 bool layer::is_visible() { return (impl_->flags_ & 2) == 0; }   //the (PSD file-format) documentation is is saying the opposite but what the heck
531 bool layer::is_position_protected() { return (impl_->protection_flags_& 4) == 4; }
532
533
534 bool layer::is_text() const { return !impl_->text_layer_info_.empty(); }
535 const boost::property_tree::wptree& layer::text_data() const { return impl_->text_layer_info_; }
536
537 bool layer::has_timeline() const { return !impl_->timeline_info_.empty(); }
538 const boost::property_tree::wptree& layer::timeline_data() const { return impl_->timeline_info_; }
539
540 bool layer::is_solid() const { return impl_->solid_color_.alpha != 0; }
541 color<unsigned char> layer::solid_color() const { return impl_->solid_color_; }
542
543 const point<long>& layer::location() const { return impl_->bitmap_rect_.location; }
544 const image8bit_ptr& layer::bitmap() const { return impl_->bitmap_; }
545
546 int layer::link_group_id() const { return impl_->link_group_id_; }
547 void layer::set_link_group_id(int id) { impl_->link_group_id_ = id; }
548
549 }       //namespace psd
550 }       //namespace caspar