From: Svjatoslav Agejenko Date: Fri, 20 Mar 2026 20:51:28 +0000 (+0200) Subject: perf: optimize texture rendering and scanlines X-Git-Url: http://www2.svjatoslav.eu/gitweb/?a=commitdiff_plain;h=387cc51e9ac6b778e552b17cbd8db5c91dccde5b;p=sixth-3d.git perf: optimize texture rendering and scanlines - Optimize scanline texture coordinate interpolation - Optimize texture sampling and add alpha blending - Optimize mipmap downsampling/upsampling with direct pixel ops - Fix y-coordinate swap in drawRectangle - Use Arrays.fill for fillColor and opaque scanlines --- diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.java index 124b679..d62c034 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.java @@ -99,9 +99,10 @@ public class SolidPolygon extends AbstractCoordinateShape { final int b = color.b; if (polygonAlpha == 255) { - final int pixel = (r << 16) | (g << 8) | b; - for (int i = 0; i < width; i++) - pixels[offset++] = pixel; + if (width > 0) { + final int pixel = (r << 16) | (g << 8) | b; + java.util.Arrays.fill(pixels, offset, offset + width, pixel); + } } else { final int backgroundAlpha = 255 - polygonAlpha; diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedPolygon.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedPolygon.java index 8042b77..094ec42 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedPolygon.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedPolygon.java @@ -129,19 +129,57 @@ public class TexturedPolygon extends AbstractCoordinateShape { final double twidth = tx2 - tx1; final double theight = ty2 - ty1; + final double txStep = twidth / realWidth; + final double tyStep = theight / realWidth; + + double tx = tx1 + txStep * (x1 - realX1); + double ty = ty1 + tyStep * (x1 - realX1); + + final int[] texPixels = textureBitmap.pixels; + final int texW = textureBitmap.width; + final int texH = textureBitmap.height; + final int texWMinus1 = texW - 1; + final int texHMinus1 = texH - 1; + for (int x = x1; x < x2; x++) { - final double distance = x - realX1; + int itx = (int) tx; + int ity = (int) ty; + + if (itx < 0) itx = 0; + else if (itx > texWMinus1) itx = texWMinus1; + + if (ity < 0) ity = 0; + else if (ity > texHMinus1) ity = texHMinus1; + + final int srcPixel = texPixels[ity * texW + itx]; + final int srcAlpha = (srcPixel >> 24) & 0xff; + + if (srcAlpha != 0) { + if (srcAlpha == 255) { + renderBufferPixels[renderBufferOffset] = srcPixel; + } else { + final int backgroundAlpha = 255 - srcAlpha; + + final int srcR = (srcPixel >> 16) & 0xff; + final int srcG = (srcPixel >> 8) & 0xff; + final int srcB = srcPixel & 0xff; - final double tx = tx1 + ((twidth * distance) / realWidth); - final double ty = ty1 + ((theight * distance) / realWidth); + final int destPixel = renderBufferPixels[renderBufferOffset]; + final int destR = (destPixel >> 16) & 0xff; + final int destG = (destPixel >> 8) & 0xff; + final int destB = destPixel & 0xff; - final int textureOffset = textureBitmap.getAddress((int) tx, - (int) ty); + final int r = ((destR * backgroundAlpha) + (srcR * srcAlpha)) / 256; + final int g = ((destG * backgroundAlpha) + (srcG * srcAlpha)) / 256; + final int b = ((destB * backgroundAlpha) + (srcB * srcAlpha)) / 256; - textureBitmap.drawPixel(textureOffset, renderBufferPixels, - renderBufferOffset); + renderBufferPixels[renderBufferOffset] = (r << 16) | (g << 8) | b; + } + } + tx += txStep; + ty += tyStep; renderBufferOffset++; } diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/TexturedRectangle.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/TexturedRectangle.java index 1ababb2..aa52272 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/TexturedRectangle.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/TexturedRectangle.java @@ -177,9 +177,4 @@ public class TexturedRectangle extends AbstractCompositeShape { addShape(texturedPolygon2); } -// public void initialize(final int width, final int height, -// final int maxTextureUpscale) { -// initialize(width, height, width, height, maxTextureUpscale); -// } - } diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java index b650230..7558eb8 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/Texture.java @@ -171,18 +171,38 @@ public class Texture { final TextureBitmap downScaled = new TextureBitmap(newWidth, newHeight, originalBitmap.multiplicationFactor / 2d); - final ColorAccumulator accumulator = new ColorAccumulator(); + final int[] srcPixels = originalBitmap.pixels; + final int[] dstPixels = downScaled.pixels; + final int srcW = originalBitmap.width; + final int srcH = originalBitmap.height; + final int srcWMinus1 = srcW - 1; + final int srcHMinus1 = srcH - 1; + + for (int y = 0; y < newHeight; y++) { + final int srcYBase = y * 2; + final int srcY1 = Math.min(srcYBase, srcHMinus1); + final int srcY2 = Math.min(srcYBase + 1, srcHMinus1); + final int row1Offset = srcY1 * srcW; + final int row2Offset = srcY2 * srcW; - for (int y = 0; y < newHeight; y++) for (int x = 0; x < newWidth; x++) { - accumulator.reset(); - accumulator.accumulate(originalBitmap, x * 2, y * 2); - accumulator.accumulate(originalBitmap, (x * 2) + 1, y * 2); - accumulator.accumulate(originalBitmap, x * 2, (y * 2) + 1); - accumulator - .accumulate(originalBitmap, (x * 2) + 1, (y * 2) + 1); - accumulator.storeResult(downScaled, x, y); + final int srcXBase = x * 2; + final int srcX1 = Math.min(srcXBase, srcWMinus1); + final int srcX2 = Math.min(srcXBase + 1, srcWMinus1); + + final int p0 = srcPixels[row1Offset + srcX1]; + final int p1 = srcPixels[row1Offset + srcX2]; + final int p2 = srcPixels[row2Offset + srcX1]; + final int p3 = srcPixels[row2Offset + srcX2]; + + final int a = (((p0 >>> 24) + (p1 >>> 24) + (p2 >>> 24) + (p3 >>> 24)) >> 2); + final int r = ((((p0 >> 16) & 0xff) + ((p1 >> 16) & 0xff) + ((p2 >> 16) & 0xff) + ((p3 >> 16) & 0xff)) >> 2); + final int g = ((((p0 >> 8) & 0xff) + ((p1 >> 8) & 0xff) + ((p2 >> 8) & 0xff) + ((p3 >> 8) & 0xff)) >> 2); + final int b = (((p0 & 0xff) + (p1 & 0xff) + (p2 & 0xff) + (p3 & 0xff)) >> 2); + + dstPixels[y * newWidth + x] = (a << 24) | (r << 16) | (g << 8) | b; } + } return downScaled; } @@ -273,41 +293,57 @@ public class Texture { * @return The upscaled bitmap */ public TextureBitmap upscaleBitmap(final TextureBitmap originalBitmap) { - final int newWidth = originalBitmap.width * 2; - final int newHeight = originalBitmap.height * 2; + final int srcW = originalBitmap.width; + final int srcH = originalBitmap.height; + final int newWidth = srcW * 2; + final int newHeight = srcH * 2; + final int srcWMinus1 = srcW - 1; + final int srcHMinus1 = srcH - 1; final TextureBitmap upScaled = new TextureBitmap(newWidth, newHeight, originalBitmap.multiplicationFactor * 2d); - final ColorAccumulator accumulator = new ColorAccumulator(); - - for (int y = 0; y < originalBitmap.height; y++) - for (int x = 0; x < originalBitmap.width; x++) { - accumulator.reset(); - accumulator.accumulate(originalBitmap, x, y); - accumulator.storeResult(upScaled, x * 2, y * 2); - - accumulator.reset(); - accumulator.accumulate(originalBitmap, x, y); - accumulator.accumulate(originalBitmap, x + 1, y); - accumulator.storeResult(upScaled, (x * 2) + 1, y * 2); - - accumulator.reset(); - accumulator.accumulate(originalBitmap, x, y); - accumulator.accumulate(originalBitmap, x, y + 1); - accumulator.storeResult(upScaled, x * 2, (y * 2) + 1); - - accumulator.reset(); - accumulator.accumulate(originalBitmap, x, y); - accumulator.accumulate(originalBitmap, x + 1, y); - accumulator.accumulate(originalBitmap, x, y + 1); - accumulator.accumulate(originalBitmap, x + 1, y + 1); - accumulator.storeResult(upScaled, (x * 2) + 1, (y * 2) + 1); + final int[] src = originalBitmap.pixels; + final int[] dst = upScaled.pixels; + + for (int y = 0; y < srcH; y++) { + final int srcRowOffset = y * srcW; + final int nextRowOffset = Math.min(y + 1, srcHMinus1) * srcW; + final int dstRow0Offset = (y * 2) * newWidth; + final int dstRow1Offset = (y * 2 + 1) * newWidth; + + for (int x = 0; x < srcW; x++) { + final int nx = Math.min(x + 1, srcWMinus1); + + final int p00 = src[srcRowOffset + x]; + final int p10 = src[srcRowOffset + nx]; + final int p01 = src[nextRowOffset + x]; + final int p11 = src[nextRowOffset + nx]; + + dst[dstRow0Offset + x * 2] = p00; + dst[dstRow0Offset + x * 2 + 1] = avg2(p00, p10); + dst[dstRow1Offset + x * 2] = avg2(p00, p01); + dst[dstRow1Offset + x * 2 + 1] = avg4(p00, p10, p01, p11); } + } return upScaled; } + private static int avg2(final int p0, final int p1) { + return (((((p0 >>> 24) + (p1 >>> 24)) >> 1) << 24) + | (((((p0 >> 16) & 0xff) + ((p1 >> 16) & 0xff)) >> 1) << 16) + | (((((p0 >> 8) & 0xff) + ((p1 >> 8) & 0xff)) >> 1) << 8) + | (((p0 & 0xff) + (p1 & 0xff)) >> 1)); + } + + private static int avg4(final int p0, final int p1, final int p2, final int p3) { + return ((((p0 >>> 24) + (p1 >>> 24) + (p2 >>> 24) + (p3 >>> 24)) >> 2) << 24) + | (((((p0 >> 16) & 0xff) + ((p1 >> 16) & 0xff) + ((p2 >> 16) & 0xff) + ((p3 >> 16) & 0xff)) >> 2) << 16) + | (((((p0 >> 8) & 0xff) + ((p1 >> 8) & 0xff) + ((p2 >> 8) & 0xff) + ((p3 >> 8) & 0xff)) >> 2) << 8) + | (((p0 & 0xff) + (p1 & 0xff) + (p2 & 0xff) + (p3 & 0xff)) >> 2); + } + /** * A helper class that accumulates color values for a given area of a bitmap. */ diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java index 7adb04f..8726bc5 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.java @@ -160,7 +160,7 @@ public class TextureBitmap { * @param y2 the bottom y coordinate (exclusive) * @param color the fill color */ - public void drawRectangle(int x1, final int y1, int x2, final int y2, + public void drawRectangle(int x1, int y1, int x2, int y2, final Color color) { if (x1 > x2) { @@ -170,9 +170,9 @@ public class TextureBitmap { } if (y1 > y2) { - final int tmp = x1; - x1 = x2; - x2 = tmp; + final int tmp = y1; + y1 = y2; + y2 = tmp; } for (int y = y1; y < y2; y++) @@ -190,8 +190,7 @@ public class TextureBitmap { */ public void fillColor(final Color color) { final int pixel = (color.a << 24) | (color.r << 16) | (color.g << 8) | color.b; - for (int i = 0; i < pixels.length; i++) - pixels[i] = pixel; + java.util.Arrays.fill(pixels, pixel); } /**