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