Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Graphics
Krita
Commits
1f8f6593
Commit
1f8f6593
authored
Jul 17, 2021
by
Amy spark
👉
Committed by
Halla Rempt
Aug 23, 2021
Browse files
TIFF: add support for writing Photoshop layers and metadata
parent
3a61a882
Changes
24
Hide whitespace changes
Inline
Side-by-side
libs/psd/psd_additional_layer_info_block.cpp
View file @
1f8f6593
...
...
@@ -155,6 +155,9 @@ void PsdAdditionalLayerInfoBlock::readImpl(QIODevice &io)
unicodeLayerName
=
readUnicodeString
<
byteOrder
>
(
io
);
dbgFile
<<
"unicodeLayerName"
<<
unicodeLayerName
;
}
else
if
(
key
==
"lyid"
)
{
quint32
id
;
psdread
<
byteOrder
>
(
io
,
id
);
dbgFile
<<
"layer ID:"
<<
id
;
}
else
if
(
key
==
"lfx2"
||
key
==
"lfxs"
)
{
// lfxs is a special variant of layer styles for group layers
layerStyleXml
=
KisAslReader
::
readLfx2PsdSection
(
io
,
byteOrder
);
...
...
@@ -343,7 +346,8 @@ void PsdAdditionalLayerInfoBlock::writePattBlockExImpl(QIODevice &io, const QDom
{
KisAslWriterUtils
::
writeFixedString
<
byteOrder
>
(
"8BIM"
,
io
);
KisAslWriterUtils
::
writeFixedString
<
byteOrder
>
(
"Patt"
,
io
);
KisAslWriterUtils
::
OffsetStreamPusher
<
quint32
,
byteOrder
>
pattSizeTag
(
io
,
2
);
const
quint32
padding
=
m_header
.
tiffStyleLayerBlock
?
4
:
2
;
KisAslWriterUtils
::
OffsetStreamPusher
<
quint32
,
byteOrder
>
pattSizeTag
(
io
,
padding
);
try
{
KisAslPatternsWriter
writer
(
patternsXmlDoc
,
io
,
byteOrder
);
...
...
libs/psd/psd_layer_record.cpp
View file @
1f8f6593
...
...
@@ -356,37 +356,41 @@ bool PSDLayerRecord::readImpl(QIODevice &io)
return
false
;
}
// dbgFile << "blending block data length" << blendingDataLength << ", pos" << io.pos();
// XXX: Check what endianness this data has
blendingRanges
.
data
=
io
.
read
(
blendingDataLength
);
if
((
quint32
)
blendingRanges
.
data
.
size
()
!=
blendingDataLength
)
{
error
=
QString
(
"Got %1 bytes for the blending range block, needed %2"
).
arg
(
blendingRanges
.
data
.
size
(),
blendingDataLength
);
}
/*
// XXX: reading this block correctly failed, I have more channel ranges than I'd expected.
if (!psdread(io, blendingRanges.blackValues[0]) ||
!psdread(io, blendingRanges.blackValues[1]) ||
!psdread(io, blendingRanges.whiteValues[0]) ||
!psdread(io, blendingRanges.whiteValues[1]) ||
!psdread(io, blendingRanges.compositeGrayBlendDestinationRange)) {
error = "Could not read blending black/white values";
return false;
}
for (int i = 0; i < nChannels; ++i) {
quint32 src;
quint32 dst;
if (!psdread(io, src) || !psdread(io, dst)) {
error = QString("could not read src/dst range for channel %1").arg(i);
quint32
blendingNchannels
=
blendingDataLength
>
0
?
(
blendingDataLength
-
8
)
/
4
/
2
:
0
;
dbgFile
<<
"
\t
Number of blending channels:"
<<
blendingNchannels
;
if
(
blendingNchannels
>
0
)
{
if
(
!
psdread
<
byteOrder
>
(
io
,
blendingRanges
.
compositeGrayRange
.
first
.
blackValues
[
0
])
||
!
psdread
<
byteOrder
>
(
io
,
blendingRanges
.
compositeGrayRange
.
first
.
blackValues
[
1
])
||
!
psdread
<
byteOrder
>
(
io
,
blendingRanges
.
compositeGrayRange
.
first
.
whiteValues
[
0
])
||
!
psdread
<
byteOrder
>
(
io
,
blendingRanges
.
compositeGrayRange
.
first
.
whiteValues
[
1
])
||
!
psdread
<
byteOrder
>
(
io
,
blendingRanges
.
compositeGrayRange
.
second
.
blackValues
[
0
])
||
!
psdread
<
byteOrder
>
(
io
,
blendingRanges
.
compositeGrayRange
.
second
.
blackValues
[
1
])
||
!
psdread
<
byteOrder
>
(
io
,
blendingRanges
.
compositeGrayRange
.
second
.
whiteValues
[
0
])
||
!
psdread
<
byteOrder
>
(
io
,
blendingRanges
.
compositeGrayRange
.
second
.
whiteValues
[
1
]))
{
error
=
"Could not read blending black/white values"
;
return
false
;
}
dbgFile << "\tread range " << src << "to" << dst << "for channel" << i;
blendingRanges.sourceDestinationRanges << QPair<quint32, quint32>(src, dst);
dbgFile
<<
"
\t
Blending ranges:"
;
dbgFile
<<
"
\t\t
composite gray (source) :"
<<
blendingRanges
.
compositeGrayRange
.
first
;
dbgFile
<<
"
\t\t
composite gray (dest):"
<<
blendingRanges
.
compositeGrayRange
.
second
;
for
(
quint32
i
=
0
;
i
<
blendingNchannels
;
++
i
)
{
LayerBlendingRanges
::
LayerBlendingRange
src
;
LayerBlendingRanges
::
LayerBlendingRange
dst
;
if
(
!
psdread
<
byteOrder
>
(
io
,
src
.
blackValues
[
0
])
||
!
psdread
<
byteOrder
>
(
io
,
src
.
blackValues
[
1
])
||
!
psdread
<
byteOrder
>
(
io
,
src
.
whiteValues
[
0
])
||
!
psdread
<
byteOrder
>
(
io
,
src
.
whiteValues
[
1
])
||
!
psdread
<
byteOrder
>
(
io
,
dst
.
blackValues
[
0
])
||
!
psdread
<
byteOrder
>
(
io
,
dst
.
blackValues
[
1
])
||
!
psdread
<
byteOrder
>
(
io
,
dst
.
whiteValues
[
0
])
||
!
psdread
<
byteOrder
>
(
io
,
dst
.
whiteValues
[
1
]))
{
error
=
QString
(
"could not read src/dst range for channel %1"
).
arg
(
i
);
return
false
;
}
dbgFile
<<
"
\t\t
read range "
<<
src
<<
"to"
<<
dst
<<
"for channel"
<<
i
;
blendingRanges
.
sourceDestinationRanges
<<
qMakePair
(
src
,
dst
);
}
}
*/
dbgFile
<<
"
\t
Going to read layer name at"
<<
io
.
pos
();
quint8
layerNameLength
;
if
(
!
psdread
<
byteOrder
>
(
io
,
layerNameLength
))
{
...
...
@@ -402,6 +406,8 @@ bool PSDLayerRecord::readImpl(QIODevice &io)
layerName
=
io
.
read
(
layerNameLength
);
dbgFile
<<
"
\t
layer name"
<<
layerName
<<
io
.
pos
();
dbgFile
<<
"
\t
About to read additional info blocks at"
<<
io
.
pos
();
if
(
!
infoBlocks
.
read
(
io
))
{
error
=
infoBlocks
.
error
;
return
false
;
...
...
@@ -460,23 +466,9 @@ void PSDLayerRecord::writeImpl(QIODevice &io,
Q_ASSERT
(
nChannels
>
0
);
try
{
QBuffer
buf_little_endian
;
buf_little_endian
.
open
(
QIODevice
::
WriteOnly
);
QBuffer
buf_big_endian
;
buf_big_endian
.
open
(
QIODevice
::
WriteOnly
);
QBuffer
buf
;
buf
.
open
(
QIODevice
::
WriteOnly
);
const
QRect
layerRect
(
left
,
top
,
right
-
left
,
bottom
-
top
);
KisAslWriterUtils
::
writeRect
<
psd_byte_order
::
psdBigEndian
>
(
layerRect
,
buf_big_endian
);
KisAslWriterUtils
::
writeRect
<
psd_byte_order
::
psdLittleEndian
>
(
layerRect
,
buf_little_endian
);
KisAslWriterUtils
::
writeRect
<
byteOrder
>
(
layerRect
,
buf
);
KisAslWriterUtils
::
writeRect
<
byteOrder
>
(
layerRect
,
io
);
if
(
byteOrder
==
psd_byte_order
::
psdLittleEndian
)
{
dbgFile
<<
"Testing LE equality of QRect: "
<<
std
::
equal
(
buf_little_endian
.
data
().
begin
(),
buf_little_endian
.
data
().
end
(),
buf
.
data
().
begin
());
}
else
{
dbgFile
<<
"Testing BE equality of QRect: "
<<
std
::
equal
(
buf_big_endian
.
data
().
begin
(),
buf_big_endian
.
data
().
end
(),
buf
.
data
().
begin
());
{
const
QRect
layerRect
(
left
,
top
,
right
-
left
,
bottom
-
top
);
KisAslWriterUtils
::
writeRect
<
byteOrder
>
(
layerRect
,
io
);
}
{
...
...
@@ -519,8 +511,9 @@ void PSDLayerRecord::writeImpl(QIODevice &io,
flags
|=
1
;
if
(
!
visible
)
flags
|=
2
;
flags
|=
(
1
<<
3
);
if
(
irrelevant
)
{
flags
|=
(
1
<<
3
)
|
(
1
<<
4
);
flags
|=
(
1
<<
4
);
}
SAFE_WRITE_EX
(
byteOrder
,
io
,
flags
);
...
...
@@ -606,6 +599,7 @@ KisPaintDeviceSP PSDLayerRecord::convertMaskDeviceIfNeeded(KisPaintDeviceSP dev)
return
result
;
}
template
<
psd_byte_order
byteOrder
>
void
PSDLayerRecord
::
writeTransparencyMaskPixelData
(
QIODevice
&
io
)
{
if
(
m_onlyTransparencyMask
)
{
...
...
@@ -620,19 +614,28 @@ void PSDLayerRecord::writeTransparencyMaskPixelData(QIODevice &io)
m_onlyTransparencyMaskRect
,
m_transparencyMaskSizeOffset
,
-
1
,
true
);
true
,
byteOrder
);
}
}
void
PSDLayerRecord
::
writePixelData
(
QIODevice
&
io
)
{
try
{
writePixelDataImpl
(
io
);
switch
(
m_header
.
byteOrder
)
{
case
psd_byte_order
::
psdLittleEndian
:
writePixelDataImpl
<
psd_byte_order
::
psdLittleEndian
>
(
io
);
break
;
default:
writePixelDataImpl
(
io
);
break
;
}
}
catch
(
KisAslWriterUtils
::
ASLWriteException
&
e
)
{
throw
KisAslWriterUtils
::
ASLWriteException
(
PREPEND_METHOD
(
e
.
what
()));
}
}
template
<
psd_byte_order
byteOrder
>
void
PSDLayerRecord
::
writePixelDataImpl
(
QIODevice
&
io
)
{
dbgFile
<<
"writing pixel data for layer"
<<
layerName
<<
"at"
<<
io
.
pos
();
...
...
@@ -645,11 +648,11 @@ void PSDLayerRecord::writePixelDataImpl(QIODevice &io)
for
(
int
i
=
0
;
i
<
nChannels
;
i
++
)
{
const
ChannelInfo
*
channelInfo
=
channelInfoRecords
[
i
];
KisAslWriterUtils
::
OffsetStreamPusher
<
quint32
,
psd_
byte
_o
rder
::
psdBigEndian
>
channelBlockSizeExternalTag
(
io
,
0
,
channelInfo
->
channelInfoPosition
);
SAFE_WRITE_EX
(
psd_
byte
_o
rder
::
psdBigEndian
,
io
,
(
quint16
)
Compression
::
Uncompressed
);
KisAslWriterUtils
::
OffsetStreamPusher
<
quint32
,
byte
O
rder
>
channelBlockSizeExternalTag
(
io
,
0
,
channelInfo
->
channelInfoPosition
);
SAFE_WRITE_EX
(
byte
O
rder
,
io
,
(
quint16
)
Compression
::
Uncompressed
);
}
writeTransparencyMaskPixelData
(
io
);
writeTransparencyMaskPixelData
<
byteOrder
>
(
io
);
return
;
}
...
...
@@ -665,8 +668,8 @@ void PSDLayerRecord::writePixelDataImpl(QIODevice &io)
writingInfoList
<<
PsdPixelUtils
::
ChannelWritingInfo
(
channelInfo
->
channelId
,
channelInfo
->
channelInfoPosition
);
}
PsdPixelUtils
::
writePixelDataCommon
(
io
,
dev
,
rc
,
colorMode
,
channelSize
,
true
,
true
,
writingInfoList
);
writeTransparencyMaskPixelData
(
io
);
PsdPixelUtils
::
writePixelDataCommon
(
io
,
dev
,
rc
,
colorMode
,
channelSize
,
true
,
true
,
writingInfoList
,
byteOrder
);
writeTransparencyMaskPixelData
<
byteOrder
>
(
io
);
}
bool
PSDLayerRecord
::
valid
()
...
...
libs/psd/psd_layer_record.h
View file @
1f8f6593
...
...
@@ -131,12 +131,15 @@ public:
LayerMaskData
layerMask
;
struct
LayerBlendingRanges
{
struct
LayerBlendingRange
{
std
::
array
<
quint8
,
2
>
blackValues
;
std
::
array
<
quint8
,
2
>
whiteValues
;
};
QByteArray
data
;
quint8
blackValues
[
2
];
quint8
whiteValues
[
2
];
quint32
compositeGrayBlendDestinationRange
;
QVector
<
QPair
<
quint32
,
quint32
>>
sourceDestinationRanges
;
QPair
<
LayerBlendingRange
,
LayerBlendingRange
>
compositeGrayRange
;
QVector
<
QPair
<
LayerBlendingRange
,
LayerBlendingRange
>>
sourceDestinationRanges
;
};
LayerBlendingRanges
blendingRanges
;
...
...
@@ -158,8 +161,10 @@ private:
const
QDomDocument
&
stylesXmlDoc
,
bool
useLfxsLayerStyleFormat
);
template
<
psd_byte_order
=
psd_byte_order
::
psdBigEndian
>
void
writeTransparencyMaskPixelData
(
QIODevice
&
io
);
template
<
psd_byte_order
=
psd_byte_order
::
psdBigEndian
>
void
writePixelDataImpl
(
QIODevice
&
io
);
KisPaintDeviceSP
convertMaskDeviceIfNeeded
(
KisPaintDeviceSP
dev
);
...
...
@@ -176,4 +181,9 @@ private:
KRITAPSD_EXPORT
QDebug
operator
<<
(
QDebug
dbg
,
const
PSDLayerRecord
&
layer
);
KRITAPSD_EXPORT
QDebug
operator
<<
(
QDebug
dbg
,
const
ChannelInfo
&
layer
);
inline
QDebug
&
operator
<<
(
QDebug
dbg
,
const
PSDLayerRecord
::
LayerBlendingRanges
::
LayerBlendingRange
&
data
)
{
return
dbg
<<
data
.
blackValues
[
0
]
<<
data
.
blackValues
[
1
]
<<
data
.
whiteValues
[
0
]
<<
data
.
whiteValues
[
1
];
}
#endif // PSD_LAYER_RECORD_H
libs/psd/psd_layer_section.cpp
View file @
1f8f6593
...
...
@@ -6,6 +6,7 @@
*/
#include
"psd_layer_section.h"
#include
<QBuffer>
#include
<QIODevice>
#include
<KoColor.h>
...
...
@@ -487,13 +488,17 @@ bool PSDLayerMaskSection::write(QIODevice &io, KisNodeSP rootLayer)
bool
retval
=
true
;
try
{
switch
(
m_header
.
byteOrder
)
{
case
psd_byte_order
::
psdLittleEndian
:
writeImpl
<
psd_byte_order
::
psdLittleEndian
>
(
io
,
rootLayer
);
break
;
default:
writeImpl
(
io
,
rootLayer
);
break
;
if
(
m_header
.
tiffStyleLayerBlock
)
{
switch
(
m_header
.
byteOrder
)
{
case
psd_byte_order
::
psdLittleEndian
:
writeTiffImpl
<
psd_byte_order
::
psdLittleEndian
>
(
io
,
rootLayer
);
break
;
default:
writeTiffImpl
(
io
,
rootLayer
);
break
;
}
}
else
{
writePsdImpl
(
io
,
rootLayer
);
}
}
catch
(
KisAslWriterUtils
::
ASLWriteException
&
e
)
{
error
=
PREPEND_METHOD
(
e
.
what
());
...
...
@@ -503,8 +508,7 @@ bool PSDLayerMaskSection::write(QIODevice &io, KisNodeSP rootLayer)
return
retval
;
}
template
<
psd_byte_order
byteOrder
>
void
PSDLayerMaskSection
::
writeImpl
(
QIODevice
&
io
,
KisNodeSP
rootLayer
)
void
PSDLayerMaskSection
::
writePsdImpl
(
QIODevice
&
io
,
KisNodeSP
rootLayer
)
{
dbgFile
<<
"Writing layer layer section"
;
...
...
@@ -518,16 +522,16 @@ void PSDLayerMaskSection::writeImpl(QIODevice &io, KisNodeSP rootLayer)
}
{
KisAslWriterUtils
::
OffsetStreamPusher
<
quint32
,
byte
O
rder
>
layerAndMaskSectionSizeTag
(
io
,
2
);
KisAslWriterUtils
::
OffsetStreamPusher
<
quint32
,
psd_
byte
_o
rder
::
psdBigEndian
>
layerAndMaskSectionSizeTag
(
io
,
2
);
QDomDocument
mergedPatternsXmlDoc
;
{
KisAslWriterUtils
::
OffsetStreamPusher
<
quint32
,
byte
O
rder
>
layerInfoSizeTag
(
io
,
4
);
KisAslWriterUtils
::
OffsetStreamPusher
<
quint32
,
psd_
byte
_o
rder
::
psdBigEndian
>
layerInfoSizeTag
(
io
,
2
);
{
// number of layers (negative, because krita always has alpha)
const
qint16
layersSize
=
static_cast
<
qint16
>
(
-
nodes
.
size
());
SAFE_WRITE_EX
(
byte
O
rder
,
io
,
layersSize
);
SAFE_WRITE_EX
(
psd_
byte
_o
rder
::
psdBigEndian
,
io
,
layersSize
);
dbgFile
<<
"Number of layers"
<<
layersSize
<<
"at"
<<
io
.
pos
();
}
...
...
@@ -635,8 +639,6 @@ void PSDLayerMaskSection::writeImpl(QIODevice &io, KisNodeSP rootLayer)
// Now save the pixel data
for
(
PSDLayerRecord
*
layerRecord
:
layers
)
{
// XXX: endianness?
// Make consistent with PSDLayerRecord::readPixelData
layerRecord
->
writePixelData
(
io
);
}
}
...
...
@@ -644,9 +646,164 @@ void PSDLayerMaskSection::writeImpl(QIODevice &io, KisNodeSP rootLayer)
{
// write the global layer mask info -- which is empty
const
quint32
globalMaskSize
=
0
;
SAFE_WRITE_EX
(
byte
O
rder
,
io
,
globalMaskSize
);
SAFE_WRITE_EX
(
psd_
byte
_o
rder
::
psdBigEndian
,
io
,
globalMaskSize
);
}
globalInfoSection
.
writePattBlockEx
(
io
,
mergedPatternsXmlDoc
);
}
}
template
<
psd_byte_order
byteOrder
>
void
PSDLayerMaskSection
::
writeTiffImpl
(
QIODevice
&
io
,
KisNodeSP
rootLayer
)
{
dbgFile
<<
"(TIFF) Writing layer section"
;
// Build the whole layer structure
QList
<
FlattenedNode
>
nodes
;
addBackgroundIfNeeded
(
rootLayer
,
nodes
);
flattenNodes
(
rootLayer
,
nodes
);
if
(
nodes
.
isEmpty
())
{
throw
KisAslWriterUtils
::
ASLWriteException
(
"Could not find paint layers to save"
);
}
{
QDomDocument
mergedPatternsXmlDoc
;
{
KisAslWriterUtils
::
writeFixedString
<
byteOrder
>
(
"8BIM"
,
io
);
KisAslWriterUtils
::
writeFixedString
<
byteOrder
>
(
"Layr"
,
io
);
KisAslWriterUtils
::
OffsetStreamPusher
<
quint32
,
byteOrder
>
layerAndMaskSectionSizeTag
(
io
,
4
);
// number of layers (negative, because krita always has alpha)
const
qint16
layersSize
=
nodes
.
size
();
SAFE_WRITE_EX
(
byteOrder
,
io
,
layersSize
);
dbgFile
<<
"Number of layers"
<<
layersSize
<<
"at"
<<
io
.
pos
();
// Layer records section
for
(
const
FlattenedNode
&
item
:
nodes
)
{
KisNodeSP
node
=
item
.
node
;
PSDLayerRecord
*
layerRecord
=
new
PSDLayerRecord
(
m_header
);
layers
.
append
(
layerRecord
);
const
QRect
maskRect
;
const
bool
nodeVisible
=
node
->
visible
();
const
KoColorSpace
*
colorSpace
=
node
->
colorSpace
();
const
quint8
nodeOpacity
=
node
->
opacity
();
const
quint8
nodeClipping
=
0
;
const
KisPaintLayer
*
paintLayer
=
qobject_cast
<
KisPaintLayer
*>
(
node
.
data
());
const
bool
alphaLocked
=
(
paintLayer
&&
paintLayer
->
alphaLocked
());
const
QString
nodeCompositeOp
=
node
->
compositeOpId
();
const
KisGroupLayer
*
groupLayer
=
qobject_cast
<
KisGroupLayer
*>
(
node
.
data
());
const
bool
nodeIsPassThrough
=
groupLayer
&&
groupLayer
->
passThroughMode
();
QDomDocument
stylesXmlDoc
=
fetchLayerStyleXmlData
(
node
);
if
(
mergedPatternsXmlDoc
.
isNull
()
&&
!
stylesXmlDoc
.
isNull
())
{
mergedPatternsXmlDoc
=
stylesXmlDoc
;
}
else
if
(
!
mergedPatternsXmlDoc
.
isNull
()
&&
!
stylesXmlDoc
.
isNull
())
{
mergePatternsXMLSection
(
stylesXmlDoc
,
mergedPatternsXmlDoc
);
}
bool
nodeIrrelevant
=
false
;
QString
nodeName
;
KisPaintDeviceSP
layerContentDevice
;
psd_section_type
sectionType
;
if
(
item
.
type
==
FlattenedNode
::
RASTER_LAYER
)
{
nodeIrrelevant
=
false
;
nodeName
=
node
->
name
();
layerContentDevice
=
node
->
projection
();
sectionType
=
psd_other
;
}
else
{
nodeIrrelevant
=
true
;
nodeName
=
item
.
type
==
FlattenedNode
::
SECTION_DIVIDER
?
QString
(
"</Layer group>"
)
:
node
->
name
();
layerContentDevice
=
0
;
sectionType
=
item
.
type
==
FlattenedNode
::
SECTION_DIVIDER
?
psd_bounding_divider
:
item
.
type
==
FlattenedNode
::
FOLDER_OPEN
?
psd_open_folder
:
psd_closed_folder
;
}
// === no access to node anymore
QRect
layerRect
;
if
(
layerContentDevice
)
{
QRect
rc
=
layerContentDevice
->
exactBounds
();
rc
=
rc
.
normalized
();
// keep to the max of photoshop's capabilities
// XXX: update this to PSB
if
(
rc
.
width
()
>
30000
)
rc
.
setWidth
(
30000
);
if
(
rc
.
height
()
>
30000
)
rc
.
setHeight
(
30000
);
layerRect
=
rc
;
}
layerRecord
->
top
=
layerRect
.
y
();
layerRecord
->
left
=
layerRect
.
x
();
layerRecord
->
bottom
=
layerRect
.
y
()
+
layerRect
.
height
();
layerRecord
->
right
=
layerRect
.
x
()
+
layerRect
.
width
();
// colors + alpha channel
// note: transparency mask not included
layerRecord
->
nChannels
=
static_cast
<
quint16
>
(
colorSpace
->
colorChannelCount
()
+
1
);
ChannelInfo
*
info
=
new
ChannelInfo
;
info
->
channelId
=
-
1
;
// For the alpha channel, which we always have in Krita, and should be saved first in
layerRecord
->
channelInfoRecords
<<
info
;
// the rest is in display order: rgb, cmyk, lab...
for
(
quint32
i
=
0
;
i
<
colorSpace
->
colorChannelCount
();
++
i
)
{
info
=
new
ChannelInfo
;
info
->
channelId
=
static_cast
<
qint16
>
(
i
);
// 0 for red, 1 = green, etc
layerRecord
->
channelInfoRecords
<<
info
;
}
layerRecord
->
blendModeKey
=
composite_op_to_psd_blendmode
(
nodeCompositeOp
);
layerRecord
->
isPassThrough
=
nodeIsPassThrough
;
layerRecord
->
opacity
=
nodeOpacity
;
layerRecord
->
clipping
=
nodeClipping
;
layerRecord
->
transparencyProtected
=
alphaLocked
;
layerRecord
->
visible
=
nodeVisible
;
layerRecord
->
irrelevant
=
nodeIrrelevant
;
layerRecord
->
layerName
=
nodeName
.
isEmpty
()
?
i18n
(
"Unnamed Layer"
)
:
nodeName
;
layerRecord
->
write
(
io
,
layerContentDevice
,
nullptr
,
QRect
(),
sectionType
,
stylesXmlDoc
,
node
->
inherits
(
"KisGroupLayer"
));
}
dbgFile
<<
"start writing layer pixel data"
<<
io
.
pos
();
// Now save the pixel data
for
(
PSDLayerRecord
*
layerRecord
:
layers
)
{
layerRecord
->
writePixelData
(
io
);
}
}
// {
// // write the global layer mask info -- which is NOT empty but fixed
// KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
// KisAslWriterUtils::writeFixedString<byteOrder>("LMsk", io);
// KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> layerAndMaskSectionSizeTag(io, 4);
// // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_22664
// psdwrite<byteOrder>(io, quint16(0)); // CS: RGB
// psdwrite<byteOrder>(io, quint16(65535)); // Pure red verification
// psdwrite<byteOrder>(io, quint16(0));
// psdwrite<byteOrder>(io, quint16(0));
// psdwrite<byteOrder>(io, quint16(0));
// psdwrite<byteOrder>(io, quint16(50)); // opacity
// psdwrite<byteOrder>(io, quint16(128)); // kind
// }
globalInfoSection
.
writePattBlockEx
(
io
,
mergedPatternsXmlDoc
);
}
}
libs/psd/psd_layer_section.h
View file @
1f8f6593
...
...
@@ -59,8 +59,9 @@ private:
bool
readTiffImpl
(
QIODevice
&
io
);
template
<
psd_byte_order
byteOrder
=
psd_byte_order
::
psdBigEndian
>
bool
readGlobalMask
(
QIODevice
&
io
);
void
writePsdImpl
(
QIODevice
&
io
,
KisNodeSP
rootLayer
);
template
<
psd_byte_order
byteOrder
=
psd_byte_order
::
psdBigEndian
>
void
writeImpl
(
QIODevice
&
io
,
KisNodeSP
rootLayer
);
void
write
Tiff
Impl
(
QIODevice
&
io
,
KisNodeSP
rootLayer
);
private:
const
PSDHeader
m_header
;
...
...
libs/psd/psd_pixel_utils.cpp
View file @
1f8f6593
...
...
@@ -569,22 +569,23 @@ void readAlphaMaskChannels(QIODevice &io,
}
}
void
writeChannelDataRLE
(
QIODevice
&
io
,
const
quint8
*
plane
,
const
int
channelSize
,
const
QRect
&
rc
,
const
qint64
sizeFieldOffset
,
const
qint64
rleBlockOffset
,
const
bool
writeCompressionType
)
{
using
Pusher
=
KisAslWriterUtils
::
OffsetStreamPusher
<
quint32
,
psd_byte_order
::
psdBigEndian
>
;
template
<
psd_byte_order
byteOrder
=
psd_byte_order
::
psdBigEndian
>
void
writeChannelDataRLEImpl
(
QIODevice
&
io
,
const
quint8
*
plane
,
const
int
channelSize
,
const
QRect
&
rc
,
const
qint64
sizeFieldOffset
,
const
qint64
rleBlockOffset
,
const
bool
writeCompressionType
)
{
using
Pusher
=
KisAslWriterUtils
::
OffsetStreamPusher
<
quint32
,
byteOrder
>
;
QScopedPointer
<
Pusher
>
channelBlockSizeExternalTag
;
if
(
sizeFieldOffset
>=
0
)
{
channelBlockSizeExternalTag
.
reset
(
new
Pusher
(
io
,
0
,
sizeFieldOffset
));
}
if
(
writeCompressionType
)
{
SAFE_WRITE_EX
(
psd_
byte
_o
rder
::
psdBigEndian
,
io
,
(
quint16
)
Compression
::
RLE
);
SAFE_WRITE_EX
(
byte
O
rder
,
io
,
(
quint16
)
Compression
::
RLE
);
}
const
bool
externalRleBlock
=
rleBlockOffset
>=
0
;
...
...
@@ -604,7 +605,7 @@ void writeChannelDataRLE(QIODevice &io,
for
(
int
i
=
0
;
i
<
rc
.
height
();
++
i
)
{
// XXX: choose size for PSB!
const
quint16
fakeRLEBLockSize
=
0
;
SAFE_WRITE_EX
(
psd_
byte
_o
rder
::
psdBigEndian
,
io
,
fakeRLEBLockSize
);
SAFE_WRITE_EX
(
byte
O
rder
,
io
,
fakeRLEBLockSize
);
}
}
...
...
@@ -613,10 +614,7 @@ void writeChannelDataRLE(QIODevice &io,
QByteArray
uncompressed
=
QByteArray
::
fromRawData
((
const
char
*
)
plane
+
row
*
stride
,
stride
);
QByteArray
compressed
=
Compression
::
compress
(
uncompressed
,
Compression
::
RLE
);
KisAslWriterUtils
::
OffsetStreamPusher
<
quint16
,
psd_byte_order
::
psdBigEndian
>
rleExternalTag
(
io
,
0
,
channelRLESizePos
+
row
*
static_cast
<
qint64
>
(
sizeof
(
quint16
)));
KisAslWriterUtils
::
OffsetStreamPusher
<
quint16
,
byteOrder
>
rleExternalTag
(
io
,
0
,
channelRLESizePos
+
row
*
static_cast
<
qint64
>
(
sizeof
(
quint16
)));
if
(
io
.
write
(
compressed
)
!=
compressed
.
size
())
{
throw
KisAslWriterUtils
::
ASLWriteException
(
"Failed to write image data"
);
...
...
@@ -624,6 +622,24 @@ void writeChannelDataRLE(QIODevice &io,
}
}
void
writeChannelDataRLE
(
QIODevice
&
io
,
const
quint8
*
plane
,
const
int
channelSize
,
const
QRect
&
rc
,
const
qint64
sizeFieldOffset
,
const
qint64
rleBlockOffset
,
const
bool
writeCompressionType
,
psd_byte_order
byteOrder
)
{
switch
(
byteOrder
)
{
case
psd_byte_order
::
psdLittleEndian
:
return
writeChannelDataRLEImpl
<
psd_byte_order
::
psdLittleEndian
>
(
io
,
plane
,
channelSize
,
rc
,
sizeFieldOffset
,
rleBlockOffset
,
writeCompressionType
);
default:
return
writeChannelDataRLEImpl
(
io
,
plane
,
channelSize
,
rc
,
sizeFieldOffset
,
rleBlockOffset
,
writeCompressionType
);
}
}
template
<
psd_byte_order
byteOrder
=
psd_byte_order
::
psdBigEndian
>
inline
void
preparePixelForWrite
(
quint8
*
dataPlane
,
int
numPixels
,
int
channelSize
,
int
channelId
,
psd_color_mode
colorMode
)
{
// if the bitdepth > 8, place the bytes in the right order
...
...
@@ -640,7 +656,8 @@ inline void preparePixelForWrite(quint8 *dataPlane, int numPixels, int channelSi
quint16
*
pixelPtr
=
reinterpret_cast
<
quint16
*>
(
dataPlane
)
+
i
;
val
=
*
pixelPtr
;
val
=
qFromBigEndian
(
val
);
if
(
byteOrder
==
psd_byte_order
::
psdBigEndian
)
val
=
qFromBigEndian
(
val
);
if
(
channelId
>=
0
&&
(
colorMode
==
CMYK
||
colorMode
==
CMYK64
))
{
val
=
quint16_MAX
-
val
;
}
...
...
@@ -652,23 +669,25 @@ inline void preparePixelForWrite(quint8 *dataPlane, int numPixels, int channelSi
quint32
*
pixelPtr
=
reinterpret_cast
<
quint32
*>
(
dataPlane
)
+
i
;
val
=
*
pixelPtr
;
val
=
qFromBigEndian
(
val
);
if
(
byteOrder
==
psd_byte_order
::
psdBigEndian
)
val
=
qFromBigEndian
(
val
);
if
(
channelId
>=
0
&&
(
colorMode
==
CMYK
||
colorMode
==
CMYK64
))
{
val
=
quint16_MAX
-
val
;
val
=
std
::
numeric_limits
<
quint32
>::
max
()
-
val
;
}
*
pixelPtr
=
val
;
}
}
}
void
writePixelDataCommon
(
QIODevice
&
io
,
KisPaintDeviceSP
dev
,
const
QRect
&
rc
,
psd_color_mode
colorMode
,
int
channelSize
,
bool
alphaFirst
,