File indexing completed on 2024-05-19 04:27:28
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Boudewijn Rempt <boud@valdyas.org> 0003 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 #ifndef PSD_ADDITIONAL_LAYER_INFO_BLOCK_H 0008 #define PSD_ADDITIONAL_LAYER_INFO_BLOCK_H 0009 0010 #include "kritapsd_export.h" 0011 0012 #include <QBitArray> 0013 #include <QByteArray> 0014 #include <QDomDocument> 0015 #include <QIODevice> 0016 #include <QString> 0017 #include <QVector> 0018 #include <functional> 0019 0020 #include <kis_types.h> 0021 #include <kis_global.h> 0022 0023 #include <kis_node.h> 0024 #include <kis_paint_device.h> 0025 #include <kis_generator_registry.h> 0026 #include <KisGlobalResourcesInterface.h> 0027 #include <KoStopGradient.h> 0028 #include <KoSegmentGradient.h> 0029 #include <psd.h> 0030 0031 #include <asl/kis_asl_xml_writer.h> 0032 0033 #include "psd_header.h" 0034 0035 // additional layer information 0036 0037 // LEVELS 0038 // Level record 0039 struct KRITAPSD_EXPORT psd_layer_level_record { 0040 quint16 input_floor {0}; // (0...253) 0041 quint16 input_ceiling {2}; // (2...255) 0042 quint16 output_floor {0}; // 255). Matched to input floor. 0043 quint16 output_ceiling {0}; // (0...255) 0044 float gamma {0.0}; // Short integer from 10...999 representing 0.1...9.99. Applied to all image data. 0045 }; 0046 0047 // Levels settings files are loaded and saved in the Levels dialog. 0048 struct KRITAPSD_EXPORT psd_layer_levels { 0049 psd_layer_level_record record[29]; // 29 sets of level records, each level containing 5 qint8 integers 0050 // Photoshop CS (8.0) Additional information 0051 // At the end of the Version 2 file is the following information: 0052 quint16 extra_level_count {0}; // Count of total level record structures. Subtract the legacy number of level record structures, 29, to determine how many are 0053 // remaining in the file for reading. 0054 psd_layer_level_record *extra_record {nullptr}; // Additional level records according to count 0055 quint8 lookup_table[3][256]; 0056 }; 0057 0058 // CURVES 0059 // The following is the data for each curve specified by count above 0060 struct KRITAPSD_EXPORT psd_layer_curves_data { 0061 quint16 channel_index {0}; // Before each curve is a channel index. 0062 quint16 point_count {0}; // Count of points in the curve (qint8 integer from 2...19) 0063 quint16 output_value[19]; // All coordinates have range 0 to 255 0064 quint16 input_value[19]; 0065 }; 0066 0067 // Curves file format 0068 struct KRITAPSD_EXPORT psd_layer_curves { 0069 quint16 curve_count {0}; // Count of curves in the file. 0070 psd_layer_curves_data *curve {nullptr}; 0071 quint8 lookup_table[3][256]; 0072 }; 0073 0074 // BRIGHTNESS AND CONTRAST 0075 struct KRITAPSD_EXPORT psd_layer_brightness_contrast { 0076 qint8 brightness {0}; 0077 qint8 contrast {0}; 0078 qint8 mean_value {0}; // for brightness and contrast 0079 qint8 Lab_color {0}; 0080 quint8 lookup_table[256]; 0081 }; 0082 0083 // COLOR BALANCE 0084 struct KRITAPSD_EXPORT psd_layer_color_balance { 0085 qint8 cyan_red[3]; // (-100...100). shadows, midtones, highlights 0086 qint8 magenta_green[3]; 0087 qint8 yellow_blue[3]; 0088 bool preserve_luminosity {false}; 0089 quint8 lookup_table[3][256]; 0090 }; 0091 0092 // HUE/SATURATION 0093 // Hue/Saturation settings files are loaded and saved in Photoshop¡¯s Hue/Saturation dialog 0094 struct KRITAPSD_EXPORT psd_layer_hue_saturation { 0095 quint8 hue_or_colorization {0}; // 0 = Use settings for hue-adjustment; 1 = Use settings for colorization. 0096 qint8 colorization_hue {0}; // Photoshop 5.0: The actual values are stored for the new version. Hue is - 180...180, Saturation is 0...100, and Lightness is 0097 // -100...100. 0098 qint8 colorization_saturation {0}; // Photoshop 4.0: Three qint8 integers Hue, Saturation, and Lightness from ¨C100...100. 0099 qint8 colorization_lightness {0}; // The user interface represents hue as ¨C180...180, saturation as 0...100, and Lightness as -100...1000, as the traditional 0100 // HSB color wheel, with red = 0. 0101 qint8 master_hue {0}; // Master hue, saturation and lightness values. 0102 qint8 master_saturation {0}; 0103 qint8 master_lightness {0}; 0104 qint8 range_values[6][4]; // For RGB and CMYK, those values apply to each of the six hextants in the HSB color wheel: those image pixels nearest to red, 0105 // yellow, green, cyan, blue, or magenta. These numbers appear in the user interface from ¨C60...60, however the slider will 0106 // reflect each of the possible 201 values from ¨C100...100. 0107 qint8 setting_values[6][3]; // For Lab, the first four of the six values are applied to image pixels in the four Lab color quadrants, yellow, green, blue, 0108 // and magenta. The other two values are ignored ( = 0). The values appear in the user interface from ¨C90 to 90. 0109 quint8 lookup_table[6][360]; 0110 }; 0111 0112 // SELECTIVE COLOR 0113 // Selective Color settings files are loaded and saved in Photoshop¡¯s Selective Color dialog. 0114 struct KRITAPSD_EXPORT psd_layer_selective_color { 0115 quint16 correction_method {0}; // 0 = Apply color correction in relative mode; 1 = Apply color correction in absolute mode. 0116 qint8 cyan_correction[10]; // Amount of cyan correction. Short integer from ¨C100...100. 0117 qint8 magenta_correction[10]; // Amount of magenta correction. Short integer from ¨C100...100. 0118 qint8 yellow_correction[10]; // Amount of yellow correction. Short integer from ¨C100...100. 0119 qint8 black_correction[10]; // Amount of black correction. Short integer from ¨C100...100. 0120 }; 0121 0122 // THRESHOLD 0123 struct KRITAPSD_EXPORT psd_layer_threshold { 0124 quint16 level {1}; // (1...255) 0125 }; 0126 0127 // INVERT 0128 // no parameter 0129 0130 // POSTERIZE 0131 struct KRITAPSD_EXPORT psd_layer_posterize { 0132 quint16 levels {2}; // (2...255) 0133 quint8 lookup_table[256]; 0134 }; 0135 0136 // CHANNEL MIXER 0137 struct KRITAPSD_EXPORT psd_layer_channel_mixer { 0138 bool monochrome {false}; 0139 qint8 red_cyan[4]; // RGB or CMYK color plus constant for the mixer settings. 4 * 2 bytes of color with 2 bytes of constant. 0140 qint8 green_magenta[4]; // (-200...200) 0141 qint8 blue_yellow[4]; 0142 qint8 black[4]; 0143 qint8 constant[4]; 0144 }; 0145 0146 // PHOTO FILTER 0147 struct KRITAPSD_EXPORT psd_layer_photo_filter { 0148 qint32 x_color {0}; // 4 bytes each for XYZ color 0149 qint32 y_color {0}; 0150 qint32 z_color {0}; 0151 qint32 density {0}; // (1...100) 0152 bool preserve_luminosity {true}; 0153 }; 0154 0155 #include <kis_psd_layer_style.h> 0156 #include <KoColorProfile.h> 0157 0158 struct KRITAPSD_EXPORT psd_layer_solid_color { 0159 KoColor fill_color; 0160 const KoColorSpace *cs {nullptr}; 0161 0162 // Used by ASL callback; 0163 void setColor(const KoColor &color) { 0164 fill_color = color; 0165 if (fill_color.colorSpace()->colorModelId() == cs->colorModelId()) { 0166 fill_color.setProfile(cs->profile()); 0167 } 0168 0169 /** 0170 * PSD files may be saved with a stripped profile that is not suitable for 0171 * displaying, so we should convert such colors into some "universal" color 0172 * space, so that we could actually manage it 0173 */ 0174 if (fill_color.profile() && !fill_color.profile()->isSuitableForOutput()) { 0175 fill_color.convertTo(KoColorSpaceRegistry::instance()-> 0176 colorSpace(LABAColorModelID.id(), Float32BitsColorDepthID.id(), 0)); 0177 } 0178 } 0179 QDomDocument getFillLayerConfig() { 0180 KisFilterConfigurationSP cfg; 0181 cfg = KisGeneratorRegistry::instance()->value("color")->defaultConfiguration(KisGlobalResourcesInterface::instance()); 0182 QVariant v; 0183 v.setValue(fill_color); 0184 cfg->setProperty("color", v); 0185 QDomDocument doc; 0186 doc.setContent(cfg->toXML()); 0187 return doc; 0188 } 0189 0190 bool loadFromConfig(KisFilterConfigurationSP cfg) { 0191 if (cfg->name() != "color") { 0192 return false; 0193 } 0194 fill_color = cfg->getColor("color"); 0195 return true; 0196 } 0197 0198 QDomDocument getASLXML() { 0199 KisAslXmlWriter w; 0200 w.enterDescriptor("", "", "null"); 0201 if (cs) { 0202 w.writeColor("Clr ", fill_color.convertedTo(cs)); 0203 } else { 0204 w.writeColor("Clr ", fill_color); 0205 } 0206 0207 w.leaveDescriptor(); 0208 0209 return w.document(); 0210 } 0211 }; 0212 0213 struct KRITAPSD_EXPORT psd_layer_gradient_fill { 0214 double angle {0.0}; 0215 QString style {QString("linear")}; 0216 QString repeat {QString("none")}; 0217 double scale {100.0}; 0218 bool reverse {false}; // Is gradient reverse 0219 bool dithered {false}; // Is gradient dithered 0220 bool align_with_layer {false}; 0221 QPointF offset; 0222 QDomDocument gradient; 0223 int imageWidth {1}; // Set when loading. 0224 int imageHeight {1}; 0225 0226 // Used by ASL callback; 0227 void setGradient(const KoAbstractGradientSP &newGradient) { 0228 QDomDocument document; 0229 QDomElement gradientElement = document.createElement("gradient"); 0230 gradientElement.setAttribute("name", newGradient->name()); 0231 0232 if (dynamic_cast<KoStopGradient*>(newGradient.data())) { 0233 KoStopGradient *gradient = dynamic_cast<KoStopGradient*>(newGradient.data()); 0234 gradient->toXML(document, gradientElement); 0235 } else if (dynamic_cast<KoSegmentGradient*>(newGradient.data())) { 0236 KoSegmentGradient *gradient = dynamic_cast<KoSegmentGradient*>(newGradient.data()); 0237 gradient->toXML(document, gradientElement); 0238 } 0239 0240 document.appendChild(gradientElement); 0241 gradient = document; 0242 } 0243 0244 void setDither(bool Dthr) { 0245 dithered = Dthr; 0246 } 0247 0248 void setReverse(bool Rvrs) { 0249 reverse = Rvrs; 0250 } 0251 0252 void setAngle(float Angl) { 0253 angle = Angl; 0254 } 0255 0256 void setType(const QString type) { 0257 repeat = "none"; 0258 if (type == "Lnr "){ 0259 style = "linear"; 0260 } else if (type == "Rdl "){ 0261 style = "radial"; 0262 } else if (type == "Angl"){ 0263 style = "conical"; 0264 } else if (type == "Rflc"){ 0265 style = "bilinear"; 0266 repeat ="alternate"; 0267 } else { 0268 style = "square"; // diamond??? 0269 } 0270 } 0271 0272 void setAlignWithLayer(bool align) { 0273 align_with_layer = align; 0274 } 0275 0276 void setScale(float Scl) { 0277 scale = Scl; 0278 } 0279 0280 void setOffset(QPointF Ofst) { 0281 offset = Ofst; 0282 } 0283 0284 QDomDocument getFillLayerConfig() { 0285 KisFilterConfigurationSP cfg; 0286 cfg = KisGeneratorRegistry::instance()->value("gradient")->defaultConfiguration(KisGlobalResourcesInterface::instance()); 0287 cfg->setProperty("gradient", gradient.toString()); 0288 cfg->setProperty("dither", dithered); 0289 cfg->setProperty("reverse", reverse); 0290 0291 cfg->setProperty("shape", style); 0292 cfg->setProperty("repeat", repeat); 0293 0294 cfg->setProperty("end_position_coordinate_system", "polar"); 0295 0296 cfg->setProperty("end_position_distance_units", "percent_of_width"); 0297 cfg->setProperty("start_position_x_units", "percent_of_width"); 0298 cfg->setProperty("start_position_y_units", "percent_of_height"); 0299 0300 // angle seems to go from -180 to +180; 0301 double fixedAngle = fmod(360.0 + angle, 360.0); 0302 0303 double scaleModifier = 1.0; 0304 0305 if (style == "square") { 0306 fixedAngle = fmod((45.0 + fixedAngle), 360.0); 0307 0308 scaleModifier = cos(kisDegreesToRadians(45.0)); 0309 double halfAngle = fmod(fabs(fixedAngle), 180.0); 0310 scaleModifier *= 1/cos(kisDegreesToRadians(45.0 - fmod(halfAngle, 45.0) )); 0311 if (halfAngle >= 45.0 && halfAngle < 135.0) { 0312 scaleModifier = (scaleModifier) * (imageHeight / imageWidth); 0313 } 0314 0315 } else { 0316 double halfAngle = fmod(fabs(fixedAngle), 180.0); 0317 scaleModifier *= 1/cos(kisDegreesToRadians(halfAngle)); 0318 if (halfAngle >= 45.0 && halfAngle < 135.0) { 0319 scaleModifier = (scaleModifier) * (imageHeight / imageWidth); 0320 } 0321 0322 } 0323 0324 cfg->setProperty("end_position_angle", fixedAngle); 0325 0326 if (style == "linear") { 0327 // linear has the problem that in Krita it rotates around the top-left, 0328 // while in psd it rotates around the middle. 0329 QPointF center(imageWidth*0.5, imageHeight*0.5); 0330 0331 QTransform rotate; 0332 rotate.rotate(fixedAngle); 0333 QTransform tf = QTransform::fromTranslate(-center.x(), -center.y()) 0334 * rotate * QTransform::fromTranslate(center.x(), center.y()); 0335 QPointF topleft = tf.inverted().map(QPointF(0.0, 0.0)); 0336 double xPercentage = (topleft.x()/double(imageWidth)) * 100.0; 0337 double yPercentage = (topleft.y()/double(imageHeight)) * 100.0; 0338 0339 cfg->setProperty("end_position_distance", scale * scaleModifier); 0340 cfg->setProperty("start_position_x", xPercentage + offset.x()); 0341 cfg->setProperty("start_position_y", yPercentage + offset.y()); 0342 } else { 0343 cfg->setProperty("end_position_distance", scale * 0.5 * fabs(scaleModifier)); 0344 cfg->setProperty("start_position_x", (50.0)+offset.x()); 0345 cfg->setProperty("start_position_y", (50.0)+offset.y()); 0346 } 0347 0348 QDomDocument doc; 0349 doc.setContent(cfg->toXML()); 0350 return doc; 0351 } 0352 0353 bool loadFromConfig(KisFilterConfigurationSP cfg) { 0354 if (cfg->name() != "gradient") { 0355 return false; 0356 } 0357 0358 bool res = false; 0359 0360 res = gradient.setContent(cfg->getString("gradient", "")); 0361 0362 dithered = cfg->getBool("dither"); 0363 reverse = cfg->getBool("reverse"); 0364 align_with_layer = false; // not supported. 0365 0366 style = cfg->getString("shape", "linear"); 0367 repeat = cfg->getString("repeat", "none"); 0368 0369 bool polar = (cfg->getString("end_position_coordinate_system") == "polar"); 0370 0371 QPointF start(cfg->getDouble("start_position_x", 0.0), cfg->getDouble("start_position_y", 0.0)); 0372 if (polar) { 0373 angle = cfg->getDouble("end_position_angle", 0.0); 0374 scale = cfg->getDouble("end_position_distance", 100.0); 0375 } else { 0376 // assume cartesian 0377 QPointF end(cfg->getDouble("end_position_x", 1.0), cfg->getDouble("end_position_y", 1.0)); 0378 // calculate angle and scale. 0379 double width = start.x() - end.x(); 0380 double height = start.y() - end.y(); 0381 angle = fmod(360.0 + kisRadiansToDegrees(atan2(width, height)), 360.0); 0382 scale = sqrt((width*width) + (height*height)); 0383 } 0384 0385 0386 if (style == "linear") { 0387 QPointF center(imageWidth*0.5, imageHeight*0.5); 0388 0389 QTransform rotate; 0390 rotate.rotate(angle); 0391 QTransform tf = QTransform::fromTranslate(-center.x(), -center.y()) 0392 * rotate * QTransform::fromTranslate(center.x(), center.y()); 0393 QPointF topleft = tf.inverted().map(QPointF(0.0, 0.0)); 0394 double xPercentage = (topleft.x()/double(imageWidth)) * 100.0; 0395 double yPercentage = (topleft.y()/double(imageHeight)) * 100.0; 0396 offset = QPointF((start.x() - xPercentage), (start.y() - yPercentage)); 0397 } else { 0398 scale = scale*2; 0399 offset = QPointF((start.x() - 50.0), (start.y() - 50.0)); 0400 } 0401 0402 double scaleModifier = 1.0; 0403 if (style == "square") { 0404 scaleModifier = cos(kisDegreesToRadians(45.0)); 0405 double halfAngle = fmod(fabs(angle), 180.0); 0406 scaleModifier *= 1/cos(kisDegreesToRadians(45.0 - fmod(halfAngle, 45.0) )); 0407 if (halfAngle >= 45.0 && halfAngle < 135.0) { 0408 scaleModifier = (scaleModifier) * (imageHeight / imageWidth); 0409 } 0410 0411 angle = angle - 45.0; 0412 if (angle < 0) { 0413 angle = 360.0 - angle; 0414 } 0415 0416 } else { 0417 double halfAngle = fmod(fabs(angle), 180.0); 0418 scaleModifier *= 1/cos(kisDegreesToRadians(halfAngle)); 0419 if (halfAngle >= 45.0 && halfAngle < 135.0) { 0420 scaleModifier = (scaleModifier) * (imageHeight / imageWidth); 0421 } 0422 0423 } 0424 0425 if (angle > 180) { 0426 angle = (0.0 - fmod(angle, 180.0)); 0427 } 0428 0429 scale /= fabs(scaleModifier); 0430 0431 0432 return res; 0433 } 0434 0435 QDomDocument getASLXML() { 0436 KisAslXmlWriter w; 0437 w.enterDescriptor("", "", "null"); 0438 0439 if (!gradient.isNull()) { 0440 const QDomElement gradientElement = gradient.firstChildElement(); 0441 if (!gradientElement.isNull()) { 0442 const QString gradientType = gradientElement.attribute("type"); 0443 if (gradientType == "stop") { 0444 const KoStopGradient grad = KoStopGradient::fromXML(gradientElement); 0445 if (grad.valid()) { 0446 w.writeStopGradient("Grad", grad); 0447 } 0448 } else if (gradientType == "segment") { 0449 const KoSegmentGradient grad = KoSegmentGradient::fromXML(gradientElement); 0450 if (grad.valid()) { 0451 w.writeSegmentGradient("Grad", grad); 0452 } 0453 } 0454 } 0455 } 0456 w.writeBoolean("Dthr", dithered); 0457 w.writeBoolean("Rvrs", reverse); 0458 w.writeUnitFloat("Angl", "#Ang", angle); 0459 0460 QString type = "Lnr "; 0461 if (style == "linear"){ 0462 type = "Lnr "; 0463 } else if (style == "radial") { 0464 type = "Rdl "; 0465 } else if (style == "conical"){ 0466 type = "Angl"; 0467 } else if (style == "bilinear"){ 0468 type = "Rflc"; 0469 } else if (style == "square") { 0470 type = "Dmnd"; 0471 } 0472 0473 w.writeEnum("Type", "GrdT", type); 0474 w.writeBoolean("Algn", align_with_layer); 0475 w.writeUnitFloat("Scl ", "#Prc", scale); 0476 w.writePoint("Ofst", offset); 0477 0478 w.leaveDescriptor(); 0479 0480 return w.document(); 0481 } 0482 }; 0483 0484 struct KRITAPSD_EXPORT psd_layer_pattern_fill { 0485 double angle {0.0}; 0486 double scale {1.0}; 0487 QPointF offset; 0488 QString patternName; 0489 QString patternID; 0490 KoPatternSP pattern; 0491 bool align_with_layer {false}; 0492 0493 void setAngle(float Angl) { 0494 angle = Angl; 0495 } 0496 void setScale(float Scl) { 0497 scale = Scl; 0498 } 0499 void setOffset(QPointF phase) { 0500 offset = phase; 0501 } 0502 0503 void setPatternRef(const QString Idnt, const QString name) { 0504 patternName = name; 0505 patternID = Idnt; 0506 } 0507 0508 void setAlignWithLayer(bool align) { 0509 align_with_layer = align; 0510 } 0511 0512 QDomDocument getFillLayerConfig() const { 0513 KisFilterConfigurationSP cfg; 0514 cfg = KisGeneratorRegistry::instance()->value("pattern")->defaultConfiguration(KisGlobalResourcesInterface::instance()); 0515 0516 cfg->setProperty("pattern", patternName); 0517 cfg->setProperty("fileName", QString(patternID + ".pat")); 0518 cfg->setProperty("md5", ""); // Zero out MD5, PSD patterns are looked up by UUID in filename 0519 0520 //angle is flipped for patterns in Krita. 0521 double fixedAngle = 360.0 - fmod(360.0 + angle, 360.0); 0522 0523 cfg->setProperty("transform_scale_x", scale / 100); 0524 cfg->setProperty("transform_scale_y", scale / 100); 0525 cfg->setProperty("transform_rotation_z", fixedAngle); 0526 0527 cfg->setProperty("transform_offset_x", offset.x()); 0528 cfg->setProperty("transform_offset_y", offset.y()); 0529 QDomDocument doc; 0530 doc.setContent(cfg->toXML()); 0531 return doc; 0532 } 0533 0534 bool loadFromConfig(KisFilterConfigurationSP cfg) { 0535 if (cfg->name() != "pattern") { 0536 return false; 0537 } 0538 0539 const QString patternMD5 = cfg->getString("md5", ""); 0540 const QString patternNameTemp = cfg->getString("pattern", "Grid01.pat"); 0541 const QString patternFileName = cfg->getString("fileName", ""); 0542 0543 KoResourceLoadResult res = KisGlobalResourcesInterface::instance()->source(ResourceType::Patterns).bestMatchLoadResult(patternMD5, patternFileName, patternNameTemp); 0544 pattern = res.resource<KoPattern>(); 0545 0546 patternName = cfg->getString("pattern", ""); 0547 0548 // Pattern ID needs the pattern to be saved first. 0549 0550 align_with_layer = false; 0551 0552 scale = cfg->getDouble("transform_scale_x", 1.0) * 100; 0553 0554 angle = 360.0 - cfg->getDouble("transform_rotation_z", 0.0); 0555 if (angle > 180) { 0556 angle = (180.0 - angle); 0557 } 0558 0559 offset = QPointF(cfg->getInt("transform_offset_x", 0), cfg->getInt("transform_offset_y", 0)); 0560 0561 return true; 0562 } 0563 0564 QDomDocument getASLXML() { 0565 KisAslXmlWriter w; 0566 w.enterDescriptor("", "", "null"); 0567 0568 // pattern ref 0569 w.enterDescriptor("Ptrn", "", "Ptrn"); 0570 0571 w.writeText("Nm ", patternName); 0572 if (patternID.isEmpty()) { 0573 qWarning() << "This pattern cannot be saved: No pattern UUID available."; 0574 return QDomDocument(); 0575 } 0576 w.writeText("Idnt", patternID); 0577 w.leaveDescriptor(); 0578 0579 // end 0580 0581 w.writeBoolean("Algn", align_with_layer); 0582 w.writeUnitFloat("Scl ", "#Prc", scale); 0583 w.writeUnitFloat("Angl", "#Ang", angle); 0584 w.writePoint("phase", offset); 0585 0586 w.leaveDescriptor(); 0587 0588 return w.document(); 0589 } 0590 }; 0591 0592 struct KRITAPSD_EXPORT psd_layer_type_face { 0593 qint8 mark {0}; // Mark value 0594 qint32 font_type {0}; // Font type data 0595 qint8 font_name[256]; // Pascal string of font name 0596 qint8 font_family_name[256]; // Pascal string of font family name 0597 qint8 font_style_name[256]; // Pascal string of font style name 0598 qint8 script {0}; // Script value 0599 qint32 number_axes_vector {0}; // Number of design axes vector to follow 0600 qint32 *vector {nullptr}; // Design vector value 0601 }; 0602 0603 struct KRITAPSD_EXPORT psd_layer_type_style { 0604 qint8 mark {0}; // Mark value 0605 qint8 face_mark {0}; // Face mark value 0606 qint32 size {0}; // Size value 0607 qint32 tracking {0}; // Tracking value 0608 qint32 kerning {0}; // Kerning value 0609 qint32 leading {0}; // Leading value 0610 qint32 base_shift {0}; // Base shift value 0611 bool auto_kern {false}; // Auto kern on/off 0612 bool rotate {false}; // Rotate up/down 0613 }; 0614 0615 struct KRITAPSD_EXPORT psd_layer_type_line { 0616 qint32 char_count {0}; // Character count value 0617 qint8 orientation {0}; // Orientation value 0618 qint8 alignment {0}; // Alignment value 0619 qint8 actual_char {0}; // Actual character as a double byte character 0620 qint8 style {0}; // Style value 0621 }; 0622 0623 struct KRITAPSD_EXPORT psd_layer_type_tool { 0624 double transform_info[6]; // 6 * 8 double precision numbers for the transform information 0625 qint8 faces_count {0}; // Count of faces 0626 psd_layer_type_face *face {nullptr}; 0627 qint8 styles_count {0}; // Count of styles 0628 psd_layer_type_style *style {nullptr}; 0629 qint8 type {0}; // Type value 0630 qint32 scaling_factor {0}; // Scaling factor value 0631 qint32 character_count {0}; // Character count value 0632 qint32 horz_place {0}; // Horizontal placement 0633 qint32 vert_place {0}; // Vertical placement 0634 qint32 select_start {0}; // Select start value 0635 qint32 select_end {0}; // Select end value 0636 qint8 lines_count {0}; // Line count 0637 psd_layer_type_line *line {nullptr}; 0638 QColor color; 0639 bool anti_alias {false}; // Anti alias on/off 0640 }; 0641 0642 /** 0643 * @brief The PsdAdditionalLayerInfoBlock class implements the Additional Layer Information block 0644 * 0645 * See: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_71546 0646 */ 0647 class KRITAPSD_EXPORT PsdAdditionalLayerInfoBlock 0648 { 0649 public: 0650 PsdAdditionalLayerInfoBlock(const PSDHeader &header); 0651 0652 using ExtraLayerInfoBlockHandler = std::function<bool(QIODevice &)>; 0653 using UserMaskInfoBlockHandler = std::function<bool(QIODevice &)>; 0654 0655 void setExtraLayerInfoBlockHandler(ExtraLayerInfoBlockHandler handler); 0656 void setUserMaskInfoBlockHandler(UserMaskInfoBlockHandler handler); 0657 0658 bool read(QIODevice &io); 0659 bool write(QIODevice &io, KisNodeSP node); 0660 0661 void writeLuniBlockEx(QIODevice &io, const QString &layerName); 0662 void writeLsctBlockEx(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey); 0663 void writeLfx2BlockEx(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat); 0664 void writePattBlockEx(QIODevice &io, const QDomDocument &patternsXmlDoc); 0665 void writeLclrBlockEx(QIODevice &io, const quint16 &labelColor); 0666 0667 void writeFillLayerBlockEx(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type); 0668 0669 bool valid(); 0670 0671 const PSDHeader &m_header; 0672 QString error; 0673 QStringList keys; // List of all the keys that we've seen 0674 0675 QString unicodeLayerName; 0676 QDomDocument layerStyleXml; 0677 QVector<QDomDocument> embeddedPatterns; 0678 quint16 labelColor{0}; // layer color. 0679 0680 QDomDocument fillConfig; 0681 psd_fill_type fillType {psd_fill_solid_color}; 0682 0683 psd_section_type sectionDividerType; 0684 QString sectionDividerBlendMode; 0685 0686 private: 0687 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian> 0688 void readImpl(QIODevice &io); 0689 0690 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian> 0691 void writeLuniBlockExImpl(QIODevice &io, const QString &layerName); 0692 0693 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian> 0694 void writeLsctBlockExImpl(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey); 0695 0696 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian> 0697 void writeLfx2BlockExImpl(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat); 0698 0699 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian> 0700 void writePattBlockExImpl(QIODevice &io, const QDomDocument &patternsXmlDoc); 0701 0702 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian> 0703 void writeLclrBlockExImpl(QIODevice &io, const quint16 &lclr); 0704 0705 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian> 0706 void writeFillLayerBlockExImpl(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type); 0707 0708 private: 0709 ExtraLayerInfoBlockHandler m_layerInfoBlockHandler; 0710 UserMaskInfoBlockHandler m_userMaskBlockHandler; 0711 }; 0712 0713 #endif // PSD_ADDITIONAL_LAYER_INFO_BLOCK_H