]> git.sesse.net Git - casparcg/blobdiff - modules/psd/layer.cpp
binding between linked layers in psd-import/scene-producer
[casparcg] / modules / psd / layer.cpp
index 9664377c7e84c135362e87b0cc49e2a2de0a4aec..e0aca51a95342dfe64761c3d47af33e1049866bc 100644 (file)
 
 #include "layer.h"
 #include "descriptor.h"
-#include <iostream>
+#include "util\pdf_reader.h"
+
+typedef unsigned char uint8_t;
+#include <algorithm>
+#include "../image/util/image_algorithms.h"
+#include "../image/util/image_view.h"
 
 namespace caspar { namespace psd {
 
-layer_ptr Layer::create(BEFileInputStream& stream)
+void read_raw_image_data(BEFileInputStream& stream, const channel_ptr& channel, image8bit_ptr target, unsigned char offset);
+void read_rle_image_data(BEFileInputStream& stream, const channel_ptr& channel, image8bit_ptr target, unsigned char offset);
+
+layer_ptr layer::create(BEFileInputStream& stream)
 {
-       layer_ptr result(std::make_shared<Layer>());
-       result->rect_.top = stream.read_long();
-       result->rect_.left = stream.read_long();
-       result->rect_.bottom = stream.read_long();
-       result->rect_.right = stream.read_long();
+       layer_ptr result(std::make_shared<layer>());
+       result->populate(stream);
+       return result;
+}
+
+void layer::populate(BEFileInputStream& stream)
+{
+       rect_.top = stream.read_long();
+       rect_.left = stream.read_long();
+       rect_.bottom = stream.read_long();
+       rect_.right = stream.read_long();
 
        //Get info about the channels in the layer
        unsigned short channelCount = stream.read_short();
@@ -41,10 +55,10 @@ layer_ptr Layer::create(BEFileInputStream& stream)
                unsigned long data_length = stream.read_long();
 
                if(channel_id <= -2)
-                       result->masks_++;
+                       masks_++;
 
                channel_ptr channel(std::make_shared<Channel>(channel_id, data_length));
-               result->channels_.push_back(channel);
+               channels_.push_back(channel);
        }
 
        unsigned long blendModeSignature = stream.read_long();
@@ -52,20 +66,20 @@ layer_ptr Layer::create(BEFileInputStream& stream)
                throw PSDFileFormatException();
 
        unsigned long blendModeKey = stream.read_long();
-       result->blendMode_ = IntToBlendMode(blendModeKey);
+       blend_mode_ = int_to_blend_mode(blendModeKey);
 
-       result->opacity_ = stream.read_byte();
-       result->baseClipping_ = stream.read_byte() == 1 ? false : true;
-       result->flags_ = stream.read_byte();
+       opacity_ = stream.read_byte();
+       baseClipping_ = stream.read_byte() == 1 ? false : true;
+       flags_ = stream.read_byte();
 
        stream.discard_bytes(1);
 
        unsigned long extraDataSize = stream.read_long();
        long position1 = stream.current_position();
-       result->read_mask_data(stream);
-       result->ReadBlendingRanges(stream);
+       mask_.read_mask_data(stream);
+       read_blending_ranges(stream);
 
-       result->name_ = stream.read_pascal_string(4);
+       name_ = stream.read_pascal_string(4);
 
        //Aditional Layer Information
        unsigned long end_of_layer_info = position1 + extraDataSize;
@@ -82,7 +96,11 @@ layer_ptr Layer::create(BEFileInputStream& stream)
                        unsigned long length = stream.read_long();
                        unsigned long end_of_chunk = stream.current_position() + length;
 
-                       if(key == 'TySh')       //type tool object settings
+                       if(key == 'lspf')
+                       {
+                               protection_flags_ = stream.read_long();
+                       }
+                       else if(key == 'TySh')  //type tool object settings
                        {
                                std::wstring text;      //the text in the layer
 
@@ -95,6 +113,14 @@ layer_ptr Layer::create(BEFileInputStream& stream)
                                descriptor text_descriptor;
                                if(!text_descriptor.populate(stream))
                                        throw PSDFileFormatException();
+                               else
+                               {
+                                       if(text_descriptor.has_item(L"EngineData"))
+                                       {
+                                               const descriptor_item& text_data = text_descriptor.get_item(L"EngineData");
+                                               read_pdf(text_layer_info_, text_data.rawdata_data);
+                                       }
+                               }
 
                                stream.read_short();    //"warp version" should be 1
                                stream.read_long();             //"descriptor version" should be 16
@@ -114,46 +140,56 @@ layer_ptr Layer::create(BEFileInputStream& stream)
        {
                stream.set_position(end_of_layer_info);
        }
-       
-       return result;
 }
 
-void Layer::read_mask_data(BEFileInputStream& stream)
+void layer::layer_mask::read_mask_data(BEFileInputStream& stream)
 {
        unsigned long length = stream.read_long();
-       if(length > 0)
+       switch(length)
        {
-               mask_rect_.top = stream.read_long();
-               mask_rect_.left = stream.read_long();
-               mask_rect_.bottom = stream.read_long();
-               mask_rect_.right = stream.read_long();
-
-               default_mask_value_ = stream.read_byte();
-               mask_flags_ = stream.read_byte();
+       case 0:
+               break;
+
+       case 20:
+               rect_.top = stream.read_long();
+               rect_.left = stream.read_long();
+               rect_.bottom = stream.read_long();
+               rect_.right = stream.read_long();
+
+               default_value_ = stream.read_byte();
+               flags_ = stream.read_byte();
+               stream.discard_bytes(2);
+               break;
+
+       case 36:
+               stream.discard_bytes(18);       //we don't care about the user mask if there is a "total user mask"
+               flags_ = stream.read_byte();
+               default_value_ = stream.read_byte();
+               rect_.top = stream.read_long();
+               rect_.left = stream.read_long();
+               rect_.bottom = stream.read_long();
+               rect_.right = stream.read_long();
+               break;
+
+       default:
+               stream.discard_bytes(length);
+               break;
+       };
+}
 
-               if(length == 20)
-                       stream.discard_bytes(2);
-               else
-               {
-                       //Override user mask with total user mask
-                       mask_flags_ = stream.read_byte();
-                       default_mask_value_ = stream.read_byte();
-                       mask_rect_.top = stream.read_long();
-                       mask_rect_.left = stream.read_long();
-                       mask_rect_.bottom = stream.read_long();
-                       mask_rect_.right = stream.read_long();
-               }
-       }
+bool layer::is_text() const
+{
+       return !text_layer_info_.empty();
 }
 
 //TODO: implement
-void Layer::ReadBlendingRanges(BEFileInputStream& stream)
+void layer::read_blending_ranges(BEFileInputStream& stream)
 {
        unsigned long length = stream.read_long();
        stream.discard_bytes(length);
 }
 
-channel_ptr Layer::get_channel(ChannelType type)
+channel_ptr layer::get_channel(channel_type type)
 {
        auto end = channels_.end();
        for(auto it = channels_.begin(); it != end; ++it)
@@ -167,22 +203,28 @@ channel_ptr Layer::get_channel(ChannelType type)
        return NULL;
 }
 
-void Layer::read_channel_data(BEFileInputStream& stream)
+void layer::read_channel_data(BEFileInputStream& stream)
 {
        image8bit_ptr img;
        image8bit_ptr mask;
-       std::clog << std::endl << "layer: " << std::string(name().begin(), name().end()) << std::endl;
+
+       bool has_transparency(get_channel(psd::Transparency));
+
+       //std::clog << std::endl << "layer: " << std::string(name().begin(), name().end()) << std::endl;
        
        if(rect_.width() > 0 && rect_.height() > 0)
        {
-               img = std::make_shared<image8bit>(rect_.width(), rect_.height(), std::min<unsigned char>(channels_.size() - masks_, 4));
-               std::clog << std::dec << "has image: [width: " << rect_.width() << " height: " << rect_.height() << "]" << std::endl;
+               img = std::make_shared<image8bit>(rect_.width(), rect_.height(), 4);
+               //std::clog << std::dec << "has image: [width: " << rect_.width() << " height: " << rect_.height() << "]" << std::endl;
+
+               if(!has_transparency)
+                       std::memset(img->data(), 255, img->width()*img->height()*img->channel_count());
        }
 
-       if(masks_ > 0 && mask_rect_.width() > 0 && mask_rect_.height() > 0)
+       if(masks_ > 0 && mask_.rect_.width() > 0 && mask_.rect_.height() > 0)
        {
-               mask = std::make_shared<image8bit>(mask_rect_.width(), mask_rect_.height(), 1);
-               std::clog << std::dec << "has mask: [width: " << mask_rect_.width() << " height: " << mask_rect_.height() << "]" << std::endl;
+               mask = std::make_shared<image8bit>(mask_.rect_.width(), mask_.rect_.height(), 1);
+               //std::clog << std::dec << "has mask: [width: " << mask_rect_.width() << " height: " << mask_rect_.height() << "]" << std::endl;
        }
 
        auto end = channels_.end();
@@ -195,14 +237,14 @@ void Layer::read_channel_data(BEFileInputStream& stream)
                //determine target bitmap and offset
                if((*it)->id() >= 3)
                        discard_channel = true; //discard channels that doesn't contribute to the final image
-               else if((*it)->id() >= -1)      //RGBA-data
+               else if((*it)->id() >= -1)      //BGRA-data
                {
                        target = img;
-                       offset = ((*it)->id() >= 0) ? (*it)->id() : 3;
+                       offset = ((*it)->id() >= 0) ? 2 - (*it)->id() : 3;
                }
                else if(mask)   //mask
                {
-                       if((*it)->id() == -2 && masks_ == 2)    //if there are two mask-channels, discard the the one that's not the total mask for now
+                       if((*it)->id() == -2 && masks_ == 2)    //if there are two mask-channels, discard the the one that's not the total mask
                                discard_channel = true;
                        else
                        {
@@ -211,20 +253,20 @@ void Layer::read_channel_data(BEFileInputStream& stream)
                        }
                }
 
-               unsigned long cp = stream.current_position();   //TODO: remove, for debug purposes only
-               std::clog << std::dec << "channel_id: " << (*it)->id() << ", reading data from: " << std::hex << cp << ", data_length: " << (*it)->data_length() << std::endl;
+               //unsigned long cp = stream.current_position(); //for debug purposes only
+               //std::clog << std::dec << "channel_id: " << (*it)->id() << ", reading data from: " << std::hex << cp << ", data_length: " << (*it)->data_length() << std::endl;
 
                if(!target)
                        discard_channel = true;
 
                if(discard_channel)
                {
-                       std::clog << "  -> discarding" << std::endl;
+                       //std::clog << "        -> discarding" << std::endl;
                        stream.discard_bytes((*it)->data_length());
                }
                else
                {
-                       std::clog << "  -> reading...";
+                       //std::clog << "        -> reading...";
                        unsigned short encoding = stream.read_short();
                        if(target)
                        {
@@ -235,15 +277,21 @@ void Layer::read_channel_data(BEFileInputStream& stream)
                                else
                                        throw PSDFileFormatException();
                        }
-                       std::clog << " " << std::hex << (stream.current_position() - cp) << " bytes read" << std::endl;
+                       //std::clog << " " << std::hex << (stream.current_position() - cp) << " bytes read" << std::endl;
                }
        }
 
+       if(img && has_transparency)
+       {
+               caspar::image::image_view<caspar::image::bgra_pixel> view(img->data(), img->width(), img->height());
+               caspar::image::premultiply(view);
+       }
+
        image_ = img;
-       mask_ = mask;
+       mask_.mask_ = mask;
 }
 
-void Layer::read_raw_image_data(BEFileInputStream& stream, const channel_ptr& channel, image8bit_ptr target, unsigned char offset)
+void read_raw_image_data(BEFileInputStream& stream, const channel_ptr& channel, image8bit_ptr target, unsigned char offset)
 {
        unsigned long total_length = target->width() * target->height();
        if(total_length != (channel->data_length() - 2))
@@ -261,7 +309,7 @@ void Layer::read_raw_image_data(BEFileInputStream& stream, const channel_ptr& ch
        }
 }
 
-void Layer::read_rle_image_data(BEFileInputStream& stream, const channel_ptr& channel, image8bit_ptr target, unsigned char offset)
+void read_rle_image_data(BEFileInputStream& stream, const channel_ptr& channel, image8bit_ptr target, unsigned char offset)
 {
        unsigned long width = target->width();
        unsigned char stride = target->channel_count();
@@ -307,34 +355,5 @@ void Layer::read_rle_image_data(BEFileInputStream& stream, const channel_ptr& ch
        }
 }
 
-//
-//image_ptr Layer::read_image(BEFileInputStream& stream)
-//{
-//     if(image == NULL)
-//     {
-//             unsigned char channels = (channels_.size() > 3) ? 4 : 3;
-//
-//             image = std::make_shared<Image>(static_cast<unsigned short>(rect_.width()), static_cast<unsigned short>(rect_.height()),  channels);
-//
-//             channel_ptr pChannel = get_channel(ColorRed);
-//             if(pChannel != NULL)
-//                     pChannel->GetChannelImageData(stream, image, 0);
-//
-//             pChannel = get_channel(ColorGreen);
-//             if(pChannel != NULL)
-//                     pChannel->GetChannelImageData(stream, image, 1);
-//
-//             pChannel = get_channel(ColorBlue);
-//             if(pChannel != NULL)
-//                     pChannel->GetChannelImageData(stream, image, 2);
-//
-//             pChannel = get_channel(Transparency);
-//             if(channels == 4 && pChannel != NULL)
-//                     pChannel->GetChannelImageData(stream, image, 3);
-//     }
-//
-//     return image;
-//}
-
 }      //namespace psd
 }      //namespace caspar
\ No newline at end of file