Skip to content

Commit

Permalink
iOS images: back to rendering to a cg context with unmultiplying.
Browse files Browse the repository at this point in the history
The method I was using to extract the pixel data doesn't always return RGBA and you cannot force Core Graphics to return RGBA (I tried CGImageCreateCopyWithColorSpace but it only works when the components per pixel match). This is particularly problematic with PNG8 where the image data is a palette, Core Graphics will return 8 bits per pixel in this case.

We let Core Graphics do the conversion by letting it render to a context and then grab the buffer as we did before. This only works with pre-multiplied alpha (the context cannot store un-multiplied, tried that too, fails on creation). So I've also re-introduced the un-multiply logic but I hand rolled it as the SIMD was failing.

Current:
```
            // CG only supports premultiplied alpha. Unmultiply now.
            size_t imageSizeInBytes = image.height * image.width * 4;
            for (size_t i = 0; i < imageSizeInBytes; i += 4)
            {
                auto alpha = image.pixels[i + 3];
                if (alpha != 0.0f)
                {
                    auto alphaFactor = 255.0f / alpha;
                    for (size_t j = 0; j < 3; j++)
                    {
                        image.pixels[i + j] *= alphaFactor;
                    }
                }
            }
```

Not working SIMD attempt (should 255/rgba.a actually do floating point here or is this the problem?) @csmartdalton:
```
            // CG only supports premultiplied alpha. Unmultiply now.
            size_t imageSizeInBytes = image.height * image.width * 4;
            for (size_t i = 0; i < imageSizeInBytes; i += 4)
            {
                auto rgba = rive::simd::load<uint8_t, 4>(&image.pixels[i]);
                if (rgba.a != 0)
                {
                    rive::simd::store(&image.pixels[i], rgba.rgb * 255 / rgba.a);
                }
            }
```

This now works with both the dwarf and the problematic png from here: https://2dimensions.slack.com/archives/CLLCU09T6/p1723735670398969?thread_ts=1723673292.651149&cid=CLLCU09T6

Diffs=
ccfcbffdf iOS images: back to rendering to a cg context with unmultiplying. (#7868)

Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
  • Loading branch information
bodymovin and luigi-rosso committed Aug 16, 2024
1 parent c0347a1 commit ee74239
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
93cc33b45086e528b22d1245a8f7855bac299a21
ccfcbffdf6dec763774aa8b65d8ce712585218db
30 changes: 26 additions & 4 deletions Source/Renderer/PlatformCGImage.mm
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,39 @@ bool PlatformCGImageDecode(const uint8_t* encodedBytes,
break;
}

CFDataRef dataRef = CGDataProviderCopyData(CGImageGetDataProvider(image));
const UInt8* pixelData = CFDataGetBytePtr(dataRef);
const size_t width = CGImageGetWidth(image);
const size_t height = CGImageGetHeight(image);
const size_t rowBytes = width * 4; // 4 bytes per pixel
const size_t size = rowBytes * height;

const size_t bitsPerComponent = 8;
CGBitmapInfo cgInfo = kCGBitmapByteOrder32Big; // rgba
if (isOpaque)
{
cgInfo |= kCGImageAlphaNoneSkipLast;
}
else
{
cgInfo |= kCGImageAlphaPremultipliedLast;
}

std::vector<uint8_t> pixels;
pixels.resize(size);

AutoCF cs = CGColorSpaceCreateDeviceRGB();
AutoCF cg =
CGBitmapContextCreate(pixels.data(), width, height, bitsPerComponent, rowBytes, cs, cgInfo);
if (!cg)
{
return false;
}

CGContextSetBlendMode(cg, kCGBlendModeCopy);
CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image);

platformImage->width = rive::castTo<uint32_t>(width);
platformImage->height = rive::castTo<uint32_t>(height);
platformImage->opaque = isOpaque;
platformImage->pixels = std::vector<uint8_t>(pixelData, pixelData + size);
CFRelease(dataRef);
platformImage->pixels = std::move(pixels);
return true;
}
15 changes: 15 additions & 0 deletions Source/Renderer/RenderContextManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,21 @@ @implementation RiveRendererContext
{
return nullptr;
}

// CG only supports premultiplied alpha. Unmultiply now.
size_t imageSizeInBytes = image.height * image.width * 4;
for (size_t i = 0; i < imageSizeInBytes; i += 4)
{
auto alpha = image.pixels[i + 3];
if (alpha != 0.0f)
{
auto alphaFactor = 255.0f / alpha;
for (size_t j = 0; j < 3; j++)
{
image.pixels[i + j] *= alphaFactor;
}
}
}
uint32_t mipLevelCount = rive::math::msb(image.height | image.width);
return makeImageTexture(image.width, image.height, mipLevelCount, image.pixels.data());
}
Expand Down

0 comments on commit ee74239

Please sign in to comment.