Skip to content

extract_palette

Extract palette from Harmony.

ExtractPalette

Bases: Extractor

Extract palette.

Source code in client/ayon_harmony/plugins/publish/extract_palette.py
 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
class ExtractPalette(publish.Extractor):
    """Extract palette."""

    label = "Extract Palette"
    hosts = ["harmony"]
    families = ["harmony.palette"]

    def process(self, instance):
        """Plugin entry point."""
        self_name = self.__class__.__name__
        result = harmony.send(
            {
                "function": f"AyonHarmony.Publish.{self_name}.getPalette",
                "args": instance.data["id"]
            })["result"]

        if not isinstance(result, list):
            self.log.error(f"Invalid reply: {result}")
            raise AssertionError("Invalid reply from server.")
        palette_name = result[0]
        palette_file = result[1]
        self.log.info(f"Got palette named {palette_name} "
                      f"and file {palette_file}.")

        tmp_thumb_path = os.path.join(os.path.dirname(palette_file),
                                      os.path.basename(palette_file)
                                      .split(".plt")[0] + "_swatches.png"
                                      )
        self.log.info(f"Temporary thumbnail path {tmp_thumb_path}")

        palette_version = str(instance.data.get("version")).zfill(3)

        self.log.info(f"Palette version {palette_version}")

        if not instance.data.get("representations"):
            instance.data["representations"] = []

        try:
            thumbnail_path = self.create_palette_thumbnail(palette_name,
                                                           palette_version,
                                                           palette_file,
                                                           tmp_thumb_path)
        except OSError as e:
            # FIXME: this happens on Mac where PIL cannot access fonts
            # for some reason.
            self.log.warning("Thumbnail generation failed")
            self.log.warning(e)
        except ValueError:
            self.log.error("Unsupported palette type for thumbnail.")

        else:
            thumbnail = {
                "name": "thumbnail",
                "ext": "png",
                "files": os.path.basename(thumbnail_path),
                "stagingDir": os.path.dirname(thumbnail_path),
                "tags": ["thumbnail"]
            }

            instance.data["representations"].append(thumbnail)

        representation = {
            "name": "plt",
            "ext": "plt",
            "files": os.path.basename(palette_file),
            "stagingDir": os.path.dirname(palette_file)
        }

        instance.data["representations"].append(representation)

    def create_palette_thumbnail(self,
                                 palette_name,
                                 palette_version,
                                 palette_path,
                                 dst_path):
        """Create thumbnail for palette file.

        Args:
            palette_name (str): Name of palette.
            palette_version (str): Version of palette.
            palette_path (str): Path to palette file.
            dst_path (str): Thumbnail path.

        Returns:
            str: Thumbnail path.

        """
        colors = {}

        with open(palette_path, newline='') as plt:
            plt_parser = csv.reader(plt, delimiter=" ")
            for i, line in enumerate(plt_parser):
                if i == 0:
                    continue
                while ("" in line):
                    line.remove("")
                # self.log.debug(line)
                if line[0] not in ["Solid"]:
                    raise ValueError("Unsupported palette type.")
                color_name = line[1].strip('"')
                colors[color_name] = {"type": line[0],
                                      "uuid": line[2],
                                      "rgba": (int(line[3]),
                                               int(line[4]),
                                               int(line[5]),
                                               int(line[6])),
                                      }
            plt.close()

        img_pad_top = 80
        label_pad_name = 30
        label_pad_rgb = 580
        swatch_pad_left = 300
        swatch_pad_top = 10
        swatch_w = 120
        swatch_h = 50

        image_w = 800
        image_h = (img_pad_top +
                   (len(colors.keys()) *
                    swatch_h) +
                   (swatch_pad_top *
                    len(colors.keys()))
                   )

        img = Image.new("RGBA", (image_w, image_h), "white")

        # For bg of colors with alpha, create checkerboard image
        checkers = Image.new("RGB", (swatch_w, swatch_h))
        pixels = checkers.load()

        # Make pixels white where (row+col) is odd
        for i in range(swatch_w):
            for j in range(swatch_h):
                if (i + j) % 2:
                    pixels[i, j] = (255, 255, 255)

        draw = ImageDraw.Draw(img)
        # TODO: This needs to be font included with AYON because
        # arial is not available on other platforms then Windows.
        title_font = ImageFont.truetype("arial.ttf", 28)
        label_font = ImageFont.truetype("arial.ttf", 20)

        draw.text((label_pad_name, 20),
                  "{} (v{})".format(palette_name, palette_version),
                  "black",
                  font=title_font)

        for i, name in enumerate(colors):
            rgba = colors[name]["rgba"]
            # @TODO: Fix this so alpha colors are displayed with checkboard
            # if not rgba[3] == "255":
            #     img.paste(checkers,
            #               (swatch_pad_left,
            #                img_pad_top + swatch_pad_top + (i * swatch_h))
            #               )
            #
            #     half_y = (img_pad_top + swatch_pad_top + (i * swatch_h))/2
            #
            #     draw.rectangle((
            #         swatch_pad_left,  # upper LX
            #         img_pad_top + swatch_pad_top + (i * swatch_h), # upper LY
            #         swatch_pad_left + (swatch_w * 2),  # lower RX
            #         half_y),  # lower RY
            #         fill=rgba[:-1], outline=(0, 0, 0), width=2)
            #     draw.rectangle((
            #         swatch_pad_left,  # upper LX
            #         half_y,  # upper LY
            #         swatch_pad_left + (swatch_w * 2),  # lower RX
            #         img_pad_top + swatch_h + (i * swatch_h)),  # lower RY
            #         fill=rgba, outline=(0, 0, 0), width=2)
            # else:

            draw.rectangle((
                swatch_pad_left,  # upper left x
                img_pad_top + swatch_pad_top + (i * swatch_h),  # upper left y
                swatch_pad_left + (swatch_w * 2),  # lower right x
                img_pad_top + swatch_h + (i * swatch_h)),  # lower right y
                fill=rgba, outline=(0, 0, 0), width=2)

            draw.text((label_pad_name, img_pad_top + (i * swatch_h) + swatch_pad_top + (swatch_h / 4)),  # noqa: E501
                      name,
                      "black",
                      font=label_font)

            draw.text((label_pad_rgb, img_pad_top + (i * swatch_h) + swatch_pad_top + (swatch_h / 4)),  # noqa: E501
                      str(rgba),
                      "black",
                      font=label_font)

        draw = ImageDraw.Draw(img)

        img.save(dst_path)
        return dst_path

