Skip to content

Instantly share code, notes, and snippets.

@sthairno
Created March 15, 2020 07:31
Show Gist options
  • Save sthairno/4ead1e6446d21a10ee0f95bd1686f870 to your computer and use it in GitHub Desktop.
Save sthairno/4ead1e6446d21a10ee0f95bd1686f870 to your computer and use it in GitHub Desktop.
Siv3D_psd
#include<Siv3D.hpp>
#include"PsdReader.h"
void Main()
{
Window::Resize(1080, 720);
Scene::SetBackground(Palette::White);
Size boxSize(5, 5);
Texture tex;
Image pattern(Scene::Size());
for (int y = 0; y < 150; y++)
for (int x = y % 2; x < 240; x += 2)
Rect(Point(x, y) * boxSize, boxSize).overwrite(pattern, HSV(0, 0, 0.6));
Texture patternTex(pattern);
Font font(10);
PsdReader reader;
Image image;
bool layerFrame = false;
bool layerName = false;
while (System::Update())
{
if (DragDrop::HasNewFilePaths())
{
ClearPrint();
Stopwatch stw;
reader.open(DragDrop::GetDroppedFilePaths()[0].path);
stw.start();
image = reader.getImage();
stw.pause();
Print << U"{}ms"_fmt(stw.msF());
tex = Texture(image);
}
patternTex.draw();
tex.draw();
if (reader.isOpened)
{
for (auto& layer : reader.layers)
{
if (layerFrame)
{
layer.rect.drawFrame(1, Palette::Red);
}
if (layerName)
{
font(layer.name).draw(layer.rect.pos,Palette::Red);
}
}
}
SimpleGUI::CheckBox(layerFrame, U"frame", Vec2(5, 5));
SimpleGUI::CheckBox(layerName, U"name", Vec2(5, 45));
}
}
#include "PsdReader.h"
// the main include that always needs to be included in every translation unit that uses the PSD library
#include "psd_sdk/Psd.h"
// for convenience reasons, we directly include the platform header from the PSD library.
// we could have just included <Windows.h> as well, but that is unnecessarily big, and triggers lots of warnings.
#include "psd_sdk/PsdPlatform.h"
// in the sample, we use the provided malloc allocator for all memory allocations. likewise, we also use the provided
// native file interface.
// in your code, feel free to use whatever allocator you have lying around.
#include "psd_sdk/PsdMallocAllocator.h"
#include "psd_sdk/PsdNativeFile.h"
#include "psd_sdk/PsdDocument.h"
#include "psd_sdk/PsdColorMode.h"
#include "psd_sdk/PsdLayer.h"
#include "psd_sdk/PsdChannel.h"
#include "psd_sdk/PsdChannelType.h"
#include "psd_sdk/PsdLayerMask.h"
#include "psd_sdk/PsdVectorMask.h"
#include "psd_sdk/PsdLayerMaskSection.h"
#include "psd_sdk/PsdImageDataSection.h"
#include "psd_sdk/PsdImageResourcesSection.h"
#include "psd_sdk/PsdParseDocument.h"
#include "psd_sdk/PsdParseLayerMaskSection.h"
#include "psd_sdk/PsdParseImageDataSection.h"
#include "psd_sdk/PsdParseImageResourcesSection.h"
#include "psd_sdk/PsdLayerCanvasCopy.h"
#include "psd_sdk/PsdInterleave.h"
#include "psd_sdk/PsdPlanarImage.h"
#include "psd_sdk/PsdExport.h"
#include "psd_sdk/PsdExportDocument.h"
#include "S3DFileReader.h"
static const unsigned int CHANNEL_NOT_FOUND = UINT_MAX;
unsigned int FindChannel(Layer* layer, int16_t channelType)
{
for (unsigned int i = 0; i < layer->channelCount; ++i)
{
Channel* channel = &layer->channels[i];
if (channel->data && channel->type == channelType)
return i;
}
return CHANNEL_NOT_FOUND;
}
template<typename T>
Image dataToImage(Size size, T maxValue, void* r, void* g, void* b, void* a = nullptr)
{
uint32 mul = 255 / maxValue;
T* rp = static_cast<T*>(r), * gp = static_cast<T*>(g), * bp = static_cast<T*>(b), * ap = static_cast<T*>(a);
auto img = Image(size);
auto data = img.data();
if (a)
{
//RGBA
for (size_t i = 0; i < size.x * size.y; i++)
{
data->r = rp[i] * mul;
data->g = gp[i] * mul;
data->b = bp[i] * mul;
data->a = ap[i] * mul;
data++;
}
}
else
{
//RGB
for (size_t i = 0; i < size.x * size.y; i++)
{
data->r = rp[i] * mul;
data->g = gp[i] * mul;
data->b = bp[i] * mul;
data->a = 255;
data++;
}
}
return img;
}
template<typename T>
void monoToGrid(Grid<float>& grid, T maxValue, void* mono)
{
T* monop = static_cast<T*>(mono);
auto size = grid.size();
auto data = grid.data();
for (size_t i = 0; i < size.x * size.y; i++)
{
*data = static_cast<float>(monop[i]) / static_cast<float>(maxValue);
data++;
}
}
Image gridToImage(Grid<float>& grid)
{
return Image::Generate(grid.size(), [grid](Point pos)
{
return HSV(0,0,grid[pos]);
}
);
}
PsdReader::PsdReader(FilePath path)
{
open(path);
}
PsdReader::PsdReader(IReader& reader)
{
open(reader);
}
void PsdReader::open(FilePath path)
{
BinaryReader reader(path);
open(reader);
reader.close();
}
void PsdReader::open(IReader& reader)
{
isOpened = false;
MallocAllocator allocator;
S3DFileReader file = S3DFileReader(reader);
if (!reader.isOpened())
{
return;
}
//�t�@�C���ǂݍ���
Document* document = CreateDocument(&file, &allocator);
if (!document)
{
file.Close();
return;
}
docSize = Size(document->width, document->height);
docImage = Image(docSize);
//(A)RGB�̂ݑΉ�
if (document->colorMode != colorMode::RGB)
{
DestroyDocument(document, &allocator);
file.Close();
return;
}
//�w�i��ǂݍ���
if (document->imageDataSection.length != 0)
{
ImageDataSection* imageData = ParseImageDataSection(document, &file, &allocator);
if (imageData)
{
const unsigned int imageCount = imageData->imageCount;
if (imageCount == 3)
{
if (document->bitsPerChannel == 8)
{
docImage = dataToImage<uint8>(docSize, UINT8_MAX, imageData->images[0].data, imageData->images[1].data, imageData->images[2].data);
}
else if (document->bitsPerChannel == 16)
{
docImage = dataToImage<uint16>(docSize, UINT16_MAX, imageData->images[0].data, imageData->images[1].data, imageData->images[2].data);
}
else if (document->bitsPerChannel == 32)
{
docImage = dataToImage<float32_t>(docSize, 1, imageData->images[0].data, imageData->images[1].data, imageData->images[2].data);
}
}
else
{
if (document->bitsPerChannel == 8)
{
docImage = dataToImage<uint8>(docSize, UINT8_MAX, imageData->images[0].data, imageData->images[1].data, imageData->images[2].data, imageData->images[3].data);
}
else if (document->bitsPerChannel == 16)
{
docImage = dataToImage<uint16>(docSize, UINT16_MAX, imageData->images[0].data, imageData->images[1].data, imageData->images[2].data, imageData->images[3].data);
}
else if (document->bitsPerChannel == 32)
{
docImage = dataToImage<float32_t>(docSize, 1, imageData->images[0].data, imageData->images[1].data, imageData->images[2].data, imageData->images[3].data);
}
}
DestroyImageDataSection(imageData, &allocator);
}
}
//�e���C���[��ǂݍ���
LayerMaskSection* layerMaskSection = ParseLayerMaskSection(document, &file, &allocator);
if (layerMaskSection)
{
layers = Array<PsdLayer>(layerMaskSection->layerCount);
rootLayers = Array<size_t>();
for (unsigned int i = 0; i < layerMaskSection->layerCount; ++i)
{
PsdLayer* dst = &layers[i];
Layer* src = &layerMaskSection->layers[i];
Rect layerRect(src->left, src->top, src->right - src->left, src->bottom - src->top);
dst->rect = layerRect;
if (src->parent)
{
size_t parentIdx = static_cast<size_t>(src->parent - layerMaskSection->layers);
layers[parentIdx].children.push_back(dst);
}
else
{
rootLayers.push_back(i);
}
dst->isVisible = src->isVisible;
dst->name = Unicode::FromUTF16(std::u16string_view((char16_t*)src->utf16Name));
if (src->isVisible)
{
ExtractLayer(document, &file, &allocator, src);
dst->opacity = src->opacity;
const unsigned int indexR = FindChannel(src, channelType::R);
const unsigned int indexG = FindChannel(src, channelType::G);
const unsigned int indexB = FindChannel(src, channelType::B);
const unsigned int indexA = FindChannel(src, channelType::TRANSPARENCY_MASK);
unsigned int channelCount = 0u;
if ((indexR != CHANNEL_NOT_FOUND) && (indexG != CHANNEL_NOT_FOUND) && (indexB != CHANNEL_NOT_FOUND))
{
channelCount = 3u;
if (indexA != CHANNEL_NOT_FOUND)
{
channelCount = 4u;
}
}
if (channelCount == 3u)
{
if (document->bitsPerChannel == 8)
{
dst->image = dataToImage<uint8>(layerRect.size, UINT8_MAX, src->channels[indexR].data, src->channels[indexG].data, src->channels[indexB].data);
}
else if (document->bitsPerChannel == 16)
{
dst->image = dataToImage<uint16>(layerRect.size, UINT16_MAX, src->channels[indexR].data, src->channels[indexG].data, src->channels[indexB].data);
}
else if (document->bitsPerChannel == 32)
{
dst->image = dataToImage<float32_t>(layerRect.size, 1, src->channels[indexR].data, src->channels[indexG].data, src->channels[indexB].data);
}
}
else if (channelCount == 4u)
{
if (document->bitsPerChannel == 8)
{
dst->image = dataToImage<uint8>(layerRect.size, UINT8_MAX, src->channels[indexR].data, src->channels[indexG].data, src->channels[indexB].data, src->channels[indexA].data);
}
else if (document->bitsPerChannel == 16)
{
dst->image = dataToImage<uint16>(layerRect.size, UINT16_MAX, src->channels[indexR].data, src->channels[indexG].data, src->channels[indexB].data, src->channels[indexA].data);
}
else if (document->bitsPerChannel == 32)
{
dst->image = dataToImage<float32_t>(layerRect.size, 1, src->channels[indexR].data, src->channels[indexG].data, src->channels[indexB].data, src->channels[indexA].data);
}
}
//���C���[�}�X�N
if (src->layerMask)
{
dst->layerMask.isExists = true;
dst->layerMask.rect.x = src->layerMask->right;
dst->layerMask.rect.y = src->layerMask->top;
dst->layerMask.rect.w = src->layerMask->right - src->layerMask->left;
dst->layerMask.rect.h = src->layerMask->bottom - src->layerMask->top;
dst->layerMask.density = src->layerMask->density;
dst->layerMask.defaultColor = src->layerMask->defaultColor;
dst->layerMask.feather = src->layerMask->feather;
dst->layerMask.data.resize(dst->layerMask.rect.w, dst->layerMask.rect.h);
if (document->bitsPerChannel == 8)
{
monoToGrid<uint8>(dst->layerMask.data, UINT8_MAX, src->layerMask->data);
}
else if (document->bitsPerChannel == 16)
{
monoToGrid<uint16>(dst->layerMask.data, UINT16_MAX, src->layerMask->data);
}
else if (document->bitsPerChannel == 32)
{
monoToGrid<float32_t>(dst->layerMask.data, 1, src->layerMask->data);
}
}
//�x�N�^�[�}�X�N
if (src->vectorMask)
{
dst->vectorMask.isExists = true;
dst->vectorMask.rect.x = src->vectorMask->right;
dst->vectorMask.rect.y = src->vectorMask->top;
dst->vectorMask.rect.w = src->vectorMask->right - src->vectorMask->left;
dst->vectorMask.rect.h = src->vectorMask->bottom - src->vectorMask->top;
dst->vectorMask.density = src->vectorMask->density;
dst->vectorMask.defaultColor = src->vectorMask->defaultColor;
dst->vectorMask.feather = src->vectorMask->feather;
dst->vectorMask.data.resize(dst->vectorMask.rect.w, dst->vectorMask.rect.h);
if (document->bitsPerChannel == 8)
{
monoToGrid<uint8>(dst->vectorMask.data, UINT8_MAX, src->vectorMask->data);
}
else if (document->bitsPerChannel == 16)
{
monoToGrid<uint16>(dst->vectorMask.data, UINT16_MAX, src->vectorMask->data);
}
else if (document->bitsPerChannel == 32)
{
monoToGrid<float32_t>(dst->vectorMask.data, 1, src->vectorMask->data);
}
}
}
}
DestroyLayerMaskSection(layerMaskSection, &allocator);
}
DestroyDocument(document, &allocator);
isOpened = true;
}
Image PsdReader::getImage()
{
if (isOpened)
{
Image img(docImage);
for (auto& layerIdx : rootLayers)
{
layers[layerIdx].overwrite(img);
}
return img;
}
else
{
return Image();
}
}
#pragma once
#include<Siv3D.hpp>
class PsdLayerMask
{
public:
bool isExists;
Rect rect;
Grid<float> data;
float feather;
uint8 density;
uint8 defaultColor;
};
class PsdVectorMask
{
public:
bool isExists;
Rect rect;
Grid<float> data;
float feather;
uint8 density;
uint8 defaultColor;
};
class PsdLayer
{
public:
bool isVisible;
uint8 opacity;
Image image;
String name;
Rect rect;
PsdLayerMask layerMask;
PsdVectorMask vectorMask;
Array<PsdLayer*> children;
void overwrite(Image& dst, uint8 opacity = 255, int depth = 0)
{
opacity = Clamp<uint8>(opacity, 0, 255);
if (isVisible)
{
for (int y = 0; y < rect.h; y++)
{
for (int x = 0; x < rect.w; x++)
{
Point srcPos(x, y);
Point dstPos = rect.pos + dstPos;
if (0 <= dstPos.x && dstPos.x < dst.width() &&
0 <= dstPos.y && dstPos.y < dst.height())
{
Color& srcCol = image[srcPos];
Color& dstCol = dst[dstPos];
srcCol.a = srcCol.a * this->opacity * opacity / 65025;
dstCol.r = (dstCol.r * (255 - srcCol.a) + srcCol.r * srcCol.a) / 255;
dstCol.g = (dstCol.g * (255 - srcCol.a) + srcCol.g * srcCol.a) / 255;
dstCol.b = (dstCol.b * (255 - srcCol.a) + srcCol.b * srcCol.a) / 255;
dstCol.a = 255 - ((255 - dstCol.a) * (255 - srcCol.a) / 255);
}
}
}
for (auto& child : children)
{
child->overwrite(dst, this->opacity * opacity / 255, depth + 1);
}
}
}
};
class PsdReader
{
public:
bool isOpened;
Size docSize;
Image docImage;
Array<PsdLayer> layers;
Array<size_t> rootLayers;
PsdReader() = default;
PsdReader(FilePath path);
PsdReader(IReader& reader);
void open(FilePath path);
void open(IReader& reader);
Image getImage();
};
#include "S3DFileReader.h"
S3DFileReader::S3DFileReader(IReader& read) : File(&MallocAllocator()), reader(read)
{
}
bool S3DFileReader::DoOpenRead(const wchar_t* filename)
{
return false;
}
bool S3DFileReader::DoOpenWrite(const wchar_t* filename)
{
return false;
}
bool S3DFileReader::DoClose(void)
{
return true;
}
File::ReadOperation S3DFileReader::DoRead(void* buffer, uint32_t count, uint64_t position)
{
reader.read(buffer, static_cast<int64>(position), static_cast<int64>(count));
return static_cast<File::ReadOperation>(NULL);
}
bool S3DFileReader::DoWaitForRead(File::ReadOperation& operation)
{
return true;
}
File::WriteOperation S3DFileReader::DoWrite(const void* buffer, uint32_t count, uint64_t position)
{
return static_cast<File::ReadOperation>(NULL);
}
bool S3DFileReader::DoWaitForWrite(File::WriteOperation& operation)
{
return false;
}
uint64_t S3DFileReader::DoGetSize(void) const
{
return static_cast<uint64_t>(reader.size());
}
#pragma once
#include<Siv3D.hpp>
// the main include that always needs to be included in every translation unit that uses the PSD library
#include "psd_sdk/Psd.h"
// for convenience reasons, we directly include the platform header from the PSD library.
// we could have just included <Windows.h> as well, but that is unnecessarily big, and triggers lots of warnings.
#include "psd_sdk/PsdPlatform.h"
// in the sample, we use the provided malloc allocator for all memory allocations. likewise, we also use the provided
// native file interface.
// in your code, feel free to use whatever allocator you have lying around.
#include "psd_sdk/PsdMallocAllocator.h"
#include "psd_sdk/PsdDocument.h"
#include "psd_sdk/PsdColorMode.h"
#include "psd_sdk/PsdLayer.h"
#include "psd_sdk/PsdChannel.h"
#include "psd_sdk/PsdChannelType.h"
#include "psd_sdk/PsdLayerMask.h"
#include "psd_sdk/PsdVectorMask.h"
#include "psd_sdk/PsdLayerMaskSection.h"
#include "psd_sdk/PsdImageDataSection.h"
#include "psd_sdk/PsdImageResourcesSection.h"
#include "psd_sdk/PsdParseDocument.h"
#include "psd_sdk/PsdParseLayerMaskSection.h"
#include "psd_sdk/PsdParseImageDataSection.h"
#include "psd_sdk/PsdParseImageResourcesSection.h"
#include "psd_sdk/PsdLayerCanvasCopy.h"
#include "psd_sdk/PsdInterleave.h"
#include "psd_sdk/PsdPlanarImage.h"
#include "psd_sdk/PsdExport.h"
#include "psd_sdk/PsdExportDocument.h"
#include "psd_sdk/PsdFile.h"
PSD_USING_NAMESPACE;
/// <summary>
/// psd::File��IReader��ϊ�����N���X
/// </summary>
class S3DFileReader : public File
{
public:
S3DFileReader(IReader& read);
private:
virtual bool DoOpenRead(const wchar_t* filename) PSD_OVERRIDE;
virtual bool DoOpenWrite(const wchar_t* filename) PSD_OVERRIDE;
virtual bool DoClose(void) PSD_OVERRIDE;
virtual File::ReadOperation DoRead(void* buffer, uint32_t count, uint64_t position) PSD_OVERRIDE;
virtual bool DoWaitForRead(File::ReadOperation& operation) PSD_OVERRIDE;
virtual File::WriteOperation DoWrite(const void* buffer, uint32_t count, uint64_t position) PSD_OVERRIDE;
virtual bool DoWaitForWrite(File::WriteOperation& operation) PSD_OVERRIDE;
virtual uint64_t DoGetSize(void) const PSD_OVERRIDE;
IReader& reader;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment