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

[TypeScript] Quantization of relatively large images doesn't work in Chrome #130

Open
FluorescentHallucinogen opened this issue Dec 3, 2023 · 4 comments
Labels
explain (Label used internally)

Comments

@FluorescentHallucinogen
Copy link

Quantization of images that contains >= 50139473 pixels doesn't work in Chrome.

Tested in Chrome x64 on Windows.

Not reproducible in Firefox.

This means e.g. an image with a resolution of 7080x7080 px can still be quantized, but 7081x7081 px cannot.

Minimal repro:

import { sourceColorFromImage } from '@material/material-color-utilities';

const image = new Image();
image.src = './7081x7081.png';

const sourceColor = await sourceColorFromImage(image);

console.log('sourceColor', sourceColor);

Error:

image_utils.ts:71 Uncaught RangeError: Invalid array length
    at Array.push (<anonymous>)
    at sourceColorFromImage (image_utils.ts:71:12)
    at async (index):24:22
sourceColorFromImage @ image_utils.ts:71
load (async)
imageBytes @ image_utils.ts:56
sourceColorFromImage @ image_utils.ts:31
(anonymous) @ (index):24

Code line:

As far as I know, JavaScript arrays are zero-based and use 32-bit indexes: the index of the first element is 0, and the highest possible index is 4294967294 (2^32−2), for a maximum array size of 4,294,967,295 elements. Not 50,139,473. :)

@FluorescentHallucinogen
Copy link
Author

@rodydavis @guidezpl Could you please take a look? 😉

@FluorescentHallucinogen
Copy link
Author

@pennzht @marshallworks PTAL.

@Nevro
Copy link

Nevro commented Jan 6, 2024

why not modify existing buffer?

const _forEach = (tarray, cb) => tarray.forEach(cb) ?? tarray;
return _forEach(new Uint32Array(data.buffer), (abgr, index, tarray) => 
    tarray.set([abgr & 0xFF00FF00 | (abgr & 255) << 16 | abgr >> 16 & 255], index));

..or map new TypedArray if raw buffer is necessary.

return new Uint32Array(data.buffer).map((abgr) => 
    abgr & 0xFF00FF00 | (abgr & 255) << 16 | abgr >> 16 & 255);

@Nevro
Copy link

Nevro commented Jan 6, 2024

Working sample tested with Chromium

<html>
    <body>
        <script type="module">

            import { QuantizerCelebi, Score } from 'https://unpkg.com/@material/material-color-utilities';

            const image = new Image();
            image.crossOrigin = 'Anonymous';
            image.src = 'https://r4.wallpaperflare.com/wallpaper/727/861/207/spiderman-ps4-spiderman-games-hd-wallpaper-2460826541419b3727663083655888cf.jpg';

            async function sourceColorFromImage(image) {
                const imageBytes = await new Promise((resolve, reject) => {
                    const canvas = document.createElement('canvas');
                    const context = canvas.getContext('2d');
                    if (!context) {
                        reject(new Error('Could not get canvas context'));
                        return;
                    }
                    const callback = () => {
                        canvas.width = image.width;
                        canvas.height = image.height;
                        context.drawImage(image, 0, 0);
                        let rect = [0, 0, image.width, image.height];
                        const area = image.dataset['area'];
                        if (area && /^\d+(\s*,\s*\d+){3}$/.test(area)) {
                            rect = area.split(/\s*,\s*/).map(s => {
                                // tslint:disable-next-line:ban
                                return parseInt(s, 10);
                            });
                        }
                        const [sx, sy, sw, sh] = rect;
                        resolve(context.getImageData(sx, sy, sw, sh).data);
                    };
                    if (image.complete) {
                        callback();
                    }
                    else {
                        image.onload = callback;
                    }
                });

                const bufferArray = new Uint32Array(imageBytes.buffer);

                bufferArray.forEach((abgr, index, tarray) =>
                      tarray.set([abgr & 0xFF00FF00 | (abgr & 255) << 16 | abgr >> 16 & 255], index));

                const result = QuantizerCelebi.quantize(bufferArray, 128);
                const ranked = Score.score(result);
                const top = ranked[0];
                return top;
            }

            const sourceColor = await sourceColorFromImage(image);
            console.log('sourceColor', sourceColor);

        </script>
    </body>
</html>

@pennzht pennzht added the explain (Label used internally) label Feb 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
explain (Label used internally)
Projects
None yet
Development

No branches or pull requests

3 participants