<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>\r
</ClCompile>\r
<ClCompile Include="producer\image_scroll_producer.cpp" />\r
+ <ClCompile Include="util\image_algorithms.cpp" />\r
<ClCompile Include="util\image_loader.cpp" />\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="consumer\image_consumer.cpp">\r
<Filter>source\consumer</Filter>\r
</ClCompile>\r
+ <ClCompile Include="util\image_algorithms.cpp">\r
+ <Filter>source\util</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="producer\image_producer.h">\r
--- /dev/null
+/*
+* Copyright 2013 Sveriges Television AB http://casparcg.com/
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se.com
+*/
+
+#include <vector>
+#include <stdint.h>
+#include "../util/image_algorithms.h"
+
+namespace caspar { namespace image {
+
+std::vector<std::pair<int, int>> get_line_points(int num_pixels, double angle_radians)
+{
+ std::vector<std::pair<int, int>> line_points;
+ line_points.reserve(num_pixels);
+
+ double delta_x = std::cos(angle_radians);
+ double delta_y = -std::sin(angle_radians); // In memory is revered
+ double max_delta = std::max(std::abs(delta_x), std::abs(delta_y));
+ double amplification = 1.0 / max_delta;
+ delta_x *= amplification;
+ delta_y *= amplification;
+
+ for (int i = 1; i <= num_pixels; ++i)
+ line_points.push_back(std::make_pair(
+ static_cast<int>(std::floor(delta_x * static_cast<double>(i) + 0.5)),
+ static_cast<int>(std::floor(delta_y * static_cast<double>(i) + 0.5))));
+
+ return std::move(line_points);
+}
+
+}} //namespace caspar::image
\ No newline at end of file
*
* @return the x-y pairs.
*/
-std::vector<std::pair<int, int>> get_line_points(int num_pixels, double angle_radians)
-{
- std::vector<std::pair<int, int>> line_points;
- line_points.reserve(num_pixels);
-
- double delta_x = std::cos(angle_radians);
- double delta_y = -std::sin(angle_radians); // In memory is revered
- double max_delta = std::max(std::abs(delta_x), std::abs(delta_y));
- double amplification = 1.0 / max_delta;
- delta_x *= amplification;
- delta_y *= amplification;
-
- for (int i = 1; i <= num_pixels; ++i)
- line_points.push_back(std::make_pair(
- static_cast<int>(std::floor(delta_x * static_cast<double>(i) + 0.5)),
- static_cast<int>(std::floor(delta_y * static_cast<double>(i) + 0.5))));
-
- return std::move(line_points);
-}
+std::vector<std::pair<int, int>> get_line_points(int num_pixels, double angle_radians);
/**
* Directionally blur a source image modelling the ImageView concept and store
for(unsigned short layerIndex = 0; layerIndex < layers_count; ++layerIndex)
{
- layers_.push_back(Layer::create(input_)); //each layer reads it's "layer record"
+ layers_.push_back(layer::create(input_)); //each layer reads it's "layer record"
//std::clog << "Added layer: " << std::string(layers_[layerIndex]->name().begin(), layers_[layerIndex]->name().end()) << std::endl;
}
#include "layer.h"
#include "descriptor.h"
-#include <iostream>
+//#include <iostream>
+
+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>());
+ 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();
throw PSDFileFormatException();
unsigned long blendModeKey = stream.read_long();
- result->blendMode_ = IntToBlendMode(blendModeKey);
+ result->blend_mode_ = int_to_blend_mode(blendModeKey);
result->opacity_ = stream.read_byte();
result->baseClipping_ = stream.read_byte() == 1 ? false : true;
unsigned long extraDataSize = stream.read_long();
long position1 = stream.current_position();
- result->read_mask_data(stream);
- result->ReadBlendingRanges(stream);
+ result->mask_.read_mask_data(stream);
+ result->read_blending_ranges(stream);
result->name_ = stream.read_pascal_string(4);
return result;
}
-void Layer::read_mask_data(BEFileInputStream& stream)
+void layer::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();
-
- 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();
- }
- }
+ case 0:
+ break;
+
+ case 20:
+ mask_.rect_.top = stream.read_long();
+ mask_.rect_.left = stream.read_long();
+ mask_.rect_.bottom = stream.read_long();
+ mask_.rect_.right = stream.read_long();
+
+ mask_.default_value_ = stream.read_byte();
+ mask_.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"
+ mask_.flags_ = stream.read_byte();
+ mask_.default_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();
+ break;
+
+ default:
+ stream.discard_bytes(length);
+ break;
+ };
}
//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)
return NULL;
}
-void Layer::read_channel_data(BEFileInputStream& stream)
+void layer::read_channel_data(BEFileInputStream& stream)
{
image8bit_ptr img;
image8bit_ptr mask;
+
+ 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;
- if(!get_channel(psd::Transparency))
+ if(!has_transparency)
std::memset(img->data(), (unsigned long)(255<<24), rect_.width()*rect_.height());
}
- 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);
+ 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;
}
}
}
+ 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))
}
}
-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();
namespace caspar { namespace psd {
-class Layer;
-typedef std::shared_ptr<Layer> layer_ptr;
+class layer;
+typedef std::shared_ptr<layer> layer_ptr;
-class Layer
+class layer
{
public:
- Layer() : blendMode_(InvalidBlendMode), opacity_(255), baseClipping_(false), flags_(0), masks_(0)
+ class layer_mask
+ {
+ friend class layer;
+ public:
+
+ bool enabled() { return !((flags_ & 2) == 2); }
+ void read_mask_data(BEFileInputStream&);
+
+ private:
+ char mask_id_;
+ image8bit_ptr mask_;
+ psd::rect<long> rect_;
+ unsigned char default_value_;
+ unsigned char flags_;
+ };
+
+ layer() : blend_mode_(InvalidBlendMode), opacity_(255), baseClipping_(false), flags_(0), masks_(0)
{}
- static std::shared_ptr<Layer> create(BEFileInputStream&);
+ static std::shared_ptr<layer> create(BEFileInputStream&);
void read_channel_data(BEFileInputStream&);
const std::wstring& name() const
{
return name_;
}
- const rect<long>& rect() const
+ const psd::rect<long>& rect() const
{
return rect_;
}
+ unsigned char opacity() const
+ {
+ return opacity_;
+ }
+ bool visible() { return (flags_ & 2) == 2; }
+
const image8bit_ptr& image() const { return image_; }
- const image8bit_ptr& mask() const { return mask_; }
+ const image8bit_ptr& mask() const { return mask_.mask_; }
private:
- channel_ptr get_channel(ChannelType);
- void read_mask_data(BEFileInputStream&);
- void ReadBlendingRanges(BEFileInputStream&);
-
- 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);
+ channel_ptr get_channel(channel_type);
+ void read_blending_ranges(BEFileInputStream&);
caspar::psd::rect<long> rect_;
std::vector<channel_ptr> channels_;
- BlendMode blendMode_;
+ blend_mode blend_mode_;
unsigned char opacity_;
bool baseClipping_;
unsigned char flags_;
std::wstring name_;
+ char masks_;
- unsigned char masks_;
- caspar::psd::rect<long> mask_rect_;
- unsigned char default_mask_value_;
- unsigned char mask_flags_;
+ layer_mask mask_;
image8bit_ptr image_;
- image8bit_ptr mask_;
};
} //namespace psd
namespace caspar { namespace psd {
-BlendMode IntToBlendMode(unsigned long x)
+blend_mode int_to_blend_mode(unsigned long x)
{
- BlendMode retVal = InvalidBlendMode;
+ blend_mode retVal = InvalidBlendMode;
switch(x)
{
case Normal: retVal = Normal; break;
return retVal;
}
-std::wstring BlendModeToString(BlendMode b)
+std::wstring blend_mode_to_string(blend_mode b)
{
std::wstring retVal = L"Invalid";
switch(b)
}
};
-enum ChannelType
+enum channel_type
{
TotalUserMask = -3,
UserMask = -2,
ColorBlue = 2
};
-enum BlendMode
+enum blend_mode
{
InvalidBlendMode = -1,
Normal = 'norm',
ColorBurn = 'idiv'
};
-BlendMode IntToBlendMode(unsigned long x);
-std::wstring BlendModeToString(BlendMode b);
+blend_mode int_to_blend_mode(unsigned long x);
+std::wstring blend_mode_to_string(blend_mode b);
enum color_mode
{
spl::shared_ptr<core::scene::scene_producer> root(spl::make_shared<core::scene::scene_producer>(doc.width(), doc.height()));
auto layers_end = doc.layers().end();
- int cnt = 0;
for(auto it = doc.layers().begin(); it != layers_end; ++it)
{
if((*it)->image())
memcpy(frame.image_data().data(), (*it)->image()->data(), frame.image_data().size());
auto layer_producer = core::create_const_producer(core::draw_frame(std::move(frame)), (*it)->rect().width(), (*it)->rect().height());
- root->create_layer(layer_producer, (*it)->rect().left, (*it)->rect().top);
+ auto& new_layer = root->create_layer(layer_producer, (*it)->rect().left, (*it)->rect().top);
+ new_layer.adjustments.opacity.set((*it)->opacity() / 255.0);
+ new_layer.hidden.set(!(*it)->visible());
}
}
return root;
-// return spl::make_shared<scene_producer>(frame_factory, filename + *ext);
}
}}
\ No newline at end of file
<ClCompile Include="psd.cpp" />
<ClCompile Include="resource.cpp" />
<ClCompile Include="util\bigendian_file_input_stream.cpp" />
+ <ClCompile Include="util\pdf_reader.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="channel.h" />
<ClInclude Include="psd.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="util\bigendian_file_input_stream.h" />
+ <ClInclude Include="util\pdf_reader.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\common\common.vcxproj">
<ProjectReference Include="..\..\core\core.vcxproj">
<Project>{79388c20-6499-4bf6-b8b9-d8c33d7d4ddd}</Project>
</ProjectReference>
+ <ProjectReference Include="..\image\image.vcxproj">
+ <Project>{3e11ff65-a9da-4f80-87f2-a7c6379ed5e2}</Project>
+ </ProjectReference>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{866A164B-6F7A-450E-8452-C6AE4E176436}</ProjectGuid>
<ClCompile Include="psd.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="util\pdf_reader.cpp">
+ <Filter>Source Files\util</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="util\bigendian_file_input_stream.h">
<ClInclude Include="psd.h">
<Filter>Source Files</Filter>
</ClInclude>
+ <ClInclude Include="util\pdf_reader.h">
+ <Filter>Source Files\util</Filter>
+ </ClInclude>
</ItemGroup>
</Project>
\ No newline at end of file
#include "bigendian_file_input_stream.h"
#include "..\..\..\common\utf.h"
#include <common/endian.h>
-#include <boost/locale.hpp>
namespace caspar { namespace psd {
unsigned short SWAP16(unsigned short inVal)
{
return caspar::swap_byte_order(inVal);
- //unsigned short outVal;
- //__asm{
- // mov ax, inVal;
- // mov bl, ah;
- // mov bh, al;
- // mov outVal, bx;
- //}
- //return outVal;
}
unsigned long SWAP32(unsigned long inVal)
{
return caspar::swap_byte_order(inVal);
- //unsigned long outVal;
- //__asm{
- // mov eax, inVal; //1 2 ah = 3 al = 4
- // mov bl, ah;
- // mov bh, al;
-
- // ror ebx, 16;
- // ror eax, 16;
-
- // mov bl, ah;
- // mov bh, al;
- // mov outVal, ebx;
- //}
- //return outVal;
}
unsigned short BEFileInputStream::read_short()
unsigned char padded_bytes = (padding - ((strLength+1) % padding)) % padding;
this->discard_bytes(padded_bytes);
- return boost::locale::conv::utf_to_utf<wchar_t>(strBuffer);
+ return caspar::u16(strBuffer);
}
result.append(1, read_byte());
}
- return boost::locale::conv::utf_to_utf<wchar_t>(result);
+ return caspar::u16(result);
}
} //namespace psd
#include "stdafx.h"
#include "../../modules/psd/doc.h"
+#include "../../modules/psd/layer.h"
+#include "../../common/utf.h"
+
#include <sstream>
+#include <iostream>
+
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/json_parser.hpp>
+
int _tmain(int argc, _TCHAR* argv[])
{
caspar::psd::psd_document doc;
doc.parse(L"C:\\Lokala Filer\\Utveckling\\CasparCG\\Server 2.1\\test\\data\\test1.psd");
- std::wstringstream trace;
-
- trace << L"<doc filename=\"" << doc.filename() << L"\" channel_count=\"" << doc.channels_count() << L"\" width=\"" << doc.width() << "\"
int a = 42;
+
+ //std::wstringstream trace;
+
+ //trace << L"<doc filename='" << doc.filename() << L"' color_mode='" << caspar::psd::color_mode_to_string(doc.color_mode()) << L"' color_depth='" << doc.color_depth() << L"' channel_count='" << doc.channels_count() << L"' width='" << doc.width() << L"' height='" << doc.height() << L"'>" << std::endl;
+
+ //
+ //auto end = doc.layers().end();
+ //for(auto it = doc.layers().begin(); it != end; ++it)
+ //{
+ // caspar::psd::layer_ptr layer = (*it);
+ // trace << L" <layer name='" << layer->name() << L"' opacity='" << layer->opacity() << L"'>" << std::endl;
+ // if(layer->image())
+ // trace << L" <bounding-box left='" << layer->rect().left << "' top='" << layer->rect().top << "' right='" << layer->rect().right << "' bottom='" << layer->rect().bottom << "' />" << std::endl;
+ // if(layer->mask())
+ // trace << L" <mask default-value='" << layer->default_mask_value() << "' left='" << layer->mask_rect().left << "' top='" << layer->mask_rect().top << "' right='" << layer->mask_rect().right << "' bottom='" << layer->mask_rect().bottom << "' />" << std::endl;
+ // trace << L" </layer>" << std::endl;
+ //}
+
+ //trace << L"</doc>" << std::endl;
+
+ //std::cout << caspar::u8(trace.str());
return 0;
}
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<LibraryPath>C:\Lokala Filer\Utveckling\CasparCG\Server 2.1\dependencies64\boost\stage\lib;$(LibraryPath)</LibraryPath>
+ <IncludePath>C:\Lokala Filer\Utveckling\CasparCG\Server 2.1\dependencies64\boost;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
- <ItemGroup>
- <None Include="ReadMe.txt" />
- </ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClCompile Include="stdafx.cpp" />
</ItemGroup>
<ItemGroup>
+ <ProjectReference Include="..\..\common\common.vcxproj">
+ <Project>{02308602-7fe0-4253-b96e-22134919f56a}</Project>
+ </ProjectReference>
<ProjectReference Include="..\..\modules\psd\psd.vcxproj">
<Project>{866a164b-6f7a-450e-8452-c6ae4e176436}</Project>
<Private>true</Private>
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
- <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
- </Filter>
- <Filter Include="Header Files">
- <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
- <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
- </Filter>
- <Filter Include="Resource Files">
- <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
- <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;h;hpp</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
- <None Include="ReadMe.txt" />
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="stdafx.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="targetver.h">
- <Filter>Header Files</Filter>
+ <Filter>Source Files</Filter>
+ </ClInclude>
+ <ClInclude Include="stdafx.h">
+ <Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>