Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions PhotoshopAPI/src/Core/TaggedBlocks/BlendFillTaggedBlock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#pragma once

#include "Macros.h"
#include "TaggedBlock.h"

#include "Core/Struct/File.h"
#include "Core/Struct/Signature.h"
#include "Util/ProgressCallback.h"
#include "Util/Enum.h"
#include "Core/FileIO/LengthMarkers.h"

#include <variant>

PSAPI_NAMESPACE_BEGIN

struct BlendFillTaggedBlock : TaggedBlock
{
uint8_t m_Fill = 255u;

BlendFillTaggedBlock() = default;

explicit BlendFillTaggedBlock(const uint8_t value) {m_Fill = value;};

void read(File& document, const uint64_t offset, const Signature signature, [[maybe_unused]] const uint16_t padding = 1u)
{
m_Key = Enum::TaggedBlockKey::lrSheetColorSetting;
m_Offset = offset;
m_Signature = signature;

if (const auto length = ReadBinaryData<uint32_t>(document); length != 4u)
{
throw std::runtime_error(
std::format(
"Error while reading BlendFillTaggedBlock tagged block, expected exactly 4 bytes of size but instead"
" got {}", length
)
);
}

m_Fill = ReadBinaryData<uint8_t>(document);
document.skip(3u);
}
void write(File& document, [[maybe_unused]] const FileHeader& header, [[maybe_unused]] ProgressCallback& callback, const uint16_t padding = 1u) override
{
WriteBinaryData<uint32_t>(document, Signature("8BIM").m_Value);
WriteBinaryData<uint32_t>(document, Signature("iOpa").m_Value);
Impl::ScopedLengthBlock<uint32_t> len_block(document, padding);

WriteBinaryData<uint8_t>(document, m_Fill);
WritePadddingBytes(document, 3u);
}
};

PSAPI_NAMESPACE_END
13 changes: 12 additions & 1 deletion PhotoshopAPI/src/Core/TaggedBlocks/TaggedBlockStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include "Macros.h"
#include "Core/FileIO/Read.h"
#include "Core/FileIO/Write.h"
#include "Logger.h"
#include "StringUtil.h"

Expand All @@ -18,6 +17,8 @@
#include "TypeToolTaggedBlock.h"
#include "UnicodeLayerNameTaggedBlock.h"
#include "SheetColorTaggedBlock.h"
#include "BlendFillTaggedBlock.h"


PSAPI_NAMESPACE_BEGIN

Expand Down Expand Up @@ -64,6 +65,7 @@ template std::shared_ptr<PlacedLayerDataTaggedBlock> TaggedBlockStorage::getTagg
template std::shared_ptr<LinkedLayerTaggedBlock> TaggedBlockStorage::getTaggedBlockView(const Enum::TaggedBlockKey key) const;
template std::shared_ptr<TypeToolTaggedBlock> TaggedBlockStorage::getTaggedBlockView(const Enum::TaggedBlockKey key) const;
template std::shared_ptr<SheetColorTaggedBlock> TaggedBlockStorage::getTaggedBlockView(const Enum::TaggedBlockKey key) const;
template std::shared_ptr<BlendFillTaggedBlock> TaggedBlockStorage::getTaggedBlockView(const Enum::TaggedBlockKey key) const;


template <typename T>
Expand Down Expand Up @@ -93,6 +95,7 @@ template std::shared_ptr<PlacedLayerDataTaggedBlock> TaggedBlockStorage::getTagg
template std::shared_ptr<LinkedLayerTaggedBlock> TaggedBlockStorage::getTaggedBlockView() const;
template std::shared_ptr<TypeToolTaggedBlock> TaggedBlockStorage::getTaggedBlockView() const;
template std::shared_ptr<SheetColorTaggedBlock> TaggedBlockStorage::getTaggedBlockView() const;
template std::shared_ptr<BlendFillTaggedBlock> TaggedBlockStorage::getTaggedBlockView() const;


