Skip to content

Fix inverted alpha for PNG textures (fully transparent pixels become opaque black) #103

Description

@Alexander-png

When loading RGBA PNG textures via gsKit_texture_png, fully transparent pixels may appear as fully opaque black in the final rendering.

This is caused by an incorrect conversion of the PNG alpha channel into the internal 7‑bit alpha representation used by gsKit. The original code inverts the alpha value, which results in transparent pixels being stored with maximum alpha.

The problematic line is located in:

  • gsKit/ee/toolkit/src/gsToolkit.c
  • Function: gsKit_texture_png
  • RGBA path (PNG_COLOR_TYPE_RGB_ALPHA)

Problematic line:

Pixels[k++].a = 128-((int) row_pointers[i][4*j+3] * 128 / 255);

This maps:

  • PNG alpha 0 (fully transparent) → 0x80 (fully opaque)
  • PNG alpha 255 (fully opaque) → ~0 (fully transparent)

As a result, with a alpha setup like:

gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
gsKit_set_primalpha(gsGlobal, GS_SETREG_ALPHA(0, 1, 0, 1, 0), 0);

fully transparent pixels end up being rendered as fully opaque (typically black, depending on the texture content), which breaks sprites and UI elements that rely on transparent padding.

Proposed fix:

Replace the inverted mapping with a direct linear mapping of the PNG alpha channel from [0;255] to [0;128]:

struct pixel { u8 r,g,b,a; };
struct pixel *Pixels = (struct pixel *) Texture->Mem;

for (i = 0; i < height; i++) {
    for (j = 0; j < width; j++) {
        Pixels[k].r = row_pointers[i][4*j];
        Pixels[k].g = row_pointers[i][4*j+1];
        Pixels[k].b = row_pointers[i][4*j+2];
        Pixels[k++].a = ((int)row_pointers[i][4*j+3] * 128 + 127) / 255;  // <-- fixed line
        // Pixels[k++].a = 128-((int) row_pointers[i][4*j+3] * 128 / 255); // <-- contains bug
    }
}

This way:

  • PNG alpha 0 → internal alpha 0 (fully transparent)
  • PNG alpha 255 → internal alpha 0x80 (fully opaque)
  • Intermediate values are mapped monotonically without inversion.

Impact:

  • Fixes fully transparent pixels turning into fully opaque black.
  • Restores expected behavior for RGBA PNG textures, especially for sprites with transparent padding and UI assets.

File:

  • gsKit/ee/toolkit/src/gsToolkit.c

Reproduction:

https://github.com/Alexander-png/gsToolKit_alpha_bug_reproduce

This repository contains a minimal sample that demonstrates the issue using an RGBA PNG with transparent padding. After applying the fix above, the artifacts disappeared in that sample.

Test environment:

  • PCSX2 v2.6.3
  • BIOS SCPH-90001_V18_USA_230

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions