### 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 ```