template <typename T>
Expand Down Expand Up @@ -124,6 +127,7 @@ template std::vector<std::shared_ptr<PlacedLayerDataTaggedBlock>> TaggedBlockSto
template std::vector<std::shared_ptr<LinkedLayerTaggedBlock>> TaggedBlockStorage::get_tagged_blocks() const;
template std::vector<std::shared_ptr<TypeToolTaggedBlock>> TaggedBlockStorage::get_tagged_blocks() const;
template std::vector<std::shared_ptr<SheetColorTaggedBlock>> TaggedBlockStorage::get_tagged_blocks() const;
template std::vector<std::shared_ptr<BlendFillTaggedBlock>> TaggedBlockStorage::get_tagged_blocks() const;



Expand Down Expand Up @@ -225,6 +229,13 @@ const std::shared_ptr<TaggedBlock> TaggedBlockStorage::readTaggedBlock(File& doc
this->m_TaggedBlocks.push_back(lrSheetColorSetting);
return lrSheetColorSetting;
}
else if (taggedBlock.value() == Enum::TaggedBlockKey::lrBlendFill)
{
auto block = std::make_shared<BlendFillTaggedBlock>();
block->read(document, offset, signature, padding);
this->m_TaggedBlocks.push_back(block);
return block;
}
else
{
auto baseTaggedBlock = std::make_shared<TaggedBlock>();
Expand Down
56 changes: 38 additions & 18 deletions PhotoshopAPI/src/LayeredFile/LayerTypes/Layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Core/TaggedBlocks/PlacedLayerTaggedBlock.h"
#include "Core/TaggedBlocks/ProtectedSettingTaggedBlock.h"
#include "Core/TaggedBlocks/ReferencePointTaggedBlock.h"
#include "Core/TaggedBlocks/BlendFillTaggedBlock.h"
#include "Core/TaggedBlocks/UnicodeLayerNameTaggedBlock.h"

#include "MaskDataMixin.h"
Expand Down Expand Up @@ -127,6 +128,20 @@ struct Layer : public MaskMixin<T>
m_Opacity = static_cast<uint8_t>(value * 255.0f);
}

/// The layers' fill value
float fill() const noexcept { return static_cast<float>(m_Fill) / 255; }
/// The layers' fill value.
void fill(float value) noexcept
{
if (value < 0.0f || value > 1.0f)
{
PSAPI_LOG_WARNING("Layer", "Encountered fill value not between 0-1. Clamping this to fit into that range");
}
value = std::clamp<float>(value, 0.0f, 1.0f);

m_Fill = static_cast<uint8_t>(value * 255.0f);
}

/// The layers' width from 0 - 300,000
virtual uint32_t width() const noexcept { return m_Width; }
/// The layers' width from 0 - 300,000
Expand Down Expand Up @@ -222,8 +237,7 @@ struct Layer : public MaskMixin<T>
// Pass along the unparsed blocks
m_UnparsedBlocks = additional_layer_info.get_base_tagged_blocks();

auto section_divider_block = additional_layer_info.getTaggedBlock<LrSectionTaggedBlock>(Enum::TaggedBlockKey::lrSectionDivider);
if (section_divider_block.has_value() && section_divider_block.value()->m_BlendMode.has_value())
if (auto section_divider_block = additional_layer_info.getTaggedBlock<LrSectionTaggedBlock>(Enum::TaggedBlockKey::lrSectionDivider); section_divider_block.has_value() && section_divider_block.value()->m_BlendMode.has_value())
{
m_BlendMode = section_divider_block.value()->m_BlendMode.value();
}
Expand All @@ -233,19 +247,17 @@ struct Layer : public MaskMixin<T>
}

// Parse the layer protection settings
auto protectionSettings = additional_layer_info.getTaggedBlock<ProtectedSettingTaggedBlock>(Enum::TaggedBlockKey::lrProtectedSetting);
if (protectionSettings)
if (auto protection_settings = additional_layer_info.getTaggedBlock<ProtectedSettingTaggedBlock>(Enum::TaggedBlockKey::lrProtectedSetting))
{
m_IsLocked = protectionSettings.value()->m_IsLocked;
m_IsLocked = protection_settings.value()->m_IsLocked;
}
else
{
m_IsLocked = false;
}

