1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
|
<sect1 id="developers-plugins">
<title
>Desenvolupant endollables per al &chalk;</title>
<sect2 id="developers-plugins-introduction">
<title
>Introducció</title>
<para
>El &chalk; és infinitament extensible amb endollables. Les eines, els filtres, grans troços de la interfície d'usuari i fins i tot espais de color són endollables. De fet, el &chalk; reconeix aquestes sis tipus d'endollables: </para>
<itemizedlist>
<listitem
><para
>espais de color — defineixen els canals que constitueixen un únic píxel</para
></listitem>
<listitem
><para
>eines — qualsevol cosa que es faci amb un ratolí o dispositiu tauler d'introducció</para
></listitem>
<listitem
><para
>operations de dibuix — efectes endollables de dibuix per a eines</para
></listitem>
<listitem
><para
>filtres d'imatge — canvia tots els píxels, o només els seleccionats en una capa</para
></listitem>
<listitem
><para
>viewplugins — estén la interfície d'usuari del Chalk amb noves caixes de diàleg, paletes i operacions</para
></listitem>
<listitem
><para
>filtres d'importació/exportació — llegeix i escriu tota mena de formats d'imatge</para
></listitem>
</itemizedlist>
<para
>El &chalk; mateix està composat per tres biblioteques en capes i un directori amb algunes classes comunes de suport: chalkcolor, chalkimage i chalkui. Dins del &chalk;, els objectes es poden identificar per un <classname
>KisID</classname
>, que és la combinació d'una única cadena no traduïda (usada quan es desa, per exemple) i una cadena traduïda per a la IGU. </para
><para
>Unes paraules sobre compatibilitat: el &chalk; està encara en desenvolupament. Des del &chalk; 1.5 a l'1.6 no s'esperen massa canvis a l'API, però potser n'hi ha alguna. Des del &chalk; 1.6 al 2.0, hi haurà el canvi de les &Qt;3 a les &Qt;4, del &kde;3 al &kde;4, del <command
>automake</command
> al <command
>cmake</command
>: s'esperen molts canvis. Si desenvolupeu un endollable per al &chalk; i escolliu fer-ho en la versió del repositori subversion del &chalk;, hi ha grans possibilitats que us ajudem a portar-lo. Aquests canvis també portaran que parts d'aquest document esdevinguin obsoletes. Comproveu sempre l'última documentació de l'API o els fitxers de capçalera instal·lats al vostre sistema. </para>
<sect3 id="developers-plugins-introduction-chalkcolor">
<title
>ChalkColor</title>
<para
>La primera biblioteca és chalkcolor. Aquest biblioteca carrega els endollables d'espai de color. </para
><para
>Un endollable d'espai de color hauria d'implementar la classe abstracta <classname
>KisColorSpace</classname
> o, si les capacitats bàsiques del nou espai de color s'implementaran per <command
>lcms</command
> (<ulink url="http://www.littlecms.com/"
></ulink
>), exteneu <classname
>KisAbstractColorSpace</classname
>. La biblioteca chalkcolor s'ha de poder usar des d'altres aplicacions i no depén del &koffice;. </para>
</sect3>
<sect3 id="developers-plugins-introduction-chalkimage">
<title
>ChalkImage</title>
<para
>La biblioteca libchalkimage carrega els endollables de filtre i paintop i és el responsable de treballar amb dades d'imatge: canviant píxels, composant i pintang. Els pinzells, les paletes, els gradients i els patrons també els carrega la libchalkimage. El nostre objectiu definit és fer la libchalkimage independent del &koffice;, però actualment compartim el gradient carregant codi amb el &koffice;. </para
><para
>No és fàcil de moment afegir nous tipus de recursos com els pinzells, les paletes, els gradients o patrons al &chalk;. (Afegir nous pinzells, paletes, gradients i patrons és fàcil, és clar.) El &chalk; segueix les línies mestres del projecte Create (<ulink url="http://create.freedesktop.org/"
></ulink
>) per a això. Afegir suport per al format de pinzell del Photoshop's necessita haquejar la libchalkimage; afegir més fitxers de dades de pinzell del Gimp, no. </para
><para
><classname
>ChalkImage</classname
> carrega els següents tipus d'endollables: </para>
<itemizedlist>
<listitem
><para
>Els filtres del &chalk; s'han d'extendre i implementar les classes abstractes <classname
>KisFilter</classname
>, <classname
>KisFilterConfiguration</classname
> i possiblement <classname
>KisFilterConfigurationWidget</classname
>. Un exemple de filtre és Deforma màscara.</para
></listitem>
<listitem
><para
>Les operacions de dibuix o paintops són el conjunt d'operacions als què tenen accès les eines de dibuix com el de mà alçada o el cercle. Són exemples de paintops el lapis, l'aerosol o l'esborrador. Els paintops ha d'extendre la classe base <classname
>KisPaintop</classname
>. Exemples de nous paintops poden ser un pinzell pastel, un pinzell de pintura a l'oli o un pinzell programable complex.</para
></listitem>
</itemizedlist>
</sect3>
<sect3 id="developers-plugins-introduction-chalkui">
<title
>ChalkUI</title>
<para
>La biblioteca libchalkui carrega les eines i els endollables visualització. Aquesta biblioteca és part del &koffice;, però també conté un número d'estris útils per a aplicacions gràfiques. Potser haurem de dividir aquesta biblioteca en chalkpart i chalkui a la versió 2.0. De moment, als escriptors d'scripts no se'ls dóna accés a aquesta biblioteca i els escriptors d'endollables només poden usar aquesta biblioteca quan escriuen eines o endollables de visualització. La <classname
>ChalkUI</classname
> carrega els següents tipus d'endollables: </para>
<itemizedlist>
<listitem
><para
>Les eines es deriven de <classname
>KisTool</classname
> o una de les classes bàsiques d'eines especialitzades com a <classname
>KisToolPaint</classname
>, <classname
>KisToolNonPaint</classname
> o <classname
>KisToolFreehand</classname
>. Una nova eina pot ser una eina de selecció d'objecte de primer pla. Les eines de dibuix (i això inclou eines que dibuixen a la selecció) pot usar qualsevol paintop per a determinar la manera en què es canvien els píxels.</para
></listitem>
<listitem
><para
>Els endollables de visualització són KParts ordinàries que usen <command
>kxmlgui</command
> per a insinuar-se a la interfície d'usuari del &chalk;. Les opcions de menú, els diàlegs, les barres d'eines — i qualsevol mena d'extensió d'interfície d'usuari pot ser un endollable de visualització. De fet, una important funcionalitat com el suport d'script del &chalk; està escrita com a un endollable de visualització.</para
></listitem>
</itemizedlist>
</sect3>
<sect3 id="developers-plugins-introduction-importexport">
<title
>Filtres d'Importació/Exportació</title>
<para
>Els filtres d'Importació/Exportació són filtres del &koffice;, subclasses del <classname
>KoFilter</classname
>. Els filtres llegeixen i escriuen dades d'imatge en qualsevol de la miríada de formats d'imatges existents. Un exemple del nou filtre d'importació/exportació del &chalk; pot ser un filtre PDF. Els filtres els carregen les biblioteques del &koffice;. </para>
</sect3>
</sect2>
<sect2 id="developers-plugins-creating">
<title
>Crea endollables</title>
<para
>Els endollables s'escriuen en C++ i poden usar tots els API de desenvolupador del &kde;, les &Qt; i el &chalk;. Només els endollables de visualització han d'usar l'API del &koffice;. No patiu: l'API del &chalk; és prou clara i prou extensament documentada (per a programari lliure) i escriure el vostre primer filtre és fàcil de debò. </para
><para
>Si no voleu usar el C++, podeu escriure scripts en Python o Ruby; són coses diferents, però, i no podeu actualment escriure eines, espais de color, paintops o filtres d'importació/exportació com a scripts. </para
><para
>Els endollables del &chalk; usen parts del mecanisme del &kde;'s per a carregar, de manera que la documentació de les parts, a <ulink url="http://developer.kde.org"
></ulink
>, també és relevant en aquest cas. </para
><para
>La vostra distribució ha de tenir els fitxers de capçalera relevants instal·lats amb el &chalk; mateix, o potser ha separat els fitxers de capçalera a un paquet &koffice; dev o &chalk; dev. Podeu trobar la documentació de l'API per a l'API pública del &chalk; a <ulink url="http://koffice.org/developer/apidocs/chalk/html/"
></ulink
>. </para>
<sect3 id="developers-plugins-creating-automake">
<title
>Automake (i CMake)</title>
<para
>El &kde; 3.x i per tant el &koffice; 1.5 i 1.6 usen <command
>automake</command
>; el &kde; 4.0 i el &koffice; 2.0 usen <command
>cmake</command
>. Aquest tutorial descriu la manera en què l'<command
>automake</command
> crea els endollables. </para
><para
>Els endollables són mòduls del &kde; i s'han d'etiquetar com a tals en el seu <filename
>Makefile.am</filename
>. Els filters, les eines, els paintops, els espais de color i els filtres d'importació/exportació necessiten fitxers <literal role="extension"
>.desktop</literal
>; els endollables de visualització necessiten a més un fitxer <application
>KXMLGui</application
> <filename
>pluginname.rc</filename
>. La manera més fàcil de començar és donar un cop d'ull al projecte chalk-plugins des del repositori de Subversion del &koffice; i usar-lo com a base per al vostre propi projecte. Volem preparar un paquet d'endollables bàsic del &chalk; per al KDevelop, però no hem tingut temps encara per a fer-ho. </para>
<sect4 id="d-p-c-a-makefile">
<title
><filename
>Makefile.am</filename
></title>
<para
>Anem a mirar l'esquelet d'un mòdul d'endollable. Primer, el <filename
>Makefile.am</filename
>. Això és el que el &kde; usa per a generar el makefile que construeix el vostre endollable: <programlisting>
kde_services_DATA = chalkLIBRARYNAME.desktop
INCLUDES = $(all_includes)
chalkLIBRARYNAME_la_SOURCES = sourcefile1.cc sourcefile2.cc
kde_module_LTLIBRARIES = chalkLIBRARYNAME.la
noinst_HEADERS = header1.h header2.h
chalkLIBRARYNAME_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
chalkLIBRARY_la_LIBADD = -lchalkcommon
chalkextensioncolorsfilters_la_METASOURCES = AUTO
</programlisting
> Aquest és el makefile per a un endollable de filtre. Reemplaceu <replaceable
>LIBRARYNAME</replaceable
> pel nom de la vostra feina, i ja estareu. </para
><para
>Si el vostre endollable és de visualització, haureu d'instal·lar també un fitxer <literal role="extension"
>.rc</literal
> amb entrades per a barres de menú i barres d'eines. De la mateixa manera, us caldrà instal·lar cursors i icones. Això es fa mitjançant els encanteris del <filename
>Makefile.am</filename
> ordinaris del &kde;: <programlisting
>chalkrcdir = $(kde_datadir)/chalk/chalkplugins
chalkrc_DATA = LIBRARYNAME.rc
EXTRA_DIST = $(chalkrc_DATA)
chalkpics_DATA = \
bla.png \
bla_cursor.png
chalkpicsdir = $(kde_datadir)/chalk/pics
</programlisting>
</para>
</sect4>
<sect4 id="d-p-c-a-desktop">
<title
>Fitxers Desktop</title>
<para
>El fitxer <literal role="extension"
>.desktop</literal
> anuncia el tipus d'endollable: <programlisting
>[Desktop Entry]
Encoding=UTF-8
Icon=
Name=User-visible Name
ServiceTypes=Chalk/Filter
Type=Service
X-TDE-Library=chalkLIBRARYNAME
X-TDE-Version=2
</programlisting>
</para
><para
>Possibles tipus de servei són: </para>
<itemizedlist>
<listitem
><para
>Chalk/Filter</para
></listitem>
<listitem
><para
>Chalk/Paintop</para
></listitem>
<listitem
><para
>Chalk/ViewPlugin</para
></listitem>
<listitem
><para
>Chalk/Tool</para
></listitem>
<listitem
><para
>Chalk/ColorSpace</para
></listitem>
</itemizedlist>
<para
>Els filtres d'importació i exportació de fitxer usen el marx de filtres genèric del &koffice; i cal que en parlem per separat. </para>
</sect4>
<sect4 id="d-p-c-a-boilerplate">
<title
>Boilerplate</title>
<para
>També us caldrà una mica de codi de boilerplate cridat per la part del marc del &kde; per a provocar l'endollable —, un fitxer de capçalera i un fitxer d'implementació. </para
><para
>Un fitxer de capçalera: <programlisting
>#ifndef TOOL_STAR_H_
#define TOOL_STAR_H_
#include <tdeparts/plugin.h>
/**
* A module that provides a star tool.
*/
class ToolStar : public KParts::Plugin
{
Q_OBJECT
public:
ToolStar(QObject *parent, const char *name, const QStringList &);
virtual ~ToolStar();
};
#endif // TOOL_STAR_H_
</programlisting>
</para>
<para
>I un fitxer d'implementació: <programlisting
>#include <kinstance.h>
#include <kgenericfactory.h>
#include <kis_tool_registry.h>
#include "tool_star.h"
#include "kis_tool_star.h"
typedef KGenericFactory<ToolStar> ToolStarFactory;
K_EXPORT_COMPONENT_FACTORY( chalktoolstar, ToolStarFactory( "chalk" ) )
ToolStar::ToolStar(QObject *parent, const char *name, const QStringList &)
: KParts::Plugin(parent, name)
{
setInstance(ToolStarFactory::instance());
if ( parent->inherits("KisToolRegistry") )
{
KisToolRegistry * r = dynamic_cast<KisToolRegistry*>( parent );
r -> add(new KisToolStarFactory());
}
}
ToolStar::~ToolStar()
{
}
#include "tool_star.moc"
</programlisting>
</para>
</sect4>
<sect4 id="d-p-c-a-registries">
<title
>Registres</title>
<para
>Les eines les carrega l'eina de registre i els registres mateixos amb l'eina de registre. Els endollables com les eines, els filtres i els paintops es carreguen només un cop: els endollables de visualització es carreguen per a cada visualització que es crea. Noteu que registrem factories, parlant genèricament. Per exemple, amb les eines, es crea una nova instància de cada eina per a cada punter (ratolí, estil, esborrador) per a pocs. I es crea un nou paintop cada cop que una eina rep un esdeveniment del ratolí. </para>
<para
>Els filtres criden el registre de filtres: <programlisting
>if (parent->inherits("KisFilterRegistry")) {
KisFilterRegistry * manager = dynamic_cast<KisFilterRegistry *>(parent);
manager->add(new KisFilterInvert());
}
</programlisting>
</para
><para
>Els paintops, el registre de paintops: <programlisting
>if ( parent->inherits("KisPaintOpRegistry") ) {
KisPaintOpRegistry * r = dynamic_cast<KisPaintOpRegistry*>(parent);
r -> add ( new KisSmearyOpFactory );
}
</programlisting>
</para
><para
>Coloreja el registre de l'espai de color (amb algunes complicacions): <programlisting
>if ( parent->inherits("KisColorSpaceFactoryRegistry") ) {
KisColorSpaceFactoryRegistry * f = dynamic_cast<isColorSpaceFactoryRegistry*>(parent);
KisProfile *defProfile = new KisProfile(cmsCreate_sRGBProfile());
f->addProfile(defProfile);
KisColorSpaceFactory * csFactory = new KisRgbColorSpaceFactory();
f->add(csFactory);
KisColorSpace * colorSpaceRGBA = new KisRgbColorSpace(f, 0);
KisHistogramProducerFactoryRegistry::instance() -> add(
new KisBasicHistogramProducerFactory<KisBasicU8HistogramProducer>
(KisID("RGB8HISTO", i18n("RGB8 Histogram")), colorSpaceRGBA) );
}
</programlisting>
</para
><para
>Els endollables de visualització no s'han de registrar, i obtenen accès a un objecte <classname
>KisView</classname
>: <programlisting
>if ( parent->inherits("KisView") )
{
setInstance(ShearImageFactory::instance());
setXMLFile(locate("data","chalkplugins/shearimage.rc"), true);
(void) new TDEAction(i18n("&Shear Image..."), 0, 0, this, SLOT(slotShearImage()), actionCollection(), "shearimage");
(void) new TDEAction(i18n("&Shear Layer..."), 0, 0, this, SLOT(slotShearLayer()), actionCollection(), "shearlayer");
m_view = (KisView*) parent;
}
</programlisting>
</para
><para
>Recordeu que això vol dir que es crearà un endollable de visualització per a cada vista que crei l'usuari; dividir una vista vol dir carregar de nou tots els endollables. </para>
</sect4>
<sect4 id="d-p-c-a-versioning">
<title
>Versions d'endollables</title>
<para
>El &chalk; 1.5 carrega els endollables amb <literal
>X-TDE-Version=2</literal
> al fitxer <literal role="extension"
>.desktop</literal
>. Els endollables del &chalk; 1.6 seran probablement binàriament incompatibles amb els de l'1.5 i necessitaran la versió número 3. Els endollables del &chalk; 2.0 plugins necessitaran la versió número 3. Sí, això no és completament lògic. </para>
</sect4>
</sect3>
</sect2>
<sect2 id="developers-plugins-colorspaces">
<title
>Espais de color</title>
<para
>Els espais de color implementa la classe purament virtual <classname
>KisColorSpace</classname
>. Hi ha dos tipus d'espais de color: aquells que usen <command
>lcms</command
> per a transformacions entre espais de color, i aquells que són massa estranys per a que els gestioni <command
>lcms</command
>. Exemples dels primers són are cmyk, rgb, yuv. Un exemple de l'últim és l'aquarel·la o wet & sticky. Els espais de color que usen <command
>lcms</command
> poden ser derivat de <classname
>KisAbstractColorSpace</classname
>, o d'una de les classes base especialitzades per a un cert número de bits per canal. </para
><para
>Implementar un espai de color és prou fàcil. El principi general és que els espais de color treballen en un rang simple de bytes. La interpretació d'aquests bytes depén de l'espai de color. Per exemple, un píxel en 16-bit GrayA està composat de quatre bytes: dos per al valor de gris i dos per al valor alfa. Sou lliures d'usar una estructura per a treballar amb format de memòria d'un píxel a la vostra implementació d'espai de color, però aquesta representació no s'exporta. L'única manera en què la resta del &chalk; pot saber quins canals i tipus de canals composen el vostre espai de color és mitjançant la classe <classname
>KisView</classname
>. </para
><para
>Els filtres i paintops fan ús del ric conjunt de mètodes oferts pel <classname
>KisColorSpace</classname
> per a fer la seva feina. En molts casos, la implementació per omissió en <classname
>KisAbstractColorSpace</classname
> funcionarà, però més lenta del què ho faria una implementació personalitzada en els vostre propi espai de color perquè <classname
>KisAbstractColorSpace</classname
> convertirà tots els píxels a 16-bit L*a*b i a l'inrevès. </para>
<sect3 id="developers-plugins-colorspaces-kischannelinfo">
<title
><classname
>KisChannelInfo</classname
></title>
<programlisting
>(http://websvn.kde.org/trunk/koffice/chalk/chalkcolor/kis_channelinfo.h)
</programlisting>
<para
>Aquesta classe defineix els canals que fan un píxel en un espai de color particular. Un canal té les següents característiques importants: </para>
<itemizedlist>
<listitem
><para
>un nom per a mostrar-lo a la interfície d'usuari</para
></listitem>
<listitem
><para
>una posició: el byte on comencen els bytes que representen aquest canal al píxel.</para
></listitem>
<listitem
><para
>un tipus: color, alfa, substància o substrat. El color ñes un color pla, alfa és la transparència, substància és una representació de la quantitat de pigment o coses com aquestes, substracte és la representació del llenç. (Noteu que això es pot rearranjar en un moment.)</para
></listitem>
<listitem
><para
>un valor de tipus: byte, short, integer, float o un altre.</para
></listitem>
<listitem
><para
>mida: el múmero de bytes que agafa aquest canal</para
></listitem>
<listitem
><para
>color: una representació <classname
>QColor</classname
> d'aquest canal per a la visualització de la interfície de l'usuari, per exemple en histogrames.</para
></listitem>
<listitem
><para
>una abreviació per a usar-la a la IGU quan no hi ha molt d'espai</para
></listitem>
</itemizedlist>
</sect3>
<sect3 id="developers-plugins-colorspaces-kiscompositeop">
<title
><classname
>KisCompositeOp</classname
></title>
<para
>Com per al Porter-Duff original, hi ha moltes maneres de combinar píxels per a obtenir un nou color. La classe <classname
>KisCompositeOp</classname
> defineix la majoria d'ells: aquest arranjament no és fàcilment extensible excepte hackejant la biblioteca chalkcolor. </para
><para
>Un endollable d'espai de color pot suportar qualsevol subconjunt d'aquestes possibles operacions de composició, però el conjunt sempre ha d'incloure "OVER" (el mateix que "NORMAL") i "COPY". La resta són més o menys opcionals, malgrat que com més és millor, és clar. </para>
</sect3>
<sect3 id="developers-plugins-colorspaces-kiscolorspace">
<title
><classname
>KisColorSpace</classname
></title>
<para
>Els mètodes a la classe pura virtual <classname
>KisColorSpace</classname
> es pot dividir en uns quants grups: conversió, identificació i manipulació. </para
><para
>Totes les classes han de ser capaces de convertir un píxel a 8 bit RGB (és a dir, un <classname
>QColor</classname
>), i preferiblement també a 16 bit L*a*b. Addicionalment, hi ha un mètode per a convertir a qualsevol espai de color des de l'actual espai de color. </para
><para
>Els espais de color es descriuen amb el vector <classname
>KisChannelInfo</classname
>, el número de canals, el número de bytes en un píxel, si dóna suport a imatges d'alt rang dinàmic i més. </para
><para
>La manipulació és per exemple la combinació de dos píxels en un nou píxel: bitBlt, obscuriment o convulció de píxels. </para
><para
>Si us plau, consulteu la documentació de l'API per a una descripció completa de tots els mètodes que necessiteu per a implementar-los en un espai de color. </para
><para
><classname
>KisAbstractColorSpace</classname
> implementa molts dels mètodes virtuals de <classname
>KisColorSpace</classname
> usant funcions des de la biblioteca <command
>lcms</command
>. A sobre de <classname
>KisAbstractColorSpace</classname
> hi ha classes base d'espais de color per a espais de color de 8 i 16 bit enters i 16 i 32 bit float que defineixen les operacions comunes per a moure entre profunditats de bit. </para>
</sect3>
</sect2>
<sect2 id="developers-plugins-filters">
<title
>Filters</title>
<para
>Els filtres són endollables que examinen els píxels d'una capa i en fa canvis. Malgrat que el &chalk; usa un rerefons de memòria en mosaic per a emmagatzemar píxels, els escriptors de píxels no s'han de preocupar per això. Quan escriviu un endollable de filtre per a l'API imaging de &Java;, el Photoshop o el Gimp, heu d'anar amb compte amb les vores del mosaic i <quote
>cobble</quote
> del mosaic alhora: el &chalk; amaga aquest detall d'implementació. </para>
<note
><para
>Noteu que és fàcil, teòricament, reemplaçar el rerefons d'emmagatzement de dades d'imatge en mosaic actual amb un altre, però els rerefons no són endollables reals de moment, per raons de representació.</para
></note>
<para
>El &chalk; usa iteratrs per a llegir i escriure valors de píxel. Alternativament, podeu llegir un bloc de píxels en una memòria intermèdia, jugar amb ell i després escriure-hi com a un bloc. Però això no és necessàriament més eficient, pot ser finsi tot més lent que usar els iterators; pot ser només més convenient. Mireu la documentació de l'API. </para
><para
>Les imatges del &chalk; es componen de capes, de les quals hi ha actualment quatre tipus: capes de dibuix, capes de grup, capes d'ajustament (que contenen un filtre que s'aplica dinàmicament a les capes per sota del seu ajustament de capa) i capes de part. Els filtres sempre operen en capes de dibuix de la classe <classname
>KisPaintDevice</classname
>. Un dispositiu de dibuix, al seu torn, dóna accès als píxels reals. </para
><para
>Els <classname
>PaintDevice</classname
>s passen generalment ajustats en punters compartits. Un punter compartit manté la traçabilitat dels llocs on s'usa el dispositiu de dibuix i esborra el dispositiu quan ja no s'usa enlloc. Reconeixereu la versió del punter compartit d'un dispositiu de dibuix pel seu sufix <literal
>SP</literal
>. Recordeu que mai heu d'esborrar explícitament un <classname
>KisPaintDeviceSP</classname
>. </para
><para
>Anem a examinar un filtre molt simple, un que inverteix cada píxel. El codi per a aquest filtre és al directori <filename class="directory"
>koffice/chalk/plugins/filters/example</filename
>. El mètode principal és <programlisting>
KisFilterInvert::process(KisPaintDeviceSP src, KisPaintDeviceSP dst,
KisFilterConfiguration* /*config*/, const QRect& rect).
</programlisting
> La funció passa dos dispositius de dibuix, un objecte de configuració (que no s'usa en aquest filtre simple) i un <varname
>rect</varname
>. El <varname
>rect</varname
> descriu l'àrea del dispositiu de dibuix on el filtre ha d'actuar. Aquesta àrea es descriu per números sencers, el que vol dir que no hi ha precisió sub-píxel. </para
><para
>El dispositiu de dibuix <varname
>src</varname
> és per a llegir-hi, el <varname
>dst</varname
> és per a escriure-hi. Aquests paràmetres poden apuntar al mateix dispositiu de dibuix, o ser dos diferents dispositius. (Nota: això pot canviar a un únic dispositiu de dibuix en el futur.) </para
><para
>Ara, anem a mirar el codi línia per línia: </para>
<programlisting
>void KisFilterInvert::process(KisPaintDeviceSP src, KisPaintDeviceSP dst,
KisFilterConfiguration* /*config*/, const QRect& rect)
{
Q_ASSERT(src != 0);
Q_ASSERT(dst != 0);
KisRectIteratorPixel srcIt = src->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), false); <co id="invert1" />
KisRectIteratorPixel dstIt = dst->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), true ); <co id="invert2" />
int pixelsProcessed = 0;
setProgressTotalSteps(rect.width() * rect.height());
KisColorSpace * cs = src->colorSpace();
Q_INT32 psize = cs->pixelSize();
while( ! srcIt.isDone() )
{
if(srcIt.isSelected()) <co id="invert3" />
{
memcpy(dstIt.rawData(), srcIt.oldRawData(), psize); <co id="invert4" />
cs->invertColor( dstIt.rawData(), 1); <co id="invert5" />
}
setProgress(++pixelsProcessed);
++srcIt;
++dstIt;
}
setProgressDone(); // Must be called even if you don't really support progression
}
</programlisting>
<calloutlist>
<callout arearefs="invert1">
<para
>Això crea un iterator per a llegir els píxels existents. El Chalk té tres tipus d'iterators: horitzontals, verticals i rectangulars. L'iterator rect agafa el camí més eficient per les dades de la imatge, però no garanteix res sobre la localització del següent píxel que retorna. Això vol dir que no podeu assegurar que el píxel que trobareu després sigui adjacent al píxel que teníeu. Els iterators de lína horitzontal i vertical sí que garanteixen la localització dels píxels que retornen. </para
></callout>
<callout arearefs="invert2"
><para
>(2) Creem l'iterator de destí amb l'arranjament <literal
>write</literal
> en <literal
>true</literal
>. Això vol dir que si el dispositiu de dibuix de destí és més petit que el rect que escrivim, s'engrandirà automàticament per a encaixar cada píxel on hi iteratem. Noteu que aquí hi ha un bug potencial: si <varname
>dst</varname
> i <varname
>src</varname
> no són el mateix dispositiu, llavors és molt possible que els píxels que retornin els iterators no corresponguin. Per a cada posició a l'iterator, <varname
>src</varname
> pot ser, per exemple, a 165,200, ,mentre que <varname
>dst</varname
> pot ser a 20,8 — i per tant la còpia que presentem a sota pot estar distorsionant la imatge... </para
></callout>
<callout arearefs="invert3"
><para
>Voleu saber si un píxel està seleccionat? Això és fàcil usant el mètode <methodname
>isSelected</methodname
>. Però selectedness no és una propietat binària d'un píxel, un píxel pot estar mig seleccionat, poc seleccionat o quasi completament seleccionada. Aquest valor també el podeu obtenir des de l'iterator. Les seleccions són de fet un dispositiu de dibuix de màscara amb un rang d'entre 0 i 255, on 0 és completament deseleccionat i 255 completament seleccionat. L'iterator té dos mètodes: <methodname
>isSelected()</methodname
> i <methodname
>selectedNess()</methodname
>. Els primer retorna true si hi ha un píxel seleccionat en qualsevol extensió (és a dir, els valor de màscara és més gran que 1), l'altre returna el valor de màscara. </para
></callout>
<callout arearefs="invert4"
><para
>Com es fa notat amunt, aquest <literal
>memcpy</literal
> és un gran bug... <methodname
>rawData()</methodname
> retorna la matriu de bytes que és l'estat actual del píxel; <methodname
>oldRawData()</methodname
> retorna la matriu de bytes com era abans de crear l'iterator. En canvi, podem copiar aquí el píxel dolent. A la pràctica, això no passarà sovint, a no ser que el <varname
>dst</varname
> ja existeixi i no està aliniat amb el <varname
>src</varname
>. </para
></callout>
<callout arearefs="invert5"
><para
>Però això és correcte: per comptes d'esbrinar quin byte representa cada canal, usem una funció subministrada per tots els espais de color per a invertir el píxel actual. Els espais de color tenen un munt d'operacions de píxel que poseu usar. </para
></callout>
</calloutlist>
<para
>Això no és tot per a crear un filtre. Els filtres tenen dos altres components importants: un objecte de configuració i un estri de configuració. Els dos interactuen de costat. L'estri de configuració crea un objecte de configuració, però també s'omplirà des d'un objecte de configuració preexistent. Els objectes de configuració es poden representar com a XML i es poden crear des d'un XML. Això és el que fa possible l'ajustament de les capes. </para>
<sect3 id="developers-plugins-filters-iterators">
<title
>Iterators</title>
<para
>Hi ha tres tipus d'iterators: </para>
<itemizedlist>
<listitem
><para
>Línies horitzontals</para
></listitem>
<listitem
><para
>Línies verticals</para
></listitem>
<listitem
><para
>Iterors rectangulars</para
></listitem>
</itemizedlist>
<para
>Els iteratos de línia horitzontal i vertical tenen un mètode per a moure l'iterator a la següent fila o columna: <methodname
>nextRow()</methodname
> i <methodname
>nextCol()</methodname
>. Usar-la és molt més ràpid que crear un nou iterator per a cada línia o columna. </para
><para
>Els iterators són segurs per als fils en el &chalk;, de manera que és possible dividir la feina en múltiples fils. En canvi, futures versions del &chalk; usarà el mètode <methodname
>supportsThreading()</methodname
> per a determinar si el vostre filtre es pot aplicar els troços de la imatge (és a dir, tots els píxels modificats independentment, per comptes de canviats per algun valor determinat des d'un examen de tots els píxels de la imatge) i automàticament enfila l'execució del vostre filtre. </para>
</sect3>
<sect3 id="developers-plugins-filters-kisfilterconfiguration">
<title
><classname
>KisFilterConfiguration</classname
></title>
<para
><classname
>KisFilterConfiguration</classname
> és una estructura que s'usa per a desar els arranjaments de filtres al disc, per exemple per a capes d'ajustament. L'endollable d'scripting usa el mapa de propietat que hi ha darrera del <classname
>KisFilterConfigaration</classname
> per a fer possibles els filtres d'script. Els filtres poden subministrar un estri personalitzat que el &chalk; mostrarà a la galeria de filtres, el diàleg de previsualització de filtres o la pestanya d'opció d'eina de l'eina paint-with-filters. </para>
<para
>Un exemple, agafat del filtre d'efecte pintura a l'oli: </para>
<programlisting
>class KisOilPaintFilterConfiguration : public KisFilterConfiguration
{
public:
KisOilPaintFilterConfiguration(Q_UINT32 brushSize, Q_UINT32 smooth)
: KisFilterConfiguration( "oilpaint", 1 )
{
setProperty("brushSize", brushSize);
setProperty("smooth", smooth);
};
public:
inline Q_UINT32 brushSize() { return getInt("brushSize"); };
inline Q_UINT32 smooth() {return getInt("smooth"); };
};
</programlisting>
</sect3>
<sect3 id="developers-plugins-filters-kisfilterconfigurationwidget">
<title
><classname
>KisFilterConfigurationWidget</classname
></title>
<para
>La majoria de filtres es poden pessigar. Podeu crear un estri de configuracií que el Chalk usarà on sigui que useu el filtre. Un exemple: </para>
<para>
<screenshot>
<screeninfo
>El diàleg <guilabel
>Pintura a l'oli</guilabel
></screeninfo>
<mediaobject>
<imageobject>
<imagedata fileref="dialogs-oilpaint.png" format="PNG"/>
</imageobject>
<textobject>
<phrase
>El diàleg <guilabel
>Pintura a l'oli</guilabel
></phrase>
</textobject>
<caption
><para
>El diàleg <guilabel
>Pintura a l'oli</guilabel
></para
></caption>
</mediaobject>
</screenshot>
</para>
<para
>Noteu que només la part esquerra d'aquest diàleg és de la vostra responsabilitat: el &chalk; té cura de la resta. Hi ha tres maneres de fer per a crear un estri d'opció: </para>
<itemizedlist>
<listitem
><para
>Useu el &Qt; Designer per a crear una base d'estri, i feu-hi una subclasse per al vostre filtre</para
></listitem>
<listitem
><para
>Useu un dels estris simples que mostren un número de barres de desplaçament per a sencers per a llistes de sencers, dobles o bools. Aquests són útils si, com a la pantallada de dalt, el vostre filtre es poden configurar amb un número de sencers, dobles o bools. Mireu l'API dox per a <classname
>KisMultiIntegerFilterWidget</classname
>, <classname
>KisMultiDoubleFilterWidget</classname
> i <classname
>KisMultiBoolFilterWidget</classname
>.</para
></listitem>
<listitem
><para
>Picar a mà un estri. Això no està recomenat, i si ho feu i voleu que el vostre filtre formi part de la versió oficial del &chalk;, llavors us demanaré de reemplaçar-lo per un estri fet amb el &Qt; Designer.</para
></listitem>
</itemizedlist>
<para
>El filtre pintura a l'oli usa l'estri multiintegral: </para>
<programlisting
>KisFilterConfigWidget * KisOilPaintFilter::createConfigurationWidget(QWidget* parent, KisPaintDeviceSP /*dev*/)
{
vKisIntegerWidgetParam param;
param.push_back( KisIntegerWidgetParam( 1, 5, 1, i18n("Brush size"), "brushSize" ) );
param.push_back( KisIntegerWidgetParam( 10, 255, 30, i18n("Smooth"), "smooth" ) );
return new KisMultiIntegerFilterWidget(parent, id().id().ascii(), id().id().ascii(), param );
}
KisFilterConfiguration* KisOilPaintFilter::configuration(QWidget* nwidget)
{
KisMultiIntegerFilterWidget* widget = (KisMultiIntegerFilterWidget*) nwidget;
if( widget == 0 )
{
return new KisOilPaintFilterConfiguration( 1, 30);
} else {
return new KisOilPaintFilterConfiguration( widget->valueAt( 0 ), widget->valueAt( 1 ) );
}
}
std::list<KisFilterConfiguration*> KisOilPaintFilter::listOfExamplesConfiguration(KisPaintDeviceSP )
{
std::list<KisFilterConfiguration*> list;
list.insert(list.begin(), new KisOilPaintFilterConfiguration( 1, 30));
return list;
}
</programlisting>
<para
>Podeu veure com funciona: ompliu un vector amb els vostres paràmetres sencers i creeu l'estri. El mètode <methodname
>configuration()</methodname
> inspecciona l'estri i crea l'objecte de configuració de filtre adequat, en aquest cas, és clar, <classname
>KisOilPaintFilterConfiguration</classname
>. El mètode <methodname
>listOfExamplesConfiguration</methodname
> (que s'ha de reanomenar en correcte anglès...) retorna una llista amb objectes de configuració d'exemple per al diàleg de la galeria de filtres. </para>
</sect3>
<sect3 id="developers-plugins-filters-conclusion">
<title
>Conclusió de filtres</title>
<para
>Hi ha més que codificar filtres interessants, és clar, però amb aquesta explicació, la documentació de l'API i l'accés al nostre codi font, hauríeu de ser capaços de començar. No dubteu a contactar els desenvolupador del &chalk; per IRC o a la llista de correu. </para>
</sect3>
</sect2>
<sect2 id="developers-plugins-tools">
<title
>Eines</title>
<para
>Les eines apareixen a la caixa d'eines del &chalk;. Això vol dir que hi ha un espai limitat per a noves eines, així que penseu bé quan una operació no és prou per als vostres propòsits. Les eines poden usar el ratolí/tauler i el teclat de maneres complexes, que les operacions de dibuix no poden. Aquesta és la raó per la qual Duplica és una eina, però esprai és una operació de dibuix. </para
><para
>Aneu amb compte amb les dades estàtiques de la vostra eina: es crea una nova instància de la vostra eina per a cada dispositiu d'introducció: ratolí, stylus, esborrador, esprai i el que sigui. Les eines venen dividides en grups lògics: </para>
<itemizedlist>
<listitem
><para
>conforma eines de dibuix (cercle, rect)</para
></listitem>
<listitem
><para
>eines de dibuix a mà alçada (pinzell)</para
></listitem>
<listitem
><para
>Eines de transformació que desordenen la geometria de la capa</para
></listitem>
<listitem
><para
>eines d'ompliment (com omple galleda o gradient)</para
></listitem>
<listitem
><para
>eines de visualització (que no canvia els píxels, però altera la manera en què visualitzeu el llenç, com l'apropament/allunyament)</para
></listitem>
<listitem
><para
>eines de selecció (que canvien la màscara de selecció)</para
></listitem>
</itemizedlist>
<para
>La interfície d'eina es descriu a la documentació de l'API per a <classname
>KisTool</classname
>. Hi ha tres subclasses: <classname
>KisToolPaint</classname
>, <classname
>KisToolNonPaint</classname
> i <classname
>KisToolShape</classname
> (que de fet és una subclasse de <classname
>KisToolPaint</classname
>) que s'especialitza <classname
>KisTool</classname
> per a tasques de dibuix (és a dir, canviar els píxels) , les tasques que no són de dibuix i les tasques de forma de dibuix. </para
><para
>Una eina té un estri d'opció, justament com els filtres. Actualment, els estris d'opció es mostren en una pestanya en una finestra flotant. Podem canviar a una banda sota el menú principal (que llavors reemplaça la barra d'eines) per al &chalk; 2.0, però per ara, dissenyeu el vostre estri d'opció per a encaixar en una pestanya. Com sempre, és millor usar el &Qt; Designer per al disseny de l'estri d'opció. </para
><para
>Unbon exemple d'una eina és l'eina d'estrella: </para>
<screen
>kis_tool_star.cc Makefile.am tool_star_cursor.png wdg_tool_star.ui
kis_tool_star.h Makefile.in tool_star.h
chalktoolstar.desktop tool_star.cc tool_star.png
</screen>
<para
>Com veieu, necessiteu dues imatges: una per al cursor i una per a la caixa d'eines. <filename
>tool_star.cc</filename
> és només el carregador d'endollable, similar al què hem vist més amunt. La qüestió real és en la implementació: </para>
<programlisting
>KisToolStar::KisToolStar()
: KisToolShape(i18n("Star")),
m_dragging (false),
m_currentImage (0)
{
setName("tool_star");
setCursor(KisCursor::load("tool_star_cursor.png", 6, 6));
m_innerOuterRatio=40;
m_vertices=5;
}
</programlisting>
<para
>El constructor arranja el nom intern "que no es tradueix" i la crida superclasse arranja el nom visible. També carreguem la imatge de cursor i arranja un munt de variables. </para>
<programlisting
>void KisToolStar::update (KisCanvasSubject *subject)
{
KisToolShape::update (subject);
if (m_subject)
m_currentImage = m_subject->currentImg();
}
</programlisting>
<para
>El mètode <methodname
>update()</methodname
> es crida quan se selecciona l'eina. Això no és un mètode <classname
>KisTool</classname
>, sinó un mètode <classname
>KisCanvasObserver</classname
>. Es notifica als observadors de llenç quan hi ha algun canvi a la visualització, que pot ser útil per a les eines. </para
><para
>Els següents mètodes (<methodname
>buttonPress</methodname
>, <methodname
>move</methodname
> i <methodname
>buttonRelease</methodname
>) els crida el &chalk; quan el dispositiu d'introducció (ratolí, stylus, esborrador, etc) es pressiona, es mou o es deixa anar. Noteu que també podeu obtenir esdeveniments de moviment si el botó del ratolí no està pressionat. Els esdeveniments no són els esdeveniments habituals de les &Qt;, sinó esdeveniments sintètics del &chalk; perquè fem ús de trucs de baix nivell per a obtenir prou esdeveniments com per a dibuixar una línia clara. Per omissió, els kits d'eines com les &Qt; (i les GTK) deixen anar esdeveniments si estan massa ocupades per a manegar-los, i nosaltres els volem tots. </para>
<programlisting
>void KisToolStar::buttonPress(KisButtonPressEvent *event)
{
if (m_currentImage && event->button() == LeftButton) {
m_dragging = true;
m_dragStart = event->pos();
m_dragEnd = event->pos();
m_vertices = m_optWidget->verticesSpinBox->value();
m_innerOuterRatio = m_optWidget->ratioSpinBox->value();
}
}
void KisToolStar::move(KisMoveEvent *event)
{
if (m_dragging) {
// erase old lines on canvas
draw(m_dragStart, m_dragEnd);
// move (alt) or resize star
if (event->state() & Qt::AltButton) {
KisPoint trans = event->pos() - m_dragEnd;
m_dragStart += trans;
m_dragEnd += trans;
} else {
m_dragEnd = event->pos();
}
// draw new lines on canvas
draw(m_dragStart, m_dragEnd);
}
}
void KisToolStar::buttonRelease(KisButtonReleaseEvent *event)
{
if (!m_subject || !m_currentImage)
return;
if (m_dragging && event->button() == LeftButton) {
// erase old lines on canvas
draw(m_dragStart, m_dragEnd);
m_dragging = false;
if (m_dragStart == m_dragEnd)
return;
if (!m_currentImage)
return;
if (!m_currentImage->activeDevice())
return;
KisPaintDeviceSP device = m_currentImage->activeDevice ();;
KisPainter painter (device);
if (m_currentImage->undo()) painter.beginTransaction (i18n("Star"));
painter.setPaintColor(m_subject->fgColor());
painter.setBackgroundColor(m_subject->bgColor());
painter.setFillStyle(fillStyle());
painter.setBrush(m_subject->currentBrush());
painter.setPattern(m_subject->currentPattern());
painter.setOpacity(m_opacity);
painter.setCompositeOp(m_compositeOp);
KisPaintOp * op =
KisPaintOpRegistry::instance()->paintOp(m_subject->currentPaintop(), m_subject->currentPaintopSettings(), &painter);
painter.setPaintOp(op); // Painter takes ownership
vKisPoint coord = starCoordinates(m_vertices, m_dragStart.x(), m_dragStart.y(), m_dragEnd.x(), m_dragEnd.y());
painter.paintPolygon(coord);
device->setDirty( painter.dirtyRect() );
notifyModified();
if (m_currentImage->undo()) {
m_currentImage->undoAdapter()->addCommand(painter.endTransaction());
}
}
}
</programlisting>
<para
>El mètode <methodname
>draw()</methodname
> és un mètode intern de <classname
>KisToolStar</classname
> i dibuixa la vora d'una estrella. Ho cridem amb el mètode <methodname
>move()</methodname
> per a donar retroalimentació a l'usuari de la mida i forma de la seva estrella. Noteu que usem l'operació <varname
>Qt::NotROP</varname
>, que vol dir que cridant <methodname
>draw()</methodname
> per segon cop amb el mateix començament i el punt final, s'esborrarà l'estrella dibuixada prèviament. </para>
<programlisting
>void KisToolStar::draw(const KisPoint& start, const KisPoint& end )
{
if (!m_subject || !m_currentImage)
return;
KisCanvasController *controller = m_subject->canvasController();
KisCanvas *canvas = controller->kiscanvas();
KisCanvasPainter p (canvas);
QPen pen(Qt::SolidLine);
KisPoint startPos;
KisPoint endPos;
startPos = controller->windowToView(start);
endPos = controller->windowToView(end);
p.setRasterOp(Qt::NotROP);
vKisPoint points = starCoordinates(m_vertices, startPos.x(), startPos.y(), endPos.x(), endPos.y());
for (uint i = 0; i < points.count() - 1; i++) {
p.drawLine(points[i].floorQPoint(), points[i + 1].floorQPoint());
}
p.drawLine(points[points.count() - 1].floorQPoint(), points[0].floorQPoint());
p.end ();
}
</programlisting>
<para
>El mètode <methodname
>setup()</methodname
> és essencial: aquí creem l'acció que s'endollarà a la caixa d'eines per tal que els usuaris puguin seleccionar de fet l'eina. També assignem una tecla de drecera. Noteu que hi ha alguna codificació en marxa: recordeu que creem una instància de l'eina per a cada dispositiu d'entrada. Això també vol dir que cridem <methodname
>setup()</methodname
> per a cada dispositiu d'entrada i que vol dir que una acció amb el mateix nom s'afegeix moltes vegades a la col·lecció d'accions. Malgrat tot, tot sembla funcionar. Així que, per què preocupar-se? </para>
<programlisting
>void KisToolStar::setup(TDEActionCollection *collection)
{
m_action = static_cast<TDERadioAction *>(collection->action(name()));
if (m_action == 0) {
TDEShortcut shortcut(Qt::Key_Plus);
shortcut.append(TDEShortcut(Qt::Key_F9));
m_action = new TDERadioAction(i18n("&Star"),
"tool_star",
shortcut,
this,
SLOT(activate()),
collection,
name());
Q_CHECK_PTR(m_action);
m_action->setToolTip(i18n("Draw a star"));
m_action->setExclusiveGroup("tools");
m_ownAction = true;
}
}
</programlisting>
<para
>El mètode <methodname
>starCoordinates()</methodname
> conté matemàtiques curioses, però no és massa interessant per a la discussió sobre com crear un endollable d'eines. </para>
<programlisting
>KisPoint KisToolStar::starCoordinates(int N, double mx, double my, double x, double y)
{
double R=0, r=0;
Q_INT32 n=0;
double angle;
vKisPoint starCoordinatesArray(2*N);
// the radius of the outer edges
R=sqrt((x-mx)*(x-mx)+(y-my)*(y-my));
// the radius of the inner edges
r=R*m_innerOuterRatio/100.0;
// the angle
angle=-atan2((x-mx),(y-my));
//set outer edges
for(n=0;n<N;n++){
starCoordinatesArray[2*n] = KisPoint(mx+R*cos(n * 2.0 * M_PI / N + angle),my+R*sin(n *2.0 * M_PI / N+angle));
}
//set inner edges
for(n=0;n<N;n++){
starCoordinatesArray[2*n+1] = KisPoint(mx+r*cos((n + 0.5) * 2.0 * M_PI / N + angle),my+r*sin((n +0.5) * 2.0 * M_PI / N + angle));
}
return starCoordinatesArray;
}
</programlisting>
<para
>El mètode <methodname
>createOptionWidget()</methodname
> es crida per a crear l'estri d'opció que el &chalk; mostrarà a l'eina. Ja que hi ha una eina per dispositiu d'entrada, l'estat d'una eina es pot mantenir a l'eina. Aquest mètode es crida només un cop: l'estri d'opció s'emmagatzema i es recupera el següent cop que l'eina s'activa. </para>
<programlisting
>QWidget* KisToolStar::createOptionWidget(QWidget* parent)
{
QWidget *widget = KisToolShape::createOptionWidget(parent);
m_optWidget = new WdgToolStar(widget);
Q_CHECK_PTR(m_optWidget);
m_optWidget->ratioSpinBox->setValue(m_innerOuterRatio);
QGridLayout *optionLayout = new QGridLayout(widget, 1, 1);
super::addOptionWidgetLayout(optionLayout);
optionLayout->addWidget(m_optWidget, 0, 0);
return widget;
}
</programlisting>
<sect3 id="developers-plugins-tools-conclusions">
<title
>Conclussions d'eina</title>
<para
>Les eines són endollables relativament simples de crear. Cal que combineu les intefiícies <classname
>KisTool</classname
> i <classname
>KisCanvasObserver</classname
> per tal que crei efectivament una eina. </para>
</sect3>
</sect2>
<sect2 id="developers-plugins-paintoperations">
<title
>Operacions de dibuix</title>
<para
>Els PaintOps són un dels tipus d'endollables més innovadors del Chalk (juntament amb els d'espais de color). Una operació de dibuix defineis com les eies canvien els píxels que toquen. L'esprai, el lapis aliased o el pinzell de píxel antialiased: aquestes són totes operacions de dibuix. Però podeu (amb molta feina) crear un paintop que llegeixi les definicions de pinzell XML del Corel Painter i les usi per a determinar com s'ha fet un dibuix. </para
><para
>Les operacions de dibuix s'inicien quan una eina de dibuix rep un esdeveniment de <literal
>mouseDown</literal
> i s'esborren quan rep l'esdeveniment mouseUp. Entre els dos, el paintop pot mantenir la traça de posicions prèvies i altres dades, com els nivells de pressió si l'usuari fa servir un tauler. </para
><para
>L'operació bàsica d'una operació de dibuix és canviar píxels a la posició del cursor d'una eina de dibuix. Això es pot fer només un cop, o aquesta pot demanar de ser usada a intervals regulars, usant un temporitzador. La primera pot ser útil per a un dibuix de tipus llapis, la segona, és clar, per a un paintop de tipus esprai. </para
><para
>Els paintops poden tenir un petit estri de configuració que s'emplaça a la barra d'eines. Així, els estris de configuració dels paintops han de tenir format horitzontal d'estris que no siguin més alts que un botó de barra d'eines. Si no, el &chalk; tindrà un aspecte molt estrany. </para
><para
>Anem a mirar un endollable simple de paintop, un que mostri una mica d'intel·ligència programàtica. Primer, al fitxer de capçalera, hi ha una factoria definida. Aquesta factoria crea un paintop quan l'eina activa en necessita un: </para>
<programlisting
>public:
KisSmearyOpFactory() {}
virtual ~KisSmearyOpFactory() {}
virtual KisPaintOp * createOp(const KisPaintOpSettings *settings, KisPainter * painter);
virtual KisID id() { return KisID("paintSmeary", i18n("Smeary Brush")); }
virtual bool userVisible(KisColorSpace * ) { return false; }
virtual QString pixmap() { return ""; }
};
</programlisting>
<para
>La factoria també conté el <classname
>KisID</classname
> amb els noms privat i públic per al paintop (assegureu-vos que el vostre nom privat del paintop no col·lisiona amb un altre paintop!) i retornarà opcionalment un mapa de píxels. El &chalk; pot llavors mostrar el mapa de píxels jnut amb el nom per a la identificació visual del vostre paintop. Per exemple, un paintop de ganivet de pintor tindria la imatge d'aquesta implementació. </para
><para
>La implementació d'un paintop és molt directa: </para>
<programlisting
>KisSmearyOp::KisSmearyOp(KisPainter * painter)
: KisPaintOp(painter)
{
}
KisSmearyOp::~KisSmearyOp()
{
}
void KisSmearyOp::paintAt(const KisPoint &pos, const KisPaintInformation& info)
{
</programlisting>
<para
>Els mètode <methodname
>paintAt()</methodname
> és realment on ha de ser, amb els paintops. Aquest mètode rep dos paràmetres: la posició actual (que és en flotants, no en píxels sencers) i un objecte <classname
>KisPaintInformation</classname
> que conté la pressió, x i y, i el vector de moviment, i potser en el futur s'extengui amb altres informacions. </para>
<programlisting
>if (!m_painter->device()) return;
KisBrush *brush = m_painter->brush();
</programlisting>
<para
>Un <classname
>KisBrush</classname
> és la representació d'un fitxer de pinzell del Gimp: això és, una màscara, ja sigui una màscara simple o una sèrie de màscares. De fet, no usem el pinzell aquí, excepte per a determinar el <quote
>hotspot</quote
> sota el cursor. </para>
<programlisting
>Q_ASSERT(brush);
if (!brush) return;
if (! brush->canPaintFor(info) )
return;
KisPaintDeviceSP device = m_painter->device();
KisColorSpace * colorSpace = device->colorSpace();
KisColor kc = m_painter->paintColor();
kc.convertTo(colorSpace);
KisPoint hotSpot = brush->hotSpot(info);
KisPoint pt = pos - hotSpot;
// Split the coordinates into integer plus fractional parts. The integer
// is where the dab will be positioned and the fractional part determines
// the sub-pixel positioning.
Q_INT32 x, y;
double xFraction, yFraction;
splitCoordinate(pt.x(), &x, &xFraction);
splitCoordinate(pt.y(), &y, &yFraction);
KisPaintDeviceSP dab = new KisPaintDevice(colorSpace, "smeary dab");
Q_CHECK_PTR(dab);
</programlisting>
<para
>Nosaltres no canviem els píxels d'un dispositiu de dibuix directament: per comptes d'això, creeem un petit dispositiu de dibuix, un dab, i el composem al dispositiu de dibuix actual. </para>
<programlisting
>m_painter->setPressure(info.pressure);
</programlisting>
<para
>Com diuen els comentaris, el següent tros de codi fa alguna feina programàtica per a crear el dab real. En aquest cas, dibuixem un número de línies. Quan acabo amb aquest paintop, la llargada, la posició i el gruix de les línies dependrà de la pressió i de la càrrega de pintura, i haurem creat un pinzell a l'oli. Però no he tingut temps d'acabar-lo encara. </para>
<programlisting
>// Compute the position of the tufts. The tufts are arranged in a line
// perpendicular to the motion of the brush, i.e, the straight line between
// the current position and the previous position.
// The tufts are spread out through the pressure
KisPoint previousPoint = info.movement.toKisPoint();
KisVector2D brushVector(-previousPoint.y(), previousPoint.x());
KisVector2D currentPointVector = KisVector2D(pos);
brushVector.normalize();
KisVector2D vl, vr;
for (int i = 0; i < (NUMBER_OF_TUFTS / 2); ++i) {
// Compute the positions on the new vector.
vl = currentPointVector + i * brushVector;
KisPoint pl = vl.toKisPoint();
dab->setPixel(pl.roundX(), pl.roundY(), kc);
vr = currentPointVector - i * brushVector;
KisPoint pr = vr.toKisPoint();
dab->setPixel(pr.roundX(), pr.roundY(), kc);
}
vr = vr - vl;
vr.normalize();
</programlisting>
<para
>Finalment, hem construït el dab al dispositiu de dibuix original i hem dit al pintor que hem embrutat un petit rectangle al dispositiu de dibuix. </para>
<programlisting
>if (m_source->hasSelection()) {
m_painter->bltSelection(x - 32, y - 32, m_painter->compositeOp(), dab.data(),
m_source->selection(), m_painter->opacity(), x - 32, y -32, 64, 64);
}
else {
m_painter->bitBlt(x - 32, y - 32, m_painter->compositeOp(), dab.data(), m_painter->opacity(), x - 32, y -32, 64, 64);
}
m_painter->addDirtyRect(QRect(x -32, y -32, 64, 64));
}
KisPaintOp * KisSmearyOpFactory::createOp(const KisPaintOpSettings */*settings*/, KisPainter * painter)
{
KisPaintOp * op = new KisSmearyOp(painter);
Q_CHECK_PTR(op);
return op;
}
</programlisting>
<para
>Això és tot: els paintops són fàcils i divertits! </para>
</sect2>
<sect2 id="developers-plugins-viewplugins">
<title
>Endollables de visualització</title>
<para
>Els endollables de visualització són els més estranys de tots:un endollable de visualització és una KPart ordinària que pot proveir una mica d'interfície d'usuari i alguna funcionalitat. Per exemple, la pestanya d'histograma és un endollable de visualització, així com el diàleg de rotació. </para>
</sect2>
<sect2 id="developers-plugins-importexport">
<title
>Filtres d'Importació/Exportació</title>
<para
>El &chalk; treballa amb l'arquitectura ordinària de filtres de fitxer del &koffice;. Hi ha un tutorial, una mica antic, però encara útil, a: <ulink url="http://koffice.org/developer/filters/oldfaq.php"
></ulink
>. És probablement millor cooperar amb l'equip &chalk; quan es desenvolupin filtres de fitxer i fer el desenvolupament a l'arbre de filtres del &koffice;. Noteu que podeu provar els vostres filtres sense usar el &chalk; només usant la utilitat <command
>koconverter</command
>. </para
><para
>Els filtres tenen dues cares: la importació i l'exportació. Normalment, hi ha dos endollables diferents que comparteixen codi. </para
><para
>Les entrades importats <filename
>Makefile.am</filename
> són: </para>
<programlisting
>service_DATA = chalk_XXX_import.desktop chalk_XXX_export.desktop
servicedir = $(kde_servicesdir)
kdelnk_DATA = chalk_XXX.desktop
kdelnkdir = $(kde_appsdir)/Office
libchalkXXXimport_la_SOURCES = XXXimport.cpp
libchalkXXXexport_la_SOURCES = XXXexport.cpp
METASOURCES = AUTO
</programlisting>
<para
>Ja construïu un filtre d'importació o un d'exportació, la vostra feina sempre acabarà implementant la següent funció: </para>
<programlisting
>virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to);
</programlisting>
<para
>Són els arranjaments als fitxers <literal role="extension"
>.desktop</literal
> els que determinen de quina manera converteix un filtre: </para
><para
>Importa: </para>
<programlisting
>X-TDE-Export=application/x-chalk
X-TDE-Import=image/x-xcf-gimp
X-TDE-Weight=1
X-TDE-Library=libchalkXXXimport
ServiceTypes=KOfficeFilter
</programlisting>
<para
>Exporta: </para>
<programlisting
>X-TDE-Export=image/x-xcf-gimp
X-TDE-Import=application/x-chalk
ServiceTypes=KOfficeFilter
Type=Service
X-TDE-Weight=1
X-TDE-Library=libchalkXXXexport
</programlisting>
<para
>I sí, el tipus mime escollit per a l'exemple és una pista. Si us plau, si us plau, implementeu un filtre xcf? </para>
<sect3 id="plugins-developers-importexport-import">
<title
>Importa</title>
<para
>El gran problema amb els filtres d'importació és, naturalment, que el vostre codi llegeixi les dades del disc. L'important per a cridar aquest codi és prou simple: </para>
<note
><para
>Nota: de debò que hem de trobar una manera de fer que el &chalk; mantingui obert un fitxer i només llegeixei dades a mida que ho vagi necessitant, per comptes de copiar tots els continguts a la representació interna del dispositiu de dibuix. Però això voldria dir rerefons de gestió de dades que entenen els fitxers tiff i d'altres, i no estan actualment implementats. Seria ideal si alguns filtres de fitxer poguessin implementar una classe provisionalment anomenada <classname
>KisFileDataManager</classname
>, creés un objecte d'aquesta instància amb el fitxer actual i el passés a KisDoc. Però el &chalk; manega l'emmagatzement per capa, no per document, de manera que això seria una bona feina per a fer.</para
></note>
<programlisting
>KoFilter::ConversionStatus XXXImport::convert(const QCString&, const QCString& to)
{
if (to != "application/x-chalk") <co id="import1" />
return KoFilter::BadMimeType;
KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument()); <co id="import2" />
KisView * view = static_cast<KisView*>(doc -> views().getFirst()); <co id="import3" />
QString filename = m_chain -> inputFile(); <co id="import4" />
if (!doc)
return KoFilter::CreationError;
doc -> prepareForImport(); <co id="import5" />
if (!filename.isEmpty()) {
KURL url(filename);
if (url.isEmpty())
return KoFilter::FileNotFound;
KisImageXXXConverter ib(doc, doc -> undoAdapter()); <co id="import6" />
if (view != 0)
view -> canvasSubject() -> progressDisplay() -> setSubject(&ib, false, true);
switch (ib.buildImage(url)) <co id="import7" /> {
case KisImageBuilder_RESULT_UNSUPPORTED:
return KoFilter::NotImplemented;
break;
case KisImageBuilder_RESULT_INVALID_ARG:
return KoFilter::BadMimeType;
break;
case KisImageBuilder_RESULT_NO_URI:
case KisImageBuilder_RESULT_NOT_LOCAL:
return KoFilter::FileNotFound;
break;
case KisImageBuilder_RESULT_BAD_FETCH:
case KisImageBuilder_RESULT_EMPTY:
return KoFilter::ParsingError;
break;
case KisImageBuilder_RESULT_FAILURE:
return KoFilter::InternalError;
break;
case KisImageBuilder_RESULT_OK:
doc -> setCurrentImage( ib.image()); <co id="import8" />
return KoFilter::OK;
default:
break;
}
}
return KoFilter::StorageCreationError;
}
</programlisting>
<calloutlist>
<callout arearefs="import1"
><para
>Això se suposa que és un filtre d'importació, així si no es crida per a covertir a una imatge &chalk;, llavors hi ha alguna cosa malament.</para
></callout>
<callout arearefs="import2"
><para
>La cadena de filtres ja ens ha creat un document de sortida. Ens cal repartir-la a la <classname
>KisDocM</classname
>, perquè els documents del &chalk; necessiten un tractament especial. De fet, no seria pas una mala idea mirar si el resultat del repartiment no és 0, perquè si és així, la importació fallarà.</para
></callout>
<callout arearefs="import3"
><para
>Si cridem aquest filtre des de l'IGU, intentem obtenir la vista. Si hi ha una vista, el codi de conversió pot intentar actualitzar la barra de progrés.</para
></callout>
<callout arearefs="import4"
><para
>El filtre ens dóna el nom de fitxer per al fitxer d'entrada.</para
></callout>
<callout arearefs="import5"
><para
>Cal preparar el <classname
>KisDoc</classname
> per a la importació. Certs arranjaments s'inicialitzen i desfés està inhabilitat. D'altra manera, podríeu desfer l'afegiment de capes que fa el filtre d'importació i seria un comportament estrany.</para
></callout>
<callout arearefs="import6"
><para
>He escollit d'implementar el codi mateix d'importació en una classe separada que inicio aquí. També podeu introduir el vostre codi directament d'aquesta manera, però seria una mica brut.</para
></callout>
<callout arearefs="import7"
><para
>El meu importador retorna un codi d'estaurs que puc usar per a arranjar l'estatus del filtre d'importació. El &koffice; té cura de mostrar els missatges d'error.</para
></callout>
<callout arearefs="import8"
><para
>Si ha tingut èxit la creació del <classname
>KisImage</classname
>, arrangem la imatge actual del document a la nostra imatge creada ara mateix. Llavos ja estem: <literal
>return KoFilter::OK;</literal
>.</para
></callout>
</calloutlist>
</sect3>
</sect2>
</sect1>
|