October 19, 2017

PlantUML Pleasantness: Creating Our Own Sprites

PlantUML supports sprites to be used in diagrams. A sprite is text encoded monochrome graphic we can reference using the syntax <$spriteName>. The sprite is defined using hexadecimal values. We can define a set of hexadecimal values to create our sprite, but we can also generate the correct values from for example a PNG image.

We start with a simple example where we create a small triangle using hexadecimal values:

@startuml
title Triangle sprite

skinparam defaultTextAlignment center

sprite $triangle {
    00000000000
    00000F00000
    0000FBF0000
    0000FBF0000
    000F999F000
    000F999F000
    00F66666F00
    00F66666F00
    0F3333333F0
    0F3333333F0
    0FFFFFFFFF0
    00000000000
}

rectangle Sample [
    Triangle
    ' Reference sprite with name triangle
    <$triangle>
]

@enduml

Let's generate a PNG image from this PlantUML definition and we get the following result:

If we have already an image we want to turn into a sprite we can use the command-line option -encodesprite when we run PlantUML. As option we must specify the gray level for our sprite. We can choose for level 4, 8 or 16. If we place the letter z after the number the sprite definition is compressed. PlantUML will output to the console the sprite definition that we can use in our PlantUML definition file. We get the best result if the original image is grayscale.

We start with the following image:

Next we run PlantUML to encode the image to a sprite definition:

$ plantuml -encodesprite 16 JDrivenIcon.png
sprite $JDrivenIcon [64x64/16] {
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0010000000000000000000000000000000000000000000000000000000000000
0016510000000000000000000000000000000000000000000000000000000000
25007FA520000000000000000000000000000000000000000000000000000000
0C4004FFFB620000000000000000000000000000000000000000000000000000
06F3002CFFFFC731000000000000000000000000000000000000000000000000
03FF30019FCEFFFF841000000000000000000000000000000000000000000000
01FFF30006FEEEDFFFF942000000000000000000000000000000000000000000
00AFEF20004FFDFFEDFFFF952000000000000000000000000000000000000000
007FDFF20002FFDFFFFEDEFFFA62000000000000000000000000000000000000
004FDEFF100019FDFFFFFFEDEFFFC73100000000000000000000000000000000
002FEFEFE100006FEEFFFFFFFEDEFFFF84100000000000000000000000000000
000BFEFEFD100004FFDFFFFFFFFFEEDFFFF94200000000000000000000000000
0007FEFFDFB100003FFDFFFFFFFFFFFFEDFFFF95200000000000000000000000
0004FDFFFDFA000001AFDFFFFFFFFFFFFFFEDEFFFB6300000000000000000000
0002FEFFFFDF90000007FEEFFFFFFFFFFFFFFFEDEFFFC7310000000000000000
0000BFEFFFFDF90000005FFEFFFFFFFFFFFFFFFFFEDEFFFF8410000000000000
00007FEFFFFFDF80000003FFDFFFFFFFFFFFFFFFFFFFEEDFFFF9410000000000
00004FDFFFFFFDF70000001BFDFFFFFFFFFFFFFFFFFFFFFFEDFFFF9520000000
00002FEFFFFFFFDF700000007FEFFFFFFFFFFFFFFFFFFFFEEEDDCDFFFB631000
00000BFFFFFFFFFDF600000005FFEFFFFEEEEDDDDDEEFFFFFFFFFFFFFFFFA520
000007FEFFFFFFFFDF600000003FFDFFFFFFFFFFFFFFFDBA8766543321100000
000004FDFFFFFFFFFDF500000002BFFECA987654332211000000000000000131
000002FEFFFFFFFFFFDF5000000002110000000000000000000000001357CF40
000000CFEFFFFFFFFFFEF4000000000000000000000000000001247AFFFFC100
0000008FEFFFFFFFFFFFEF4000000000000000000000001247AFFFFFFCF80000
0000004FDFFFFFFFFFFFFEF30000000000000000002469EFFFFFEDEDEF600000
0000002FEFFFFFFFFFFFFEFF10000000000001368CFFFFFEDEEFFFDFF4000000
0000000DFEFFFFFFFFFFFEFB000000001358BFFFFFEDEEFFFFFFFDFD20000000
00000008FEFFFFFFFFFFFDF60000007BFFFFFFEDEFFFFFFFFFFFDF9100000000
00000005FDFFFFFFFFFFFDF3000003FFFDDEEFFFFFFFFFFFFFEEF60000000000
00000002FEFFFFFFFFFFFFE1000009FDFFFFFFFFFFFFFFFFFDFF400000000000
00000001EFFFFFFFFFFFEF8000002FEFFFFFFFFFFFFFFFFFDFF3000000000000
000000008FEFFFFFFFFFDF4000007FEFFFFFFFFFFFFFFFFDFA10000000000000
000000005FDFFFFFFFFFEF200001FFFFFFFFFFFFFFFFFEEF7000000000000000
000000002FEFFFFFFFFEFA000005FDFFFFFFFFFFFFFFEFF50000000000000000
000000001EFFFFFFFFFDF600001CFFFFFFFFFFFFFFFDFF300000000000000000
0000000008FEFFFFFFFEF300004FDFFFFFFFFFFFFFDFB1000000000000000000
0000000005FDFFFFFFFFE10000AFEFFFFFFFFFFFFDF800000000000000000000
0000000003FEFFFFFFEF800003FDFFFFFFFFFFFEFF5000000000000000000000
0000000001EFFFFFFFDF400008FEFFFFFFFFFFDFF30000000000000000000000
00000000009FEFFFFFEF20002FEFFFFFFFFFFDFC200000000000000000000000
00000000005FDFFFFEFA00006FDFFFFFFFFFDF91000000000000000000000000
00000000003FEFFFFDF60001EFFFFFFFFFEEF600000000000000000000000000
00000000001FFFFFFDF30004FDFFFFFFFDFF4000000000000000000000000000
000000000009FEFFFFE1000BFEFFFFFFDFF20000000000000000000000000000
000000000006FDFFEF80003FDFFFFFFDFA100000000000000000000000000000
000000000003FDFFDF40009FEFFFFEEF70000000000000000000000000000000
000000000001FFFFEF1002FEFFFFEFF400000000000000000000000000000000
000000000000AFEEFA0007FDFFFDFF3000000000000000000000000000000000
0000000000006FDEF6001FFFFFDFB10000000000000000000000000000000000
0000000000003FDEF3005FDFFEF8000000000000000000000000000000000000
0000000000001FEFD100CFFEFF50000000000000000000000000000000000000
0000000000000AFF8004FEDFF300000000000000000000000000000000000000
00000000000006FF4009FDFC2000000000000000000000000000000000000000
00000000000003FF203FDF910000000000000000000000000000000000000000
00000000000001FA008FF6000000000000000000000000000000000000000000
00000000000000C602FF40000000000000000000000000000000000000000000
000000000000007307F200000000000000000000000000000000000000000000
0000000000000031191000000000000000000000000000000000000000000000
0000000000000000200000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
}

$

The following PlantUML definition contains the result of encoding the image as sprite for grey levels 4, 8, 16, 4z, 8z and 16z:

@startuml
nsprite $JDrivenIcon4 [64x64/4] {
0000000000000000000000000000000000000000000000000000000000000000
071GKDB732100000000000000000000000000000000000000000000000000000
0G_F300muz___VFFB73210000000000000000000000000000000000000000000
00b___F300Gmy-________lVFB73310000000000000000000000000000000000
000v_____F2000Gqy_______________lVFB7321000000000000000000000000
000G-______lB20000Wqz____________________lVFF7332100000000000000
0000K_________l7100000muz__________________________lVFB732100000
00000b___________V7100000Gmy-____---zzzzyyyyyyuuuqqqqqmmmmmmWG00
000000-_____________V7100000000000000000000000001123377BFFVUymG0
000000G________________E0000000000122337BFFVVl__________yqG00000
0000000a_______________G000002Vl____________________zqW000000000
00000000v_____________a000007___________________zum0000000000000
000000000-___________a00001V________________-ymG0000000000000000
000000000G__________v00002l______________yqW00000000000000000000
0000000000a________v00007____________zum000000000000000000000000
00000000000v______-0000R_________-ymG000000000000000000000000000
000000000000-____-G001l_______yqG0000000000000000000000000000000
000000000000G____G007_____zuW00000000000000000000000000000000000
0000000000000a__a00R__-ym000000000000000000000000000000000000000
00000000000000za01lyqG000000000000000000000000000000000000000000
00000000000000000W0000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
}

