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

Performance optimization of EscPosPrinterCommands.bitmapToBytes() logic #490

Open
wzy820715 opened this issue Jan 19, 2024 · 2 comments
Open

Comments

@wzy820715
Copy link

Hi, I just used this library today to implement the bitmap print feature. It helped me a lot. Great work! But there is a small piece of code that might need to be improved.

I found that the bitmapToBytes function is performing very slowly. I looked through the source code and found that there is no need to call bitmap.getPixel(posX, posY) in every iteration, as this is a time-consuming operation. Instead, we can obtain the entire pixel array outside of the iteration and get the corresponding color information from this array within each iteration.

I have tested this modification and it has significantly improved the processing performance of this function.

The updated code is as follows:

public static byte[] bitmapToBytes(Bitmap bitmap, boolean gradient) {
        int
            bitmapWidth = bitmap.getWidth(),
            bitmapHeight = bitmap.getHeight(),
            bytesByLine = (int) Math.ceil(((float) bitmapWidth) / 8f);

        byte[] imageBytes = EscPosPrinterCommands.initGSv0Command(bytesByLine, bitmapHeight);

        int i = 8,
            greyscaleCoefficientInit = 0,
            gradientStep = 6;

        double
            colorLevelStep = 765.0 / (15 * gradientStep + gradientStep - 1);

        // Before iteration, obtain the whole pixel array of the bitmap first.
        int[] pixels = new int[bitmapWidth * bitmapHeight];
        bitmap.getPixels(pixels, 0, bitmapWidth, 0, 0, bitmapWidth, bitmapHeight);

        for (int posY = 0; posY < bitmapHeight; posY++) {
            int greyscaleCoefficient = greyscaleCoefficientInit,
                greyscaleLine = posY % gradientStep;
            for (int j = 0; j < bitmapWidth; j += 8) {
                int b = 0;
                for (int k = 0; k < 8; k++) {
                    int posX = j + k;
                    if (posX < bitmapWidth) {
                        //int color = bitmap.getPixel(posX, posY),

                        // get the color of the pixel in pixel array
                        int color = pixels[bitmapWidth * posY + posX],
                            red = (color >> 16) & 255,
                            green = (color >> 8) & 255,
                            blue = color & 255;

                        if (
                            (gradient && (red + green + blue) < ((greyscaleCoefficient * gradientStep + greyscaleLine) * colorLevelStep)) ||
                                (!gradient && (red < 160 || green < 160 || blue < 160))
                        ) {
                            b |= 1 << (7 - k);
                        }

                        greyscaleCoefficient += 5;
                        if (greyscaleCoefficient > 15) {
                            greyscaleCoefficient -= 16;
                        }
                    }
                }
                imageBytes[i++] = (byte) b;
            }

            greyscaleCoefficientInit += 2;
            if (greyscaleCoefficientInit > 15) {
                greyscaleCoefficientInit = 0;
            }
        }

        return imageBytes;
    }

The bigger the bitmap, the more significant the effect

Besides, there is no need to limit the maximum height of the printed image and break it down into smaller parts for printing. Parsing it as a whole one time is enough. This will save more time.

I extracted the bitmapToBytes function to use in my project and didn't break the bitmap down. It printed very fast with no problems. I didn't even resize the bitmap.

@DantSu
Copy link
Owner

DantSu commented Jan 19, 2024

Thanks for this tips, CPU use versus RAM use... I don't know which is better. Your code can crash on low memory devices. Perhaps the right compromise is to get pixels row by row. I will think about it.

256px height limitation is because bigs pictures crash on many printers (Error : Buffer not large enough for pixels). Or with bluetooth, too many data send print characters instead of image (I think some data is lost but we can't have feedback of printer to have the error).
If users don't want this limitation, they can use this piece of code : #70 (comment)

@wzy820715
Copy link
Author

I have tested the two different models of printers I currently have on hand, under high load testing for some time, and so far there has been no crash problem. Yeah, you remind me, I haven't considered the use case for low memory devices yet.

Maybe introducing a thread pool to process this conversion logic in parallel is a feasible solution, I guess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants