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.
This commit is contained in:
@@ -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)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 <agg_conv_curve.h>
|
||||
#include <agg_path_storage.h>
|
||||
|
||||
#include <AffineTransform.h>
|
||||
#include <Font.h>
|
||||
@@ -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<typename sourcePixel>
|
||||
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 <class F>
|
||||
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<agg::path_storage> fCurve;
|
||||
private:
|
||||
mutable PainterAggInterface fInternal;
|
||||
|
||||
// for internal coordinate rounding/transformation
|
||||
bool fSubpixelPrecise : 1;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2005-2007, Stephan Aßmus <[email protected]>.
|
||||
* Copyright 2008, Andrej Spielmann <[email protected]>.
|
||||
* Copyright 2015, Julian Harnath <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef PAINTER_DATA_H
|
||||
#define PAINTER_DATA_H
|
||||
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#include <agg_path_storage.h>
|
||||
|
||||
|
||||
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<agg::path_storage> fCurve;
|
||||
};
|
||||
|
||||
|
||||
#endif // PAINTER_DATA_H
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Copyright 2009, Christian Packmann.
|
||||
* Copyright 2008, Andrej Spielmann <[email protected]>.
|
||||
* Copyright 2005-2014, Stephan Aßmus <[email protected]>.
|
||||
* Copyright 2015, Julian Harnath <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#include "BitmapPainter.h"
|
||||
|
||||
#include <Bitmap.h>
|
||||
|
||||
#include <agg_image_accessors.h>
|
||||
#include <agg_pixfmt_rgba.h>
|
||||
#include <agg_span_image_filter_rgba.h>
|
||||
|
||||
#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<CMap8Copy>::Draw(fPainter->fInternal,
|
||||
fBitmap, 1, fOffset, fDestinationRect);
|
||||
return;
|
||||
}
|
||||
if (fPainter->fDrawingMode == B_OP_OVER) {
|
||||
DrawBitmapNoScale<CMap8Over>::Draw(fPainter->fInternal,
|
||||
fBitmap, 1, fOffset, fDestinationRect);
|
||||
return;
|
||||
}
|
||||
} else if (fColorSpace == B_RGB32) {
|
||||
if (fPainter->fDrawingMode == B_OP_OVER) {
|
||||
DrawBitmapNoScale<Bgr32Over>::Draw(fPainter->fInternal,
|
||||
fBitmap, 4, fOffset, fDestinationRect);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjectDeleter<BBitmap> convertedBitmapDeleter;
|
||||
_ConvertColorSpace(convertedBitmapDeleter);
|
||||
|
||||
// optimized version if there is no scale
|
||||
if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) {
|
||||
if (fPainter->fDrawingMode == B_OP_COPY) {
|
||||
DrawBitmapNoScale<Bgr32Copy>::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<Bgr32Alpha>::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<BBitmap>& 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<typename sourcePixel>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2005-2007, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>.
|
||||
* Copyright 2015, Julian Harnath <julian.harnath@rwth-aachen.de>
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef BITMAP_PAINTER_H
|
||||
#define BITMAP_PAINTER_H
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
#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<BBitmap>&
|
||||
convertedBitmapDeleter);
|
||||
|
||||
template<typename sourcePixel>
|
||||
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
|
||||
@@ -0,0 +1,520 @@
|
||||
/*
|
||||
* Copyright 2009, Christian Packmann.
|
||||
* Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>.
|
||||
* Copyright 2005-2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2015, Julian Harnath <julian.harnath@rwth-aachen.de>
|
||||
* 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<class OptimizedVersion>
|
||||
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<BilinearDefault> {
|
||||
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<BilinearLowFilterRatio> {
|
||||
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<BilinearSimd> {
|
||||
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
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2009, Christian Packmann.
|
||||
* Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>.
|
||||
* Copyright 2005-2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2015, Julian Harnath <julian.harnath@rwth-aachen.de>
|
||||
* 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<pixfmt_image::color_type> spanAllocator;
|
||||
|
||||
// image accessor attached to pixel format of bitmap
|
||||
typedef agg::image_accessor_clone<pixfmt_image> 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<agg::path_storage> 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
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2009, Christian Packmann.
|
||||
* Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>.
|
||||
* Copyright 2005-2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2015, Julian Harnath <julian.harnath@rwth-aachen.de>
|
||||
* 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
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2009, Christian Packmann.
|
||||
* Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>.
|
||||
* Copyright 2005-2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2015, Julian Harnath <julian.harnath@rwth-aachen.de>
|
||||
* 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<class BlendType>
|
||||
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<CMap8Copy>
|
||||
{
|
||||
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<CMap8Over>
|
||||
{
|
||||
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<Bgr32Copy>
|
||||
{
|
||||
static void BlendRow(uint8* dst, const uint8* src, int32 numPixels,
|
||||
const rgb_color*)
|
||||
{
|
||||
memcpy(dst, src, numPixels * 4);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Bgr32Over : public DrawBitmapNoScale<Bgr32Over>
|
||||
{
|
||||
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<Bgr32Alpha>
|
||||
{
|
||||
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
|
||||
Reference in New Issue
Block a user