sprite $JDrivenIcon8 [64x64/8] {
0000000000000000000000000000000000000000000000000000000000000000
0003200000000000000000000000000000000000000000000000000000000000
8M20OwlNF5310000000000000000000000000000000000000000000000000000
0P_F108my_-_tVF7420000000000000000000000000000000000000000000000
00z__F100Ow__-t__-_dNF421000000000000000000000000000000000000000
00Q_s__F0008uyt-____t__-_lVF631000000000000000000000000000000000
008z_____60000Ow__-_______t___-_dN742100000000000000000000000000
000Q_-__t-l500008uzt-____________t__-_dNF53100000000000000000000
0008z_____t-d400000Ow__________________t__-_tVF74200000000000000
0000Q_-_____t-d3000008uzt-____________________t__-_dN74210000000
00008z________t-V3000000Ow___________-----________tttt___lVF5210
00000Q_-________t-V20000008vzt__-zyyxxwwvvvvumeeWOOOGG8880000010
000008-___________t_N200000008000000000000000000000012357FNVsuG0
000000Y_-_____________N100000000000000000012347FNVl__-_-_txW0000
0000008-_______________z00000000012457FVdt_-____t____-t-vG000000
0000000Y_-___________s_P000001Vl_--____t____________txW000000000
00000008______________y000001d_t________________-t_vG00000000000
00000000Y_-_________t_H00000V__________________txe00000000000000
000000008__________-_h00000M_t_____________-__vG0000000000000000
000000000Y_-_________80000L_t____________-tye0000000000000000000
0000000008________-_Y0000C_t__________-__vG000000000000000000000
0000000000Y_-______z8000B_-_________-tym800000000000000000000000
00000000008______s_P0002_-_______-__wO00000000000000000000000000
00000000000Z_-____y0001l-______-tzu80000000000000000000000000000
000000000008_t__t_G001d________wO0000000000000000000000000000000
000000000000h_-__h000V_t__-tzu8000000000000000000000000000000000
0000000000008_t_-800M_t___wW000000000000000000000000000000000000
0000000000000h__Y00K_-t-v800000000000000000000000000000000000000
00000000000008_z80C_txW00000000000000000000000000000000000000000
00000000000000pP0B_vG0000000000000000000000000000000000000000000
00000000000000801W0000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
}

sprite $JDrivenIcon16 [64x64/16] {
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0010000000000000000000000000000000000000000000000000000000000000
0016510000000000000000000000000000000000000000000000000000000000
25007FA520000000000000000000000000000000000000000000000000000000
0C4004FFFB620000000000000000000000000000000000000000000000000000
06F3002CFFFFC731000000000000000000000000000000000000000000000000
03FF30019FCEFFFF841000000000000000000000000000000000000000000000
01FFF30006FEEEDFFFF942000000000000000000000000000000000000000000
00AFEF20004FFDFFEDFFFF952000000000000000000000000000000000000000
007FDFF20002FFDFFFFEDEFFFA62000000000000000000000000000000000000
004FDEFF100019FDFFFFFFEDEFFFC73100000000000000000000000000000000
002FEFEFE100006FEEFFFFFFFEDEFFFF84100000000000000000000000000000
000BFEFEFD100004FFDFFFFFFFFFEEDFFFF94200000000000000000000000000
0007FEFFDFB100003FFDFFFFFFFFFFFFEDFFFF95200000000000000000000000
0004FDFFFDFA000001AFDFFFFFFFFFFFFFFEDEFFFB6300000000000000000000
0002FEFFFFDF90000007FEEFFFFFFFFFFFFFFFEDEFFFC7310000000000000000
0000BFEFFFFDF90000005FFEFFFFFFFFFFFFFFFFFEDEFFFF8410000000000000
00007FEFFFFFDF80000003FFDFFFFFFFFFFFFFFFFFFFEEDFFFF9410000000000
00004FDFFFFFFDF70000001BFDFFFFFFFFFFFFFFFFFFFFFFEDFFFF9520000000
00002FEFFFFFFFDF700000007FEFFFFFFFFFFFFFFFFFFFFEEEDDCDFFFB631000
00000BFFFFFFFFFDF600000005FFEFFFFEEEEDDDDDEEFFFFFFFFFFFFFFFFA520
000007FEFFFFFFFFDF600000003FFDFFFFFFFFFFFFFFFDBA8766543321100000
000004FDFFFFFFFFFDF500000002BFFECA987654332211000000000000000131
000002FEFFFFFFFFFFDF5000000002110000000000000000000000001357CF40
000000CFEFFFFFFFFFFEF4000000000000000000000000000001247AFFFFC100
0000008FEFFFFFFFFFFFEF4000000000000000000000001247AFFFFFFCF80000
0000004FDFFFFFFFFFFFFEF30000000000000000002469EFFFFFEDEDEF600000
0000002FEFFFFFFFFFFFFEFF10000000000001368CFFFFFEDEEFFFDFF4000000
0000000DFEFFFFFFFFFFFEFB000000001358BFFFFFEDEEFFFFFFFDFD20000000
00000008FEFFFFFFFFFFFDF60000007BFFFFFFEDEFFFFFFFFFFFDF9100000000
00000005FDFFFFFFFFFFFDF3000003FFFDDEEFFFFFFFFFFFFFEEF60000000000
00000002FEFFFFFFFFFFFFE1000009FDFFFFFFFFFFFFFFFFFDFF400000000000
00000001EFFFFFFFFFFFEF8000002FEFFFFFFFFFFFFFFFFFDFF3000000000000
000000008FEFFFFFFFFFDF4000007FEFFFFFFFFFFFFFFFFDFA10000000000000
000000005FDFFFFFFFFFEF200001FFFFFFFFFFFFFFFFFEEF7000000000000000
000000002FEFFFFFFFFEFA000005FDFFFFFFFFFFFFFFEFF50000000000000000
000000001EFFFFFFFFFDF600001CFFFFFFFFFFFFFFFDFF300000000000000000
0000000008FEFFFFFFFEF300004FDFFFFFFFFFFFFFDFB1000000000000000000
0000000005FDFFFFFFFFE10000AFEFFFFFFFFFFFFDF800000000000000000000
0000000003FEFFFFFFEF800003FDFFFFFFFFFFFEFF5000000000000000000000
0000000001EFFFFFFFDF400008FEFFFFFFFFFFDFF30000000000000000000000
00000000009FEFFFFFEF20002FEFFFFFFFFFFDFC200000000000000000000000
00000000005FDFFFFEFA00006FDFFFFFFFFFDF91000000000000000000000000
00000000003FEFFFFDF60001EFFFFFFFFFEEF600000000000000000000000000
00000000001FFFFFFDF30004FDFFFFFFFDFF4000000000000000000000000000
000000000009FEFFFFE1000BFEFFFFFFDFF20000000000000000000000000000
000000000006FDFFEF80003FDFFFFFFDFA100000000000000000000000000000
000000000003FDFFDF40009FEFFFFEEF70000000000000000000000000000000
000000000001FFFFEF1002FEFFFFEFF400000000000000000000000000000000
000000000000AFEEFA0007FDFFFDFF3000000000000000000000000000000000
0000000000006FDEF6001FFFFFDFB10000000000000000000000000000000000
0000000000003FDEF3005FDFFEF8000000000000000000000000000000000000
0000000000001FEFD100CFFEFF50000000000000000000000000000000000000
0000000000000AFF8004FEDFF300000000000000000000000000000000000000
00000000000006FF4009FDFC2000000000000000000000000000000000000000
00000000000003FF203FDF910000000000000000000000000000000000000000
00000000000001FA008FF6000000000000000000000000000000000000000000
00000000000000C602FF40000000000000000000000000000000000000000000
000000000000007307F200000000000000000000000000000000000000000000
0000000000000031191000000000000000000000000000000000000000000000
0000000000000000200000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
}

