Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Odd color change in GIF after overlaying text #4202

Closed
Technerder opened this issue Nov 8, 2019 · 7 comments · Fixed by #5564
Closed

Odd color change in GIF after overlaying text #4202

Technerder opened this issue Nov 8, 2019 · 7 comments · Fixed by #5564

Comments

@Technerder
Copy link

What did you do?

Tried to overlay some text over a GIF file

What did you expect to happen?

I expected the outcome to look almost identical to the original in terms of quality.

What actually happened?

The final result quality text looks odd, and the GIF looks like it had it's color inverted

What are your OS, Python and Pillow versions?

  • OS: Windows
  • Python: 3.8
  • Pillow: 6.2.1

Code used to modify the GIF from a URL:

image = Image.open(BytesIO(await request.read()))
frames = []
for frame in ImageSequence.Iterator(image):
    frame_bytes = BytesIO()
    draw = ImageDraw.Draw(frame)
    draw_text(frame, draw, top_text, 'top')
    draw_text(frame, draw, bottom_text, 'bottom')
    frame.save(frame_bytes, format='GIF')
    frames.append(Image.open(frame_bytes))
    del draw
edited_image_bytes = BytesIO()
frames[0].save(edited_image_bytes, format='GIF', save_all=True, append_images=frames[1:])

draw_text and draw_text_with_outline methods: https://hastebin.com/xoxecosawu.py

Files: Gifs.zip

@hugovk
Copy link
Member

hugovk commented Nov 8, 2019

For reference, draw_text and draw_text_with_outline methods:

font = ImageFont.truetype("impact.ttf", 42)

def draw_text_with_outline(draw, text, x, y):
    draw.text((x - 2, y - 2), text, (0, 0, 0), font=font)
    draw.text((x + 2, y - 2), text, (0, 0, 0), font=font)
    draw.text((x + 2, y + 2), text, (0, 0, 0), font=font)
    draw.text((x - 2, y + 2), text, (0, 0, 0), font=font)
    draw.text((x, y), text, (255, 255, 255), font=font)

def draw_text(img, draw, text, pos):
    text = text.upper()
    w, h = draw.textsize(text, font)
    line_count = int(round((w / img.width) + 1)) if w > img.width else 1

    lines = []
    if line_count > 1:
        last_cut = 0
        is_last = False
        for i in range(0, line_count):
            cut = (len(text) / line_count) * i if last_cut == 0 else last_cut

            if i < line_count - 1:
                next_cut = (len(text) / line_count) * (i + 1)
            else:
                next_cut = len(text)
                is_last = True

            if not (next_cut == len(text) or text[next_cut] == " "):
                while text[next_cut] != " ":
                    next_cut += 1

            line = text[cut:next_cut].strip()

            w, h = draw.textsize(line, font)
            if not is_last and w > img.width:
                next_cut -= 1
                while text[next_cut] != " ":
                    next_cut -= 1

            last_cut = next_cut
            lines.append(text[cut:next_cut].strip())
    else:
        lines.append(text)

    last_y = img.height - h * (line_count + 1) - 10 if pos == 'bottom' else -h

    for i in range(0, line_count):
        w, h = draw.textsize(lines[i], font)
        x = img.width / 2 - w / 2
        y = last_y + h
        draw_text_with_outline(draw, lines[i], x, y)
        last_y = y

@radarhere
Copy link
Member

For clarity, when you're talking about color inversion, I presume you're just talking about the first frame.

Regarding the text, I'm confused. You're saying it should be similar in quality to the original - what are you referring to? There is no original for the text.

@radarhere
Copy link
Member

This looks related to the palette. If I replace the RGB values with P values like so -

def draw_text_with_outline(draw, text, x, y):
    draw.text((x - 2, y - 2), text, 2, font=font)
    draw.text((x + 2, y - 2), text, 2, font=font)
    draw.text((x + 2, y + 2), text, 2, font=font)
    draw.text((x - 2, y + 2), text, 2, font=font)
    draw.text((x, y), text, 17, font=font)

I find that the output image does not have the color inversion problem.

@radarhere radarhere added the GIF label Nov 11, 2019
@Technerder
Copy link
Author

That seems to have fixed it. Now it seems to be filling in the background of clear background gifs with white.

@aclark4life aclark4life added this to New Issues in Pillow Jan 9, 2020
@radarhere
Copy link
Member

Are you referring to files not mentioned here when you say 'clear background gifs'? Because with the change from my last comment, the output GIF looks fine to me.

@radarhere
Copy link
Member

Looking at the first frame, there is no local palette, and (0, 0, 0) and (255, 255, 255) are not part of the global palette. So to allow those colors to be added, we will have to implement a system to start discarding unused colors.

@radarhere
Copy link
Member

A system for discarding unused colors was added in #5552. So now you can use the original RGB values and get actual black and white... sometimes. It turns out that the new palette indexes may coincide with the transparency index, so #5564 fixes that and resolves this.

Pillow automation moved this from New Issues to Closed Jun 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Pillow
  
Closed
Development

Successfully merging a pull request may close this issue.

3 participants