tom-weatherhead/thaw-image-processing.ts

View on GitHub
src/processing/pixelate.ts

Summary

Maintainability
C
1 day
Test Coverage
// thaw-image-processing.ts/src/pixelate.ts

/* eslint-disable no-fallthrough */

import { createThAWImage, IThAWImage } from '../util/image';

export function pixelateImage(
    srcImage: IThAWImage,
    pixelWidth: number,
    pixelHeight?: number
): IThAWImage {
    const dstImage = createThAWImage(
        srcImage.width,
        srcImage.height,
        srcImage.bytesPerPixel
    );
    let dstRowOffset = 0;
    let srcRowOffset = 0;

    if (pixelHeight === undefined) {
        pixelHeight = pixelWidth;
    }

    for (let row = 0; row < srcImage.height; row += pixelHeight) {
        const thisPixelHeight = Math.min(pixelHeight, srcImage.height - row);
        let dstPixelOffset = dstRowOffset;
        let srcPixelOffset = srcRowOffset;

        for (let col = 0; col < srcImage.width; col += pixelWidth) {
            const thisPixelWidth = Math.min(pixelWidth, srcImage.width - col);
            let accumulator3 = 0;
            let accumulator2 = 0;
            let accumulator1 = 0;
            let accumulator0 = 0;

            let srcRowOffset2 = srcPixelOffset;

            for (let row2 = 0; row2 < thisPixelHeight; row2++) {
                let srcPixelOffset2 = srcRowOffset2;

                for (let col2 = 0; col2 < thisPixelWidth; col2++) {
                    switch (srcImage.bytesPerPixel) {
                        case 4:
                            accumulator3 +=
                                srcImage.data[srcPixelOffset2 + 3];
                        case 3:
                            accumulator2 +=
                                srcImage.data[srcPixelOffset2 + 2];
                            accumulator1 +=
                                srcImage.data[srcPixelOffset2 + 1];
                        case 1:
                            accumulator0 +=
                                srcImage.data[srcPixelOffset2 + 0];
                        default:
                            break;
                    }

                    srcPixelOffset2 += srcImage.bytesPerPixel;
                }

                srcRowOffset2 += srcImage.bytesPerLine;
            }

            const numPixelsInThisPixel = thisPixelWidth * thisPixelHeight;

            const result3 = Math.round(accumulator3 / numPixelsInThisPixel);
            const result2 = Math.round(accumulator2 / numPixelsInThisPixel);
            const result1 = Math.round(accumulator1 / numPixelsInThisPixel);
            const result0 = Math.round(accumulator0 / numPixelsInThisPixel);

            let dstRowOffset2 = dstPixelOffset;

            for (let row2 = 0; row2 < thisPixelHeight; row2++) {
                let dstPixelOffset2 = dstRowOffset2;

                for (let col2 = 0; col2 < thisPixelWidth; col2++) {
                    switch (srcImage.bytesPerPixel) {
                        case 4:
                            dstImage.data[dstPixelOffset2 + 3] = result3;
                        case 3:
                            dstImage.data[dstPixelOffset2 + 2] = result2;
                            dstImage.data[dstPixelOffset2 + 1] = result1;
                        case 1:
                            dstImage.data[dstPixelOffset2 + 0] = result0;
                        default:
                            break;
                    }

                    dstPixelOffset2 += dstImage.bytesPerPixel;
                }

                dstRowOffset2 += dstImage.bytesPerLine;
            }

            dstPixelOffset += dstImage.bytesPerPixel * pixelWidth;
            srcPixelOffset += srcImage.bytesPerPixel * pixelWidth;
        }

        dstRowOffset += dstImage.bytesPerLine * pixelHeight;
        srcRowOffset += srcImage.bytesPerLine * pixelHeight;
    }

    return dstImage;
}