// Parse the layer color
auto layer_sheet_color_block = additional_layer_info.getTaggedBlock<SheetColorTaggedBlock>(Enum::TaggedBlockKey::lrSheetColorSetting);
if (layer_sheet_color_block.has_value())
if (auto layer_sheet_color_block = additional_layer_info.getTaggedBlock<SheetColorTaggedBlock>(Enum::TaggedBlockKey::lrSheetColorSetting); layer_sheet_color_block.has_value())
{
m_LayerColor = layer_sheet_color_block.value()->m_Color;
}
Expand Down Expand Up @@ -323,20 +335,23 @@ struct Layer : public MaskMixin<T>
if (layerRecord.m_AdditionalLayerInfo.has_value())
{
// Get the reference point (if it is there)
auto& additionalLayerInfo = layerRecord.m_AdditionalLayerInfo.value();
auto reference_point = additionalLayerInfo.get_tagged_block<ReferencePointTaggedBlock>();
if (reference_point)
auto& additional_layer_info = layerRecord.m_AdditionalLayerInfo.value();
if (auto reference_point = additional_layer_info.get_tagged_block<ReferencePointTaggedBlock>())
{
m_ReferencePointX.emplace(reference_point->m_ReferenceX);
m_ReferencePointY.emplace(reference_point->m_ReferenceY);
}

// Get the unicode layer name (if it is there) and override the pascal string name
auto unicode_name = additionalLayerInfo.get_tagged_block<UnicodeLayerNameTaggedBlock>();
if (unicode_name)
if (auto unicode_name = additional_layer_info.get_tagged_block<UnicodeLayerNameTaggedBlock>())
{
m_LayerName = unicode_name->m_Name.string();
}

if (auto blend_fill = additional_layer_info.get_tagged_block<BlendFillTaggedBlock>())
{
m_Fill = blend_fill->m_Fill;
}
}
}

Expand Down Expand Up @@ -405,6 +420,8 @@ struct Layer : public MaskMixin<T>

/// 0 - 255 despite the appearance being 0-100 in photoshop
uint8_t m_Opacity{};
/// 0 - 255 despite the appearance being 0-100 in photoshop
uint8_t m_Fill{};

uint32_t m_Width{};

Expand Down Expand Up @@ -477,15 +494,18 @@ struct Layer : public MaskMixin<T>

// Generate our unicode layer name block, we always include this as its size is trivial and this avoids
// any issues with names being truncated
auto unicodeNamePtr = std::make_shared<UnicodeLayerNameTaggedBlock>(m_LayerName, static_cast<uint8_t>(4u));
block_vec.push_back(unicodeNamePtr);
const auto unicode_layer_name_tagged_block = std::make_shared<UnicodeLayerNameTaggedBlock>(m_LayerName, static_cast<uint8_t>(4u));
block_vec.push_back(unicode_layer_name_tagged_block);

// Generate our LockedSettings Tagged block
auto protectionSettingsPtr = std::make_shared<ProtectedSettingTaggedBlock>(m_IsLocked);
block_vec.push_back(protectionSettingsPtr);
const auto protected_setting_tagged_block = std::make_shared<ProtectedSettingTaggedBlock>(m_IsLocked);
block_vec.push_back(protected_setting_tagged_block);

const auto sheet_color_tagged_block = std::make_shared<SheetColorTaggedBlock>(m_LayerColor);
block_vec.push_back(sheet_color_tagged_block);

auto lr_color_ptr = std::make_shared<SheetColorTaggedBlock>(m_LayerColor);
block_vec.push_back(lr_color_ptr);
const auto blend_fill_tagged_block = std::make_shared<BlendFillTaggedBlock>(m_Fill);
block_vec.push_back(blend_fill_tagged_block);

