### Set up Python Virtual Environment and Install Requirements
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopBenchmark/README.md
Create a virtual environment and install necessary Python packages for running benchmarks on Windows.
```bash
py -m venv .venv
.venv/Scripts/activate.bat
py -m pip install -r requirements.txt
```
--------------------------------
### Install Python Dependencies for Docs
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/building.md
Install the required Python packages for building the documentation by navigating to the docs/doxygen directory and running pip install with the requirements file.
```bash
$ cd
$ cd docs/doxygen
$ py -m pip install -r requirements.txt
```
--------------------------------
### Navigate to PhotoshopBenchmark Directory
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopBenchmark/README.md
Change the current directory to the PhotoshopBenchmark folder to begin setup.
```bash
cd /PhotoshopBenchmark
```
--------------------------------
### Asynchronous File Write with Progress Callback (C++)
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/examples/progress_callbacks.md
This C++ example shows how to write a large Photoshop file asynchronously while using a progress callback to continuously query the state and progress of the file read operation. It's designed to emulate longer write times by generating large amounts of compressible image data and adding multiple layers. Note that this example is C++ specific due to the `ProgressCallback` functionality.
```cpp
/*
Example of writing a large PhotoshopFile asynchronously while using a callback to continuously query the state and progress of the file read operation
WARNING: This will write a rather large file to your disk ~1GB
This example does not have a python counterpart as we do not have an equivalent counterpart for the ProgressCallback& in python, if you would like
to suggest a change that would implement this please do so by creating a github pull request.
*/
#include "PhotoshopAPI.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace NAMESPACE_PSAPI;
// Simple wrapper function which simply executes the write asynchronously, this could also just be a lambda
template
void AsyncWriteFile(LayeredFile&& document, std::filesystem::path filePath, ProgressCallback& callback)
{
LayeredFile::write(std::move(document), filePath, callback);
}
int main()
{
uint32_t width = 4096;
uint32_t height = 4096;
auto document = LayeredFile(Enum::ColorMode::RGB, width, height);
// Generate some random image data which will compress badly to emulate longer write times
std::mt19937 rng(static_cast(std::time(0)));
std::uniform_real_distribution dist(0.0, 1.0);
std::vector imgData(width * height);
for (auto& value : imgData)
{
value = dist(rng);
}
// Add the layer 5 times to make execution longer
for (int i = 0; i < 5; ++i)
{
std::unordered_map > channelMap;
channelMap[Enum::ChannelID::Red] = imgData;
channelMap[Enum::ChannelID::Green] = imgData;
channelMap[Enum::ChannelID::Blue] = imgData;
ImageLayer::Params layerParams = {};
layerParams.name = "Layer_" + std::to_string(i);
layerParams.width = width;
layerParams.height = height;
auto layer = std::make_shared>(std::move(channelMap), layerParams);
document.add_layer(layer);
}
// Launch the file read asynchronously while attaching a callback which we pass by reference
ProgressCallback callback{};
auto future = std::async(std::launch::async,
&AsyncWriteFile, std::move(document), "ProgressCallbackExample.psd", std::ref(callback));
// Simulate some kind of loop where we continuously query the state of the progress until done
while (true)
{
if (future.valid())
if (future.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
std::cout << "Writing of file " << callback.getProgress() * 100 << "% completed. Current task: " << callback.getTask() << std::endl;
break;
}
std::cout << "Writing of file " << callback.getProgress()*100 << "% completed. Current task: " << callback.getTask() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
std::cout << "Finished writing file: 'ProgressCallbackExample.psd'" << std::endl;
}
```
--------------------------------
### Create and Write PSD File in Python
Source: https://github.com/emildohne/photoshopapi/blob/master/README.md
Demonstrates creating an 8-bit RGB layered PSD file using numpy for image data, adding a red image layer with 50% opacity, and writing it to disk. Ensure the `photoshopapi` library is installed.
```python
import os
import numpy as np
import photoshopapi as psapi
# Initialize some constants that we will need throughout the program
width = 64
height = 64
color_mode = psapi.enum.ColorMode.rgb
# Generate our LayeredFile instance
document = psapi.LayeredFile_8bit(color_mode, width, height)
img_data = np.zeros((3, height, width), np.uint8)
img_data[0] = 255
# When creating an image layer the width and height parameter are required if its not a zero sized layer
img_layer = psapi.ImageLayer_8bit(img_data, "Layer Red", width=width, height=height)
document.add_layer(img_layer)
# Similar to the C++ version we can adjust parameters of the layer after it has been added to the document
# as long as it happens before we write to disk
img_layer.opacity = .5
document.write(os.path.join(os.path.dirname(__file__), "WriteSimpleFile.psd"))
```
--------------------------------
### Create Simple Document in Python
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/examples/create_simple_document.md
Use this Python snippet to create an 8-bit RGB document with a single red-tinted layer. Ensure the photoshopapi library is installed.
```python
# Example of creating a simple document with a single layer using the PhotoshopAPI.
import os
import numpy as np
import photoshopapi as psapi
def main() -> None:
# Initialize some constants that we will need throughout the program
width = 64
height = 64
color_mode = psapi.enum.ColorMode.rgb
# Generate our LayeredFile instance
document = psapi.LayeredFile_8bit(color_mode, width, height)
img_data = np.zeros((3, height, width), np.uint8)
img_data[0] = 255
# When creating an image layer the width and height parameter are required if its not a zero sized layer
img_layer = psapi.ImageLayer_8bit(img_data, "Layer Red", width=width, height=height)
document.add_layer(img_layer)
# Similar to the C++ version we can adjust parameters of the layer after it has been added to the document
# as long as it happens before we write to disk
img_layer.opacity = .5
document.write(os.path.join(os.path.dirname(__file__), "WriteSimpleFile.psd"))
if __name__ == "__main__":
main()
```
--------------------------------
### Create Layered File with Groups (C++)
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/examples/create_groups.md
This C++ example shows how to initialize an 8-bit LayeredFile, create a group layer, add it to the document, and then add an image layer as a child of the group. It concludes by writing the document to a PSD file.
```cpp
/*
Example of creating a layered file with groups through the PhotoshopAPI.
*/
#include "PhotoshopAPI.h"
#include
#include
int main()
{
using namespace NAMESPACE_PSAPI;
// Initialize some constants that we will need throughout the program
static constexpr uint32_t width = 64u;
static constexpr uint32_t height = 64u;
// Create an 8-bit LayeredFile as our starting point, 8- 16- and 32-bit are fully supported
LayeredFile document = { Enum::ColorMode::RGB, width, height };
GroupLayer::Params groupParams = {};
groupParams.name = "Group";
// We don't need to specify a width or height if we do not have a mask channel
// As with image layers we can first add the group to the document root and modify the group after
auto groupLayer = std::make_shared>(groupParams);
document.add_layer(groupLayer);
// Create an image layer and insert it under the group
{
std::unordered_map > channelMap;
channelMap[Enum::ChannelID::Red] = std::vector(width * height, 255u);
channelMap[Enum::ChannelID::Green] = std::vector(width * height, 0u);
channelMap[Enum::ChannelID::Blue] = std::vector(width * height, 0u);
ImageLayer::Params layerParams = {};
layerParams.name = "Layer Red";
layerParams.width = width;
layerParams.height = height;
auto layer = std::make_shared>(
std::move(channelMap),
layerParams
);
// Adding the layer twice would be invalid and would raise a warning as each layer needs to be created uniquely
// document.addLayer(layer);
groupLayer->add_layer(document, layer);
}
// Convert to PhotoshopDocument and write to disk. Note that from this point onwards
// our LayeredFile instance is no longer usable
LayeredFile::write(std::move(document), "WriteGroupedFile.psd");
}
```
--------------------------------
### Create Layered File with Groups (Python)
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/examples/create_groups.md
This Python example demonstrates creating an 8-bit LayeredFile, adding a group layer, and then adding an image layer as a child of that group. The resulting file is saved as a PSD.
```python
# Example of creating a document with groups using the PhotoshopAPI.
import os
import numpy as np
import photoshopapi as psapi
def main() -> None:
# Initialize some constants that we will need throughout the program
width = 64
height = 64
color_mode = psapi.enum.ColorMode.rgb
# Generate our LayeredFile instance
document = psapi.LayeredFile_8bit(color_mode, width, height)
# Create an empty group layer, width and height are only relevant if we want to add a mask channel
group_layer = psapi.GroupLayer_8bit("Group")
document.add_layer(group_layer)
img_data = np.zeros((3, height, width), np.uint8)
img_data[0] = 255
# When creating an image layer the width and height parameter are required if its not a zero sized layer
img_layer = psapi.ImageLayer_8bit(img_data, "Layer Red", width=width, height=height)
# In this example we add the image layer under the group instead which requires us to pass the document as first
# argument. This is required for runtime checks that a layer isnt added twice
group_layer.add_layer(document, img_layer)
document.write(os.path.join(os.path.dirname(__file__), "WriteGroupedFile.psd"))
if __name__ == "__main__":
main()
```
--------------------------------
### Define Executable and Link Library
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopExamples/ExtendedSignature/CMakeLists.txt
Defines the main executable for the project and links it against the PhotoshopAPI library. Ensure the PhotoshopAPI is correctly installed or available in the build environment.
```cmake
add_executable(ExtendedSignature main.cpp)
target_link_libraries(ExtendedSignature PRIVATE PhotoshopAPI)
```
--------------------------------
### Define Executable and Link Library
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopExamples/SmartObjects/CMakeLists.txt
Defines the main executable for the project and links it with the PhotoshopAPI library. Ensure PhotoshopAPI is correctly installed or available in the build environment.
```cmake
add_executable(SmartObjects main.cpp)
target_link_libraries(SmartObjects PRIVATE PhotoshopAPI)
```
--------------------------------
### Install Compiled Module
Source: https://github.com/emildohne/photoshopapi/blob/master/python/CMakeLists.txt
Defines the installation rules for the compiled Python module. It specifies the destination directories for libraries, runtime components, and archives based on the CMAKE_INSTALL_LIBDIR and CMAKE_INSTALL_BINDIR variables.
```cmake
install(TARGETS photoshopapi_py
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
```
--------------------------------
### Add Executable and Link PhotoshopAPI
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopExamples/CreateSimpleDocument/CMakeLists.txt
Defines the executable target and links it with the PhotoshopAPI library. Ensure PhotoshopAPI is correctly installed or available in the build environment.
```cmake
add_executable(CreateSimpleDocument main.cpp)
target_link_libraries(CreateSimpleDocument PRIVATE PhotoshopAPI)
```
--------------------------------
### Build Executable and Link Photoshop API
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopExamples/ModifyLayerStructure/CMakeLists.txt
Defines the executable target and links it against the PhotoshopAPI library. This is a standard CMake setup for projects using the PhotoshopAPI.
```cmake
add_executable(ModifyLayerStructure main.cpp)
target_link_libraries(ModifyLayerStructure PRIVATE PhotoshopAPI)
```
--------------------------------
### Define Executable and Link Library
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopBenchmark/CMakeLists.txt
This snippet defines the executable name, includes all source files from the src directory, and links the executable against the PhotoshopAPI library. Ensure PhotoshopAPI is correctly installed or available in the build environment.
```cmake
file(GLOB_RECURSE MY_SOURCES CONFIGURE_DEPENDS "src/*.cpp")
add_executable(PhotoshopBenchmark ${MY_SOURCES})
target_link_libraries(PhotoshopBenchmark PRIVATE PhotoshopAPI)
```
--------------------------------
### Create and Write Simple PSD File (C++)
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/index.md
Demonstrates creating a simple document with a single layer, modifying its opacity, and writing it to disk. Requires PhotoshopAPI.h and standard library includes.
```cpp
/*
Example of creating a simple document with a single layer using the PhotoshopAPI.
*/
#include "PhotoshopAPI.h"
#include
#include
int main()
{
using namespace NAMESPACE_PSAPI;
// Initialize some constants that we will need throughout the program
const static uint32_t width = 64u;
const static uint32_t height = 64u;
// Create an 8-bit LayeredFile as our starting point, 8- 16- and 32-bit are fully supported
LayeredFile document = { Enum::ColorMode::RGB, width, height };
// Create our individual channels to add to our image layer. Keep in mind that all these 3 channels need to
// be specified for RGB mode
std::unordered_map > channelMap;
channelMap[Enum::ChannelID::Red] = std::vector(width * height, 255u);
channelMap[Enum::ChannelID::Green] = std::vector(width * height, 0u);
channelMap[Enum::ChannelID::Blue] = std::vector(width * height, 0u);
ImageLayer::Params layerParams = {};
layerParams.name = "Layer Red";
layerParams.width = width;
layerParams.height = height;
layerParams.display_color = Enum::LayerColor::blue;
auto layer = std::make_shared>(std::move(channelMap), layerParams);
document.add_layer(layer);
// It is perfectly legal to modify a layers properties even after it was added to the document as attributes
// are only finalized on export
layer->opacity(.5);
// Convert to PhotoshopFile and write to disk. Note that from this point onwards
// our LayeredFile instance is no longer usable
LayeredFile::write(std::move(document), "WriteSimpleFile.psd");
}
```
--------------------------------
### Get Text Layer Attributes (C++)
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/concepts/text-layers.md
Retrieves various attributes of a text layer, including its content, position, dimensions, and orientation. It also shows how to iterate through style and paragraph runs to get their specific properties.
```cpp
auto text = layer->text();
auto [x, y] = layer->position();
auto box_w = layer->box_width();
auto box_h = layer->box_height();
auto orientation = layer->orientation();
if (auto lengths = layer->style_run_lengths(); lengths.has_value()) {
for (size_t i = 0; i < lengths->size(); ++i) {
auto font_idx = layer->style_run_font(i);
auto size = layer->style_run_font_size(i);
auto fill = layer->style_run_fill_color(i);
auto char_dir = layer->style_run_character_direction(i);
}
}
if (auto p_lengths = layer->paragraph_run_lengths(); p_lengths.has_value()) {
for (size_t i = 0; i < p_lengths->size(); ++i) {
auto just = layer->paragraph_run_justification(i);
}
}
```
--------------------------------
### Create and Write PSD File in C++
Source: https://github.com/emildohne/photoshopapi/blob/master/README.md
Demonstrates creating an 8-bit RGB layered PSD file, adding a red image layer with 50% opacity, and writing it to disk. Ensure the `NAMESPACE_PSAPI` is correctly defined and included.
```cpp
using namespace NAMESPACE_PSAPI;
// Initialize some constants that we will need throughout the program
const static uint32_t width = 64u;
const static uint32_t height = 64u;
// Create an 8-bit LayeredFile as our starting point, 8- 16- and 32-bit are fully supported
LayeredFile document = { Enum::ColorMode::RGB, width, height };
// Create our individual channels to add to our image layer. Keep in mind that all these 3 channels need to
// be specified for RGB mode
std::unordered_map > channelMap;
channelMap[Enum::ChannelID::Red] = std::vector(width * height, 255u);
channelMap[Enum::ChannelID::Green] = std::vector(width * height, 0u);
channelMap[Enum::ChannelID::Blue] = std::vector(width * height, 0u);
ImageLayer::Params layerParams = {};
layerParams.name = "Layer Red";
layerParams.width = width;
layerParams.height = height;
auto layer = std::make_shared>(std::move(channelMap), layerParams);
document.add_layer(layer);
// It is perfectly legal to modify a layers properties even after it was added to the document as attributes
// are only finalized on export
layer->opacity(.5f);
// Convert to PhotoshopFile and write to disk. Note that from this point onwards
// our LayeredFile instance is no longer usable
LayeredFile::write(std::move(document), "WriteSimpleFile.psd");
```
--------------------------------
### Get High-Level Text Layer Attributes
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/concepts/text-layers.md
Retrieve common text layer properties such as the text content, position, dimensions, and orientation.
```python
text = layer.text
x, y = layer.position()
box_w = layer.box_width()
box_h = layer.box_height()
orientation = layer.orientation()
```
--------------------------------
### Add Project Subdirectories
Source: https://github.com/emildohne/photoshopapi/blob/master/CMakeLists.txt
Conditionally adds subdirectories for the main library, tests, benchmarks, examples, and documentation based on build options.
```cmake
# Projects
# --------------------------------------------------------------------------
if(PSAPI_BUILD_STATIC)
add_subdirectory (PhotoshopAPI)
endif()
if(PSAPI_BUILD_TESTS)
add_subdirectory (PhotoshopTest)
if (CMAKE_UNITY_BUILD)
set_target_properties(PhotoshopTest PROPERTIES UNITY_BUILD OFF)
endif()
endif()
if(PSAPI_BUILD_BENCHMARKS)
add_subdirectory (PhotoshopBenchmark)
endif()
if(PSAPI_BUILD_EXAMPLES)
add_subdirectory (PhotoshopExamples/AddLayerMasks)
add_subdirectory (PhotoshopExamples/CreateGroups)
add_subdirectory (PhotoshopExamples/CreateSimpleDocument)
add_subdirectory (PhotoshopExamples/ExtendedSignature)
add_subdirectory (PhotoshopExamples/ExtractImageData)
add_subdirectory (PhotoshopExamples/ModifyLayerStructure)
add_subdirectory (PhotoshopExamples/ProgressCallbacks)
add_subdirectory (PhotoshopExamples/ReplaceImageData)
add_subdirectory (PhotoshopExamples/SmartObjects)
endif()
if(PSAPI_BUILD_DOCS)
if(NOT PSAPI_BUILD_PYTHON)
message(WARNING "Building the documentation without the python bindings, this means the python bindings wont show up in your local copy")
endif()
add_subdirectory (docs/doxygen)
endif()
if(PSAPI_BUILD_PYTHON)
add_subdirectory (thirdparty/pybind11)
add_subdirectory (python)
endif()
```
--------------------------------
### Configure PhotoshopAPI Project
Source: https://github.com/emildohne/photoshopapi/blob/master/CMakeLists.txt
Initializes the project, sets the minimum CMake version, and defines the C++ standard.
```cmake
cmake_minimum_required (VERSION 3.20)
set(VCPKG_LIBRARY_LINKAGE static)
set (PhotoshopAPI_VERSION "0.9.1")
project ("PhotoshopAPIBuild" VERSION 0.9.1 LANGUAGES CXX)
if (MSVC)
add_compile_definitions(NOMINMAX)
endif()
# --------------------------------------------------------------------------
# Configurable options
option(PSAPI_BUILD_STATIC "Build a static library version of the PhotoshopAPI, currently must be on as we dont create a dynamic library target" ON)
option(PSAPI_USE_VCPKG "Build the extra dependencies using vckpg instead of system installed libraries" ON)
option(PSAPI_BUILD_TESTS "Build the tests associated with the PhotoshopAPI" ON)
option(PSAPI_BUILD_EXAMPLES "Build the examples associated with the PhotoshopAPI" ON)
option(PSAPI_BUILD_BENCHMARKS "Build the benchmarks associated with the PhotoshopAPI" ON)
option(PSAPI_BUILD_DOCS "Builds the documentation, requires some external installs which are documented in the README.md" OFF)
option(PSAPI_BUILD_PYTHON "Build the python bindings associated with the PhotoshopAPI" ON)
if (PSAPI_BUILD_PYTHON)
# Link in the msvc runtime so that users dont need vcredist when using the python bindings
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>")
endif()
# Build setup
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
set (CMAKE_CXX_STANDARD 20)
# Add the cmake/ folder so the FindSphinx module is found
# --------------------------------------------------------------------------
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
```
--------------------------------
### Create and Add Image Layer to LayeredFile
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/code/layeredfile.md
Shows how to create a new LayeredFile, define channels for an RGB image, and add it as a layer. Requires the PhotoshopAPI namespace.
```cpp
using namespace PhotoshopAPI;
const static uint32_t width = 64u;
const static uint32_t height = 64u;
LayeredFile document = { Enum::ColorMode::RGB, width, height };
// Create our individual channels to add to our image layer. Keep in mind that all these 3 channels
// need to be specified for RGB mode
std::unordered_map > channelMap;
channelMap[Enum::ChannelID::Red] = std::vector(width * height, 255u);
channelMap[Enum::ChannelID::Green] = std::vector(width * height, 0u);
channelMap[Enum::ChannelID::Blue] = std::vector(width * height, 0u);
ImageLayer::Params layerParams = {};
layerParams.layerName = "Layer Red";
layerParams.width = width;
layerParams.height = height;
auto layer = std::make_shared>(std::move(channelMap), layerParams);
document.add_layer(layer);
LayeredFile::write(std::move(layeredFile), "OutFile.psd");
```
--------------------------------
### Get Run-Level Text Attributes
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/concepts/text-layers.md
Iterate through style and paragraph runs to retrieve detailed attributes like font, size, color, and justification.
```python
for i, run_len in enumerate(layer.style_run_lengths() or []):
font_idx = layer.style_run_font(i)
font_ps = layer.font_postscript_name(font_idx) if font_idx is not None else None
size = layer.style_run_font_size(i)
fill = layer.style_run_fill_color(i)
stroke = layer.style_run_stroke_color(i)
underline = layer.style_run_underline(i)
char_dir = layer.style_run_character_direction(i)
for i, run_len in enumerate(layer.paragraph_run_lengths() or []):
just = layer.paragraph_run_justification(i)
```
--------------------------------
### Folder Structure for Benchmark Files
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopBenchmark/README.md
Illustrates the expected directory structure after downloading and placing benchmark files for PhotoshopAPI.
```python
PhotoshopAPI/
PhotoshopBenchmark/
documents/
write/
# Test .psd and .psb are here
read/
# Empty folder which will be populated on write
```
--------------------------------
### Create LayeredFile with Image Layer and Mask (C++)
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/examples/add_layer_masks.md
Initializes a LayeredFile, defines channels for an RGB image layer, creates a semi-grey mask channel, and adds the layer to the document before writing to disk. Ensure mask channel dimensions match layer dimensions.
```cpp
/*
Example of creating a simple document with a single layer and a mask using the PhotoshopAPI.
*/
#include "PhotoshopAPI.h"
#include
#include
int main()
{
using namespace NAMESPACE_PSAPI;
// Initialize some constants that we will need throughout the program
constexpr uint32_t width = 64u;
constexpr uint32_t height = 64u;
// Create an 8-bit LayeredFile as our starting point, 8- 16- and 32-bit are fully supported
LayeredFile document = { Enum::ColorMode::RGB, width, height };
// Create our individual channels to add to our image layer. Keep in mind that all these 3 channels need to
// be specified for RGB mode
std::unordered_map > channelMap;
channelMap[Enum::ChannelID::Red] = std::vector(width * height, 255u);
channelMap[Enum::ChannelID::Green] = std::vector(width * height, 0u);
channelMap[Enum::ChannelID::Blue] = std::vector(width * height, 0u);
// Create a mask channel which for now is just a semi grey channel. This channel for the time being
// needs to be the exact same size as the layer even though Photoshop officially supports masks being smaller
// or larger than channels
auto maskchannel = std::vector(width * height, 128u);
ImageLayer::Params layerParams = {};
layerParams.name = "Layer Red";
layerParams.width = width;
layerParams.height = height;
layerParams.mask = maskchannel;
layerParams.center_x = 32;
layerParams.center_y = 32;
auto layer = std::make_shared>(
std::move(channelMap),
layerParams
);
document.add_layer(layer);
// Convert to PhotoshopDocument and write to disk. Note that from this point onwards
// our LayeredFile instance is no longer usable
LayeredFile::write(std::move(document), "WriteLayerMasks.psd");
}
```
--------------------------------
### CMakeLists.txt Configuration for Progress Callbacks
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopExamples/ProgressCallbacks/CMakeLists.txt
This CMakeLists.txt file sets up an executable named ProgressCallbacks, links it against the PhotoshopAPI library, and specifies the main source file.
```cmake
add_executable(ProgressCallbacks main.cpp)
target_link_libraries(ProgressCallbacks PRIVATE PhotoshopAPI)
```
--------------------------------
### Manipulating Smart Object Layers
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/examples/smart_objects.md
Demonstrates creating a new Smart Object layer from a file, applying transformations (rotation, scaling, translation, perspective), and modifying its warp. It also shows how to reset transformations and warp, and how to add the layer to a file.
```cpp
#include "PhotoshopAPI.h"
#include "Core/Geometry/MeshOperations.h"
#include
int main()
{
using namespace NAMESPACE_PSAPI;
// Reading the file works as normal, as well as accessing the layers.
LayeredFile file = LayeredFile::read("SmartObject.psd");
auto layer_ptr = find_layer_as("warped", file);
// If we want to add an additional smart object layer we can do this just like you would with a normal layer.
// The only exception is that we have to provide a path for the image file we wish to link. We can additionally provide a warp
// object which will reapply the given warp to the new layer. This is convenient if you wish to create a copy of a layer without
// removing the other layer
auto params = Layer::Params{};
params.name = "uv_grid";
auto layer_new = std::make_shared>(file, params, "uv_grid.jpg", layer_ptr->warp(), LinkedLayerType::external);
// Smart objects also have more freedom in their transformations which we expose in the API:
layer_new->rotate(45); // Rotate by 45 degrees
layer_new->scale(.65f); // Scale by a factor of .65
layer_new->move(Geometry::Point2D(50.0f, 0.0f)); // Move to the right by 50
// We can additionally also apply a perspective transformation using a 3x3 projective matrix.
// This could also be used to skew or to directly apply all the above operations. This matrix e.g.
// applies a perspective transform where the vanishing point is at 2048, 0 (keeping in mind that 0
// in this case is at the top of the canvas).
// If you wish to know more about how these 3x3 transformation matrices work this is a great video/channel:
// https://www.youtube.com/watch?v=B8kMB6Hv2eI.
Eigen::Matrix3d transformation;
transformation << 1, 0, 0,
0, 1, 0,
1.0f / 2048, 0, 1;
layer_new->transform(transformation);
// If you want to be a bit more descriptive of how these matrices are to be built you can create one using a source
// and destination quad like this. Keep in mind that these have to be in the coordinate space of the image itself:
std::array, 4> source_transform = Geometry::create_quad(2048, 2048);
std::array, 4> destination_transform = Geometry::create_quad(2048, 2048);
// Squash together the top of the image
destination_transform[0].x = 512;
destination_transform[1].x = 1536;
// Create a 3x3 homography mapping the source_transform quad to the destination_transform quad.
auto homography = Geometry::Operations::create_homography_matrix(source_transform, destination_transform);
layer_new->transform(homography);
// If now we wanted to clear the transformation and/or warp that could be done as well. This will now just
// be a smart object layer with width and height of 2048 (like the original image data).
layer_new->reset_transform();
layer_new->reset_warp();
// For modifying the warp directly (bezier) we can push the individual points, although this is a bit more
// advanced and we don't currently have a very convenient interface for it. This code right here
// pushes the top left corner to the 50, 50 position which will create a slightly rounded corner
auto warp = layer_new->warp();
warp.point(0, 0).x = 50.0f;
warp.point(0, 0).y = 50.0f;
layer_new->warp(std::move(warp));
// adding these works just as with any other layers, writing also works as usual
file.add_layer(layer_new);
LayeredFile::write(std::move(file), "SmartObject_out.psb");
}
```
--------------------------------
### Create Simple Document in C++
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/examples/create_simple_document.md
Use this snippet to create an 8-bit RGB document with a single red-tinted layer. Ensure the PhotoshopAPI library is included and linked.
```cpp
/*
Example of creating a simple document with a single layer using the PhotoshopAPI.
*/
#include "PhotoshopAPI.h"
#include
#include
int main()
{
using namespace NAMESPACE_PSAPI;
// Initialize some constants that we will need throughout the program
const static uint32_t width = 64u;
const static uint32_t height = 64u;
// Create an 8-bit LayeredFile as our starting point, 8- 16- and 32-bit are fully supported
LayeredFile document = { Enum::ColorMode::RGB, width, height };
// Create our individual channels to add to our image layer. Keep in mind that all these 3 channels need to
// be specified for RGB mode
std::unordered_map > channelMap;
channelMap[Enum::ChannelID::Red] = std::vector(width * height, 255u);
channelMap[Enum::ChannelID::Green] = std::vector(width * height, 0u);
channelMap[Enum::ChannelID::Blue] = std::vector(width * height, 0u);
ImageLayer::Params layerParams = {};
layerParams.name = "Layer Red";
layerParams.width = width;
layerParams.height = height;
layerParams.display_color = Enum::LayerColor::blue;
auto layer = std::make_shared>(std::move(channelMap), layerParams);
document.add_layer(layer);
// It is perfectly legal to modify a layers properties even after it was added to the document as attributes
// are only finalized on export
layer->opacity(.5);
// Convert to PhotoshopFile and write to disk. Note that from this point onwards
// our LayeredFile instance is no longer usable
LayeredFile::write(std::move(document), "WriteSimpleFile.psd");
}
```
--------------------------------
### Locate PhotoshopAPI Binaries and Includes
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/building.md
After compiling from source, the include files and static library will be located in specific subdirectories within the PhotoshopAPI build output.
```bash
$ /bin-int/PhotoshopAPI//include
```
```bash
$ /bin-int/PhotoshopAPI//PhotoshopAPI/PhotoshopAPI.lib
```
--------------------------------
### Integrate PhotoshopAPI with CMakeLists.txt
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/building.md
Add the PhotoshopAPI to your project by including its CMakeLists.txt file. Configure build options like PSAPI_BUILD_DOCS before adding the subdirectory.
```cmake
# Set some appropriate options such as PSAPI_BUILD_DOCS OFF
# for a full list please refer to the cmakeflags section
set( OFF)
add_subdirectory()
```
--------------------------------
### Add Static Library
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopAPI/CMakeLists.txt
Creates a static library named PhotoshopAPI from the collected source files.
```cmake
add_library(PhotoshopAPI STATIC ${MY_SOURCES})
```
--------------------------------
### Copy uv_grid.jpg Post-Build
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopExamples/SmartObjects/CMakeLists.txt
Configures a custom command to copy the uv_grid.jpg image file to the target binary directory after the SmartObjects executable is built. This ensures the image is available at runtime.
```cmake
add_custom_command(TARGET SmartObjects POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/uv_grid.jpg $/uv_grid.jpg)
```
--------------------------------
### Create Text Layer (Python)
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/concepts/text-layers.md
Initializes a new 8-bit text layer with specified properties. The `font` parameter expects the PostScript name of the font.
```python
import photoshopapi as psapi
doc = psapi.LayeredFile_8bit(psapi.enum.ColorMode.rgb, 1200, 1800)
layer = psapi.TextLayer_8bit(
layer_name="Caption",
text="Hello\nWorld",
font="ArialMT", # PostScript font name
font_size=36.0,
fill_color=[1.0, 0.0, 0.0, 0.0], # [A, R, G, B]
position_x=120.0,
position_y=160.0,
box_width=600.0,
box_height=260.0,
)
doc.add_layer(layer)
```
--------------------------------
### Define Project and Source Files
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopAPI/CMakeLists.txt
Defines the project name and recursively finds all .cpp source files in the src directory.
```cmake
project(PhotoshopAPI)
file(GLOB_RECURSE MY_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/*.cpp")
```
--------------------------------
### Run Photoshop API Benchmarks
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopBenchmark/README.md
Execute the compiled PhotoshopBenchmark executable to generate benchmark statistics for the PhotoshopAPI.
```bash
PhotoshopAPI/bin-int/PhotoshopAPI/x64-release/PhotoshopBenchmark
```
--------------------------------
### Configure Include Directories
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopAPI/CMakeLists.txt
Specifies the public and private include directories for the PhotoshopAPI target. 'include' and 'src' are public, while 'src/Util' is private.
```cmake
target_include_directories(PhotoshopAPI PUBLIC include src)
target_include_directories(PhotoshopAPI PRIVATE src/Util)
```
--------------------------------
### Create and Transform Smart Object Layer
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/examples/smart_objects.md
Demonstrates creating a new smart object layer linked to an external file, applying transformations (rotate, scale, move, perspective matrix, homography), and resetting them. This is useful for complex image manipulations where original data needs to be preserved.
```python
import os
from typing import Union
import numpy as np
import cv2
import photoshopapi as psapi
def main() -> None:
# Reading the file works as normal, as well as accessing the layers.
layered_file = psapi.LayeredFile.read(os.path.join(os.path.dirname(__file__), "SmartObject.psd"))
warped_layer: psapi.SmartObjectLayer_8bit = layered_file["warped"]
# If we want to add an additional smart object layer we can do this just like you would with a normal layer.
# The only exception is that we have to provide a path for the image file we wish to link. We can additionally provide a warp
# object which will reapply the given warp to the new layer. This is convenient if you wish to create a copy of a layer without
# removing the other layer. These images are read using OpenImageIO so all of the file formats supported by it are supported
# here
layer_new = psapi.SmartObjectLayer_8bit(
layered_file,
path = os.path.join(os.path.dirname(__file__), "uv_grid.jpg"),
layer_name = "uv_grid",
link_type = psapi.enum.LinkedLayerType.external,
warp = warped_layer.warp
)
# Smart objects also have more freedom in their transformations which we expose in the API:
layer_new.rotate(45, layer_new.center_x, layer_new.center_y)
layer_new.scale(.65, .65, layer_new.center_x, layer_new.center_y)
layer_new.move(50, 50)
# We can additionally also apply a perspective transformation using a 3x3 projective matrix.
# This could also be used to skew or to directly apply all the above operations. This matrix e.g.
# applies a perspective transform where the vanishing point is at 2048, 0 (keeping in mind that 0
# in this case is at the top of the canvas).
# If you wish to know more about how these 3x3 transformation matrices work this is a great video/channel:
# https://www.youtube.com/watch?v=B8kMB6Hv2eI.
matrix = np.zeros((3, 3), dtype=np.double)
matrix[0][0] = 1
matrix[1][1] = 1
matrix[0][2] = 1 / 2048
matrix[2][2] = 1
layer_new.transform(matrix)
# If you want to be a bit more descriptive of how these matrices are to be built you can create one using a source
# and destination quad like this. Keep in mind that these have to be in the coordinate space of the image itself:
source_transform = psapi.geometry.create_quad(2048, 2048)
destination_transform = psapi.geometry.create_quad(2048, 2048)
# Squash together the top of the image
destination_transform[0].x = 512
destination_transform[1].x = 1536
# Create a 3x3 homography mapping the source_transform quad to the destination_transform quad.
homography = psapi.geometry.create_homography(source_transform, destination_transform)
layer_new.transform(homography)
# If now we wanted to clear the transformation and/or warp that could be done as well. This will now just
# be a smart object layer with width and height of 2048 (like the original image data).
layer_new.reset_transform()
layer_new.reset_warp()
# For modifying the warp directly (bezier) we can push the individual points, although this is a bit more
# advanced and we don't currently have a very convenient interface for it. This code right here
# pushes the top left corner to the 50, 50 position which will create a slightly rounded corner
warp_pts = layer_new.warp.points
warp_pts[0].x = 50
warp_pts[0].y = 50
layer_new.warp.points = warp_pts
# adding these works just as with any other layers, writing also works as usual
layered_file.add_layer(layer_new)
layered_file.write(os.path.join(os.path.dirname(__file__), "SmartObjectOut.psd"))
if __name__ == "__main__":
main()
```
--------------------------------
### Compile PhotoshopAPI with Python Bindings
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/building.md
Build the PhotoshopAPI project with the Python bindings enabled. This command compiles the source code, including the Python extensions, into a usable format.
```bash
$ cmake --build /bin --config Release
```
--------------------------------
### Create LayeredFile with Image Layer and Mask (Python)
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/examples/add_layer_masks.md
Generates a LayeredFile instance, defines image data and a mask, creates an 8-bit image layer, adds it to the document, and writes the result to a PSD file. Uses numpy for image data manipulation.
```python
# Example of creating a simple document with a single layer and a mask using the PhotoshopAPI.
import os
import numpy as np
import photoshopapi as psapi
def main() -> None:
# Initialize some constants that we will need throughout the program
width = 64
height = 64
color_mode = psapi.enum.ColorMode.rgb
# Generate our LayeredFile instance
document = psapi.LayeredFile_8bit(color_mode, width, height)
img_data = np.zeros((3, height, width), np.uint8)
img_data[0] = 255
mask = np.full((height, width), 128, np.uint8)
img_layer = psapi.ImageLayer_8bit(img_data, "Layer Red", layer_mask=mask, width=width, height=height)
# Add the layer and write to disk
document.add_layer(img_layer)
document.write(os.path.join(os.path.dirname(__file__), "WriteLayerMasks.psd"))
if __name__ == "__main__":
main()
```
--------------------------------
### Clone PhotoshopAPI Repository
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/building.md
Clone the PhotoshopAPI repository to your preferred build location. Use the --recurse-submodules flag to ensure all necessary dependencies are downloaded.
```bash
$ cd
$ git clone --recurse-submodules
```
--------------------------------
### Create Image Layer from Dictionary (Numeric Indices)
Source: https://github.com/emildohne/photoshopapi/blob/master/docs/doxygen/python/layers/image.md
Constructs an 8-bit image layer using a dictionary where keys are numeric channel indices (0 for Red, 1 for Green, 2 for Blue, -1 for Alpha). Ensure width, height, and color mode are specified.
```python
import photoshopapi as psapi
import numpy as np
document_color_mode = psapi.enum.ColorMode.rgb
width = 32
height = 32
file = psapi.LayeredFile_8bit(document_color_mode, width, height)
# We can use logical indices for these as should be familiar from other software
image_dict = {}
image_dict[0] = np.full((height, width), 0, np.uint8) # Red channel
image_dict[1] = np.full((height, width), 255, np.uint8) # Green channel
image_dict[2] = np.full((height, width), 0, np.uint8) # Blue channel
image_dict[-1] = np.full((height, width), 128, np.uint8) # Alpha channel
# Construct our layer instance, width and height must be specified for this to work!
img_lr = psapi.ImageLayer_8bit(
image_dict,
layer_name="img_lr_dict",
width=width,
height=height,
color_mode=document_color_mode)
# Add to the file and write out
file.add_layer(img_lr)
file.write("Out.psd")
```
--------------------------------
### Copy SmartObject.psd Post-Build
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopExamples/SmartObjects/CMakeLists.txt
Configures a custom command to copy the SmartObject.psd file to the target binary directory after the SmartObjects executable is built. This ensures the PSD file is available at runtime.
```cmake
add_custom_command(TARGET SmartObjects POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/SmartObject.psd $/SmartObject.psd)
```
--------------------------------
### Generate Benchmark Graphs
Source: https://github.com/emildohne/photoshopapi/blob/master/PhotoshopBenchmark/README.md
Invoke the Python script to create graphs from the benchmark statistics files. Ensure both Photoshop and PhotoshopAPI statistics files are present in the PhotoshopBenchmark directory.
```bash
py -u src/create_plots.py
```