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