From e353fe396a362ab9c10a7c058e76007939370f66 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Mon, 3 Aug 2015 23:32:07 +0200 Subject: [PATCH] app_server Painter: refactoring, extract bitmap drawing * Extract bitmap drawing from Painter into separate class Painter::BitmapPainter. This will allow to add new optimized drawing modes without making Painter larger. * BitmapPainter itself is further decomposed into separate (method object) structs per drawing mode (currently, those are: generic, no scale, nearest neighbor, bilinear). New optimized implementations can be added by writing additional method objects and calling them from BitmapPainter. * DrawBitmapNoScale and DrawBitmapBilinear are implemented using CRTP. This removes the function pointer in the 'no scale' version, which was previously used to select the row copy type. In the bilinear version it untangles the three variants (default, low filter ratio, SIMD) into separate methods. * While BitmapPainter is a nested class in Painter, the specialized method objects are not. Instead, the AGG-specific data fields from Painter are moved into a new struct PainterAggInterface. This struct is passed to the method objects and allows them to access the Painter's AGG renderer/rasterizer/scanline containers/etc. Alternatives would be to make all the involved structs friends of Painter, or nesting them all, or exposing all of Painter's internals via getter methods -- all of these would be quite messy. The details of the bitmap painting implementations are intentionally hidden from Painter: there is no need for it to know about their internals -- it does not even know their type names. (Nesting or making them friend would expose their type names to Painter.) Furthermore, there is another level of information hiding between BitmapPainter and the DrawBitmap[...] method objects. BitmapPainter itself only needs to decide that it uses e.g. the bilinear version. It has no knowledge that DrawBitmapBilinear is internally made out of several structs implementing specially optimized versions. * Refactoring only, no functional change intended. Performance should be unaffected. --- src/servers/app/drawing/Painter/Jamfile | 4 + src/servers/app/drawing/Painter/Painter.cpp | 1055 +---------------- src/servers/app/drawing/Painter/Painter.h | 73 +- .../app/drawing/Painter/PainterAggInterface.h | 67 ++ .../Painter/bitmap_painter/BitmapPainter.cpp | 314 +++++ .../Painter/bitmap_painter/BitmapPainter.h | 60 + .../bitmap_painter/DrawBitmapBilinear.h | 520 ++++++++ .../bitmap_painter/DrawBitmapGeneric.h | 112 ++ .../DrawBitmapNearestNeighbor.h | 143 +++ .../bitmap_painter/DrawBitmapNoScale.h | 169 +++ .../painter_bilinear_scale.nasm | 0 11 files changed, 1425 insertions(+), 1092 deletions(-) create mode 100644 src/servers/app/drawing/Painter/PainterAggInterface.h create mode 100644 src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp create mode 100644 src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.h create mode 100644 src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapBilinear.h create mode 100644 src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapGeneric.h create mode 100644 src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNearestNeighbor.h create mode 100644 src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNoScale.h rename src/servers/app/drawing/Painter/{ => bitmap_painter}/painter_bilinear_scale.nasm (100%) diff --git a/src/servers/app/drawing/Painter/Jamfile b/src/servers/app/drawing/Painter/Jamfile index 47703c8b28..61bf424b85 100644 --- a/src/servers/app/drawing/Painter/Jamfile +++ b/src/servers/app/drawing/Painter/Jamfile @@ -12,6 +12,7 @@ UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing Painter drawing_modes UseBuildFeatureHeaders freetype ; SEARCH_SOURCE += [ FDirName $(SUBDIR) drawing_modes ] ; +SEARCH_SOURCE += [ FDirName $(SUBDIR) bitmap_painter ] ; local PAINTER_ARCH_SOURCES ; if $(TARGET_ARCH) = x86 { @@ -29,6 +30,9 @@ StaticLibrary libpainter.a : # drawing_modes PixelFormat.cpp + # bitmap_painter + BitmapPainter.cpp + AGGTextRenderer.cpp $(PAINTER_ARCH_SOURCES) diff --git a/src/servers/app/drawing/Painter/Painter.cpp b/src/servers/app/drawing/Painter/Painter.cpp index 1ff042ceba..45cd532cc8 100644 --- a/src/servers/app/drawing/Painter/Painter.cpp +++ b/src/servers/app/drawing/Painter/Painter.cpp @@ -54,6 +54,7 @@ #include #include "AlphaMask.h" +#include "BitmapPainter.h" #include "DrawingMode.h" #include "GlobalSubpixelSettings.h" #include "PatternHandler.h" @@ -86,19 +87,28 @@ using std::nothrow; #define CHECK_CLIPPING if (!fValidClipping) return BRect(0, 0, -1, -1); #define CHECK_CLIPPING_NO_RETURN if (!fValidClipping) return; -// Defines for SIMD support. -#define APPSERVER_SIMD_MMX (1 << 0) -#define APPSERVER_SIMD_SSE (1 << 1) -// Prototypes for assembler routines -extern "C" { - void bilinear_scale_xloop_mmxsse(const uint8* src, void* dst, - void* xWeights, uint32 xmin, uint32 xmax, uint32 wTop, uint32 srcBPR); -} +// Shortcuts for accessing internal data +#define fBuffer fInternal.fBuffer +#define fPixelFormat fInternal.fPixelFormat +#define fBaseRenderer fInternal.fBaseRenderer +#define fUnpackedScanline fInternal.fUnpackedScanline +#define fPackedScanline fInternal.fPackedScanline +#define fRasterizer fInternal.fRasterizer +#define fRenderer fInternal.fRenderer +#define fRendererBin fInternal.fRendererBin +#define fSubpixPackedScanline fInternal.fSubpixPackedScanline +#define fSubpixUnpackedScanline fInternal.fSubpixUnpackedScanline +#define fSubpixRasterizer fInternal.fSubpixRasterizer +#define fSubpixRenderer fInternal.fSubpixRenderer +#define fMaskedUnpackedScanline fInternal.fMaskedUnpackedScanline +#define fPath fInternal.fPath +#define fCurve fInternal.fCurve + static uint32 detect_simd(); -static uint32 sSIMDFlags = detect_simd(); +uint32 gSIMDFlags = detect_simd(); /*! Detect SIMD flags for use in AppServer. Checks all CPUs in the system @@ -170,23 +180,7 @@ detect_simd() Painter::Painter() : - fBuffer(), - fPixelFormat(fBuffer, &fPatternHandler), - fBaseRenderer(fPixelFormat), - fUnpackedScanline(), - fPackedScanline(), - fRasterizer(), - fRenderer(fBaseRenderer), - fRendererBin(fBaseRenderer), - fSubpixPackedScanline(), - fSubpixUnpackedScanline(), - fSubpixRasterizer(), - fSubpixRenderer(fBaseRenderer), - fMaskedUnpackedScanline(NULL), - - fPath(), - fCurve(fPath), - + fInternal(fPatternHandler), fSubpixelPrecise(false), fValidClipping(false), fDrawingText(false), @@ -1431,27 +1425,11 @@ Painter::DrawBitmap(const ServerBitmap* bitmap, BRect bitmapRect, BRect touched = TransformAlignAndClipRect(viewRect); - if (bitmap && bitmap->IsValid() && touched.IsValid()) { - // the native bitmap coordinate system - BRect actualBitmapRect(bitmap->Bounds()); - - TRACE("Painter::DrawBitmap()\n"); - TRACE(" actualBitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", - actualBitmapRect.left, actualBitmapRect.top, - actualBitmapRect.right, actualBitmapRect.bottom); - TRACE(" bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", - bitmapRect.left, bitmapRect.top, bitmapRect.right, - bitmapRect.bottom); - TRACE(" viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n", - viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); - - agg::rendering_buffer srcBuffer; - srcBuffer.attach(bitmap->Bits(), bitmap->Width(), bitmap->Height(), - bitmap->BytesPerRow()); - - _DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, - bitmapRect, viewRect, options); + if (touched.IsValid()) { + BitmapPainter bitmapPainter(this, bitmap, options); + bitmapPainter.Draw(bitmapRect, viewRect); } + return touched; } @@ -1642,61 +1620,6 @@ Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const } -// copy_bitmap_row_cmap8_copy -static inline void -copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color* colorMap) -{ - uint32* d = (uint32*)dst; - const uint8* s = src; - while (numPixels--) { - const rgb_color c = colorMap[*s++]; - *d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); - } -} - - -// copy_bitmap_row_cmap8_over -static inline void -copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color* colorMap) -{ - uint32* d = (uint32*)dst; - const uint8* s = src; - while (numPixels--) { - const rgb_color c = colorMap[*s++]; - if (c.alpha) - *d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); - d++; - } -} - - -// copy_bitmap_row_bgr32_copy -static inline void -copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color* colorMap) -{ - memcpy(dst, src, numPixels * 4); -} - - -// copy_bitmap_row_bgr32_over -static inline void -copy_bitmap_row_bgr32_over(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color* colorMap) -{ - uint32* d = (uint32*)dst; - uint32* s = (uint32*)src; - while (numPixels--) { - if (*s != B_TRANSPARENT_MAGIC_RGBA32) - *(uint32*)d = *(uint32*)s; - d++; - s++; - } -} - - void Painter::_IterateShapeData(const int32& opCount, const uint32* opList, const int32& ptCount, const BPoint* points, @@ -1764,935 +1687,6 @@ Painter::_IterateShapeData(const int32& opCount, const uint32* opList, } -// copy_bitmap_row_bgr32_alpha -static inline void -copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color* colorMap) -{ - uint32* d = (uint32*)dst; - int32 bytes = numPixels * 4; - uint8 buffer[bytes]; - uint8* b = buffer; - while (numPixels--) { - if (src[3] == 255) { - *(uint32*)b = *(uint32*)src; - } else { - *(uint32*)b = *d; - b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8; - b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8; - b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8; - } - d++; - b += 4; - src += 4; - } - memcpy(dst, buffer, bytes); -} - - -// _TransparentMagicToAlpha -template -void -Painter::_TransparentMagicToAlpha(sourcePixel* buffer, uint32 width, - uint32 height, uint32 sourceBytesPerRow, sourcePixel transparentMagic, - BBitmap* output) const -{ - uint8* sourceRow = (uint8*)buffer; - uint8* destRow = (uint8*)output->Bits(); - uint32 destBytesPerRow = output->BytesPerRow(); - - for (uint32 y = 0; y < height; y++) { - sourcePixel* pixel = (sourcePixel*)sourceRow; - uint32* destPixel = (uint32*)destRow; - for (uint32 x = 0; x < width; x++, pixel++, destPixel++) { - if (*pixel == transparentMagic) - *destPixel &= 0x00ffffff; - } - - sourceRow += sourceBytesPerRow; - destRow += destBytesPerRow; - } -} - - -// _DrawBitmap -void -Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format, - BRect actualBitmapRect, BRect bitmapRect, BRect viewRect, - uint32 options) const -{ - if (!fValidClipping - || !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect) - || !viewRect.IsValid()) { - return; - } - - if (!fSubpixelPrecise) { - align_rect_to_pixels(&bitmapRect); - align_rect_to_pixels(&viewRect); - } - - TRACE("Painter::_DrawBitmap()\n"); - TRACE(" bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", - bitmapRect.left, bitmapRect.top, bitmapRect.right, bitmapRect.bottom); - TRACE(" viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n", - viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); - - double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1); - double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1); - - if (xScale == 0.0 || yScale == 0.0) - return; - - // compensate for the lefttop offset the actualBitmapRect might have - // actualBitmapRect has the right size, but put it at B_ORIGIN - // bitmapRect is already in good coordinates - actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top); - - // constrain rect to passed bitmap bounds - // and transfer the changes to the viewRect with the right scale - if (bitmapRect.left < actualBitmapRect.left) { - float diff = actualBitmapRect.left - bitmapRect.left; - viewRect.left += diff * xScale; - bitmapRect.left = actualBitmapRect.left; - } - if (bitmapRect.top < actualBitmapRect.top) { - float diff = actualBitmapRect.top - bitmapRect.top; - viewRect.top += diff * yScale; - bitmapRect.top = actualBitmapRect.top; - } - if (bitmapRect.right > actualBitmapRect.right) { - float diff = bitmapRect.right - actualBitmapRect.right; - viewRect.right -= diff * xScale; - bitmapRect.right = actualBitmapRect.right; - } - if (bitmapRect.bottom > actualBitmapRect.bottom) { - float diff = bitmapRect.bottom - actualBitmapRect.bottom; - viewRect.bottom -= diff * yScale; - bitmapRect.bottom = actualBitmapRect.bottom; - } - - double xOffset = viewRect.left - bitmapRect.left; - double yOffset = viewRect.top - bitmapRect.top; - - // optimized code path for B_CMAP8 and no scale - if (xScale == 1.0 && yScale == 1.0 && fIdentityTransform - && fMaskedUnpackedScanline == NULL) { - if (format == B_CMAP8) { - if (fDrawingMode == B_OP_COPY) { - _DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1, - srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); - return; - } - if (fDrawingMode == B_OP_OVER) { - _DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1, - srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); - return; - } - } else if (format == B_RGB32) { - if (fDrawingMode == B_OP_OVER) { - _DrawBitmapNoScale32(copy_bitmap_row_bgr32_over, 4, - srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); - return; - } - } - } - - BBitmap* temp = NULL; - ObjectDeleter tempDeleter; - - if ((format != B_RGBA32 && format != B_RGB32) - || (format == B_RGB32 && fDrawingMode != B_OP_COPY -#if 1 -// Enabling this would make the behavior compatible to BeOS, which -// treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in -// all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled. -// B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually -// use this color, unless the alpha channel contains 255 for all other -// pixels, which is inconsistent. - && fDrawingMode != B_OP_ALPHA -#endif - )) { - temp = new (nothrow) BBitmap(actualBitmapRect, B_BITMAP_NO_SERVER_LINK, - B_RGBA32); - if (temp == NULL) { - fprintf(stderr, "Painter::_DrawBitmap() - " - "out of memory for creating temporary conversion bitmap\n"); - return; - } - - tempDeleter.SetTo(temp); - - status_t err = temp->ImportBits(srcBuffer.buf(), - srcBuffer.height() * srcBuffer.stride(), - srcBuffer.stride(), 0, format); - if (err < B_OK) { - fprintf(stderr, "Painter::_DrawBitmap() - " - "colorspace conversion failed: %s\n", strerror(err)); - return; - } - - // the original bitmap might have had some of the - // transaparent magic colors set that we now need to - // make transparent in our RGBA32 bitmap again. - switch (format) { - case B_RGB32: - _TransparentMagicToAlpha((uint32 *)srcBuffer.buf(), - srcBuffer.width(), srcBuffer.height(), - srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA32, - temp); - break; - - // TODO: not sure if this applies to B_RGBA15 too. It - // should not because B_RGBA15 actually has an alpha - // channel itself and it should have been preserved - // when importing the bitmap. Maybe it applies to - // B_RGB16 though? - case B_RGB15: - _TransparentMagicToAlpha((uint16 *)srcBuffer.buf(), - srcBuffer.width(), srcBuffer.height(), - srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA15, - temp); - break; - - default: - break; - } - - srcBuffer.attach((uint8*)temp->Bits(), - (uint32)actualBitmapRect.IntegerWidth() + 1, - (uint32)actualBitmapRect.IntegerHeight() + 1, - temp->BytesPerRow()); - } - - // maybe we can use an optimized version if there is no scale - if (xScale == 1.0 && yScale == 1.0 && fIdentityTransform - && fMaskedUnpackedScanline == NULL) { - if (fDrawingMode == B_OP_COPY) { - _DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4, srcBuffer, - (int32)xOffset, (int32)yOffset, viewRect); - return; - } - if (fDrawingMode == B_OP_OVER || (fDrawingMode == B_OP_ALPHA - && fAlphaSrcMode == B_PIXEL_ALPHA - && fAlphaFncMode == B_ALPHA_OVERLAY)) { - _DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4, srcBuffer, - (int32)xOffset, (int32)yOffset, viewRect); - return; - } - } - - if (fDrawingMode == B_OP_COPY && fIdentityTransform - && fMaskedUnpackedScanline == NULL) { - if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { - _DrawBitmapBilinearCopy32(srcBuffer, xOffset, yOffset, xScale, - yScale, viewRect); - } else { - _DrawBitmapNearestNeighborCopy32(srcBuffer, xOffset, yOffset, - xScale, yScale, viewRect); - } - return; - } - - // for all other cases (non-optimized drawing mode or scaled drawing) - _DrawBitmapGeneric32(srcBuffer, xOffset, yOffset, xScale, yScale, viewRect, - options); -} - - -#define DEBUG_DRAW_BITMAP 0 - - -// _DrawBitmapNoScale32 -template -void -Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel, - agg::rendering_buffer& srcBuffer, int32 xOffset, int32 yOffset, - BRect viewRect) const -{ - // NOTE: this would crash if viewRect was large enough to read outside the - // bitmap, so make sure this is not the case before calling this function! - uint8* dst = fBuffer.row_ptr(0); - uint32 dstBPR = fBuffer.stride(); - - const uint8* src = srcBuffer.row_ptr(0); - uint32 srcBPR = srcBuffer.stride(); - - int32 left = (int32)viewRect.left; - int32 top = (int32)viewRect.top; - int32 right = (int32)viewRect.right; - int32 bottom = (int32)viewRect.bottom; - -#if DEBUG_DRAW_BITMAP -if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() || - right - xOffset >= (int32)srcBuffer.width() || - top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() || - bottom - yOffset >= (int32)srcBuffer.height()) { - - char message[256]; - sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) " - "(%d, %d) (%ld, %ld)", - left - xOffset, top - yOffset, right - xOffset, bottom - yOffset, - srcBuffer.width(), srcBuffer.height(), xOffset, yOffset); - debugger(message); -} -#endif - - const rgb_color* colorMap = SystemPalette(); - - // copy rects, iterate over clipping boxes - fBaseRenderer.first_clip_box(); - do { - int32 x1 = max_c(fBaseRenderer.xmin(), left); - int32 x2 = min_c(fBaseRenderer.xmax(), right); - if (x1 <= x2) { - int32 y1 = max_c(fBaseRenderer.ymin(), top); - int32 y2 = min_c(fBaseRenderer.ymax(), bottom); - if (y1 <= y2) { - uint8* dstHandle = dst + y1 * dstBPR + x1 * 4; - const uint8* srcHandle = src + (y1 - yOffset) * srcBPR - + (x1 - xOffset) * bytesPerSourcePixel; - - for (; y1 <= y2; y1++) { - copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1, colorMap); - - dstHandle += dstBPR; - srcHandle += srcBPR; - } - } - } - } while (fBaseRenderer.next_clip_box()); -} - - -// _DrawBitmapNearestNeighborCopy32 -void -Painter::_DrawBitmapNearestNeighborCopy32(agg::rendering_buffer& srcBuffer, - double xOffset, double yOffset, double xScale, double yScale, - BRect viewRect) const -{ - //bigtime_t now = system_time(); - uint32 dstWidth = viewRect.IntegerWidth() + 1; - uint32 dstHeight = viewRect.IntegerHeight() + 1; - uint32 srcWidth = srcBuffer.width(); - uint32 srcHeight = srcBuffer.height(); - - // Do not calculate more filter weights than necessary and also - // keep the stack based allocations reasonably sized - if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth) - dstWidth = fClippingRegion->Frame().IntegerWidth() + 1; - if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight) - dstHeight = fClippingRegion->Frame().IntegerHeight() + 1; - - // When calculating less filter weights than specified by viewRect, - // we need to compensate the offset. - uint32 filterWeightXIndexOffset = 0; - uint32 filterWeightYIndexOffset = 0; - if (fClippingRegion->Frame().left > viewRect.left) { - filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left - - viewRect.left); - } - if (fClippingRegion->Frame().top > viewRect.top) { - filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top - - viewRect.top); - } - - // should not pose a problem with stack overflows - // (needs around 6Kb for 1920x1200) - uint16 xIndices[dstWidth]; - uint16 yIndices[dstHeight]; - - // Extract the cropping information for the source bitmap, - // If only a part of the source bitmap is to be drawn with scale, - // the offset will be different from the viewRect left top corner. - int32 xBitmapShift = (int32)(viewRect.left - xOffset); - int32 yBitmapShift = (int32)(viewRect.top - yOffset); - - for (uint32 i = 0; i < dstWidth; i++) { - // index into source - uint16 index = (uint16)((i + filterWeightXIndexOffset) * srcWidth - / (srcWidth * xScale)); - // round down to get the left pixel - xIndices[i] = index; - // handle cropped source bitmap - xIndices[i] += xBitmapShift; - // precompute index for 32 bit pixels - xIndices[i] *= 4; - } - - for (uint32 i = 0; i < dstHeight; i++) { - // index into source - uint16 index = (uint16)((i + filterWeightYIndexOffset) * srcHeight - / (srcHeight * yScale)); - // round down to get the top pixel - yIndices[i] = index; - // handle cropped source bitmap - yIndices[i] += yBitmapShift; - } -//printf("X: %d ... %d, %d (%ld or %f)\n", -// xIndices[0], xIndices[dstWidth - 2], xIndices[dstWidth - 1], dstWidth, -// srcWidth * xScale); -//printf("Y: %d ... %d, %d (%ld or %f)\n", -// yIndices[0], yIndices[dstHeight - 2], yIndices[dstHeight - 1], dstHeight, -// srcHeight * yScale); - - const int32 left = (int32)viewRect.left; - const int32 top = (int32)viewRect.top; - const int32 right = (int32)viewRect.right; - const int32 bottom = (int32)viewRect.bottom; - - const uint32 dstBPR = fBuffer.stride(); - - // iterate over clipping boxes - fBaseRenderer.first_clip_box(); - do { - const int32 x1 = max_c(fBaseRenderer.xmin(), left); - const int32 x2 = min_c(fBaseRenderer.xmax(), right); - if (x1 > x2) - continue; - - int32 y1 = max_c(fBaseRenderer.ymin(), top); - int32 y2 = min_c(fBaseRenderer.ymax(), bottom); - if (y1 > y2) - continue; - - // buffer offset into destination - uint8* dst = fBuffer.row_ptr(y1) + x1 * 4; - - // x and y are needed as indeces into the wheight arrays, so the - // offset into the target buffer needs to be compensated - const int32 xIndexL = x1 - left - filterWeightXIndexOffset; - const int32 xIndexR = x2 - left - filterWeightXIndexOffset; - y1 -= top + filterWeightYIndexOffset; - y2 -= top + filterWeightYIndexOffset; - -//printf("x: %ld - %ld\n", xIndexL, xIndexR); -//printf("y: %ld - %ld\n", y1, y2); - - for (; y1 <= y2; y1++) { - // buffer offset into source (top row) - register const uint8* src = srcBuffer.row_ptr(yIndices[y1]); - // buffer handle for destination to be incremented per pixel - register uint32* d = (uint32*)dst; - - for (int32 x = xIndexL; x <= xIndexR; x++) { - *d = *(uint32*)(src + xIndices[x]); - d++; - } - dst += dstBPR; - } - } while (fBaseRenderer.next_clip_box()); - -//printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now); -} - - -// _DrawBitmapBilinearCopy32 -void -Painter::_DrawBitmapBilinearCopy32(agg::rendering_buffer& srcBuffer, - double xOffset, double yOffset, double xScale, double yScale, - BRect viewRect) const -{ - //bigtime_t now = system_time(); - uint32 dstWidth = viewRect.IntegerWidth() + 1; - uint32 dstHeight = viewRect.IntegerHeight() + 1; - uint32 srcWidth = srcBuffer.width(); - uint32 srcHeight = srcBuffer.height(); - - // Do not calculate more filter weights than necessary and also - // keep the stack based allocations reasonably sized - if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth) - dstWidth = fClippingRegion->Frame().IntegerWidth() + 1; - if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight) - dstHeight = fClippingRegion->Frame().IntegerHeight() + 1; - - // When calculating less filter weights than specified by viewRect, - // we need to compensate the offset. - uint32 filterWeightXIndexOffset = 0; - uint32 filterWeightYIndexOffset = 0; - if (fClippingRegion->Frame().left > viewRect.left) { - filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left - - viewRect.left); - } - if (fClippingRegion->Frame().top > viewRect.top) { - filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top - - viewRect.top); - } - - struct FilterInfo { - uint16 index; // index into source bitmap row/column - uint16 weight; // weight of the pixel at index [0..255] - }; - -//#define FILTER_INFOS_ON_HEAP -#ifdef FILTER_INFOS_ON_HEAP - FilterInfo* xWeights = new (nothrow) FilterInfo[dstWidth]; - FilterInfo* yWeights = new (nothrow) FilterInfo[dstHeight]; - if (xWeights == NULL || yWeights == NULL) { - delete[] xWeights; - delete[] yWeights; - return; - } -#else - // stack based saves about 200µs on 1.85 GHz Core 2 Duo - // should not pose a problem with stack overflows - // (needs around 12Kb for 1920x1200) - FilterInfo xWeights[dstWidth]; - FilterInfo yWeights[dstHeight]; -#endif - - // Extract the cropping information for the source bitmap, - // If only a part of the source bitmap is to be drawn with scale, - // the offset will be different from the viewRect left top corner. - int32 xBitmapShift = (int32)(viewRect.left - xOffset); - int32 yBitmapShift = (int32)(viewRect.top - yOffset); - - for (uint32 i = 0; i < dstWidth; i++) { - // fractional index into source - // NOTE: It is very important to calculate the fractional index - // into the source pixel grid like this to prevent out of bounds - // access! It will result in the rightmost pixel of the destination - // to access the rightmost pixel of the source with a weighting - // of 255. This in turn will trigger an optimization in the loop - // that also prevents out of bounds access. - float index = (i + filterWeightXIndexOffset) * (srcWidth - 1) - / (srcWidth * xScale - 1); - // round down to get the left pixel - xWeights[i].index = (uint16)index; - xWeights[i].weight = 255 - (uint16)((index - xWeights[i].index) * 255); - // handle cropped source bitmap - xWeights[i].index += xBitmapShift; - // precompute index for 32 bit pixels - xWeights[i].index *= 4; - } - - for (uint32 i = 0; i < dstHeight; i++) { - // fractional index into source - // NOTE: It is very important to calculate the fractional index - // into the source pixel grid like this to prevent out of bounds - // access! It will result in the bottommost pixel of the destination - // to access the bottommost pixel of the source with a weighting - // of 255. This in turn will trigger an optimization in the loop - // that also prevents out of bounds access. - float index = (i + filterWeightYIndexOffset) * (srcHeight - 1) - / (srcHeight * yScale - 1); - // round down to get the top pixel - yWeights[i].index = (uint16)index; - yWeights[i].weight = 255 - (uint16)((index - yWeights[i].index) * 255); - // handle cropped source bitmap - yWeights[i].index += yBitmapShift; - } -//printf("X: %d/%d ... %d/%d, %d/%d (%ld)\n", -// xWeights[0].index, xWeights[0].weight, -// xWeights[dstWidth - 2].index, xWeights[dstWidth - 2].weight, -// xWeights[dstWidth - 1].index, xWeights[dstWidth - 1].weight, -// dstWidth); -//printf("Y: %d/%d ... %d/%d, %d/%d (%ld)\n", -// yWeights[0].index, yWeights[0].weight, -// yWeights[dstHeight - 2].index, yWeights[dstHeight - 2].weight, -// yWeights[dstHeight - 1].index, yWeights[dstHeight - 1].weight, -// dstHeight); - - const int32 left = (int32)viewRect.left; - const int32 top = (int32)viewRect.top; - const int32 right = (int32)viewRect.right; - const int32 bottom = (int32)viewRect.bottom; - - const uint32 dstBPR = fBuffer.stride(); - const uint32 srcBPR = srcBuffer.stride(); - - // Figure out which version of the code we want to use... - enum { - kOptimizeForLowFilterRatio = 0, - kUseDefaultVersion, - kUseSIMDVersion - }; - - int codeSelect = kUseDefaultVersion; - - uint32 neededSIMDFlags = APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE; - if ((sSIMDFlags & neededSIMDFlags) == neededSIMDFlags) - codeSelect = kUseSIMDVersion; - else { - if (xScale == yScale && (xScale == 1.5 || xScale == 2.0 - || xScale == 2.5 || xScale == 3.0)) { - codeSelect = kOptimizeForLowFilterRatio; - } - } - - // iterate over clipping boxes - fBaseRenderer.first_clip_box(); - do { - const int32 x1 = max_c(fBaseRenderer.xmin(), left); - const int32 x2 = min_c(fBaseRenderer.xmax(), right); - if (x1 > x2) - continue; - - int32 y1 = max_c(fBaseRenderer.ymin(), top); - int32 y2 = min_c(fBaseRenderer.ymax(), bottom); - if (y1 > y2) - continue; - - // buffer offset into destination - uint8* dst = fBuffer.row_ptr(y1) + x1 * 4; - - // x and y are needed as indeces into the wheight arrays, so the - // offset into the target buffer needs to be compensated - const int32 xIndexL = x1 - left - filterWeightXIndexOffset; - const int32 xIndexR = x2 - left - filterWeightXIndexOffset; - y1 -= top + filterWeightYIndexOffset; - y2 -= top + filterWeightYIndexOffset; - -//printf("x: %ld - %ld\n", xIndexL, xIndexR); -//printf("y: %ld - %ld\n", y1, y2); - - switch (codeSelect) { - case kOptimizeForLowFilterRatio: - { - // In this mode, we anticipate to hit many destination pixels - // that map directly to a source pixel, we have more branches - // in the inner loop but save time because of the special - // cases. If there are too few direct hit pixels, the branches - // only waste time. - for (; y1 <= y2; y1++) { - // cache the weight of the top and bottom row - const uint16 wTop = yWeights[y1].weight; - const uint16 wBottom = 255 - yWeights[y1].weight; - - // buffer offset into source (top row) - register const uint8* src - = srcBuffer.row_ptr(yWeights[y1].index); - // buffer handle for destination to be incremented per - // pixel - register uint8* d = dst; - - if (wTop == 255) { - for (int32 x = xIndexL; x <= xIndexR; x++) { - const uint8* s = src + xWeights[x].index; - // This case is important to prevent out - // of bounds access at bottom edge of the source - // bitmap. If the scale is low and integer, it will - // also help the speed. - if (xWeights[x].weight == 255) { - // As above, but to prevent out of bounds - // on the right edge. - *(uint32*)d = *(uint32*)s; - } else { - // Only the left and right pixels are - // interpolated, since the top row has 100% - // weight. - const uint16 wLeft = xWeights[x].weight; - const uint16 wRight = 255 - wLeft; - d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; - d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; - d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; - } - d += 4; - } - } else { - for (int32 x = xIndexL; x <= xIndexR; x++) { - const uint8* s = src + xWeights[x].index; - if (xWeights[x].weight == 255) { - // Prevent out of bounds access on the right - // edge or simply speed up. - const uint8* sBottom = s + srcBPR; - d[0] = (s[0] * wTop + sBottom[0] * wBottom) - >> 8; - d[1] = (s[1] * wTop + sBottom[1] * wBottom) - >> 8; - d[2] = (s[2] * wTop + sBottom[2] * wBottom) - >> 8; - } else { - // calculate the weighted sum of all four - // interpolated pixels - const uint16 wLeft = xWeights[x].weight; - const uint16 wRight = 255 - wLeft; - // left and right of top row - uint32 t0 = (s[0] * wLeft + s[4] * wRight) - * wTop; - uint32 t1 = (s[1] * wLeft + s[5] * wRight) - * wTop; - uint32 t2 = (s[2] * wLeft + s[6] * wRight) - * wTop; - - // left and right of bottom row - s += srcBPR; - t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; - t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; - t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; - - d[0] = t0 >> 16; - d[1] = t1 >> 16; - d[2] = t2 >> 16; - } - d += 4; - } - } - dst += dstBPR; - } - break; - } - - case kUseDefaultVersion: - { - // In this mode we anticipate many pixels wich need filtering, - // there are no special cases for direct hit pixels except for - // the last column/row and the right/bottom corner pixel. - - // The last column/row handling does not need to be performed - // for all clipping rects! - int32 yMax = y2; - if (yWeights[yMax].weight == 255) - yMax--; - int32 xIndexMax = xIndexR; - if (xWeights[xIndexMax].weight == 255) - xIndexMax--; - - for (; y1 <= yMax; y1++) { - // cache the weight of the top and bottom row - const uint16 wTop = yWeights[y1].weight; - const uint16 wBottom = 255 - yWeights[y1].weight; - - // buffer offset into source (top row) - register const uint8* src - = srcBuffer.row_ptr(yWeights[y1].index); - // buffer handle for destination to be incremented per - // pixel - register uint8* d = dst; - - for (int32 x = xIndexL; x <= xIndexMax; x++) { - const uint8* s = src + xWeights[x].index; - // calculate the weighted sum of all four - // interpolated pixels - const uint16 wLeft = xWeights[x].weight; - const uint16 wRight = 255 - wLeft; - // left and right of top row - uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop; - uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop; - uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop; - - // left and right of bottom row - s += srcBPR; - t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; - t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; - t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; - d[0] = t0 >> 16; - d[1] = t1 >> 16; - d[2] = t2 >> 16; - d += 4; - } - // last column of pixels if necessary - if (xIndexMax < xIndexR) { - const uint8* s = src + xWeights[xIndexR].index; - const uint8* sBottom = s + srcBPR; - d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; - d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; - d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; - } - - dst += dstBPR; - } - - // last row of pixels if necessary - // buffer offset into source (bottom row) - register const uint8* src - = srcBuffer.row_ptr(yWeights[y2].index); - // buffer handle for destination to be incremented per pixel - register uint8* d = dst; - - if (yMax < y2) { - for (int32 x = xIndexL; x <= xIndexMax; x++) { - const uint8* s = src + xWeights[x].index; - const uint16 wLeft = xWeights[x].weight; - const uint16 wRight = 255 - wLeft; - d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; - d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; - d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; - d += 4; - } - } - - // pixel in bottom right corner if necessary - if (yMax < y2 && xIndexMax < xIndexR) { - const uint8* s = src + xWeights[xIndexR].index; - *(uint32*)d = *(uint32*)s; - } - break; - } - -#ifdef __INTEL__ - case kUseSIMDVersion: - { - // Basically the same as the "standard" mode, but we use SIMD - // routines for the processing of the single display lines. - - // The last column/row handling does not need to be performed - // for all clipping rects! - int32 yMax = y2; - if (yWeights[yMax].weight == 255) - yMax--; - int32 xIndexMax = xIndexR; - if (xWeights[xIndexMax].weight == 255) - xIndexMax--; - - for (; y1 <= yMax; y1++) { - // cache the weight of the top and bottom row - const uint16 wTop = yWeights[y1].weight; - const uint16 wBottom = 255 - yWeights[y1].weight; - - // buffer offset into source (top row) - const uint8* src = srcBuffer.row_ptr(yWeights[y1].index); - // buffer handle for destination to be incremented per - // pixel - uint8* d = dst; - bilinear_scale_xloop_mmxsse(src, dst, xWeights, xIndexL, - xIndexMax, wTop, srcBPR); - // increase pointer by processed pixels - d += (xIndexMax - xIndexL + 1) * 4; - - // last column of pixels if necessary - if (xIndexMax < xIndexR) { - const uint8* s = src + xWeights[xIndexR].index; - const uint8* sBottom = s + srcBPR; - d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; - d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; - d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; - } - - dst += dstBPR; - } - - // last row of pixels if necessary - // buffer offset into source (bottom row) - register const uint8* src - = srcBuffer.row_ptr(yWeights[y2].index); - // buffer handle for destination to be incremented per pixel - register uint8* d = dst; - - if (yMax < y2) { - for (int32 x = xIndexL; x <= xIndexMax; x++) { - const uint8* s = src + xWeights[x].index; - const uint16 wLeft = xWeights[x].weight; - const uint16 wRight = 255 - wLeft; - d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; - d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; - d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; - d += 4; - } - } - - // pixel in bottom right corner if necessary - if (yMax < y2 && xIndexMax < xIndexR) { - const uint8* s = src + xWeights[xIndexR].index; - *(uint32*)d = *(uint32*)s; - } - break; - } -#endif // __INTEL__ - } - } while (fBaseRenderer.next_clip_box()); - -#ifdef FILTER_INFOS_ON_HEAP - delete[] xWeights; - delete[] yWeights; -#endif -//printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now); -} - - -// _DrawBitmapGeneric32 -void -Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer, - double xOffset, double yOffset, double xScale, double yScale, - BRect viewRect, uint32 options) const -{ - TRACE("Painter::_DrawBitmapGeneric32()\n"); - TRACE(" offset: %.1f, %.1f\n", xOffset, yOffset); - TRACE(" scale: %.3f, %.3f\n", xScale, yScale); - TRACE(" viewRect: (%.1f, %.1f) - (%.1f, %.1f)\n", - viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); - // AGG pipeline - - // pixel format attached to bitmap - typedef agg::pixfmt_bgra32 pixfmt_image; - pixfmt_image pixf_img(srcBuffer); - - agg::trans_affine srcMatrix; - // NOTE: R5 seems to ignore this offset when drawing bitmaps - // srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, - // -actualBitmapRect.top); - srcMatrix *= fTransform; - - agg::trans_affine imgMatrix; - imgMatrix *= agg::trans_affine_translation(xOffset - viewRect.left, - yOffset - viewRect.top); - imgMatrix *= agg::trans_affine_scaling(xScale, yScale); - imgMatrix *= agg::trans_affine_translation(viewRect.left, viewRect.top); - imgMatrix *= fTransform; - imgMatrix.invert(); - - // image interpolator - typedef agg::span_interpolator_linear<> interpolator_type; - interpolator_type interpolator(imgMatrix); - - // scanline allocator - agg::span_allocator spanAllocator; - - // image accessor attached to pixel format of bitmap - typedef agg::image_accessor_clone source_type; - source_type source(pixf_img); - - // clip to the current clipping region's frame - if (fIdentityTransform) - viewRect = viewRect & fClippingRegion->Frame(); - // convert to pixel coords (versus pixel indices) - viewRect.right++; - viewRect.bottom++; - - // path enclosing the bitmap - fPath.remove_all(); - fPath.move_to(viewRect.left, viewRect.top); - fPath.line_to(viewRect.right, viewRect.top); - fPath.line_to(viewRect.right, viewRect.bottom); - fPath.line_to(viewRect.left, viewRect.bottom); - fPath.close_polygon(); - - agg::conv_transform transformedPath(fPath, srcMatrix); - fRasterizer.reset(); - fRasterizer.add_path(transformedPath); - - if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { - // image filter (bilinear) - typedef agg::span_image_filter_rgba_bilinear< - source_type, interpolator_type> span_gen_type; - span_gen_type spanGenerator(source, interpolator); - - // render the path with the bitmap as scanline fill - if (fMaskedUnpackedScanline != NULL) { - agg::render_scanlines_aa(fRasterizer, *fMaskedUnpackedScanline, - fBaseRenderer, spanAllocator, spanGenerator); - } else { - agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, - fBaseRenderer, spanAllocator, spanGenerator); - } - } else { - // image filter (nearest neighbor) - typedef agg::span_image_filter_rgba_nn< - source_type, interpolator_type> span_gen_type; - span_gen_type spanGenerator(source, interpolator); - - // render the path with the bitmap as scanline fill - if (fMaskedUnpackedScanline != NULL) { - agg::render_scanlines_aa(fRasterizer, *fMaskedUnpackedScanline, - fBaseRenderer, spanAllocator, spanGenerator); - } else { - agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, - fBaseRenderer, spanAllocator, spanGenerator); - } - } -} - - // _InvertRect32 void Painter::_InvertRect32(BRect r) const @@ -3132,4 +2126,3 @@ Painter::_RasterizePath(VertexSource& path, const BGradient& gradient, gradientRenderer); } } - diff --git a/src/servers/app/drawing/Painter/Painter.h b/src/servers/app/drawing/Painter/Painter.h index 4402113438..fc22b08193 100644 --- a/src/servers/app/drawing/Painter/Painter.h +++ b/src/servers/app/drawing/Painter/Painter.h @@ -14,6 +14,7 @@ #include "AGGTextRenderer.h" #include "FontManager.h" +#include "PainterAggInterface.h" #include "PatternHandler.h" #include "ServerFont.h" #include "Transformable.h" @@ -21,7 +22,6 @@ #include "defines.h" #include -#include #include #include @@ -43,6 +43,11 @@ class ServerBitmap; class ServerFont; +// Defines for SIMD support. +#define APPSERVER_SIMD_MMX (1 << 0) +#define APPSERVER_SIMD_SSE (1 << 1) + + class Painter { public: Painter(); @@ -68,6 +73,8 @@ public: inline bool IsIdentityTransform() const { return fIdentityTransform; } + const Transformable& Transform() const + { return fTransform; } void SetHighColor(const rgb_color& color); inline rgb_color HighColor() const @@ -273,41 +280,6 @@ private: const BPoint& viewToScreenOffset, float viewScale) const; - template - void _TransparentMagicToAlpha(sourcePixel *buffer, - uint32 width, uint32 height, - uint32 sourceBytesPerRow, - sourcePixel transparentMagic, - BBitmap *output) const; - - void _DrawBitmap(agg::rendering_buffer& srcBuffer, - color_space format, - BRect actualBitmapRect, - BRect bitmapRect, BRect viewRect, - uint32 bitmapFlags) const; - template - void _DrawBitmapNoScale32( F copyRowFunction, - uint32 bytesPerSourcePixel, - agg::rendering_buffer& srcBuffer, - int32 xOffset, int32 yOffset, - BRect viewRect) const; - void _DrawBitmapNearestNeighborCopy32( - agg::rendering_buffer& srcBuffer, - double xOffset, double yOffset, - double xScale, double yScale, - BRect viewRect) const; - void _DrawBitmapBilinearCopy32( - agg::rendering_buffer& srcBuffer, - double xOffset, double yOffset, - double xScale, double yScale, - BRect viewRect) const; - void _DrawBitmapGeneric32( - agg::rendering_buffer& srcBuffer, - double xOffset, double yOffset, - double xScale, double yScale, - BRect viewRect, - uint32 bitmapFlags) const; - void _InvertRect32(BRect r) const; void _BlendRect32(const BRect& r, const rgb_color& c) const; @@ -356,33 +328,12 @@ private: int gradientStop = 100) const; private: - mutable agg::rendering_buffer fBuffer; + class BitmapPainter; - // AGG rendering and rasterization classes - pixfmt fPixelFormat; - mutable renderer_base fBaseRenderer; + friend class BitmapPainter; // needed only for gcc2 - // Regular drawing mode: pixel-aligned, no alpha masking - mutable scanline_unpacked_type fUnpackedScanline; - mutable scanline_packed_type fPackedScanline; - mutable rasterizer_type fRasterizer; - mutable renderer_type fRenderer; - - // Fast mode: no antialiasing needed (horizontal/vertical lines, ...) - mutable renderer_bin_type fRendererBin; - - // Subpixel mode - mutable scanline_packed_subpix_type fSubpixPackedScanline; - mutable scanline_unpacked_subpix_type fSubpixUnpackedScanline; - mutable rasterizer_subpix_type fSubpixRasterizer; - mutable renderer_subpix_type fSubpixRenderer; - - // Alpha-Masked mode: for ClipToPicture - // (this uses the standard rasterizer and renderer) - mutable scanline_unpacked_masked_type* fMaskedUnpackedScanline; - - mutable agg::path_storage fPath; - mutable agg::conv_curve fCurve; +private: + mutable PainterAggInterface fInternal; // for internal coordinate rounding/transformation bool fSubpixelPrecise : 1; diff --git a/src/servers/app/drawing/Painter/PainterAggInterface.h b/src/servers/app/drawing/Painter/PainterAggInterface.h new file mode 100644 index 0000000000..57e9c6fa7e --- /dev/null +++ b/src/servers/app/drawing/Painter/PainterAggInterface.h @@ -0,0 +1,67 @@ +/* + * Copyright 2005-2007, Stephan Aßmus . + * Copyright 2008, Andrej Spielmann . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef PAINTER_DATA_H +#define PAINTER_DATA_H + + +#include "defines.h" + +#include + + +struct PainterAggInterface { + PainterAggInterface(PatternHandler& patternHandler) + : + fBuffer(), + fPixelFormat(fBuffer, &patternHandler), + fBaseRenderer(fPixelFormat), + fUnpackedScanline(), + fPackedScanline(), + fRasterizer(), + fRenderer(fBaseRenderer), + fRendererBin(fBaseRenderer), + fSubpixPackedScanline(), + fSubpixUnpackedScanline(), + fSubpixRasterizer(), + fSubpixRenderer(fBaseRenderer), + fMaskedUnpackedScanline(NULL), + fPath(), + fCurve(fPath) + { + } + + agg::rendering_buffer fBuffer; + + // AGG rendering and rasterization classes + pixfmt fPixelFormat; + renderer_base fBaseRenderer; + + // Regular drawing mode: pixel-aligned, no alpha masking + scanline_unpacked_type fUnpackedScanline; + scanline_packed_type fPackedScanline; + rasterizer_type fRasterizer; + renderer_type fRenderer; + + // Fast mode: no antialiasing needed (horizontal/vertical lines, ...) + renderer_bin_type fRendererBin; + + // Subpixel mode + scanline_packed_subpix_type fSubpixPackedScanline; + scanline_unpacked_subpix_type fSubpixUnpackedScanline; + rasterizer_subpix_type fSubpixRasterizer; + renderer_subpix_type fSubpixRenderer; + + // Alpha-Masked mode: for ClipToPicture + // (this uses the standard rasterizer and renderer) + scanline_unpacked_masked_type* fMaskedUnpackedScanline; + + agg::path_storage fPath; + agg::conv_curve fCurve; +}; + + +#endif // PAINTER_DATA_H diff --git a/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp b/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp new file mode 100644 index 0000000000..a56635630d --- /dev/null +++ b/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp @@ -0,0 +1,314 @@ +/* + * Copyright 2009, Christian Packmann. + * Copyright 2008, Andrej Spielmann . + * Copyright 2005-2014, Stephan Aßmus . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#include "BitmapPainter.h" + +#include + +#include +#include +#include + +#include "DrawBitmapBilinear.h" +#include "DrawBitmapGeneric.h" +#include "DrawBitmapNearestNeighbor.h" +#include "DrawBitmapNoScale.h" +#include "drawing_support.h" +#include "ServerBitmap.h" +#include "SystemPalette.h" + + +// #define TRACE_BITMAP_PAINTER +#ifdef TRACE_BITMAP_PAINTER +# define TRACE(x...) printf(x) +#else +# define TRACE(x...) +#endif + + +Painter::BitmapPainter::BitmapPainter(const Painter* painter, + const ServerBitmap* bitmap, uint32 options) + : + fPainter(painter), + fStatus(B_NO_INIT), + fOptions(options) +{ + if (bitmap == NULL || !bitmap->IsValid()) + return; + + fBitmapBounds = bitmap->Bounds(); + fBitmapBounds.OffsetBy(-fBitmapBounds.left, -fBitmapBounds.top); + // Compensate for the lefttop offset the bitmap bounds might have + // It has the right size, but put it at B_ORIGIN + + fColorSpace = bitmap->ColorSpace(); + + fBitmap.attach(bitmap->Bits(), bitmap->Width(), bitmap->Height(), + bitmap->BytesPerRow()); + + fStatus = B_OK; +} + + +void +Painter::BitmapPainter::Draw(const BRect& sourceRect, + const BRect& destinationRect) +{ + using namespace BitmapPainterPrivate; + + if (fStatus != B_OK) + return; + + TRACE("BitmapPainter::Draw()\n"); + TRACE(" bitmapBounds = (%.1f, %.1f) - (%.1f, %.1f)\n", + bitmapBounds.left, bitmapBounds.top, + bitmapBounds.right, bitmapBounds.bottom); + TRACE(" sourceRect = (%.1f, %.1f) - (%.1f, %.1f)\n", + sourceRect.left, sourceRect.top, + sourceRect.right, sourceRect.bottom); + TRACE(" destinationRect = (%.1f, %.1f) - (%.1f, %.1f)\n", + destinationRect.left, destinationRect.top, + destinationRect.right, destinationRect.bottom); + + bool success = _DetermineTransform(sourceRect, destinationRect); + if (!success) + return; + + // optimized version for no scale in CMAP8 or RGB32 OP_OVER + if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) { + if (fColorSpace == B_CMAP8) { + if (fPainter->fDrawingMode == B_OP_COPY) { + DrawBitmapNoScale::Draw(fPainter->fInternal, + fBitmap, 1, fOffset, fDestinationRect); + return; + } + if (fPainter->fDrawingMode == B_OP_OVER) { + DrawBitmapNoScale::Draw(fPainter->fInternal, + fBitmap, 1, fOffset, fDestinationRect); + return; + } + } else if (fColorSpace == B_RGB32) { + if (fPainter->fDrawingMode == B_OP_OVER) { + DrawBitmapNoScale::Draw(fPainter->fInternal, + fBitmap, 4, fOffset, fDestinationRect); + return; + } + } + } + + ObjectDeleter convertedBitmapDeleter; + _ConvertColorSpace(convertedBitmapDeleter); + + // optimized version if there is no scale + if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) { + if (fPainter->fDrawingMode == B_OP_COPY) { + DrawBitmapNoScale::Draw(fPainter->fInternal, + fBitmap, 4, fOffset, fDestinationRect); + return; + } + if (fPainter->fDrawingMode == B_OP_OVER + || (fPainter->fDrawingMode == B_OP_ALPHA + && fPainter->fAlphaSrcMode == B_PIXEL_ALPHA + && fPainter->fAlphaFncMode == B_ALPHA_OVERLAY)) { + DrawBitmapNoScale::Draw(fPainter->fInternal, + fBitmap, 4, fOffset, fDestinationRect); + return; + } + } + + // bilinear and nearest-neighbor scaled, OP_COPY only + if (fPainter->fDrawingMode == B_OP_COPY + && !_HasAffineTransform() && !_HasAlphaMask()) { + if ((fOptions & B_FILTER_BITMAP_BILINEAR) != 0) { + DrawBitmapBilinearCopy drawBilinear; + drawBilinear.Draw(fPainter, fPainter->fInternal, + fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect); + } + else { + DrawBitmapNearestNeighborCopy::Draw(fPainter, fPainter->fInternal, + fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect); + } + return; + } + + // for all other cases (non-optimized drawing mode or scaled drawing) + DrawBitmapGeneric::Draw(fPainter, fPainter->fInternal, fBitmap, fOffset, + fScaleX, fScaleY, fDestinationRect, fOptions); +} + + +bool +Painter::BitmapPainter::_DetermineTransform(BRect sourceRect, + const BRect& destinationRect) +{ + if (!fPainter->fValidClipping + || !sourceRect.IsValid() + || !sourceRect.Intersects(fBitmapBounds) + || !destinationRect.IsValid()) { + return false; + } + + fDestinationRect = destinationRect; + + if (!fPainter->fSubpixelPrecise) { + align_rect_to_pixels(&sourceRect); + align_rect_to_pixels(&fDestinationRect); + } + + fScaleX = (fDestinationRect.Width() + 1) / (sourceRect.Width() + 1); + fScaleY = (fDestinationRect.Height() + 1) / (sourceRect.Height() + 1); + + if (fScaleX == 0.0 || fScaleY == 0.0) + return false; + + // constrain source rect to bitmap bounds and transfer the changes to + // the destination rect with the right scale + if (sourceRect.left < fBitmapBounds.left) { + float diff = fBitmapBounds.left - sourceRect.left; + fDestinationRect.left += diff * fScaleX; + sourceRect.left = fBitmapBounds.left; + } + if (sourceRect.top < fBitmapBounds.top) { + float diff = fBitmapBounds.top - sourceRect.top; + fDestinationRect.top += diff * fScaleY; + sourceRect.top = fBitmapBounds.top; + } + if (sourceRect.right > fBitmapBounds.right) { + float diff = sourceRect.right - fBitmapBounds.right; + fDestinationRect.right -= diff * fScaleX; + sourceRect.right = fBitmapBounds.right; + } + if (sourceRect.bottom > fBitmapBounds.bottom) { + float diff = sourceRect.bottom - fBitmapBounds.bottom; + fDestinationRect.bottom -= diff * fScaleY; + sourceRect.bottom = fBitmapBounds.bottom; + } + + fOffset.x = fDestinationRect.left - sourceRect.left; + fOffset.y = fDestinationRect.top - sourceRect.top; + + return true; +} + + +bool +Painter::BitmapPainter::_HasScale() +{ + return fScaleX != 1.0 || fScaleY != 1.0; +} + + +bool +Painter::BitmapPainter::_HasAffineTransform() +{ + return !fPainter->fIdentityTransform; +} + + +bool +Painter::BitmapPainter::_HasAlphaMask() +{ + return fPainter->fInternal.fMaskedUnpackedScanline != NULL; +} + + +void +Painter::BitmapPainter::_ConvertColorSpace( + ObjectDeleter& convertedBitmapDeleter) +{ + if (fColorSpace == B_RGBA32) + return; + + if (fColorSpace == B_RGB32 + && (fPainter->fDrawingMode == B_OP_COPY +#if 1 +// Enabling this would make the behavior compatible to BeOS, which +// treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in +// all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled. +// B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually +// use this color, unless the alpha channel contains 255 for all other +// pixels, which is inconsistent. + || fPainter->fDrawingMode == B_OP_ALPHA +#endif + )) { + return; + } + + BBitmap* conversionBitmap = new(nothrow) BBitmap(fBitmapBounds, + B_BITMAP_NO_SERVER_LINK, B_RGBA32); + if (conversionBitmap == NULL) { + fprintf(stderr, "BitmapPainter::_ConvertColorSpace() - " + "out of memory for creating temporary conversion bitmap\n"); + return; + } + convertedBitmapDeleter.SetTo(conversionBitmap); + + status_t err = conversionBitmap->ImportBits(fBitmap.buf(), + fBitmap.height() * fBitmap.stride(), + fBitmap.stride(), 0, fColorSpace); + if (err < B_OK) { + fprintf(stderr, "BitmapPainter::_ConvertColorSpace() - " + "colorspace conversion failed: %s\n", strerror(err)); + return; + } + + // the original bitmap might have had some of the + // transaparent magic colors set that we now need to + // make transparent in our RGBA32 bitmap again. + switch (fColorSpace) { + case B_RGB32: + _TransparentMagicToAlpha((uint32 *)fBitmap.buf(), + fBitmap.width(), fBitmap.height(), + fBitmap.stride(), B_TRANSPARENT_MAGIC_RGBA32, + conversionBitmap); + break; + + // TODO: not sure if this applies to B_RGBA15 too. It + // should not because B_RGBA15 actually has an alpha + // channel itself and it should have been preserved + // when importing the bitmap. Maybe it applies to + // B_RGB16 though? + case B_RGB15: + _TransparentMagicToAlpha((uint16 *)fBitmap.buf(), + fBitmap.width(), fBitmap.height(), + fBitmap.stride(), B_TRANSPARENT_MAGIC_RGBA15, + conversionBitmap); + break; + + default: + break; + } + + fBitmap.attach((uint8*)conversionBitmap->Bits(), + (uint32)fBitmapBounds.IntegerWidth() + 1, + (uint32)fBitmapBounds.IntegerHeight() + 1, + conversionBitmap->BytesPerRow()); +} + + +template +void +Painter::BitmapPainter::_TransparentMagicToAlpha(sourcePixel* buffer, + uint32 width, uint32 height, uint32 sourceBytesPerRow, + sourcePixel transparentMagic, BBitmap* output) +{ + uint8* sourceRow = (uint8*)buffer; + uint8* destRow = (uint8*)output->Bits(); + uint32 destBytesPerRow = output->BytesPerRow(); + + for (uint32 y = 0; y < height; y++) { + sourcePixel* pixel = (sourcePixel*)sourceRow; + uint32* destPixel = (uint32*)destRow; + for (uint32 x = 0; x < width; x++, pixel++, destPixel++) { + if (*pixel == transparentMagic) + *destPixel &= 0x00ffffff; + } + + sourceRow += sourceBytesPerRow; + destRow += destBytesPerRow; + } +} diff --git a/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.h b/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.h new file mode 100644 index 0000000000..4afaa50645 --- /dev/null +++ b/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.h @@ -0,0 +1,60 @@ +/* + * Copyright 2005-2007, Stephan Aßmus . + * Copyright 2008, Andrej Spielmann . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef BITMAP_PAINTER_H +#define BITMAP_PAINTER_H + +#include + +#include "Painter.h" + + +class Painter::BitmapPainter { +public: + +public: + BitmapPainter(const Painter* painter, + const ServerBitmap* bitmap, + uint32 options); + + void Draw(const BRect& sourceRect, + const BRect& destinationRect); + +private: + bool _DetermineTransform( + BRect sourceRect, + const BRect& destinationRect); + + bool _HasScale(); + bool _HasAffineTransform(); + bool _HasAlphaMask(); + + void _ConvertColorSpace(ObjectDeleter& + convertedBitmapDeleter); + + template + void _TransparentMagicToAlpha(sourcePixel *buffer, + uint32 width, uint32 height, + uint32 sourceBytesPerRow, + sourcePixel transparentMagic, + BBitmap *output); + +private: + const Painter* fPainter; + status_t fStatus; + agg::rendering_buffer fBitmap; + BRect fBitmapBounds; + color_space fColorSpace; + uint32 fOptions; + + BRect fDestinationRect; + double fScaleX; + double fScaleY; + BPoint fOffset; +}; + + +#endif // BITMAP_PAINTER_H diff --git a/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapBilinear.h b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapBilinear.h new file mode 100644 index 0000000000..5dc75257a1 --- /dev/null +++ b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapBilinear.h @@ -0,0 +1,520 @@ +/* + * Copyright 2009, Christian Packmann. + * Copyright 2008, Andrej Spielmann . + * Copyright 2005-2014, Stephan Aßmus . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef DRAW_BITMAP_BILINEAR_H +#define DRAW_BITMAP_BILINEAR_H + +#include "Painter.h" + + +// Prototypes for assembler routines +extern "C" { + void bilinear_scale_xloop_mmxsse(const uint8* src, void* dst, + void* xWeights, uint32 xmin, uint32 xmax, uint32 wTop, uint32 srcBPR); +} + + +extern uint32 gSIMDFlags; + + +namespace BitmapPainterPrivate { + + +struct FilterInfo { + uint16 index; // index into source bitmap row/column + uint16 weight; // weight of the pixel at index [0..255] +}; + + +struct FilterData { + FilterInfo* fWeightsX; + FilterInfo* fWeightsY; + uint32 fIndexOffsetX; + uint32 fIndexOffsetY; +}; + + +template +struct DrawBitmapBilinearOptimized { + void Draw(PainterAggInterface& aggInterface, const BRect& destinationRect, + agg::rendering_buffer* bitmap, const FilterData& filterData) + { + fSource = bitmap; + fSourceBytesPerRow = bitmap->stride(); + fDestination = NULL; + fDestinationBytesPerRow = aggInterface.fBuffer.stride(); + fWeightsX = filterData.fWeightsX; + fWeightsY = filterData.fWeightsY; + + const int32 left = (int32)destinationRect.left; + const int32 top = (int32)destinationRect.top; + const int32 right = (int32)destinationRect.right; + const int32 bottom = (int32)destinationRect.bottom; + + renderer_base& baseRenderer = aggInterface.fBaseRenderer; + + // iterate over clipping boxes + baseRenderer.first_clip_box(); + do { + const int32 x1 = max_c(baseRenderer.xmin(), left); + const int32 x2 = min_c(baseRenderer.xmax(), right); + if (x1 > x2) + continue; + + int32 y1 = max_c(baseRenderer.ymin(), top); + int32 y2 = min_c(baseRenderer.ymax(), bottom); + if (y1 > y2) + continue; + + // buffer offset into destination + fDestination = aggInterface.fBuffer.row_ptr(y1) + x1 * 4; + + // x and y are needed as indices into the weight arrays, so the + // offset into the target buffer needs to be compensated + const int32 xIndexL = x1 - left - filterData.fIndexOffsetX; + const int32 xIndexR = x2 - left - filterData.fIndexOffsetX; + y1 -= top + filterData.fIndexOffsetY; + y2 -= top + filterData.fIndexOffsetY; + + //printf("x: %ld - %ld\n", xIndexL, xIndexR); + //printf("y: %ld - %ld\n", y1, y2); + + OptimizedVersion optimizedVersion; + optimizedVersion.DrawToClipRect(xIndexL, xIndexR, y1, y2); + + } while (baseRenderer.next_clip_box()); + } + +protected: + agg::rendering_buffer* fSource; + uint32 fSourceBytesPerRow; + uint8* fDestination; + uint32 fDestinationBytesPerRow; + FilterInfo* fWeightsX; + FilterInfo* fWeightsY; +}; + + +struct BilinearDefault : DrawBitmapBilinearOptimized { + void DrawToClipRect(int32 xIndexL, int32 xIndexR, int32 y1, int32 y2) + { + // In this mode we anticipate many pixels wich need filtering, + // there are no special cases for direct hit pixels except for + // the last column/row and the right/bottom corner pixel. + + // The last column/row handling does not need to be performed + // for all clipping rects! + int32 yMax = y2; + if (fWeightsY[yMax].weight == 255) + yMax--; + int32 xIndexMax = xIndexR; + if (fWeightsX[xIndexMax].weight == 255) + xIndexMax--; + + for (; y1 <= yMax; y1++) { + // cache the weight of the top and bottom row + const uint16 wTop = fWeightsY[y1].weight; + const uint16 wBottom = 255 - fWeightsY[y1].weight; + + // buffer offset into source (top row) + register const uint8* src = fSource->row_ptr(fWeightsY[y1].index); + // buffer handle for destination to be incremented per + // pixel + register uint8* d = fDestination; + + for (int32 x = xIndexL; x <= xIndexMax; x++) { + const uint8* s = src + fWeightsX[x].index; + // calculate the weighted sum of all four + // interpolated pixels + const uint16 wLeft = fWeightsX[x].weight; + const uint16 wRight = 255 - wLeft; + // left and right of top row + uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop; + uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop; + uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop; + + // left and right of bottom row + s += fSourceBytesPerRow; + t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; + t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; + t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; + d[0] = t0 >> 16; + d[1] = t1 >> 16; + d[2] = t2 >> 16; + d += 4; + } + // last column of pixels if necessary + if (xIndexMax < xIndexR) { + const uint8* s = src + fWeightsX[xIndexR].index; + const uint8* sBottom = s + fSourceBytesPerRow; + d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; + d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; + d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; + } + + fDestination += fDestinationBytesPerRow; + } + + // last row of pixels if necessary + // buffer offset into source (bottom row) + register const uint8* src + = fSource->row_ptr(fWeightsY[y2].index); + // buffer handle for destination to be incremented per pixel + register uint8* d = fDestination; + + if (yMax < y2) { + for (int32 x = xIndexL; x <= xIndexMax; x++) { + const uint8* s = src + fWeightsX[x].index; + const uint16 wLeft = fWeightsX[x].weight; + const uint16 wRight = 255 - wLeft; + d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; + d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; + d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; + d += 4; + } + } + + // pixel in bottom right corner if necessary + if (yMax < y2 && xIndexMax < xIndexR) { + const uint8* s = src + fWeightsX[xIndexR].index; + *(uint32*)d = *(uint32*)s; + } + } +}; + + +struct BilinearLowFilterRatio : + DrawBitmapBilinearOptimized { + void DrawToClipRect(int32 xIndexL, int32 xIndexR, int32 y1, int32 y2) + { + // In this mode, we anticipate to hit many destination pixels + // that map directly to a source pixel, we have more branches + // in the inner loop but save time because of the special + // cases. If there are too few direct hit pixels, the branches + // only waste time. + + for (; y1 <= y2; y1++) { + // cache the weight of the top and bottom row + const uint16 wTop = fWeightsY[y1].weight; + const uint16 wBottom = 255 - fWeightsY[y1].weight; + + // buffer offset into source (top row) + register const uint8* src = fSource->row_ptr(fWeightsY[y1].index); + // buffer handle for destination to be incremented per + // pixel + register uint8* d = fDestination; + + if (wTop == 255) { + for (int32 x = xIndexL; x <= xIndexR; x++) { + const uint8* s = src + fWeightsX[x].index; + // This case is important to prevent out + // of bounds access at bottom edge of the source + // bitmap. If the scale is low and integer, it will + // also help the speed. + if (fWeightsX[x].weight == 255) { + // As above, but to prevent out of bounds + // on the right edge. + *(uint32*)d = *(uint32*)s; + } else { + // Only the left and right pixels are + // interpolated, since the top row has 100% + // weight. + const uint16 wLeft = fWeightsX[x].weight; + const uint16 wRight = 255 - wLeft; + d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; + d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; + d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; + } + d += 4; + } + } else { + for (int32 x = xIndexL; x <= xIndexR; x++) { + const uint8* s = src + fWeightsX[x].index; + if (fWeightsX[x].weight == 255) { + // Prevent out of bounds access on the right + // edge or simply speed up. + const uint8* sBottom = s + fSourceBytesPerRow; + d[0] = (s[0] * wTop + sBottom[0] * wBottom) + >> 8; + d[1] = (s[1] * wTop + sBottom[1] * wBottom) + >> 8; + d[2] = (s[2] * wTop + sBottom[2] * wBottom) + >> 8; + } else { + // calculate the weighted sum of all four + // interpolated pixels + const uint16 wLeft = fWeightsX[x].weight; + const uint16 wRight = 255 - wLeft; + // left and right of top row + uint32 t0 = (s[0] * wLeft + s[4] * wRight) + * wTop; + uint32 t1 = (s[1] * wLeft + s[5] * wRight) + * wTop; + uint32 t2 = (s[2] * wLeft + s[6] * wRight) + * wTop; + + // left and right of bottom row + s += fSourceBytesPerRow; + t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; + t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; + t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; + + d[0] = t0 >> 16; + d[1] = t1 >> 16; + d[2] = t2 >> 16; + } + d += 4; + } + } + fDestination += fDestinationBytesPerRow; + } + } +}; + + +#ifdef __INTEL__ + +struct BilinearSimd : DrawBitmapBilinearOptimized { + void DrawToClipRect(int32 xIndexL, int32 xIndexR, int32 y1, int32 y2) + { + // Basically the same as the "standard" mode, but we use SIMD + // routines for the processing of the single display lines. + + // The last column/row handling does not need to be performed + // for all clipping rects! + int32 yMax = y2; + if (fWeightsY[yMax].weight == 255) + yMax--; + int32 xIndexMax = xIndexR; + if (fWeightsX[xIndexMax].weight == 255) + xIndexMax--; + + for (; y1 <= yMax; y1++) { + // cache the weight of the top and bottom row + const uint16 wTop = fWeightsY[y1].weight; + const uint16 wBottom = 255 - fWeightsY[y1].weight; + + // buffer offset into source (top row) + const uint8* src = fSource->row_ptr(fWeightsY[y1].index); + // buffer handle for destination to be incremented per + // pixel + uint8* d = fDestination; + bilinear_scale_xloop_mmxsse(src, fDestination, fWeightsX, xIndexL, + xIndexMax, wTop, fSourceBytesPerRow); + // increase pointer by processed pixels + d += (xIndexMax - xIndexL + 1) * 4; + + // last column of pixels if necessary + if (xIndexMax < xIndexR) { + const uint8* s = src + fWeightsX[xIndexR].index; + const uint8* sBottom = s + fSourceBytesPerRow; + d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; + d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; + d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; + } + + fDestination += fDestinationBytesPerRow; + } + + // last row of pixels if necessary + // buffer offset into source (bottom row) + register const uint8* src = fSource->row_ptr(fWeightsY[y2].index); + // buffer handle for destination to be incremented per pixel + register uint8* d = fDestination; + + if (yMax < y2) { + for (int32 x = xIndexL; x <= xIndexMax; x++) { + const uint8* s = src + fWeightsX[x].index; + const uint16 wLeft = fWeightsX[x].weight; + const uint16 wRight = 255 - wLeft; + d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; + d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; + d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; + d += 4; + } + } + + // pixel in bottom right corner if necessary + if (yMax < y2 && xIndexMax < xIndexR) { + const uint8* s = src + fWeightsX[xIndexR].index; + *(uint32*)d = *(uint32*)s; + } + } +}; + +#endif // __INTEL__ + + +struct DrawBitmapBilinearCopy { + void + Draw(const Painter* painter, PainterAggInterface& aggInterface, + agg::rendering_buffer& bitmap, BPoint offset, + double scaleX, double scaleY, BRect destinationRect) + { + //bigtime_t now = system_time(); + uint32 dstWidth = destinationRect.IntegerWidth() + 1; + uint32 dstHeight = destinationRect.IntegerHeight() + 1; + uint32 srcWidth = bitmap.width(); + uint32 srcHeight = bitmap.height(); + + // Do not calculate more filter weights than necessary and also + // keep the stack based allocations reasonably sized + const BRegion& clippingRegion = *painter->ClippingRegion(); + if (clippingRegion.Frame().IntegerWidth() + 1 < (int32)dstWidth) + dstWidth = clippingRegion.Frame().IntegerWidth() + 1; + if (clippingRegion.Frame().IntegerHeight() + 1 < (int32)dstHeight) + dstHeight = clippingRegion.Frame().IntegerHeight() + 1; + + // When calculating less filter weights than specified by + // destinationRect, we need to compensate the offset. + FilterData filterData; + filterData.fIndexOffsetX = 0; + filterData.fIndexOffsetY = 0; + if (clippingRegion.Frame().left > destinationRect.left) { + filterData.fIndexOffsetX = (int32)(clippingRegion.Frame().left + - destinationRect.left); + } + if (clippingRegion.Frame().top > destinationRect.top) { + filterData.fIndexOffsetY = (int32)(clippingRegion.Frame().top + - destinationRect.top); + } + +//#define FILTER_INFOS_ON_HEAP +#ifdef FILTER_INFOS_ON_HEAP + filterData.fWeightsX = new (nothrow) FilterInfo[dstWidth]; + filterData.fWeightsY = new (nothrow) FilterInfo[dstHeight]; + if (filterData.fWeightsX == NULL || filterData.fWeightsY == NULL) { + delete[] filterData.fWeightsX; + delete[] filterData.fWeightsY; + return; + } +#else + // stack based saves about 200µs on 1.85 GHz Core 2 Duo + // should not pose a problem with stack overflows + // (needs around 12Kb for 1920x1200) + FilterInfo xWeights[dstWidth]; + FilterInfo yWeights[dstHeight]; + filterData.fWeightsX = &xWeights[0]; + filterData.fWeightsY = &yWeights[0]; +#endif + + // Extract the cropping information for the source bitmap, + // If only a part of the source bitmap is to be drawn with scale, + // the offset will be different from the destinationRect left top + // corner. + const int32 xBitmapShift = (int32)(destinationRect.left - offset.x); + const int32 yBitmapShift = (int32)(destinationRect.top - offset.y); + + for (uint32 i = 0; i < dstWidth; i++) { + // fractional index into source + // NOTE: It is very important to calculate the fractional index + // into the source pixel grid like this to prevent out of bounds + // access! It will result in the rightmost pixel of the destination + // to access the rightmost pixel of the source with a weighting + // of 255. This in turn will trigger an optimization in the loop + // that also prevents out of bounds access. + float index = (i + filterData.fIndexOffsetX) * (srcWidth - 1) + / (srcWidth * scaleX - 1); + // round down to get the left pixel + filterData.fWeightsX[i].index = (uint16)index; + filterData.fWeightsX[i].weight = + 255 - (uint16)((index - filterData.fWeightsX[i].index) * 255); + // handle cropped source bitmap + filterData.fWeightsX[i].index += xBitmapShift; + // precompute index for 32 bit pixels + filterData.fWeightsX[i].index *= 4; + } + + for (uint32 i = 0; i < dstHeight; i++) { + // fractional index into source + // NOTE: It is very important to calculate the fractional index + // into the source pixel grid like this to prevent out of bounds + // access! It will result in the bottommost pixel of the + // destination to access the bottommost pixel of the source with a + // weighting of 255. This in turn will trigger an optimization in + // the loop that also prevents out of bounds access. + float index = (i + filterData.fIndexOffsetY) * (srcHeight - 1) + / (srcHeight * scaleY - 1); + // round down to get the top pixel + filterData.fWeightsY[i].index = (uint16)index; + filterData.fWeightsY[i].weight = + 255 - (uint16)((index - filterData.fWeightsY[i].index) * 255); + // handle cropped source bitmap + filterData.fWeightsY[i].index += yBitmapShift; + } + //printf("X: %d/%d ... %d/%d, %d/%d (%ld)\n", + // xWeights[0].index, xWeights[0].weight, + // xWeights[dstWidth - 2].index, xWeights[dstWidth - 2].weight, + // xWeights[dstWidth - 1].index, xWeights[dstWidth - 1].weight, + // dstWidth); + //printf("Y: %d/%d ... %d/%d, %d/%d (%ld)\n", + // yWeights[0].index, yWeights[0].weight, + // yWeights[dstHeight - 2].index, yWeights[dstHeight - 2].weight, + // yWeights[dstHeight - 1].index, yWeights[dstHeight - 1].weight, + // dstHeight); + + // Figure out which version of the code we want to use... + enum { + kOptimizeForLowFilterRatio = 0, + kUseDefaultVersion, + kUseSIMDVersion + }; + + int codeSelect = kUseDefaultVersion; + + uint32 neededSIMDFlags = APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE; + if ((gSIMDFlags & neededSIMDFlags) == neededSIMDFlags) + codeSelect = kUseSIMDVersion; + else { + if (scaleX == scaleY && (scaleX == 1.5 || scaleX == 2.0 + || scaleX == 2.5 || scaleX == 3.0)) { + codeSelect = kOptimizeForLowFilterRatio; + } + } + + switch (codeSelect) { + case kUseDefaultVersion: + { + BilinearDefault bilinearPainter; + bilinearPainter.Draw(aggInterface, destinationRect, &bitmap, + filterData); + break; + } + + case kOptimizeForLowFilterRatio: + { + BilinearLowFilterRatio bilinearPainter; + bilinearPainter.Draw(aggInterface, destinationRect, + &bitmap, filterData); + break; + } + +#ifdef __INTEL__ + case kUseSIMDVersion: + { + BilinearSimd bilinearPainter; + bilinearPainter.Draw(aggInterface, destinationRect, &bitmap, + filterData); + break; + } +#endif // __INTEL__ + } + +#ifdef FILTER_INFOS_ON_HEAP + delete[] filterData.fWeightsX; + delete[] filterData.fWeightsY; +#endif + //printf("draw bitmap %.5fx%.5f: %lld\n", scaleX, scaleY, + // system_time() - now); + } +}; + + +} // namespace BitmapPainterPrivate + + +#endif // DRAW_BITMAP_BILINEAR_H diff --git a/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapGeneric.h b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapGeneric.h new file mode 100644 index 0000000000..f148583f81 --- /dev/null +++ b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapGeneric.h @@ -0,0 +1,112 @@ +/* + * Copyright 2009, Christian Packmann. + * Copyright 2008, Andrej Spielmann . + * Copyright 2005-2014, Stephan Aßmus . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef DRAW_BITMAP_GENERIC_H +#define DRAW_BITMAP_GENERIC_H + +#include "Painter.h" + + +struct DrawBitmapGeneric { + static void + Draw(const Painter* painter, PainterAggInterface& aggInterface, + agg::rendering_buffer& bitmap, BPoint offset, + double scaleX, double scaleY, BRect destinationRect, uint32 options) + { + // pixel format attached to bitmap + typedef agg::pixfmt_bgra32 pixfmt_image; + pixfmt_image pixf_img(bitmap); + + agg::trans_affine srcMatrix; + // NOTE: R5 seems to ignore this offset when drawing bitmaps + // srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, + // -actualBitmapRect.top); + srcMatrix *= painter->Transform(); + + agg::trans_affine imgMatrix; + imgMatrix *= agg::trans_affine_translation( + offset.x - destinationRect.left, offset.y - destinationRect.top); + imgMatrix *= agg::trans_affine_scaling(scaleX, scaleY); + imgMatrix *= agg::trans_affine_translation(destinationRect.left, + destinationRect.top); + imgMatrix *= painter->Transform(); + imgMatrix.invert(); + + // image interpolator + typedef agg::span_interpolator_linear<> interpolator_type; + interpolator_type interpolator(imgMatrix); + + // scanline allocator + agg::span_allocator spanAllocator; + + // image accessor attached to pixel format of bitmap + typedef agg::image_accessor_clone source_type; + source_type source(pixf_img); + + // clip to the current clipping region's frame + if (painter->IsIdentityTransform()) { + destinationRect = destinationRect + & painter->ClippingRegion()->Frame(); + } + // convert to pixel coords (versus pixel indices) + destinationRect.right++; + destinationRect.bottom++; + + // path enclosing the bitmap + agg::path_storage& path = aggInterface.fPath; + rasterizer_type& rasterizer = aggInterface.fRasterizer; + + path.remove_all(); + path.move_to(destinationRect.left, destinationRect.top); + path.line_to(destinationRect.right, destinationRect.top); + path.line_to(destinationRect.right, destinationRect.bottom); + path.line_to(destinationRect.left, destinationRect.bottom); + path.close_polygon(); + + agg::conv_transform transformedPath(path, + srcMatrix); + rasterizer.reset(); + rasterizer.add_path(transformedPath); + + if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { + // image filter (bilinear) + typedef agg::span_image_filter_rgba_bilinear< + source_type, interpolator_type> span_gen_type; + span_gen_type spanGenerator(source, interpolator); + + // render the path with the bitmap as scanline fill + if (aggInterface.fMaskedUnpackedScanline != NULL) { + agg::render_scanlines_aa(rasterizer, + *aggInterface.fMaskedUnpackedScanline, + aggInterface.fBaseRenderer, spanAllocator, spanGenerator); + } else { + agg::render_scanlines_aa(rasterizer, + aggInterface.fUnpackedScanline, + aggInterface.fBaseRenderer, spanAllocator, spanGenerator); + } + } else { + // image filter (nearest neighbor) + typedef agg::span_image_filter_rgba_nn< + source_type, interpolator_type> span_gen_type; + span_gen_type spanGenerator(source, interpolator); + + // render the path with the bitmap as scanline fill + if (aggInterface.fMaskedUnpackedScanline != NULL) { + agg::render_scanlines_aa(rasterizer, + *aggInterface.fMaskedUnpackedScanline, + aggInterface.fBaseRenderer, spanAllocator, spanGenerator); + } else { + agg::render_scanlines_aa(rasterizer, + aggInterface.fUnpackedScanline, + aggInterface.fBaseRenderer, spanAllocator, spanGenerator); + } + } + } +}; + + +#endif // DRAW_BITMAP_GENERIC_H diff --git a/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNearestNeighbor.h b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNearestNeighbor.h new file mode 100644 index 0000000000..59fc68b097 --- /dev/null +++ b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNearestNeighbor.h @@ -0,0 +1,143 @@ +/* + * Copyright 2009, Christian Packmann. + * Copyright 2008, Andrej Spielmann . + * Copyright 2005-2014, Stephan Aßmus . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef DRAW_BITMAP_NEAREST_NEIGHBOR_H +#define DRAW_BITMAP_NEAREST_NEIGHBOR_H + +#include "Painter.h" + + +struct DrawBitmapNearestNeighborCopy { + static void + Draw(const Painter* painter, PainterAggInterface& aggInterface, + agg::rendering_buffer& bitmap, BPoint offset, + double scaleX, double scaleY, BRect destinationRect) + { + //bigtime_t now = system_time(); + uint32 dstWidth = destinationRect.IntegerWidth() + 1; + uint32 dstHeight = destinationRect.IntegerHeight() + 1; + uint32 srcWidth = bitmap.width(); + uint32 srcHeight = bitmap.height(); + + // Do not calculate more filter weights than necessary and also + // keep the stack based allocations reasonably sized + const BRegion& clippingRegion = *painter->ClippingRegion(); + if (clippingRegion.Frame().IntegerWidth() + 1 < (int32)dstWidth) + dstWidth = clippingRegion.Frame().IntegerWidth() + 1; + if (clippingRegion.Frame().IntegerHeight() + 1 < (int32)dstHeight) + dstHeight = clippingRegion.Frame().IntegerHeight() + 1; + + // When calculating less filter weights than specified by + // destinationRect, we need to compensate the offset. + uint32 filterWeightXIndexOffset = 0; + uint32 filterWeightYIndexOffset = 0; + if (clippingRegion.Frame().left > destinationRect.left) { + filterWeightXIndexOffset = (int32)(clippingRegion.Frame().left + - destinationRect.left); + } + if (clippingRegion.Frame().top > destinationRect.top) { + filterWeightYIndexOffset = (int32)(clippingRegion.Frame().top + - destinationRect.top); + } + + // should not pose a problem with stack overflows + // (needs around 6Kb for 1920x1200) + uint16 xIndices[dstWidth]; + uint16 yIndices[dstHeight]; + + // Extract the cropping information for the source bitmap, + // If only a part of the source bitmap is to be drawn with scale, + // the offset will be different from the destinationRect left top + // corner. + const int32 xBitmapShift = (int32)(destinationRect.left - offset.x); + const int32 yBitmapShift = (int32)(destinationRect.top - offset.y); + + for (uint32 i = 0; i < dstWidth; i++) { + // index into source + uint16 index = (uint16)((i + filterWeightXIndexOffset) * srcWidth + / (srcWidth * scaleX)); + // round down to get the left pixel + xIndices[i] = index; + // handle cropped source bitmap + xIndices[i] += xBitmapShift; + // precompute index for 32 bit pixels + xIndices[i] *= 4; + } + + for (uint32 i = 0; i < dstHeight; i++) { + // index into source + uint16 index = (uint16)((i + filterWeightYIndexOffset) * srcHeight + / (srcHeight * scaleY)); + // round down to get the top pixel + yIndices[i] = index; + // handle cropped source bitmap + yIndices[i] += yBitmapShift; + } + //printf("X: %d ... %d, %d (%ld or %f)\n", + // xIndices[0], xIndices[dstWidth - 2], xIndices[dstWidth - 1], + // dstWidth, srcWidth * scaleX); + //printf("Y: %d ... %d, %d (%ld or %f)\n", + // yIndices[0], yIndices[dstHeight - 2], yIndices[dstHeight - 1], + // dstHeight, srcHeight * scaleY); + + const int32 left = (int32)destinationRect.left; + const int32 top = (int32)destinationRect.top; + const int32 right = (int32)destinationRect.right; + const int32 bottom = (int32)destinationRect.bottom; + + const uint32 dstBPR = aggInterface.fBuffer.stride(); + + renderer_base& baseRenderer = aggInterface.fBaseRenderer; + + // iterate over clipping boxes + baseRenderer.first_clip_box(); + do { + const int32 x1 = max_c(baseRenderer.xmin(), left); + const int32 x2 = min_c(baseRenderer.xmax(), right); + if (x1 > x2) + continue; + + int32 y1 = max_c(baseRenderer.ymin(), top); + int32 y2 = min_c(baseRenderer.ymax(), bottom); + if (y1 > y2) + continue; + + // buffer offset into destination + uint8* dst = aggInterface.fBuffer.row_ptr(y1) + x1 * 4; + + // x and y are needed as indeces into the wheight arrays, so the + // offset into the target buffer needs to be compensated + const int32 xIndexL = x1 - left - filterWeightXIndexOffset; + const int32 xIndexR = x2 - left - filterWeightXIndexOffset; + y1 -= top + filterWeightYIndexOffset; + y2 -= top + filterWeightYIndexOffset; + + //printf("x: %ld - %ld\n", xIndexL, xIndexR); + //printf("y: %ld - %ld\n", y1, y2); + + for (; y1 <= y2; y1++) { + // buffer offset into source (top row) + register const uint8* src = bitmap.row_ptr(yIndices[y1]); + // buffer handle for destination to be incremented per pixel + register uint32* d = (uint32*)dst; + + for (int32 x = xIndexL; x <= xIndexR; x++) { + *d = *(uint32*)(src + xIndices[x]); + d++; + } + dst += dstBPR; + } + } while (baseRenderer.next_clip_box()); + + //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, + // system_time() - now); + } +}; + + + +#endif // DRAW_BITMAP_NEAREST_NEIGHBOR_H diff --git a/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNoScale.h b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNoScale.h new file mode 100644 index 0000000000..61be834b4f --- /dev/null +++ b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNoScale.h @@ -0,0 +1,169 @@ +/* + * Copyright 2009, Christian Packmann. + * Copyright 2008, Andrej Spielmann . + * Copyright 2005-2014, Stephan Aßmus . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef DRAW_BITMAP_NO_SCALE_H +#define DRAW_BITMAP_NO_SCALE_H + +#include "IntPoint.h" +#include "Painter.h" + + +template +struct DrawBitmapNoScale { + static void + Draw(PainterAggInterface& aggInterface, agg::rendering_buffer& bitmap, + uint32 bytesPerSourcePixel, IntPoint offset, BRect destinationRect) + { + // NOTE: this would crash if destinationRect was large enough to read + // outside the bitmap, so make sure this is not the case before calling + // this function! + uint8* dst = aggInterface.fBuffer.row_ptr(0); + const uint32 dstBPR = aggInterface.fBuffer.stride(); + + const uint8* src = bitmap.row_ptr(0); + const uint32 srcBPR = bitmap.stride(); + + const int32 left = (int32)destinationRect.left; + const int32 top = (int32)destinationRect.top; + const int32 right = (int32)destinationRect.right; + const int32 bottom = (int32)destinationRect.bottom; + +#if DEBUG_DRAW_BITMAP + if (left - offset.x < 0 + || left - offset.x >= (int32)bitmap.width() + || right - offset.x >= (int32)srcBuffer.width() + || top - offset.y < 0 + || top - offset.y >= (int32)bitmap.height() + || bottom - offset.y >= (int32)bitmap.height()) { + char message[256]; + sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) " + "(%d, %d) (%ld, %ld)", + left - offset.x, top - offset.y, + right - offset.x, bottom - offset.y, + bitmap.width(), bitmap.height(), offset.x, offset.y); + debugger(message); + } +#endif + + const rgb_color* colorMap = SystemPalette(); + renderer_base& baseRenderer = aggInterface.fBaseRenderer; + + // copy rects, iterate over clipping boxes + baseRenderer.first_clip_box(); + do { + int32 x1 = max_c(baseRenderer.xmin(), left); + int32 x2 = min_c(baseRenderer.xmax(), right); + if (x1 <= x2) { + int32 y1 = max_c(baseRenderer.ymin(), top); + int32 y2 = min_c(baseRenderer.ymax(), bottom); + if (y1 <= y2) { + uint8* dstHandle = dst + y1 * dstBPR + x1 * 4; + const uint8* srcHandle = src + (y1 - offset.y) * srcBPR + + (x1 - offset.x) * bytesPerSourcePixel; + + for (; y1 <= y2; y1++) { + BlendType::BlendRow(dstHandle, srcHandle, + x2 - x1 + 1, colorMap); + + dstHandle += dstBPR; + srcHandle += srcBPR; + } + } + } + } while (baseRenderer.next_clip_box()); + } +}; + + +struct CMap8Copy : public DrawBitmapNoScale +{ + static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, + const rgb_color* colorMap) + { + uint32* d = (uint32*)dst; + const uint8* s = src; + while (numPixels--) { + const rgb_color c = colorMap[*s++]; + *d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); + } + } +}; + + +struct CMap8Over : public DrawBitmapNoScale +{ + static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, + const rgb_color* colorMap) + { + uint32* d = (uint32*)dst; + const uint8* s = src; + while (numPixels--) { + const rgb_color c = colorMap[*s++]; + if (c.alpha) + *d = (c.alpha << 24) | (c.red << 16) + | (c.green << 8) | (c.blue); + d++; + } + } +}; + + +struct Bgr32Copy : public DrawBitmapNoScale +{ + static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, + const rgb_color*) + { + memcpy(dst, src, numPixels * 4); + } +}; + + +struct Bgr32Over : public DrawBitmapNoScale +{ + static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, + const rgb_color*) + { + uint32* d = (uint32*)dst; + uint32* s = (uint32*)src; + while (numPixels--) { + if (*s != B_TRANSPARENT_MAGIC_RGBA32) + *(uint32*)d = *(uint32*)s; + d++; + s++; + } + } +}; + + +struct Bgr32Alpha : public DrawBitmapNoScale +{ + static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, + const rgb_color*) + { + uint32* d = (uint32*)dst; + int32 bytes = numPixels * 4; + uint8 buffer[bytes]; + uint8* b = buffer; + while (numPixels--) { + if (src[3] == 255) { + *(uint32*)b = *(uint32*)src; + } else { + *(uint32*)b = *d; + b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8; + b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8; + b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8; + } + d++; + b += 4; + src += 4; + } + memcpy(dst, buffer, bytes); + } +}; + + +#endif // DRAW_BITMAP_NO_SCALE_H diff --git a/src/servers/app/drawing/Painter/painter_bilinear_scale.nasm b/src/servers/app/drawing/Painter/bitmap_painter/painter_bilinear_scale.nasm similarity index 100% rename from src/servers/app/drawing/Painter/painter_bilinear_scale.nasm rename to src/servers/app/drawing/Painter/bitmap_painter/painter_bilinear_scale.nasm