maplibre / flutter-maplibre-gl

Customizable, performant and vendor-free vector and raster maps, flutter wrapper for maplibre-native and maplibre-gl-js (fork of flutter-mapbox-gl/maps)
https://pub.dev/packages/maplibre_gl
Other
226 stars 125 forks source link

[BUG] "mismatched image size" crash on web when using any PNG that isn't 32-bit RGBA #469

Open themightychris opened 4 months ago

themightychris commented 4 months ago

Platforms

web

Version of flutter maplibre_gl

0.20.0

Bug Description

addImage crashes on web if you attempt to load a PNG that has been compressed with any kind of palette optimization, which most tools do. They work on android and iOS though.

You'll see an error like:

mismatched image size. expected: 12345 but got: 82944

(this error message is also backwards, expected and actual are swapped)

This took me forever to track down, the crash comes from this line: https://github.com/maplibre/maplibre-gl-js/blob/68fd40f6962cadb315266cc9a360514f380c55fe/src/util/image.ts#L22

Which seems to make an invalid assumption about how many bytes a valid PNG will be based on its width * height * channels. If this is a hard constraint with GL then the limitation needs to be better documented with addImage and the error message more helpful

Here's an example that crashes:

drag-marker-broken

And then this version doesn't:

drag-marker-working

A broken PNG can be fixed (and blown up in size a bit) with ImageMagick's magick command to force it to be encoded as 32-bit RGBA:

magick drag-marker.orig.png PNG32:drag-marker.png

There's what looks like a ticket on in mapbox-gl's repository: https://github.com/mapbox/mapbox-maps-flutter/issues/555

The suggestion there is to process the PNG data more before passing it in. Ideally the framework would just handle that as there appears to be a mountain of image processing libraries being pulled in already, but if that's the case than maybe the docs and examples could be extended.

Steps to Reproduce

  1. Compress a PNG icon with tinypng or pngcrush or optipng—or save out of any editor that optimizes for web by reducing the palette
  2. Use addImage in a Flutter project to load the PNG
  3. Run the project on the web

Expected Results

The PNG renders on web just as it does on android/ios

Actual Results

Unhandled exception and addImage fails:

mismatched image size. expected: 12345 but got: 82944

Code Sample

final dragSymbol = await controller.addSymbol(SymbolOptions(
    geometry: const LatLng(39.9550, -75.1605),
    iconImage: 'drag-marker',
    iconSize: 1,
    iconAnchor: 'top'));