create_palette_thumbnail(palette_name, palette_version, palette_path, dst_path)

Create thumbnail for palette file.

Parameters:

Name Type Description Default
palette_name str

Name of palette.

required
palette_version str

Version of palette.

required
palette_path str

Path to palette file.

required
dst_path str

Thumbnail path.

required

Returns:

Name Type Description
str

Thumbnail path.

Source code in client/ayon_harmony/plugins/publish/extract_palette.py
 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
def create_palette_thumbnail(self,
                             palette_name,
                             palette_version,
                             palette_path,
                             dst_path):
    """Create thumbnail for palette file.

    Args:
        palette_name (str): Name of palette.
        palette_version (str): Version of palette.
        palette_path (str): Path to palette file.
        dst_path (str): Thumbnail path.

    Returns:
        str: Thumbnail path.

    """
    colors = {}

    with open(palette_path, newline='') as plt:
        plt_parser = csv.reader(plt, delimiter=" ")
        for i, line in enumerate(plt_parser):
            if i == 0:
                continue
            while ("" in line):
                line.remove("")
            # self.log.debug(line)
            if line[0] not in ["Solid"]:
                raise ValueError("Unsupported palette type.")
            color_name = line[1].strip('"')
            colors[color_name] = {"type": line[0],
                                  "uuid": line[2],
                                  "rgba": (int(line[3]),
                                           int(line[4]),
                                           int(line[5]),
                                           int(line[6])),
                                  }
        plt.close()

    img_pad_top = 80
    label_pad_name = 30
    label_pad_rgb = 580
    swatch_pad_left = 300
    swatch_pad_top = 10
    swatch_w = 120
    swatch_h = 50

    image_w = 800
    image_h = (img_pad_top +
               (len(colors.keys()) *
                swatch_h) +
               (swatch_pad_top *
                len(colors.keys()))
               )

    img = Image.new("RGBA", (image_w, image_h), "white")

    # For bg of colors with alpha, create checkerboard image
    checkers = Image.new("RGB", (swatch_w, swatch_h))
    pixels = checkers.load()

    # Make pixels white where (row+col) is odd
    for i in range(swatch_w):
        for j in range(swatch_h):
            if (i + j) % 2:
                pixels[i, j] = (255, 255, 255)

    draw = ImageDraw.Draw(img)
    # TODO: This needs to be font included with AYON because
    # arial is not available on other platforms then Windows.
    title_font = ImageFont.truetype("arial.ttf", 28)
    label_font = ImageFont.truetype("arial.ttf", 20)

    draw.text((label_pad_name, 20),
              "{} (v{})".format(palette_name, palette_version),
              "black",
              font=title_font)

    for i, name in enumerate(colors):
        rgba = colors[name]["rgba"]
        # @TODO: Fix this so alpha colors are displayed with checkboard
        # if not rgba[3] == "255":
        #     img.paste(checkers,
        #               (swatch_pad_left,
        #                img_pad_top + swatch_pad_top + (i * swatch_h))
        #               )
        #
        #     half_y = (img_pad_top + swatch_pad_top + (i * swatch_h))/2
        #
        #     draw.rectangle((
        #         swatch_pad_left,  # upper LX
        #         img_pad_top + swatch_pad_top + (i * swatch_h), # upper LY
        #         swatch_pad_left + (swatch_w * 2),  # lower RX
        #         half_y),  # lower RY
        #         fill=rgba[:-1], outline=(0, 0, 0), width=2)
        #     draw.rectangle((
        #         swatch_pad_left,  # upper LX
        #         half_y,  # upper LY
        #         swatch_pad_left + (swatch_w * 2),  # lower RX
        #         img_pad_top + swatch_h + (i * swatch_h)),  # lower RY
        #         fill=rgba, outline=(0, 0, 0), width=2)
        # else:

        draw.rectangle((
            swatch_pad_left,  # upper left x
            img_pad_top + swatch_pad_top + (i * swatch_h),  # upper left y
            swatch_pad_left + (swatch_w * 2),  # lower right x
            img_pad_top + swatch_h + (i * swatch_h)),  # lower right y
            fill=rgba, outline=(0, 0, 0), width=2)

        draw.text((label_pad_name, img_pad_top + (i * swatch_h) + swatch_pad_top + (swatch_h / 4)),  # noqa: E501
                  name,
                  "black",
                  font=label_font)

        draw.text((label_pad_rgb, img_pad_top + (i * swatch_h) + swatch_pad_top + (swatch_h / 4)),  # noqa: E501
                  str(rgba),
                  "black",
                  font=label_font)

    draw = ImageDraw.Draw(img)

    img.save(dst_path)
    return dst_path

process(instance)

Plugin entry point.

Source code in client/ayon_harmony/plugins/publish/extract_palette.py
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
def process(self, instance):
    """Plugin entry point."""
    self_name = self.__class__.__name__
    result = harmony.send(
        {
            "function": f"AyonHarmony.Publish.{self_name}.getPalette",
            "args": instance.data["id"]
        })["result"]

    if not isinstance(result, list):
        self.log.error(f"Invalid reply: {result}")
        raise AssertionError("Invalid reply from server.")
    palette_name = result[0]
    palette_file = result[1]
    self.log.info(f"Got palette named {palette_name} "
                  f"and file {palette_file}.")

    tmp_thumb_path = os.path.join(os.path.dirname(palette_file),
                                  os.path.basename(palette_file)
                                  .split(".plt")[0] + "_swatches.png"
                                  )
    self.log.info(f"Temporary thumbnail path {tmp_thumb_path}")

    palette_version = str(instance.data.get("version")).zfill(3)

    self.log.info(f"Palette version {palette_version}")

    if not instance.data.get("representations"):
        instance.data["representations"] = []

    try:
        thumbnail_path = self.create_palette_thumbnail(palette_name,
                                                       palette_version,
                                                       palette_file,
                                                       tmp_thumb_path)
    except OSError as e:
        # FIXME: this happens on Mac where PIL cannot access fonts
        # for some reason.
        self.log.warning("Thumbnail generation failed")
        self.log.warning(e)
    except ValueError:
        self.log.error("Unsupported palette type for thumbnail.")

    else:
        thumbnail = {
            "name": "thumbnail",
            "ext": "png",
            "files": os.path.basename(thumbnail_path),
            "stagingDir": os.path.dirname(thumbnail_path),
            "tags": ["thumbnail"]
        }

        instance.data["representations"].append(thumbnail)

    representation = {
        "name": "plt",
        "ext": "plt",
        "files": os.path.basename(palette_file),
        "stagingDir": os.path.dirname(palette_file)
    }

    instance.data["representations"].append(representation)