Krita Source Code Documentation
Loading...
Searching...
No Matches
psd_additional_layer_info_block.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2014 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#ifndef PSD_ADDITIONAL_LAYER_INFO_BLOCK_H
8#define PSD_ADDITIONAL_LAYER_INFO_BLOCK_H
9
10#include "kritapsd_export.h"
11
12#include <QDomDocument>
13#include <QIODevice>
14#include <QString>
15#include <QVector>
16#include <functional>
17
18#include <kis_types.h>
19#include <kis_global.h>
20
21#include <kis_node.h>
22#include <kis_paint_device.h>
26#include <KoStopGradient.h>
27#include <KoSegmentGradient.h>
28#include <KoShapeBackground.h>
29#include <KoColorBackground.h>
31#include <KoPatternBackground.h>
32#include <KoShapeStroke.h>
33#include <psd.h>
34
37#include <cos/kis_cos_parser.h>
38#include <cos/kis_cos_writer.h>
39#include <QVariantHash>
40
41#include "psd_header.h"
42
43// additional layer information
44
45// LEVELS
46// Level record
47struct KRITAPSD_EXPORT psd_layer_level_record {
48 quint16 input_floor {0}; // (0...253)
49 quint16 input_ceiling {2}; // (2...255)
50 quint16 output_floor {0}; // 255). Matched to input floor.
51 quint16 output_ceiling {0}; // (0...255)
52 float gamma {0.0}; // Short integer from 10...999 representing 0.1...9.99. Applied to all image data.
53};
54
55// Levels settings files are loaded and saved in the Levels dialog.
56struct KRITAPSD_EXPORT psd_layer_levels {
57 psd_layer_level_record record[29]; // 29 sets of level records, each level containing 5 qint8 integers
58 // Photoshop CS (8.0) Additional information
59 // At the end of the Version 2 file is the following information:
60 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
61 // remaining in the file for reading.
62 psd_layer_level_record *extra_record {nullptr}; // Additional level records according to count
63 quint8 lookup_table[3][256];
64};
65
66// CURVES
67// The following is the data for each curve specified by count above
68struct KRITAPSD_EXPORT psd_layer_curves_data {
69 quint16 channel_index {0}; // Before each curve is a channel index.
70 quint16 point_count {0}; // Count of points in the curve (qint8 integer from 2...19)
71 quint16 output_value[19]; // All coordinates have range 0 to 255
72 quint16 input_value[19];
73};
74
75// Curves file format
76struct KRITAPSD_EXPORT psd_layer_curves {
77 quint16 curve_count {0}; // Count of curves in the file.
78 psd_layer_curves_data *curve {nullptr};
79 quint8 lookup_table[3][256];
80};
81
82// BRIGHTNESS AND CONTRAST
83struct KRITAPSD_EXPORT psd_layer_brightness_contrast {
84 qint8 brightness {0};
85 qint8 contrast {0};
86 qint8 mean_value {0}; // for brightness and contrast
87 qint8 Lab_color {0};
88 quint8 lookup_table[256];
89};
90
91// COLOR BALANCE
92struct KRITAPSD_EXPORT psd_layer_color_balance {
93 qint8 cyan_red[3]; // (-100...100). shadows, midtones, highlights
94 qint8 magenta_green[3];
95 qint8 yellow_blue[3];
96 bool preserve_luminosity {false};
97 quint8 lookup_table[3][256];
98};
99
100// HUE/SATURATION
101// Hue/Saturation settings files are loaded and saved in Photoshop¡¯s Hue/Saturation dialog
102struct KRITAPSD_EXPORT psd_layer_hue_saturation {
103 quint8 hue_or_colorization {0}; // 0 = Use settings for hue-adjustment; 1 = Use settings for colorization.
104 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
105 // -100...100.
106 qint8 colorization_saturation {0}; // Photoshop 4.0: Three qint8 integers Hue, Saturation, and Lightness from ¨C100...100.
107 qint8 colorization_lightness {0}; // The user interface represents hue as ¨C180...180, saturation as 0...100, and Lightness as -100...1000, as the traditional
108 // HSB color wheel, with red = 0.
109 qint8 master_hue {0}; // Master hue, saturation and lightness values.
110 qint8 master_saturation {0};
111 qint8 master_lightness {0};
112 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,
113 // yellow, green, cyan, blue, or magenta. These numbers appear in the user interface from ¨C60...60, however the slider will
114 // reflect each of the possible 201 values from ¨C100...100.
115 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,
116 // and magenta. The other two values are ignored ( = 0). The values appear in the user interface from ¨C90 to 90.
117 quint8 lookup_table[6][360];
118};
119
120// SELECTIVE COLOR
121// Selective Color settings files are loaded and saved in Photoshop¡¯s Selective Color dialog.
122struct KRITAPSD_EXPORT psd_layer_selective_color {
123 quint16 correction_method {0}; // 0 = Apply color correction in relative mode; 1 = Apply color correction in absolute mode.
124 qint8 cyan_correction[10]; // Amount of cyan correction. Short integer from ¨C100...100.
125 qint8 magenta_correction[10]; // Amount of magenta correction. Short integer from ¨C100...100.
126 qint8 yellow_correction[10]; // Amount of yellow correction. Short integer from ¨C100...100.
127 qint8 black_correction[10]; // Amount of black correction. Short integer from ¨C100...100.
128};
129
130// THRESHOLD
131struct KRITAPSD_EXPORT psd_layer_threshold {
132 quint16 level {1}; // (1...255)
133};
134
135// INVERT
136// no parameter
137
138// POSTERIZE
139struct KRITAPSD_EXPORT psd_layer_posterize {
140 quint16 levels {2}; // (2...255)
141 quint8 lookup_table[256];
142};
143
144// CHANNEL MIXER
145struct KRITAPSD_EXPORT psd_layer_channel_mixer {
146 bool monochrome {false};
147 qint8 red_cyan[4]; // RGB or CMYK color plus constant for the mixer settings. 4 * 2 bytes of color with 2 bytes of constant.
148 qint8 green_magenta[4]; // (-200...200)
149 qint8 blue_yellow[4];
150 qint8 black[4];
151 qint8 constant[4];
152};
153
154// PHOTO FILTER
155struct KRITAPSD_EXPORT psd_layer_photo_filter {
156 qint32 x_color {0}; // 4 bytes each for XYZ color
157 qint32 y_color {0};
158 qint32 z_color {0};
159 qint32 density {0}; // (1...100)
160 bool preserve_luminosity {true};
161};
162
163#include <kis_psd_layer_style.h>
164#include <KoColorProfile.h>
165
166struct KRITAPSD_EXPORT psd_layer_solid_color {
168 const KoColorSpace *cs {nullptr};
169
170 // Used by ASL callback;
171 void setColor(const KoColor &color) {
172 fill_color = color;
173 if (fill_color.colorSpace()->colorModelId() == cs->colorModelId()) {
174 fill_color.setProfile(cs->profile());
175 }
176
182 if (fill_color.profile() && !fill_color.profile()->isSuitableForOutput()) {
184 colorSpace(LABAColorModelID.id(), Float32BitsColorDepthID.id(), 0));
185 }
186 }
187
188 static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_layer_solid_color *data) {
189 catcher.subscribeColor(path + "/Clr ", std::bind(&psd_layer_solid_color::setColor, data, std::placeholders::_1));
190 }
191
192 QDomDocument getFillLayerConfig() {
195 QVariant v;
196 v.setValue(fill_color);
197 cfg->setProperty("color", v);
198 QDomDocument doc;
199 doc.setContent(cfg->toXML());
200 return doc;
201 }
202
204 if (cfg->name() != "color") {
205 return false;
206 }
207 fill_color = cfg->getColor("color");
208 return true;
209 }
210
211 QDomDocument getASLXML() {
213 w.enterDescriptor("", "", "null");
214 writeASL(w);
215 w.leaveDescriptor();
216
217 return w.document();
218 }
219
221 if (cs) {
222 w.writeColor("Clr ", fill_color.convertedTo(cs));
223 } else {
224 w.writeColor("Clr ", fill_color);
225 }
226 }
227
228 QBrush getBrush() {
229 QColor c;
230 fill_color.toQColor(&c);
231 return QBrush(c, Qt::SolidPattern);
232 }
233
237};
238
239struct KRITAPSD_EXPORT psd_layer_gradient_fill {
240 double angle {0.0};
241 QString style {QString("linear")};
242 QString repeat {QString("none")};
243 double scale {100.0};
244 bool reverse {false}; // Is gradient reverse
245 bool dithered {false}; // Is gradient dithered
246 bool align_with_layer {false};
247 QPointF offset;
248 QDomDocument gradient;
249 int imageWidth {1}; // Set when loading.
250 int imageHeight {1};
251
252 // Used by ASL callback;
253 void setGradient(const KoAbstractGradientSP &newGradient) {
254 QDomDocument document;
255 QDomElement gradientElement = document.createElement("gradient");
256 gradientElement.setAttribute("name", newGradient->name());
257
258 if (dynamic_cast<KoStopGradient*>(newGradient.data())) {
259 KoStopGradient *gradient = dynamic_cast<KoStopGradient*>(newGradient.data());
260 gradient->toXML(document, gradientElement);
261 } else if (dynamic_cast<KoSegmentGradient*>(newGradient.data())) {
262 KoSegmentGradient *gradient = dynamic_cast<KoSegmentGradient*>(newGradient.data());
263 gradient->toXML(document, gradientElement);
264 }
265
266 document.appendChild(gradientElement);
267 gradient = document;
268 }
269
270 void setDither(bool Dthr) {
271 dithered = Dthr;
272 }
273
274 void setReverse(bool Rvrs) {
275 reverse = Rvrs;
276 }
277
278 void setAngle(float Angl) {
279 angle = Angl;
280 }
281
282 void setType(const QString type) {
283 repeat = "none";
284 if (type == "Lnr "){
285 style = "linear";
286 } else if (type == "Rdl "){
287 style = "radial";
288 } else if (type == "Angl"){
289 style = "conical";
290 } else if (type == "Rflc"){
291 style = "bilinear";
292 repeat ="alternate";
293 } else {
294 style = "square"; // diamond???
295 }
296 }
297
298 void setAlignWithLayer(bool align) {
299 align_with_layer = align;
300 }
301
302 void setScale(float Scl) {
303 scale = Scl;
304 }
305
306 void setOffset(QPointF Ofst) {
307 offset = Ofst;
308 }
309
310 static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_layer_gradient_fill *data) {
311 catcher.subscribeGradient(path + "/Grad", std::bind(&psd_layer_gradient_fill::setGradient, data, std::placeholders::_1));
312 catcher.subscribeBoolean(path + "/Dthr", std::bind(&psd_layer_gradient_fill::setDither, data, std::placeholders::_1));
313 catcher.subscribeBoolean(path + "/Rvrs", std::bind(&psd_layer_gradient_fill::setReverse, data, std::placeholders::_1));
314 catcher.subscribeUnitFloat(path + "/Angl", "#Ang", std::bind(&psd_layer_gradient_fill::setAngle, data, std::placeholders::_1));
315 catcher.subscribeEnum(path + "/Type", "GrdT", std::bind(&psd_layer_gradient_fill::setType, data, std::placeholders::_1));
316 catcher.subscribeBoolean(path + "/Algn", std::bind(&psd_layer_gradient_fill::setAlignWithLayer, data, std::placeholders::_1));
317 catcher.subscribeUnitFloat(path + "/Scl ", "#Prc", std::bind(&psd_layer_gradient_fill::setScale, data, std::placeholders::_1));
318 catcher.subscribePoint(path + "/Ofst", std::bind(&psd_layer_gradient_fill::setOffset, data, std::placeholders::_1));
319 }
320
321 QDomDocument getFillLayerConfig() {
324 cfg->setProperty("gradient", gradient.toString());
325 cfg->setProperty("dither", dithered);
326 cfg->setProperty("reverse", reverse);
327
328 cfg->setProperty("shape", style);
329 cfg->setProperty("repeat", repeat);
330
331 cfg->setProperty("end_position_coordinate_system", "polar");
332
333 cfg->setProperty("end_position_distance_units", "percent_of_width");
334 cfg->setProperty("start_position_x_units", "percent_of_width");
335 cfg->setProperty("start_position_y_units", "percent_of_height");
336
337 // angle seems to go from -180 to +180;
338 double fixedAngle = fmod(360.0 + angle, 360.0);
339
340 double scaleModifier = 1.0;
341
342 if (style == "square") {
343 fixedAngle = fmod((45.0 + fixedAngle), 360.0);
344
345 scaleModifier = cos(kisDegreesToRadians(45.0));
346 double halfAngle = fmod(fabs(fixedAngle), 180.0);
347 scaleModifier *= 1/cos(kisDegreesToRadians(45.0 - fmod(halfAngle, 45.0) ));
348 if (halfAngle >= 45.0 && halfAngle < 135.0) {
349 scaleModifier = (scaleModifier) * (imageHeight / imageWidth);
350 }
351
352 } else {
353 double halfAngle = fmod(fabs(fixedAngle), 180.0);
354 scaleModifier *= 1/cos(kisDegreesToRadians(halfAngle));
355 if (halfAngle >= 45.0 && halfAngle < 135.0) {
356 scaleModifier = (scaleModifier) * (imageHeight / imageWidth);
357 }
358
359 }
360
361 cfg->setProperty("end_position_angle", fixedAngle);
362
363 if (style == "linear") {
364 // linear has the problem that in Krita it rotates around the top-left,
365 // while in psd it rotates around the middle.
366 QPointF center(imageWidth*0.5, imageHeight*0.5);
367
368 QTransform rotate;
369 rotate.rotate(fixedAngle);
370 QTransform tf = QTransform::fromTranslate(-center.x(), -center.y())
371 * rotate * QTransform::fromTranslate(center.x(), center.y());
372 QPointF topleft = tf.inverted().map(QPointF(0.0, 0.0));
373 double xPercentage = (topleft.x()/double(imageWidth)) * 100.0;
374 double yPercentage = (topleft.y()/double(imageHeight)) * 100.0;
375
376 cfg->setProperty("end_position_distance", scale * scaleModifier);
377 cfg->setProperty("start_position_x", xPercentage + offset.x());
378 cfg->setProperty("start_position_y", yPercentage + offset.y());
379 } else {
380 cfg->setProperty("end_position_distance", scale * 0.5 * fabs(scaleModifier));
381 cfg->setProperty("start_position_x", (50.0)+offset.x());
382 cfg->setProperty("start_position_y", (50.0)+offset.y());
383 }
384
385 QDomDocument doc;
386 doc.setContent(cfg->toXML());
387 return doc;
388 }
389
391 if (cfg->name() != "gradient") {
392 return false;
393 }
394
395 bool res = bool(gradient.setContent(cfg->getString("gradient", "")));
396 dithered = cfg->getBool("dither");
397 reverse = cfg->getBool("reverse");
398 align_with_layer = false; // not supported.
399
400 style = cfg->getString("shape", "linear");
401 repeat = cfg->getString("repeat", "none");
402
403 bool polar = (cfg->getString("end_position_coordinate_system") == "polar");
404
405 QPointF start(cfg->getDouble("start_position_x", 0.0), cfg->getDouble("start_position_y", 0.0));
406 if (polar) {
407 angle = cfg->getDouble("end_position_angle", 0.0);
408 scale = cfg->getDouble("end_position_distance", 100.0);
409 } else {
410 // assume cartesian
411 QPointF end(cfg->getDouble("end_position_x", 1.0), cfg->getDouble("end_position_y", 1.0));
412 // calculate angle and scale.
413 double width = start.x() - end.x();
414 double height = start.y() - end.y();
415 angle = fmod(360.0 + kisRadiansToDegrees(atan2(width, height)), 360.0);
416 scale = sqrt((width*width) + (height*height));
417 }
418
419
420 if (style == "linear") {
421 QPointF center(imageWidth*0.5, imageHeight*0.5);
422
423 QTransform rotate;
424 rotate.rotate(angle);
425 QTransform tf = QTransform::fromTranslate(-center.x(), -center.y())
426 * rotate * QTransform::fromTranslate(center.x(), center.y());
427 QPointF topleft = tf.inverted().map(QPointF(0.0, 0.0));
428 double xPercentage = (topleft.x()/double(imageWidth)) * 100.0;
429 double yPercentage = (topleft.y()/double(imageHeight)) * 100.0;
430 offset = QPointF((start.x() - xPercentage), (start.y() - yPercentage));
431 } else {
432 scale = scale*2;
433 offset = QPointF((start.x() - 50.0), (start.y() - 50.0));
434 }
435
436 double scaleModifier = 1.0;
437 if (style == "square") {
438 scaleModifier = cos(kisDegreesToRadians(45.0));
439 double halfAngle = fmod(fabs(angle), 180.0);
440 scaleModifier *= 1/cos(kisDegreesToRadians(45.0 - fmod(halfAngle, 45.0) ));
441 if (halfAngle >= 45.0 && halfAngle < 135.0) {
442 scaleModifier = (scaleModifier) * (imageHeight / imageWidth);
443 }
444
445 angle = angle - 45.0;
446 if (angle < 0) {
447 angle = 360.0 - angle;
448 }
449
450 } else {
451 double halfAngle = fmod(fabs(angle), 180.0);
452 scaleModifier *= 1/cos(kisDegreesToRadians(halfAngle));
453 if (halfAngle >= 45.0 && halfAngle < 135.0) {
454 scaleModifier = (scaleModifier) * (imageHeight / imageWidth);
455 }
456
457 }
458
459 if (angle > 180) {
460 angle = (0.0 - fmod(angle, 180.0));
461 }
462
463 scale /= fabs(scaleModifier);
464
465
466 return res;
467 }
468
469 QDomDocument getASLXML() {
471 w.enterDescriptor("", "", "null");
472 writeASL(w);
473
474 w.leaveDescriptor();
475
476 return w.document();
477 }
478
480 if (!gradient.isNull()) {
481 const QDomElement gradientElement = gradient.firstChildElement();
482 if (!gradientElement.isNull()) {
483 const QString gradientType = gradientElement.attribute("type");
484 if (gradientType == "stop") {
485 const KoStopGradient grad = KoStopGradient::fromXML(gradientElement);
486 if (grad.valid()) {
487 w.writeStopGradient("Grad", grad);
488 }
489 } else if (gradientType == "segment") {
490 const KoSegmentGradient grad = KoSegmentGradient::fromXML(gradientElement);
491 if (grad.valid()) {
492 w.writeSegmentGradient("Grad", grad);
493 }
494 }
495 }
496 }
497 w.writeBoolean("Dthr", dithered);
498 w.writeBoolean("Rvrs", reverse);
499 w.writeUnitFloat("Angl", "#Ang", angle);
500
501 QString type = "Lnr ";
502 if (style == "linear"){
503 type = "Lnr ";
504 } else if (style == "radial") {
505 type = "Rdl ";
506 } else if (style == "conical"){
507 type = "Angl";
508 } else if (style == "bilinear"){
509 type = "Rflc";
510 } else if (style == "square") {
511 type = "Dmnd";
512 }
513
514 w.writeEnum("Type", "GrdT", type);
515 w.writeBoolean("Algn", align_with_layer);
516 w.writeUnitFloat("Scl ", "#Prc", scale);
517 w.writePoint("Ofst", offset);
518 }
519
520 QBrush getBrush() {
521 QGradient *grad = getGradient();
522 if (grad) {
523 QBrush brush = *grad;
524 return brush;
525 }
526 return QBrush(Qt::transparent);
527 }
528 QGradient *getGradient() {
529 QGradient *pointer = nullptr;
530 if (!gradient.isNull()) {
531 const QDomElement gradientElement = gradient.firstChildElement();
532 if (!gradientElement.isNull()) {
533 const QString gradientType = gradientElement.attribute("type");
534 if (gradientType == "stop") {
535 const KoStopGradient grad = KoStopGradient::fromXML(gradientElement);
536 if (grad.valid()) {
537 pointer = grad.toQGradient();
538 }
539 } else if (gradientType == "segment") {
540 const KoSegmentGradient grad = KoSegmentGradient::fromXML(gradientElement);
541 if (grad.valid()) {
542 pointer = grad.toQGradient();
543 }
544 }
545 }
546 }
547 if (pointer) {
548 QGradient::CoordinateMode mode = QGradient::ObjectBoundingMode;
549 pointer->setCoordinateMode(mode);
550
551 if (reverse) {
552 QGradientStops newStops;
553 Q_FOREACH(QGradientStop stop, pointer->stops()) {
554 newStops.append(QPair<double, QColor>(1.0-stop.first, stop.second));
555 }
556 pointer->setStops(newStops);
557 }
558
559 QLinearGradient *g = static_cast<QLinearGradient*>(pointer);
560 QLineF line = QLineF::fromPolar(0.5*(scale*0.01), angle);
561 line.translate(QPointF(0.5, 0.5)+(offset*0.01));
562
563 if (style == "radial") {
564 QRadialGradient *r = new QRadialGradient(line.p1(), line.length());
565 r->setCoordinateMode(mode);
566
567 r->setSpread(pointer->spread());
568 r->setStops(pointer->stops());
569 pointer = r;
570
571 } else if (style == "bilinear") {
572 QLinearGradient *b = new QLinearGradient();
573 Q_FOREACH(QGradientStop stop, pointer->stops()) {
574 double pos = 0.5 - stop.first*0.5;
575 b->setColorAt(pos, stop.second);
576 double pos2 = 1.0 - pos;
577 if (pos != pos2) {
578 b->setColorAt(pos2, stop.second);
579 }
580 }
581 b->setCoordinateMode(mode);
582
583 b->setFinalStop(line.p2());
584 line.setAngle(180+angle);
585 b->setStart(line.p2());
586 pointer = b;
587 } else if (g) {
588 g->setFinalStop(line.p2());
589 line.setAngle(180+angle);
590 g->setStart(line.p2());
591 pointer = g;
592 }
593 }
594
595 return pointer;
596 }
597
598 void setFromQGradient(const QGradient *gradient) {
599 setGradient(KoStopGradient::fromQGradient(gradient));
600 if (gradient->coordinateMode() == QGradient::ObjectBoundingMode) {
601 align_with_layer = true;
602 }
603 if (gradient->type() == QGradient::LinearGradient) {
604 const QLinearGradient *g = static_cast<const QLinearGradient*>(gradient);
605 QLineF line(g->start(), g->finalStop());
606 offset = (line.center()-QPointF(0.5, 0.5))*100.0;
607 angle = line.angle();
608 if (angle > 180) {
609 angle = (0.0 - fmod(angle, 180.0));
610 }
611 scale = (line.length())*100.0;
612 style = "linear";
613 } else {
614 const QRadialGradient *g = static_cast<const QRadialGradient*>(gradient);
615 offset = (g->center()-QPointF(0.5, 0.5)) * 100.0;
616 angle = 0;
617 scale = (g->radius()*2) * 100.0;
618 style = "radial";
619 }
620 }
621
623 QGradient *pointer = getGradient();
625 return bg;
626 }
627
629 if (style == "radial" || style == "linear") {
630 return true;
631 }
632 return false;
633 }
634};
635
636struct KRITAPSD_EXPORT psd_layer_pattern_fill {
637 double angle {0.0};
638 double scale {100.0};
639 QPointF offset;
640 QString patternName;
641 QString patternID;
643 bool align_with_layer {false};
644
645 void setAngle(float Angl) {
646 angle = Angl;
647 }
648 void setScale(float Scl) {
649 scale = Scl;
650 }
651 void setOffset(QPointF phase) {
652 offset = phase;
653 }
654
655 void setPatternRef(const QString Idnt, const QString name) {
656 patternName = name;
657 patternID = Idnt;
658 }
659
660 void setAlignWithLayer(bool align) {
661 align_with_layer = align;
662 }
663
664 static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_layer_pattern_fill *data) {
665 catcher.subscribeUnitFloat(path + "/Angl", "#Ang", std::bind(&psd_layer_pattern_fill::setAngle, data, std::placeholders::_1));
666 catcher.subscribeUnitFloat(path + "/Scl ", "#Prc", std::bind(&psd_layer_pattern_fill::setScale, data, std::placeholders::_1));
667 catcher.subscribeBoolean(path + "/Algn", std::bind(&psd_layer_pattern_fill::setAlignWithLayer, data, std::placeholders::_1));
668 catcher.subscribePoint(path + "/phase", std::bind(&psd_layer_pattern_fill::setOffset, data, std::placeholders::_1));
669 catcher.subscribePatternRef(path + "/Ptrn", std::bind(&psd_layer_pattern_fill::setPatternRef, data, std::placeholders::_1, std::placeholders::_2));
670 }
671
672 QDomDocument getFillLayerConfig() const {
675
676 cfg->setProperty("pattern", patternName);
677 cfg->setProperty("fileName", QString(patternID + ".pat"));
678 cfg->setProperty("md5", ""); // Zero out MD5, PSD patterns are looked up by UUID in filename
679
680 //angle is flipped for patterns in Krita.
681 double fixedAngle = 360.0 - fmod(360.0 + angle, 360.0);
682
683 cfg->setProperty("transform_scale_x", scale / 100);
684 cfg->setProperty("transform_scale_y", scale / 100);
685 cfg->setProperty("transform_rotation_z", fixedAngle);
686
687 cfg->setProperty("transform_offset_x", offset.x());
688 cfg->setProperty("transform_offset_y", offset.y());
689 QDomDocument doc;
690 doc.setContent(cfg->toXML());
691 return doc;
692 }
693
695 if (cfg->name() != "pattern") {
696 return false;
697 }
698
699 const QString patternMD5 = cfg->getString("md5", "");
700 const QString patternNameTemp = cfg->getString("pattern", "Grid01.pat");
701 const QString patternFileName = cfg->getString("fileName", "");
702
703 KoResourceLoadResult res = KisGlobalResourcesInterface::instance()->source(ResourceType::Patterns).bestMatchLoadResult(patternMD5, patternFileName, patternNameTemp);
704 pattern = res.resource<KoPattern>();
705
706 patternName = cfg->getString("pattern", "");
707
708 // Pattern ID needs the pattern to be saved first.
709
710 align_with_layer = false;
711
712 scale = cfg->getDouble("transform_scale_x", 1.0) * 100;
713
714 angle = 360.0 - cfg->getDouble("transform_rotation_z", 0.0);
715 if (angle > 180) {
716 angle = (180.0 - angle);
717 }
718
719 offset = QPointF(cfg->getInt("transform_offset_x", 0), cfg->getInt("transform_offset_y", 0));
720
721 return true;
722 }
723
724 QDomDocument getASLXML() {
726 w.enterDescriptor("", "", "null");
727
728 if (patternID.isEmpty()) {
729 qWarning() << "This pattern cannot be saved: No pattern UUID available.";
730 return QDomDocument();
731 }
732
733 writeASL(w);
734
735 w.leaveDescriptor();
736
737 return w.document();
738 }
739
741 // pattern ref
742 w.enterDescriptor("Ptrn", "", "Ptrn");
743
744 w.writeText("Nm ", patternName);
745 w.writeText("Idnt", patternID);
746 w.leaveDescriptor();
747
748 // end
749
750 w.writeBoolean("Algn", align_with_layer);
751 w.writeUnitFloat("Scl ", "#Prc", scale);
752 w.writeUnitFloat("Angl", "#Ang", angle);
753 w.writePoint("phase", offset);
754 }
755
757 const QString patternMD5 = "";
758 const QString patternNameTemp = patternName;
759 const QString patternFileName = QString(patternID + ".pat");
760
761 KoResourceLoadResult res = embeddedProxy.resourcesInterface()->source(ResourceType::Patterns).bestMatchLoadResult(patternMD5, patternFileName, patternNameTemp);
762 pattern = res.resource<KoPattern>();
763 }
764
767
768 loadPattern(embeddedProxy);
769 if (pattern) {
770 bg->setPattern(pattern->pattern());
771 } else {
773 bg->setPattern(res.resource<KoPattern>()->pattern());
774 }
775 QSizeF size = bg->patternOriginalSize();
776 QPointF refPoint(offset.x()/size.width(), offset.y()/size.height());
777 size = QSizeF(size.width() * (0.01*scale), size.height() * (0.01*scale));
778 bg->setPatternDisplaySize(size);
779 bg->setReferencePointOffset(refPoint);
780 return bg;
781 }
782
784 QBrush brush;
785 loadPattern(embeddedProxy);
786 if (pattern) {
787 brush.setTextureImage(pattern->pattern());
788 } else {
790 brush.setTextureImage(res.resource<KoPattern>()->pattern());
791 }
792 QTransform t = QTransform::fromScale(scale*0.01, scale*0.01);
793 t.rotate(angle);
794 brush.setTransform(t);
795 return brush;
796 }
797};
798
799struct KRITAPSD_EXPORT psd_layer_type_face {
800 qint8 mark {0}; // Mark value
801 qint32 font_type {0}; // Font type data
802 qint8 font_name[256]; // Pascal string of font name
803 qint8 font_family_name[256]; // Pascal string of font family name
804 qint8 font_style_name[256]; // Pascal string of font style name
805 qint8 script {0}; // Script value
806 qint32 number_axes_vector {0}; // Number of design axes vector to follow
807 qint32 *vector {nullptr}; // Design vector value
808};
809
810struct KRITAPSD_EXPORT psd_layer_type_style {
811 qint8 mark {0}; // Mark value
812 qint8 face_mark {0}; // Face mark value
813 qint32 size {0}; // Size value
814 qint32 tracking {0}; // Tracking value
815 qint32 kerning {0}; // Kerning value
816 qint32 leading {0}; // Leading value
817 qint32 base_shift {0}; // Base shift value
818 bool auto_kern {false}; // Auto kern on/off
819 bool rotate {false}; // Rotate up/down
820};
821
822struct KRITAPSD_EXPORT psd_layer_type_line {
823 qint32 char_count {0}; // Character count value
824 qint8 orientation {0}; // Orientation value
825 qint8 alignment {0}; // Alignment value
826 qint8 actual_char {0}; // Actual character as a double byte character
827 qint8 style {0}; // Style value
828};
829
830struct KRITAPSD_EXPORT psd_layer_type_tool {
831 double transform_info[6]; // 6 * 8 double precision numbers for the transform information
832 qint8 faces_count {0}; // Count of faces
833 psd_layer_type_face *face {nullptr};
834 qint8 styles_count {0}; // Count of styles
835 psd_layer_type_style *style {nullptr};
836 qint8 type {0}; // Type value
837 qint32 scaling_factor {0}; // Scaling factor value
838 qint32 character_count {0}; // Character count value
839 qint32 horz_place {0}; // Horizontal placement
840 qint32 vert_place {0}; // Vertical placement
841 qint32 select_start {0}; // Select start value
842 qint32 select_end {0}; // Select end value
843 qint8 lines_count {0}; // Line count
844 psd_layer_type_line *line {nullptr};
845 QColor color;
846 bool anti_alias {false}; // Anti alias on/off
847};
848
849struct KRITAPSD_EXPORT psd_layer_type_shape {
850 QTransform transform;
851
852 QVariantHash engineData;
853 QRectF bounds; // bounding box of the text in pixels, relative to first baseline, absent in point text.
854 QRectF boundingBox; //no clue, maybe relative to topleft of first glyph(?), same size as bounds, absent in point text.
856 QString text;
857 bool isHorizontal {true};
858
859 void setEngineData(QByteArray ba) {
860 KisCosParser parser;
861 engineData = parser.parseCosToJson(&ba);
862 }
863 void setIndex(int val) {
864 textIndex = val;
865 }
866 void setTop(float val) {
867 bounds.setTop(val);
868 }
869 void setLeft(float val) {
870 bounds.setLeft(val);
871 }
872 void setRight(float val) {
873 bounds.setRight(val);
874 }
875 void setBottom(float val) {
876 bounds.setBottom(val);
877 }
878 void setWritingMode(const QString val) {
879 if (val == "Hrzn") {
880 isHorizontal = true;
881 } else {
882 isHorizontal = false;
883 }
884 }
885
886 static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_layer_type_shape *data) {
887 catcher.subscribeInteger(path + "/TxLr/TextIndex", std::bind(&psd_layer_type_shape::setIndex, data, std::placeholders::_1));
888 catcher.subscribeRawData(path + "/TxLr/EngineData", std::bind(&psd_layer_type_shape::setEngineData, data, std::placeholders::_1));
889 catcher.subscribeEnum(path + "/TxLr/Ornt", "Ornt", std::bind(&psd_layer_type_shape::setWritingMode, data, std::placeholders::_1));
890
891 // Older psd use Pnt as the unit, even though its pixel.
892 catcher.subscribeUnitFloat(path + "/TxLr/bounds/Left", "#Pnt", std::bind(&psd_layer_type_shape::setLeft, data, std::placeholders::_1));
893 catcher.subscribeUnitFloat(path + "/TxLr/bounds/Top ", "#Pnt", std::bind(&psd_layer_type_shape::setTop, data, std::placeholders::_1));
894 catcher.subscribeUnitFloat(path + "/TxLr/bounds/Rght", "#Pnt", std::bind(&psd_layer_type_shape::setRight, data, std::placeholders::_1));
895 catcher.subscribeUnitFloat(path + "/TxLr/bounds/Btom", "#Pnt", std::bind(&psd_layer_type_shape::setBottom, data, std::placeholders::_1));
896
897 // Newer photoshop files use pxl as the unit, which is correct...
898 catcher.subscribeUnitFloat(path + "/TxLr/bounds/Left", "#Pxl", std::bind(&psd_layer_type_shape::setLeft, data, std::placeholders::_1));
899 catcher.subscribeUnitFloat(path + "/TxLr/bounds/Top ", "#Pxl", std::bind(&psd_layer_type_shape::setTop, data, std::placeholders::_1));
900 catcher.subscribeUnitFloat(path + "/TxLr/bounds/Rght", "#Pxl", std::bind(&psd_layer_type_shape::setRight, data, std::placeholders::_1));
901 catcher.subscribeUnitFloat(path + "/TxLr/bounds/Btom", "#Pxl", std::bind(&psd_layer_type_shape::setBottom, data, std::placeholders::_1));
902 }
903
904 QDomDocument textDataASLXML() {
906 w.enterDescriptor("", "", "TxLr");
907
908 w.writeText("Txt ", text);
909 w.writeEnum("textGridding", "textGridding", "none");
910 if (isHorizontal) {
911 w.writeEnum("Ornt", "Ornt", "Hrzn");
912 } else {
913 w.writeEnum("Ornt", "Ornt", "Vrtc");
914 }
915 w.writeEnum("AntA", "Annt", "AnCr");
916 if (!bounds.isEmpty()) {
917 w.enterDescriptor("bounds", "", "bounds");
918 w.writeUnitFloat("Left", "#Pnt", bounds.left());
919 w.writeUnitFloat("Top ", "#Pnt", bounds.top());
920 w.writeUnitFloat("Rght", "#Pnt", bounds.right());
921 w.writeUnitFloat("Btom", "#Pnt", bounds.bottom());
922 w.leaveDescriptor();
923 }
924 if (!boundingBox.isEmpty()) {
925 w.enterDescriptor("boundingBox", "", "boundingBox");
926 w.writeUnitFloat("Left", "#Pnt", boundingBox.left());
927 w.writeUnitFloat("Top ", "#Pnt", boundingBox.top());
928 w.writeUnitFloat("Rght", "#Pnt", boundingBox.right());
929 w.writeUnitFloat("Btom", "#Pnt", boundingBox.bottom());
930 w.leaveDescriptor();
931 }
932 w.writeInteger("TextIndex", textIndex);
933 QByteArray ba = KisCosWriter::writeCosFromVariantHash(engineData);
934 w.writeRawData("EngineData", &ba);
935
936 w.leaveDescriptor();
937
938 return w.document();
939 }
940
941 QDomDocument textWarpXML() {
943 w.enterDescriptor("", "", "warp");
944
945 w.writeEnum("warpStyle", "warpStyle", "warpNone");
946 w.writeDouble("warpValue", 0);
947 w.writeDouble("warpPerspective", 0);
948 w.writeDouble("warpPerspectiveOther", 0);
949 w.writeEnum("warpRotate", "Ornt", "Hrzn");
950
951 w.leaveDescriptor();
952
953 return w.document();
954 }
955
956};
957
958struct KRITAPSD_EXPORT psd_path_node {
959 QPointF control1;
960 QPointF node;
961 QPointF control2;
962 bool isSmooth {false};
963};
964
965struct KRITAPSD_EXPORT psd_path_sub_path {
967 bool isClosed {false};
968};
969struct KRITAPSD_EXPORT psd_path {
970 bool initialFillRecord {false};
974};
975
976struct KRITAPSD_EXPORT psd_vector_mask {
977 bool invert {false};
978 bool notLink {false};
979 bool disable {false};
981};
982
983struct KRITAPSD_EXPORT psd_vector_stroke_data {
984
985 int strokeVersion {2};
986
987 bool strokeEnabled {false};
988 bool fillEnabled {true};
989
990 QPen pen;
991 bool gradient{false};
992
993 bool scaleLock {false};
994 bool strokeAdjust {false};
995
997
998 double opacity {1.0};
999
1000 double resolution {72.0};
1001 bool pixelWidth {false};
1002
1003 void setVersion(int version) {
1004 strokeVersion = version;
1005 }
1006
1007 void setStrokeEnabled(bool enabled) {
1008 strokeEnabled = enabled;
1009 }
1010 void setFillEnabled(bool enabled) {
1011 fillEnabled = enabled;
1012 }
1013 void setStrokeWidth(double width) {
1014 pen.setWidthF(width);
1015 pixelWidth = false;
1016 }
1017 void setStrokePixel(double width) {
1018 pen.setWidthF(width);
1019 pixelWidth = true;
1020 }
1021
1022 void setStrokeDashOffset(double dashOffset) {
1023 pen.setDashOffset(dashOffset);
1024 }
1025 void setStrokeMiterLimit(double limit) {
1026 pen.setMiterLimit(limit);
1027 }
1028 void setLineCapType(const QString val) {
1029 if (val == "strokeStyleButtCap") {
1030 pen.setCapStyle(Qt::FlatCap);
1031 } else if (val == "strokeStyleSquareCap") {
1032 pen.setCapStyle(Qt::SquareCap);
1033 } else if (val == "strokeStyleRoundCap") {
1034 pen.setCapStyle(Qt::RoundCap);
1035 }
1036 }
1037 void setLineJoinType(const QString val) {
1038 if (val == "strokeStyleMiterJoin") {
1039 pen.setJoinStyle(Qt::MiterJoin);
1040 } else if (val == "strokeStyleBevelJoin") {
1041 pen.setJoinStyle(Qt::BevelJoin);
1042 } else if (val == "strokeStyleRoundJoin") {
1043 pen.setJoinStyle(Qt::RoundJoin);
1044 }
1045 }
1046
1047 void setScaleLock(bool enabled) {
1048 scaleLock = enabled;
1049 }
1050
1051 void setStrokeAdjust(bool enabled) {
1052 strokeAdjust = enabled;
1053 }
1054
1055 void appendToDashPattern(double val) {
1056 // QPen doesn't like 0 in the dash pattern,
1057 // even though it's necessary for some styles
1058 dashPattern.append(qMax(val, 1e-6));
1059 }
1061 opacity = o * 0.01;
1062 }
1063 void setResolution(double res) {
1064 resolution = res;
1065 }
1066
1068 pen = stroke->resultLinePen();
1069 gradient = stroke->lineBrush().gradient()? true: false;
1070 opacity = stroke->color().alphaF();
1071 dashPattern = stroke->lineDashes();
1072 }
1073
1074 static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_vector_stroke_data *data) {
1075 catcher.subscribeInteger(path + "/strokeStyle/strokeStyleVersion", std::bind(&psd_vector_stroke_data::setVersion, data, std::placeholders::_1));
1076 catcher.subscribeBoolean(path + "/strokeStyle/strokeEnabled", std::bind(&psd_vector_stroke_data::setStrokeEnabled, data, std::placeholders::_1));
1077 catcher.subscribeBoolean(path + "/strokeStyle/fillEnabled", std::bind(&psd_vector_stroke_data::setFillEnabled, data, std::placeholders::_1));
1078
1079 catcher.subscribeUnitFloat(path + "/strokeStyle/strokeStyleLineWidth", "#Pxl", std::bind(&psd_vector_stroke_data::setStrokeWidth, data, std::placeholders::_1));
1080 //catcher.subscribeUnitFloat(path + "/strokeStyle/strokeStyleLineWidth", "#Pnt", std::bind(&psd_vector_stroke_data::setStrokePixel, data, std::placeholders::_1));
1081 catcher.subscribeUnitFloat(path + "/strokeStyle/strokeStyleLineDashOffset", "#Pnt", std::bind(&psd_vector_stroke_data::setStrokeDashOffset, data, std::placeholders::_1));
1082 catcher.subscribeDouble(path + "/strokeStyle/strokeStyleMiterLimit", std::bind(&psd_vector_stroke_data::setStrokeMiterLimit, data, std::placeholders::_1));
1083
1084 catcher.subscribeEnum(path + "/strokeStyle/strokeStyleLineCapType", "strokeStyleLineCapType", std::bind(&psd_vector_stroke_data::setLineCapType, data, std::placeholders::_1));
1085 catcher.subscribeEnum(path + "/strokeStyle/strokeStyleLineJoinType", "strokeStyleLineJoinType", std::bind(&psd_vector_stroke_data::setLineJoinType, data, std::placeholders::_1));
1086
1087 catcher.subscribeBoolean(path + "/strokeStyle/strokeStyleScaleLock", std::bind(&psd_vector_stroke_data::setScaleLock, data, std::placeholders::_1));
1088 catcher.subscribeBoolean(path + "/strokeStyle/strokeStyleStrokeAdjust", std::bind(&psd_vector_stroke_data::setStrokeAdjust, data, std::placeholders::_1));
1089
1090 catcher.subscribeUnitFloat(path + "/strokeStyle/strokeStyleLineDashSet/", "#Nne", std::bind(&psd_vector_stroke_data::appendToDashPattern, data, std::placeholders::_1));
1091
1092 catcher.subscribeUnitFloat(path + "/strokeStyle/strokeStyleOpacity", "#Prc", std::bind(&psd_vector_stroke_data::setOpacityFromPercentage, data, std::placeholders::_1));
1093 catcher.subscribeDouble(path + "/strokeStyle/strokeStyleResolution", std::bind(&psd_vector_stroke_data::setResolution, data, std::placeholders::_1));
1094 }
1095
1096 QDomDocument getASLXML() {
1098 w.enterDescriptor("", "", "strokeStyle");
1099
1100 w.writeInteger("strokeStyleVersion", 2);
1101 w.writeBoolean("strokeEnabled", strokeEnabled);
1102 w.writeBoolean("fillEnabled", fillEnabled);
1103
1104 w.writeUnitFloat("strokeStyleLineWidth", "#Pxl", pen.widthF() * (resolution / 72.0));
1105 w.writeUnitFloat("strokeStyleLineDashOffset ", "#Pnt", pen.dashOffset());
1106 w.writeDouble("strokeStyleMiterLimit", pen.miterLimit());
1107
1108 QString linecap = "strokeStyleButtCap";
1109 if (pen.capStyle() == Qt::SquareCap) {
1110 linecap = "strokeStyleSquareCap";
1111 } else if (pen.capStyle() == Qt::RoundCap) {
1112 linecap = "strokeStyleRoundCap";
1113 }
1114 QString linejoin = "strokeStyleMiterJoin";
1115 if (pen.joinStyle() == Qt::BevelJoin) {
1116 linejoin = "strokeStyleBevelJoin";
1117 } else if (pen.joinStyle() == Qt::RoundJoin) {
1118 linejoin = "strokeStyleRoundJoin";
1119 }
1120 w.writeEnum("strokeStyleLineCapType", "strokeStyleLineCapType", linecap);
1121 w.writeEnum("strokeStyleLineJoinType", "strokeStyleLineJoinType", linejoin);
1122
1123 // Other values are "strokeStyleAlignInside" and "strokeStyleAlignOutside" but we don't support these.
1124 w.writeEnum("strokeStyleLineAlignment", "strokeStyleLineAlignment", "strokeStyleAlignCenter");
1125
1126 w.writeBoolean("strokeStyleScaleLock", false);
1127 w.writeBoolean("strokeStyleStrokeAdjust", false);
1128
1129 w.enterList("strokeStyleLineDashSet");
1130 Q_FOREACH(const double val, dashPattern) {
1131 if (val <= 1e-6) {
1132 w.writeUnitFloat("", "#Nne", 0);
1133 } else {
1134 w.writeUnitFloat("", "#Nne", val);
1135 }
1136 }
1137 w.leaveList();
1138
1139 w.writeEnum("strokeStyleBlendMode", "BlnM", "Nrml");
1140 w.writeUnitFloat("strokeStyleOpacity ", "#Prc", opacity * 100.0);
1141
1142 // Color Descriptor
1143 // Missing is "patternLayer"
1144 if (gradient) {
1145 w.enterDescriptor("strokeStyleContent", "", "gradientLayer");
1147 fill.setFromQGradient(pen.brush().gradient());
1148 fill.writeASL(w);
1149 w.leaveDescriptor();
1150 } else {
1151 w.enterDescriptor("strokeStyleContent", "", "solidColorLayer");
1153 c.fromQColor(pen.color());
1154 c.setOpacity(1.0);
1156 fill.fill_color = c;
1157 fill.writeASL(w);
1158 w.leaveDescriptor();
1159 }
1160
1161 w.writeDouble("strokeStyleResolution", resolution);
1162
1163 w.leaveDescriptor();
1164
1165 return w.document();
1166 }
1167
1169 double width = pen.widthF();
1170 width = (width / resolution) * 72.0;
1171 stroke->setLineWidth(width);
1172 stroke->setCapStyle(pen.capStyle());
1173 stroke->setJoinStyle(pen.joinStyle());
1174 stroke->setDashOffset(pen.dashOffset());
1175 stroke->setMiterLimit(pen.miterLimit());
1176 if (dashPattern.isEmpty()) {
1177 stroke->setLineStyle(Qt::SolidLine, QVector<double>());
1178 } else {
1179 if (dashPattern.size() % 2 > 0) {
1180 QVector<double> pattern = dashPattern;
1181 pattern.append(dashPattern);
1182 stroke->setLineStyle(Qt::CustomDashLine, pattern);
1183 } else {
1184 stroke->setLineStyle(Qt::CustomDashLine, dashPattern);
1185 }
1186 }
1187 }
1188};
1189
1191 int originType = -1;
1192
1193 const QMap<int, QString> typeToName {
1194 {1, "RectangleShape"},
1195 {5, "EllipseShape"},
1196 {7, "StarShape"},
1197 {8, "StarShape"},
1198 };
1199
1201 double originResolution = 72.0;
1202 QTransform transform;
1203 int originIndex = 0; // unknown, always 0.
1204
1207 double originPolyStarRatio = 100.0;
1208 bool isStar = false;
1209 double originPolyPixelHSF = 1.0;
1212
1213 void setOriginType(int type) {
1214 originType = type;
1215 }
1216
1217 void setOriginResolution(double res) {
1218 originResolution = res;
1219 }
1220
1221 void setTransform(QTransform t) {
1222 transform = t;
1223 }
1224
1225 void setOriginShapeBBox(QRectF ShapeBBox) {
1226 originShapeBBox = ShapeBBox;
1227 }
1228 void setOriginBoxCorners(QPointF p) {
1229 originBoxCorners.append(p);
1230 }
1236 }
1237
1238 void setOriginPolySides(int sides) {
1239 originPolySides = sides;
1240 }
1241
1242 void setOriginPolyStarRatio(double ratio) {
1243 originPolyStarRatio = ratio;
1244 isStar = true;
1245 }
1246
1247 static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_vector_origination_data *data) {
1248 QString descriptorPath = path+"/keyDescriptorList/null";
1249 catcher.subscribeInteger(descriptorPath + "/keyOriginType", std::bind(&psd_vector_origination_data::setOriginType, data, std::placeholders::_1));
1250 catcher.subscribeDouble(descriptorPath + "/keyOriginResolution", std::bind(&psd_vector_origination_data::setOriginResolution, data, std::placeholders::_1));
1251 catcher.subscribeTransform(descriptorPath + "/Trnf", std::bind(&psd_vector_origination_data::setTransform, data, std::placeholders::_1));
1252 catcher.subscribeUnitRect(descriptorPath + "/keyOriginShapeBBox", "#Pxl", std::bind(&psd_vector_origination_data::setOriginShapeBBox, data, std::placeholders::_1));
1253
1254 catcher.subscribePoint(descriptorPath + "/keyOriginBoxCorners/rectangleCornerA", std::bind(&psd_vector_origination_data::setOriginBoxCorners, data, std::placeholders::_1));
1255 catcher.subscribePoint(descriptorPath + "/keyOriginBoxCorners/rectangleCornerB", std::bind(&psd_vector_origination_data::setOriginBoxCorners, data, std::placeholders::_1));
1256 catcher.subscribePoint(descriptorPath + "/keyOriginBoxCorners/rectangleCornerC", std::bind(&psd_vector_origination_data::setOriginBoxCorners, data, std::placeholders::_1));
1257 catcher.subscribePoint(descriptorPath + "/keyOriginBoxCorners/rectangleCornerD", std::bind(&psd_vector_origination_data::setOriginBoxCorners, data, std::placeholders::_1));
1258
1259 catcher.subscribePoint(descriptorPath + "/keyOriginPolyPreviousTightBoxCorners/rectangleCornerA", std::bind(&psd_vector_origination_data::setOriginPolyTightBoxCorners, data, std::placeholders::_1));
1260 catcher.subscribePoint(descriptorPath + "/keyOriginPolyPreviousTightBoxCorners/rectangleCornerB", std::bind(&psd_vector_origination_data::setOriginPolyTightBoxCorners, data, std::placeholders::_1));
1261 catcher.subscribePoint(descriptorPath + "/keyOriginPolyPreviousTightBoxCorners/rectangleCornerC", std::bind(&psd_vector_origination_data::setOriginPolyTightBoxCorners, data, std::placeholders::_1));
1262 catcher.subscribePoint(descriptorPath + "/keyOriginPolyPreviousTightBoxCorners/rectangleCornerD", std::bind(&psd_vector_origination_data::setOriginPolyTightBoxCorners, data, std::placeholders::_1));
1263
1264 catcher.subscribePoint(descriptorPath + "/keyOriginPolyTrueRectCorners/rectangleCornerA", std::bind(&psd_vector_origination_data::setOriginPolyTrueRectCorners, data, std::placeholders::_1));
1265 catcher.subscribePoint(descriptorPath + "/keyOriginPolyTrueRectCorners/rectangleCornerB", std::bind(&psd_vector_origination_data::setOriginPolyTrueRectCorners, data, std::placeholders::_1));
1266 catcher.subscribePoint(descriptorPath + "/keyOriginPolyTrueRectCorners/rectangleCornerC", std::bind(&psd_vector_origination_data::setOriginPolyTrueRectCorners, data, std::placeholders::_1));
1267 catcher.subscribePoint(descriptorPath + "/keyOriginPolyTrueRectCorners/rectangleCornerD", std::bind(&psd_vector_origination_data::setOriginPolyTrueRectCorners, data, std::placeholders::_1));
1268
1269 catcher.subscribeInteger(descriptorPath + "/keyOriginPolySides", std::bind(&psd_vector_origination_data::setOriginPolySides, data, std::placeholders::_1));
1270 catcher.subscribeUnitFloat(descriptorPath + "/keyOriginPolyStarRatio", "#Prc", std::bind(&psd_vector_origination_data::setOriginPolyStarRatio, data, std::placeholders::_1));
1271
1272 }
1273
1274 QDomDocument getASL() {
1276 w.enterDescriptor("", "", "null");
1277
1278 w.enterList("keyDescriptorList");
1279
1280 w.enterDescriptor("", "", "null");
1281
1282 w.writeInteger("keyOriginType", originType);
1283 w.writeDouble("keyOriginResolution", originResolution);
1284
1285 if (originType == 1 || originType == 5) {
1286 w.writeUnitRect("keyOriginShapeBBox", "#Pxl", originShapeBBox);
1287 w.writePointRect("keyOriginBoxCorners", originBoxCorners);
1288 } else if (originType == 7 || originType == 8) {
1289 w.writeInteger("keyOriginPolySides", originPolySides);
1290 if (originPolyStarRatio != 100) {
1291 w.writeUnitFloat("keyOriginPolyStarRatio", "#Prc", originPolyStarRatio);
1292 }
1293 w.writePointRect("keyOriginBoxCorners", originBoxCorners);
1294 w.writeFloatRect("keyOriginShapeBBox", originShapeBBox);
1295 w.writePointRect("keyOriginPolyPreviousTightBoxCorners", originBoxCorners);
1296 w.writePointRect("keyOriginPolyTrueRectCorners", originBoxCorners);
1297 w.writeDouble("keyOriginPolyPixelHSF", 1);
1298 }
1299
1300 w.writeTransform("Trnf", transform);
1301
1302 w.writeInteger("keyOriginIndex", originIndex);
1303
1304 w.leaveDescriptor();
1305
1306 w.leaveList();
1307
1308 w.leaveDescriptor();
1309
1310 return w.document();
1311 }
1312
1313
1314
1315 QString shapeName () {
1316 return typeToName.value(originType, QString());
1317 }
1318
1320 bool p = !shapeName().isEmpty();
1321 return p;
1322 }
1323
1331 void OriginalSizeAndAngle(QSizeF &size, double &angle) {
1332 size = originShapeBBox.size();
1333 angle = QLineF(originShapeBBox.topLeft(), originShapeBBox.topRight()).angle();
1334 if (originBoxCorners.size() == 4) {
1335 QPolygonF poly = transform.inverted().map(originBoxCorners);
1336 angle = QLineF(poly.first(), poly.value(1)).angle();
1337 double w = QLineF(poly.first(), poly.value(1)).length();
1338 double h = QLineF(poly.first(), poly.value(3)).length();
1339 size = QSizeF(w, h);
1340 }
1341 }
1342};
1343
1349class KRITAPSD_EXPORT PsdAdditionalLayerInfoBlock
1350{
1351public:
1353
1354 using ExtraLayerInfoBlockHandler = std::function<bool(QIODevice &)>;
1355 using UserMaskInfoBlockHandler = std::function<bool(QIODevice &)>;
1356
1357 void setExtraLayerInfoBlockHandler(ExtraLayerInfoBlockHandler handler);
1358 void setUserMaskInfoBlockHandler(UserMaskInfoBlockHandler handler);
1359
1360 bool read(QIODevice &io);
1361 bool write(QIODevice &io, KisNodeSP node);
1362
1363 void writeLuniBlockEx(QIODevice &io, const QString &layerName);
1364 void writeLsctBlockEx(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey);
1365 void writeLfx2BlockEx(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat);
1366 void writePattBlockEx(QIODevice &io, const QDomDocument &patternsXmlDoc);
1367 void writeLclrBlockEx(QIODevice &io, const quint16 &labelColor);
1368
1369 void writeFillLayerBlockEx(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type);
1370 void writeVmskBlockEx(QIODevice &io, psd_vector_mask mask);
1371 void writeTypeToolBlockEx(QIODevice &io, psd_layer_type_shape typeTool);
1372 void writeVectorStrokeDataEx(QIODevice &io, const QDomDocument &vectorStroke);
1373 void writeVectorOriginationDataEx(QIODevice &io, const QDomDocument &vectorOrigination);
1374 void writeTxt2BlockEx(QIODevice &io, const QVariantHash txt2Hash);
1375
1376 bool valid();
1377
1379 QString error;
1380 QStringList keys; // List of all the keys that we've seen
1381
1383 QDomDocument layerStyleXml;
1384
1386 QVariantHash txt2Data;
1387
1388 quint16 labelColor{0}; // layer color.
1389
1390 QDomDocument fillConfig;
1392
1393 QTransform textTransform;
1394 QDomDocument textData;
1395
1397 QDomDocument vectorStroke;
1399
1402
1403private:
1404 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
1405 void readImpl(QIODevice &io);
1406
1407 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
1408 void writeLuniBlockExImpl(QIODevice &io, const QString &layerName);
1409
1410 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
1411 void writeLsctBlockExImpl(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey);
1412
1413 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
1414 void writeLfx2BlockExImpl(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat);
1415
1416 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
1417 void writePattBlockExImpl(QIODevice &io, const QDomDocument &patternsXmlDoc);
1418
1419 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
1420 void writeLclrBlockExImpl(QIODevice &io, const quint16 &lclr);
1421
1422 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
1423 void writeFillLayerBlockExImpl(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type);
1424
1425 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
1426 void writeVectorMaskImpl(QIODevice &io, psd_vector_mask mask);
1427
1428 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
1429 void writeTypeToolImpl(QIODevice &io, psd_layer_type_shape tool);
1430
1431 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
1432 void writeVectorStrokeDataImpl(QIODevice &io, const QDomDocument &vectorStroke);
1433
1434 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
1435 void writeVectorOriginationDataImpl(QIODevice &io, const QDomDocument &vectorOrigination);
1436
1437 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
1438 void writeTxt2BlockExImpl(QIODevice &io, const QVariantHash txt2Hash);
1439
1440private:
1443};
1444
1445#endif // PSD_ADDITIONAL_LAYER_INFO_BLOCK_H
const Params2D p
qreal v
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID LABAColorModelID("LABA", ki18n("L*a*b*/Alpha"))
void subscribeDouble(const QString &path, ASLCallbackDouble callback)
void subscribeRawData(const QString &path, ASLCallbackRawData callback)
void subscribeEnum(const QString &path, const QString &typeId, ASLCallbackString callback)
void subscribeInteger(const QString &path, ASLCallbackInteger callback)
void subscribeUnitRect(const QString &path, const QString &unit, ASLCallbackRect callback)
void subscribeUnitFloat(const QString &path, const QString &unit, ASLCallbackDouble callback)
void subscribeGradient(const QString &path, ASLCallbackGradient callback)
void subscribeColor(const QString &path, ASLCallbackColor callback)
void subscribePoint(const QString &path, ASLCallbackPoint callback)
void subscribeBoolean(const QString &path, ASLCallbackBoolean callback)
void subscribeTransform(const QString &path, ASLCallbackTransform callback)
void subscribePatternRef(const QString &path, ASLCallbackPatternRef callback)
The KisCosParser class.
QVariantHash parseCosToJson(QByteArray *ba)
static QByteArray writeCosFromVariantHash(const QVariantHash doc)
static KisGeneratorRegistry * instance()
static KisResourcesInterfaceSP instance()
A simple solid color shape background.
virtual KoID colorModelId() const =0
void convertTo(const KoColorSpace *cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
Definition KoColor.cpp:136
const KoColorProfile * profile() const
return the current profile
Definition KoColor.cpp:299
void setOpacity(quint8 alpha)
Definition KoColor.cpp:333
KoColor convertedTo(const KoColorSpace *cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
Definition KoColor.cpp:163
void fromQColor(const QColor &c)
Convenient function for converting from a QColor.
Definition KoColor.cpp:213
const KoColorSpace * colorSpace() const
return the current colorSpace
Definition KoColor.h:82
void toQColor(QColor *c) const
a convenience method for the above.
Definition KoColor.cpp:198
void setProfile(const KoColorProfile *profile)
assign new profile without converting pixel data
Definition KoColor.cpp:177
const T value(const QString &id) const
A gradient shape background.
QString id() const
Definition KoID.cpp:63
A pattern shape background.
Write API docs here.
Definition KoPattern.h:21
QImage pattern() const
pattern the actual pattern image
KoResourceSP resource() const noexcept
void toXML(QDomDocument &doc, QDomElement &gradientElt) const
toXML convert the gradient to xml.
static KoSegmentGradient fromXML(const QDomElement &elt)
fromXML get a segment gradient from xml.
QGradient * toQGradient() const override
reimplemented
static QSharedPointer< KoStopGradient > fromQGradient(const QGradient *gradient)
Creates KoStopGradient from a QGradient.
QGradient * toQGradient() const override
reimplemented
void toXML(QDomDocument &doc, QDomElement &gradientElt) const
toXML Convert the gradient to an XML string.
static KoStopGradient fromXML(const QDomElement &elt)
fromXML convert a gradient from xml.
The PsdAdditionalLayerInfoBlock class implements the Additional Layer Information block.
ExtraLayerInfoBlockHandler m_layerInfoBlockHandler
std::function< bool(QIODevice &)> ExtraLayerInfoBlockHandler
std::function< bool(QIODevice &)> UserMaskInfoBlockHandler
#define bounds(x, a, b)
T kisRadiansToDegrees(T radians)
Definition kis_global.h:181
T kisDegreesToRadians(T degrees)
Definition kis_global.h:176
const QString Patterns
psd_section_type
Definition psd.h:144
psd_fill_type
Definition psd.h:123
@ psd_fill_solid_color
Definition psd.h:124
virtual KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
virtual bool isSuitableForOutput() const =0
static KoColorSpaceRegistry * instance()
bool loadFromConfig(KisFilterConfigurationSP cfg)
void setFromQGradient(const QGradient *gradient)
void setGradient(const KoAbstractGradientSP &newGradient)
static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_layer_gradient_fill *data)
QSharedPointer< KoShapeBackground > getBackground()
QSharedPointer< KoShapeBackground > getBackground(KisEmbeddedResourceStorageProxy &embeddedProxy)
static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_layer_pattern_fill *data)
void setPatternRef(const QString Idnt, const QString name)
bool loadFromConfig(KisFilterConfigurationSP cfg)
QBrush getBrush(KisEmbeddedResourceStorageProxy &embeddedProxy)
void loadPattern(KisEmbeddedResourceStorageProxy &embeddedProxy)
QSharedPointer< KoShapeBackground > getBackground()
static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_layer_solid_color *data)
void setColor(const KoColor &color)
bool loadFromConfig(KisFilterConfigurationSP cfg)
static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_layer_type_shape *data)
void setWritingMode(const QString val)
QList< psd_path_sub_path > subPaths
QPolygonF originPolyTrueRectCorners
Only 7 an 8, same as originBoxCorners.
double originPolyPixelHSF
Only 7 and 8, no clue.
static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_vector_origination_data *data)
int originType
1 = rect, 2 = rounded rect?, 3 = ? 4 = ? 5 = ellipse, 6 = ?, 7 = polygon, 8 = star,...
double originResolution
Resolution of the coordinates.
void OriginalSizeAndAngle(QSizeF &size, double &angle)
QPolygonF originPolyPreviousTightBoxCorners
Only 7 an 8, same as originBoxCorners.
QPolygonF originBoxCorners
post-transform bbox as a polygon, seen on 1, 7, 8 and 9.
double originPolyStarRatio
Only for 8, percentage of small radius to big radius.
void loadFromShapeStroke(KoShapeStrokeSP stroke)
static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_vector_stroke_data *data)
void setStrokeDashOffset(double dashOffset)
void setupShapeStroke(KoShapeStrokeSP stroke)