sprite $JDrivenIcon4z [64x64/4z] {
vPVHbi8m24IPuV__sJLD3JKa1VhaMOwFpWN6O8Z8pmVmIFttKTOHsW1QHa0RfKK9eGt0Wr0eGmy0L6i8Je0Sekbac91577d5cd06eWQS0EXt1ChW5d2Fw1hA
R488SUQak2RS8_GAWAu2MmC20ByCS_HaOy8IWRb2QWRXj0ZLE88LW470DTbJa7Z7sW0Be954HY0tDi3uPdBk08wHoGtkAJM7nJFxHZhbq1i0k9-5wOUoKkym
haReIiTgc6Lx0Zxu-1oxUiR_1jpwVFd-FyHaGAXrpu1rUaH6WClq28qGLkdlRv7jNHIuHTQNuLvkowLRFYIgXvS-Ueswoq10FWpWx1xZUq0llxn7pCjGTAsZ
qJFL-hn795kt3U2IFhCBZd9BydvVMVUogtJFc6zzD30jP7bzUPbdIuf2wny3q6qiFcPw-PIYdYrz-JNrLZwG7u-vPwz9oh-A5m00
}
sprite $JDrivenIcon8z [64x64/8z] {
rPTHiiCW24L5GFQ_uvUg94emnVJh-T5fft831oIOqhzVc7ymXiCOXJEy5IY7G1OHndSIA8STb4D1oXi9a8z08gaIGjimf0bWzQ19RBd1NI0tysq9_9X0YwJ5
K3_3F7FTNxygX2uHumdDeljohJ1FRjkxCwSBOPwTFAiteq148dVkhExC0bzvWkwYT8CW4Pvi1BB8IiBbYM84RX0C31T0_HlJ2i83JnZ-nBI6qFSSo_1aHo0R
4yUVi-kWE08EXC9CY9Wp0BW0ge3Y0QucHDt2IHvO09D0WiV6YTTEusZ-rdEHhoFJ3cvGG4s7Oh40wWBNbCuGZ-teLe0kOujKnYekraMRAHuUIpoQzkSR1ydK
Q7TEWvt3cqBt09GUFzJQknSUhdg02P-SKrJyxCoRQ45LjDVV5ZaLY9uvFpN1EVtUwSNd9ZXcBplHvwVNgCbU2OP-1m3UuvUXtm6OfhGgcXK0Qh_t3dKosQDu
w1O0t5jIWU0SeDd3wRXHV0xesGiLZGUWz66eF9-N1m1JyYBana6gPG-kDo9lJ995iqUReOy0fkHjJQEisKET9FVcOTIslXtw003Ev57QNVsTLlP3Fm6Gpf1l
RXDOdqgnyxAwIq3mlBW0kDOElZHF1Jy-lB_F7JCPokkhN1ruAFsq8FslzGS0
}

sprite $JDrivenIcon16z [64x64/16z] {
rPVPTeKW34MP11dp_tzRvacqUEz3L-bBL-ivafq40q9_lV2tUdvyOK4EX0IeWtngO1X2300q_yo20_MxCDu1ZA3l8w4G3B04uuB7oLvQO8W6VX_EEHii97iL
YG87GU0XU7csUC5J15bud4I3O14YKRiy6OJdGzGUGZGe5diyYT-__y4eGu3UOeUdZWOMemgXMEpn55xjLHhdT3O3sEB9eiA2IlbKeq69H7Fw020QoBeV1okB
EvvwCZW0PeD7dY8_Rk6iDMtXkYfFl08G3KGkQhqqkE59wdQhGTtJPGlEMcCJJzm0G3NWQ8JWeiIl2zHswdGlQmOB25QhKt1-C4e9nWi0qU0eOVbjEQEabqJ5
84cOSWYa3xOPeAkYanx202k7O6_Wwfzlf8G95S-xwduEjEyDgjIBIx7C0AB1ejO9uzALKlJ5o9SLaFM0ns3vQOhKHKRJ5kqKWEuudRgN9iHswkSHG2q2eOVc
gV-MSrwE0O3DyTFm-rnuRWnz3I3voqKNNK9FOOxXdolJ8b-UGpq0cz-mw45_ogrBiWUGlaVnopPtR-tl1m0kdwJ7vI25rXeF07BsiDaCVGQGgtUgwVg5k5dT
RbFsr4ZFzlsod2FSa3qwlFufz0b0pjuvdYbFeQDSRAxBtdGacT-7WGmWPuyFHwB4EuEKQzdhqhdibvj1glPUIzwwNnu0nEpf4dwUZVOcILkoHziNKMtFWr6K
ibUsSjylTm1SE3bpyjnsw7MIJDaJUQBvlMXc035NUATVrW1YxnqDvCjHEes1PgTVrW32IraSKkXxkGSGWCkzVbdV9KWi0_dPZGg7x9smro_BonGFjm3sgHm9
wclduwkS1u2n_Eu-Ij3_MZy0
}

rectangle JDrivenIcon4 [
    Gray level 4

    <$JDrivenIcon4>
]

rectangle JDrivenIcon8 [
    Gray level 8

    <$JDrivenIcon8>
]

rectangle JDrivenIcon16 [
    Gray level 16

    <$JDrivenIcon16>
]

rectangle JDrivenIcon4z [
    Gray level 4
    compressed

    <$JDrivenIcon4z>
]

rectangle JDrivenIcon8z [
    Gray level 8
    compressed

    <$JDrivenIcon8z>
]

rectangle JDrivenIcon16z [
    Gray level 16
    compressed

    <$JDrivenIcon16z>
]
@enduml

The PNG image of the previous PlantUML definition looks like this:

Written with PlantUML 1.2017.16.