Krita Source Code Documentation
Loading...
Searching...
No Matches
psd_header.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2009 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7#include "psd_header.h"
8
9#include <QIODevice>
10#include <QtEndian>
11#include <psd_utils.h>
12
13struct Header {
14 char signature[4]; // 8PBS
15 char version[2]; // 1 or 2
16 char padding[6];
17 char nChannels[2]; // 1 - 56
18 char height[4]; // 1-30,000 or 1 - 300,000
19 char width[4]; // 1-30,000 or 1 - 300,000
20 char channelDepth[2]; // 1, 8, 16
21 char colormode[2]; // 0-9
22};
23
25 : version(0)
26 , nChannels(0)
27 , height(0)
28 , width(0)
29 , channelDepth(0)
30 , colormode(COLORMODE_UNKNOWN)
31 , byteOrder(psd_byte_order::psdBigEndian)
32 , tiffStyleLayerBlock(false)
33{
34}
35
36bool PSDHeader::read(QIODevice &device)
37{
38 Header header;
39 quint64 bytesRead = device.read((char *)&header, sizeof(Header));
40 if (bytesRead != sizeof(Header)) {
41 error = "Could not read header: not enough bytes";
42 return false;
43 }
44
45 signature = QString(header.signature);
46 memcpy(&version, header.version, 2);
47 version = qFromBigEndian(version);
48 memcpy(&nChannels, header.nChannels, 2);
49 nChannels = qFromBigEndian(nChannels);
50 memcpy(&height, header.height, 4);
51 height = qFromBigEndian(height);
52 memcpy(&width, header.width, 4);
53 width = qFromBigEndian(width);
54 memcpy(&channelDepth, header.channelDepth, 2);
55 channelDepth = qFromBigEndian(channelDepth);
56 memcpy(&colormode, header.colormode, 2);
57 colormode = (psd_color_mode)qFromBigEndian((quint16)colormode);
58
59 return valid();
60}
61
62bool PSDHeader::write(QIODevice &device)
63{
64 if (!valid())
65 return false;
66 if (!psdwrite(device, signature))
67 return false;
68 if (!psdwrite(device, version))
69 return false;
70 if (!psdpad(device, 6))
71 return false;
72 if (!psdwrite(device, nChannels))
73 return false;
74 if (!psdwrite(device, height))
75 return false;
76 if (!psdwrite(device, width))
77 return false;
78 if (!psdwrite(device, channelDepth))
79 return false;
80 if (!psdwrite(device, (quint16)colormode))
81 return false;
82 return true;
83}
84
86{
87 if (signature != "8BPS") {
88 error = "Not a PhotoShop document. Signature is: " + signature;
89 return false;
90 }
91 if (version < 1 || version > 2) {
92 error = QString("Wrong version: %1").arg(version);
93 return false;
94 }
95 if (nChannels < 1 || nChannels > 56) {
96 error = QString("Channel count out of range: %1").arg(nChannels);
97 return false;
98 }
99 if (version == 1) {
100 if (height < 1 || height > 30000) {
101 error = QString("Height out of range: %1").arg(height);
102 return false;
103 }
104 if (width < 1 || width > 30000) {
105 error = QString("Width out of range: %1").arg(width);
106 return false;
107 }
108 } else /* if (version == 2) */ {
109 if (height < 1 || height > 300000) {
110 error = QString("Height out of range: %1").arg(height);
111 return false;
112 }
113 if (width < 1 || width > 300000) {
114 error = QString("Width out of range: %1").arg(width);
115 return false;
116 }
117 }
118 if (channelDepth != 1 && channelDepth != 8 && channelDepth != 16) {
119 error = QString("Channel depth incorrect: %1").arg(channelDepth);
120 return false;
121 }
122 if (colormode < 0 || colormode > 9) {
123 error = QString("Colormode is out of range: %1").arg(colormode);
124 return false;
125 }
126
127 return true;
128}
129
130QDebug operator<<(QDebug dbg, const PSDHeader &header)
131{
132#ifndef NODEBUG
133 dbg.nospace() << "(valid: " << const_cast<PSDHeader *>(&header)->valid();
134 dbg.nospace() << ", signature: " << header.signature;
135 dbg.nospace() << ", version:" << header.version;
136 dbg.nospace() << ", number of channels: " << header.nChannels;
137 dbg.nospace() << ", height: " << header.height;
138 dbg.nospace() << ", width: " << header.width;
139 dbg.nospace() << ", channel depth: " << header.channelDepth;
140 dbg.nospace() << ", color mode: ";
141 switch (header.colormode) {
142 case (Bitmap):
143 dbg.nospace() << "Bitmap";
144 break;
145 case (Grayscale):
146 dbg.nospace() << "Grayscale";
147 break;
148 case (Indexed):
149 dbg.nospace() << "Indexed";
150 break;
151 case (RGB):
152 dbg.nospace() << "RGB";
153 break;
154 case (CMYK):
155 dbg.nospace() << "CMYK";
156 break;
157 case (MultiChannel):
158 dbg.nospace() << "MultiChannel";
159 break;
160 case (DuoTone):
161 dbg.nospace() << "DuoTone";
162 break;
163 case (Lab):
164 dbg.nospace() << "Lab";
165 break;
166 default:
167 dbg.nospace() << "Unknown";
168 };
169 dbg.nospace() << ")";
170#endif
171 return dbg.nospace();
172}
psd_color_mode colormode
Definition psd_header.h:48
bool valid()
quint16 channelDepth
Definition psd_header.h:47
quint16 version
Definition psd_header.h:43
quint16 nChannels
Definition psd_header.h:44
quint32 height
Definition psd_header.h:45
bool write(QIODevice &device)
quint32 width
Definition psd_header.h:46
bool read(QIODevice &device)
QString error
Definition psd_header.h:52
QString signature
Definition psd_header.h:42
psd_byte_order
Definition psd.h:33
psd_color_mode
Definition psd.h:50
@ Lab
Definition psd.h:58
@ RGB
Definition psd.h:54
@ DuoTone
Definition psd.h:57
@ CMYK
Definition psd.h:55
@ COLORMODE_UNKNOWN
Definition psd.h:65
@ Indexed
Definition psd.h:53
@ MultiChannel
Definition psd.h:56
@ Grayscale
Definition psd.h:52
@ Bitmap
Definition psd.h:51
QDebug operator<<(QDebug dbg, const PSDHeader &header)
bool psdpad(QIODevice &io, quint32 padding)
Definition psd_utils.h:248
std::enable_if_t< std::is_arithmetic< T >::value, bool > psdwrite(QIODevice &io, T v)
Definition psd_utils.h:170
char version[2]
char signature[4]
char height[4]
char channelDepth[2]
char nChannels[2]
char width[4]
char colormode[2]
char padding[6]