return block_vec;
}
Expand Down
4 changes: 4 additions & 0 deletions PhotoshopAPI/src/Util/Enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,8 @@ namespace Enum
lrSavingMergedTransparency, // Holds no data, just indicates channel Image data section includes transparency (needs to be tested)
lrPixelSourceData, // Data for 3d or video layers
lrUserMask,
lrObjectBasedFXInfo,
lrBlendFill,
// 16- and 32-bit files store their layer records under these tagged blocks at the end of the layer
// and mask information section
Lr16,
Expand Down Expand Up @@ -880,6 +882,8 @@ namespace Enum
{"Mt32", TaggedBlockKey::lrSavingMergedTransparency},
{"PxSD", TaggedBlockKey::lrPixelSourceData},
{"LMsk", TaggedBlockKey::lrUserMask},
{"lfx2", TaggedBlockKey::lrObjectBasedFXInfo },
{"iOpa", TaggedBlockKey::lrBlendFill},
{"Lr16", TaggedBlockKey::Lr16},
{"Lr32", TaggedBlockKey::Lr32},
{"Layr", TaggedBlockKey::Layr},
Expand Down
Binary file added PhotoshopTest/documents/BlendFill/blend_fill.psd
Binary file not shown.
28 changes: 28 additions & 0 deletions PhotoshopTest/src/TestBlendFill/TestBlendFill.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "doctest.h"

#include "Macros.h"
#include "LayeredFile/LayeredFile.h"

#include <vector>


// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
TEST_CASE("Blend fill round tripping")
{
using namespace NAMESPACE_PSAPI;

{
auto file = LayeredFile<bpp8_t>::read("documents/BlendFill/blend_fill.psd");
const auto layer_ptr = file.layers().at(0);
// We choose fairly aggressive epsilon as this is internally represented by a
CHECK(layer_ptr->fill() == doctest::Approx(0.51f).epsilon(1e-2));
LayeredFile<bpp8_t>::write(std::move(file), "documents/out/blend_fill_out.psd");
}

{
auto file = LayeredFile<bpp8_t>::read("documents/out/blend_fill_out.psd");
const auto layer_ptr = file.layers().at(0);
CHECK(layer_ptr->fill() == doctest::Approx(0.51f).epsilon(1e-2));
}
}
24 changes: 24 additions & 0 deletions python/py_module/photoshopapi/_layer.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ class Layer_8bit:
def opacity(self: Layer_8bit, value: float) -> None:
...

@property
def fill(self: Layer_8bit) -> float:
...

@fill.setter
def fill(self: Layer_8bit, value: float) -> None:
...

@property
def width(self: Layer_8bit) -> int:
...
Expand Down Expand Up @@ -201,6 +209,14 @@ class Layer_16bit:
def opacity(self: Layer_16bit, value: float) -> None:
...

@property
def fill(self: Layer_16bit) -> float:
...

@fill.setter
def fill(self: Layer_16bit, value: float) -> None:
...

@property
def width(self: Layer_16bit) -> int:
...
Expand Down Expand Up @@ -361,6 +377,14 @@ class Layer_32bit:
def opacity(self: Layer_32bit, value: float) -> None:
...

@property
def fill(self: Layer_32bit) -> float:
...

@fill.setter
def fill(self: Layer_32bit, value: float) -> None:
...

@property
def width(self: Layer_32bit) -> int:
...
Expand Down
3 changes: 3 additions & 0 deletions python/src/Layers/DeclareLayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ void declare_layer(py::module& m, const std::string& extension) {
The blend mode of the layer, 'Passthrough' is reserved for group layers
opacity : float
The layers opacity from 0.0 - 1.0
fill : float
The layers fill from 0.0 - 1.0
width : int
The width of the layer ranging up to 30,000 for PSD and 300,000 for PSB,
this does not have to match the files width
Expand Down Expand Up @@ -93,6 +95,7 @@ void declare_layer(py::module& m, const std::string& extension) {
layer.def_property("name", &Class::name, &Class::name);
layer.def_property("blend_mode", [](const Class& self) { return self.blendmode(); }, [](Class& self, Enum::BlendMode blendmode) { self.blendmode(blendmode); });
layer.def_property("opacity", [](const Class& self) { return self.opacity(); }, [](Class& self, float opacity) { self.opacity(opacity); });
layer.def_property("fill", [](const Class& self) { return self.fill(); }, [](Class& self, float fill) { self.fill(fill); });
layer.def_property("width", [](const Class& self) { return self.width(); }, [](Class& self, uint32_t width) { self.width(width); });
layer.def_property("height", [](const Class& self) { return self.height(); }, [](Class& self, uint32_t height) { self.height(height); });
layer.def_property("center_x", [](const Class& self) { return self.center_x(); }, [](Class& self, float center_x) { self.center_x(center_x); });
Expand Down
Loading