app_server: Add tiled bitmap drawing routines

Change-Id: I9a7bd967f2cc95d815a66707b764cf5e33b3f8ed
Reviewed-on: https://review.haiku-os.org/c/haiku/+/1962
Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
This commit is contained in:
Kacper Kasper 2019-11-17 15:04:21 +01:00 committed by Adrien Destugues
parent 4dcc9761a7
commit c67dde0f2c
8 changed files with 373 additions and 100 deletions

View File

@ -2706,8 +2706,13 @@ SetViewColor(Parent()->ViewColor());
otherwise stated. Notes on specific methods are provided below:
DrawBitmap()
DrawTiledBitmap()
If the the image is bigger than the destination rectangle, it is scaled to fit.
DrawBitmap() scales the image to fit if the image has different size than
the destination rectangle.
DrawTiledBitmap() fills the destination rectangle with copies of the source
image, without changing its size.
The asynchronous versions pass the image to Application Server and return
immediately.
@ -2752,6 +2757,7 @@ SetViewColor(Parent()->ViewColor());
\fn void BView::DrawBitmapAsync(const BBitmap* bitmap, BRect bitmapRect,
BRect viewRect, uint32 options)
\brief Draws \a bitmap on the view within \a viewRect asynchronously.
\a bitmap portion is scaled to fit \a viewRect.
\param bitmap The bitmap to draw onto the view.
\param bitmapRect The portion of the bitmap to draw in the bitmap's
@ -2768,6 +2774,7 @@ SetViewColor(Parent()->ViewColor());
\fn void BView::DrawBitmapAsync(const BBitmap* bitmap, BRect bitmapRect,
BRect viewRect)
\brief Draws \a bitmap on the view within \a viewRect asynchronously.
\a bitmap portion is scaled to fit \a viewRect.
\param bitmap The bitmap to draw onto the view.
\param bitmapRect The portion of the bitmap to draw in the bitmap's
@ -2782,6 +2789,7 @@ SetViewColor(Parent()->ViewColor());
/*!
\fn void BView::DrawBitmapAsync(const BBitmap* bitmap, BRect viewRect)
\brief Draws \a bitmap on the view within \a viewRect asynchronously.
\a bitmap is scaled to fit \a viewRect.
\param bitmap The bitmap to draw onto the view.
\param viewRect The area in the view's coordinate system to draw the
@ -2815,7 +2823,8 @@ SetViewColor(Parent()->ViewColor());
/*!
\fn void BView::DrawBitmap(const BBitmap* bitmap, BRect bitmapRect,
BRect viewRect, uint32 options)
\brief brief Draws \a bitmap on the view within \a viewRect.
\brief Draws \a bitmap on the view within \a viewRect. \a bitmap portion
is scaled to fit \a viewRect.
\param bitmap The bitmap to draw onto the view.
\param bitmapRect The portion of the bitmap to draw in the bitmap's
@ -2831,7 +2840,8 @@ SetViewColor(Parent()->ViewColor());
/*!
\fn void BView::DrawBitmap(const BBitmap* bitmap, BRect bitmapRect,
BRect viewRect)
\brief Draws \a bitmap on the view within \a viewRect.
\brief Draws \a bitmap on the view within \a viewRect. \a bitmap portion
is scaled to fit \a viewRect.
\param bitmap The bitmap to draw onto the view.
\param bitmapRect The portion of the bitmap to draw in the bitmap's
@ -2845,7 +2855,8 @@ SetViewColor(Parent()->ViewColor());
/*!
\fn void BView::DrawBitmap(const BBitmap* bitmap, BRect viewRect)
\brief Draws \a bitmap on the view within \a viewRect.
\brief Draws \a bitmap on the view within \a viewRect. \a bitmap is scaled
to fit \a viewRect.
\param bitmap The bitmap to draw onto the view.
\param viewRect The area in the view's coordinate system to draw the
@ -2876,6 +2887,37 @@ SetViewColor(Parent()->ViewColor());
*/
/*!
\fn void BView::DrawTiledBitmapAsync(const BBitmap* bitmap, BRect viewRect,
BPoint phase = B_ORIGIN)
\brief Draws \a bitmap on the view within \a viewRect asynchronously.
If \a bitmap is smaller, it is cloned to fill remaining space
in \a viewRect.
\param bitmap The bitmap to draw onto the view.
\param viewRect The area in the view's coordinate system to draw the
bitmap in.
\param phase Source bitmap offset used as starting point for drawing.
\since Haiku R1
*/
/*!
\fn void BView::DrawTiledBitmap(const BBitmap* bitmap, BRect viewRect,
BPoint phase = B_ORIGIN)
\brief Draws \a bitmap on the view within \a viewRect. If \a bitmap is
smaller, it is cloned to fill remaining space in \a viewRect.
\param bitmap The bitmap to draw onto the view.
\param viewRect The area in the view's coordinate system to draw the
bitmap in.
\param phase Source bitmap offset used as starting point for drawing.
\since Haiku R1
*/
/*!
\fn void BView::DrawChar(char c)
\brief Draws character \a c onto to the view at the current pen position.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2001-2015 Haiku, Inc. All rights reserved.
* Copyright 2001-2019 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _VIEW_H
@ -472,6 +472,12 @@ public:
BPoint where);
void DrawBitmap(const BBitmap* aBitmap);
void DrawTiledBitmapAsync(const BBitmap* aBitmap,
BRect viewRect, BPoint phase = B_ORIGIN);
void DrawTiledBitmap(const BBitmap* aBitmap,
BRect viewRect, BPoint phase = B_ORIGIN);
void DrawChar(char aChar);
void DrawChar(char aChar, BPoint location);
void DrawString(const char* string,

View File

@ -1,5 +1,5 @@
/*
* Copyright 2001-2017 Haiku, Inc. All rights reserved.
* Copyright 2001-2019 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@ -3120,6 +3120,38 @@ BView::DrawBitmap(const BBitmap* bitmap)
}
void
BView::DrawTiledBitmapAsync(const BBitmap* bitmap, BRect viewRect,
BPoint phase)
{
if (bitmap == NULL || fOwner == NULL || !viewRect.IsValid())
return;
_CheckLockAndSwitchCurrent();
ViewDrawBitmapInfo info;
info.bitmapToken = bitmap->_ServerToken();
info.options = B_TILE_BITMAP;
info.viewRect = viewRect;
info.bitmapRect = bitmap->Bounds().OffsetToCopy(phase);
fOwner->fLink->StartMessage(AS_VIEW_DRAW_BITMAP);
fOwner->fLink->Attach<ViewDrawBitmapInfo>(info);
_FlushIfNotInTransaction();
}
void
BView::DrawTiledBitmap(const BBitmap* bitmap, BRect viewRect, BPoint phase)
{
if (fOwner) {
DrawTiledBitmapAsync(bitmap, viewRect, phase);
Sync();
}
}
void
BView::DrawChar(char c)
{

View File

@ -65,8 +65,8 @@ Painter::BitmapPainter::Draw(const BRect& sourceRect,
TRACE("BitmapPainter::Draw()\n");
TRACE(" bitmapBounds = (%.1f, %.1f) - (%.1f, %.1f)\n",
bitmapBounds.left, bitmapBounds.top,
bitmapBounds.right, bitmapBounds.bottom);
fBitmapBounds.left, fBitmapBounds.top,
fBitmapBounds.right, fBitmapBounds.bottom);
TRACE(" sourceRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
sourceRect.left, sourceRect.top,
sourceRect.right, sourceRect.bottom);
@ -78,27 +78,29 @@ Painter::BitmapPainter::Draw(const BRect& sourceRect,
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> drawNoScale;
drawNoScale.Draw(fPainter->fInternal, fBitmap, 1, fOffset,
fDestinationRect);
return;
}
if (fPainter->fDrawingMode == B_OP_OVER) {
DrawBitmapNoScale<CMap8Over> drawNoScale;
drawNoScale.Draw(fPainter->fInternal, fBitmap, 1, fOffset,
fDestinationRect);
return;
}
} else if (fColorSpace == B_RGB32) {
if (fPainter->fDrawingMode == B_OP_OVER) {
DrawBitmapNoScale<Bgr32Over> drawNoScale;
drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
fDestinationRect);
return;
if ((fOptions & B_TILE_BITMAP) == 0) {
// 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> drawNoScale;
drawNoScale.Draw(fPainter->fInternal, fBitmap, 1, fOffset,
fDestinationRect);
return;
}
if (fPainter->fDrawingMode == B_OP_OVER) {
DrawBitmapNoScale<CMap8Over> drawNoScale;
drawNoScale.Draw(fPainter->fInternal, fBitmap, 1, fOffset,
fDestinationRect);
return;
}
} else if (fColorSpace == B_RGB32) {
if (fPainter->fDrawingMode == B_OP_OVER) {
DrawBitmapNoScale<Bgr32Over> drawNoScale;
drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
fDestinationRect);
return;
}
}
}
}
@ -106,62 +108,69 @@ Painter::BitmapPainter::Draw(const BRect& sourceRect,
ObjectDeleter<BBitmap> convertedBitmapDeleter;
_ConvertColorSpace(convertedBitmapDeleter);
// optimized version if there is no scale
if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) {
if (fPainter->fDrawingMode == B_OP_COPY) {
DrawBitmapNoScale<Bgr32Copy> drawNoScale;
drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
fDestinationRect);
return;
if ((fOptions & B_TILE_BITMAP) == 0) {
// optimized version if there is no scale
if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) {
if (fPainter->fDrawingMode == B_OP_COPY) {
DrawBitmapNoScale<Bgr32Copy> drawNoScale;
drawNoScale.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> drawNoScale;
drawNoScale.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> drawNoScale;
drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
fDestinationRect);
return;
}
}
if (!_HasScale() && !_HasAffineTransform() && _HasAlphaMask()) {
if (fPainter->fDrawingMode == B_OP_COPY) {
DrawBitmapNoScale<Bgr32CopyMasked> drawNoScale;
drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
fDestinationRect);
if (!_HasScale() && !_HasAffineTransform() && _HasAlphaMask()) {
if (fPainter->fDrawingMode == B_OP_COPY) {
DrawBitmapNoScale<Bgr32CopyMasked> drawNoScale;
drawNoScale.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) {
DrawBitmapBilinear<ColorTypeRgb, DrawModeCopy> drawBilinear;
drawBilinear.Draw(fPainter, fPainter->fInternal,
fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect);
} else {
DrawBitmapNearestNeighborCopy::Draw(fPainter, fPainter->fInternal,
fBitmap, fOffset, fScaleX, fScaleY, 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) {
DrawBitmapBilinear<ColorTypeRgb, DrawModeCopy> drawBilinear;
if (fPainter->fDrawingMode == B_OP_ALPHA
&& fPainter->fAlphaSrcMode == B_PIXEL_ALPHA
&& fPainter->fAlphaFncMode == B_ALPHA_OVERLAY
&& !_HasAffineTransform() && !_HasAlphaMask()
&& (fOptions & B_FILTER_BITMAP_BILINEAR) != 0) {
DrawBitmapBilinear<ColorTypeRgba, DrawModeAlphaOverlay> drawBilinear;
drawBilinear.Draw(fPainter, fPainter->fInternal,
fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect);
} else {
DrawBitmapNearestNeighborCopy::Draw(fPainter, fPainter->fInternal,
fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect);
return;
}
return;
}
if (fPainter->fDrawingMode == B_OP_ALPHA
&& fPainter->fAlphaSrcMode == B_PIXEL_ALPHA
&& fPainter->fAlphaFncMode == B_ALPHA_OVERLAY
&& !_HasAffineTransform() && !_HasAlphaMask()
&& (fOptions & B_FILTER_BITMAP_BILINEAR) != 0) {
DrawBitmapBilinear<ColorTypeRgba, DrawModeAlphaOverlay> drawBilinear;
drawBilinear.Draw(fPainter, fPainter->fInternal,
fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect);
return;
if ((fOptions & B_TILE_BITMAP) != 0) {
DrawBitmapGeneric<Tile>::Draw(fPainter, fPainter->fInternal, fBitmap,
fOffset, fScaleX, fScaleY, fDestinationRect, fOptions);
} else {
// for all other cases (non-optimized drawing mode or scaled drawing)
DrawBitmapGeneric<Fill>::Draw(fPainter, fPainter->fInternal, fBitmap,
fOffset, fScaleX, fScaleY, fDestinationRect, fOptions);
}
// for all other cases (non-optimized drawing mode or scaled drawing)
DrawBitmapGeneric::Draw(fPainter, fPainter->fInternal, fBitmap, fOffset,
fScaleX, fScaleY, fDestinationRect, fOptions);
}
@ -183,33 +192,38 @@ Painter::BitmapPainter::_DetermineTransform(BRect sourceRect,
align_rect_to_pixels(&fDestinationRect);
}
fScaleX = (fDestinationRect.Width() + 1) / (sourceRect.Width() + 1);
fScaleY = (fDestinationRect.Height() + 1) / (sourceRect.Height() + 1);
if((fOptions & B_TILE_BITMAP) == 0) {
fScaleX = (fDestinationRect.Width() + 1) / (sourceRect.Width() + 1);
fScaleY = (fDestinationRect.Height() + 1) / (sourceRect.Height() + 1);
if (fScaleX == 0.0 || fScaleY == 0.0)
return false;
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;
// 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;
}
} else {
fScaleX = 1.0;
fScaleY = 1.0;
}
fOffset.x = fDestinationRect.left - sourceRect.left;

View File

@ -11,6 +11,25 @@
#include "Painter.h"
struct Fill {};
struct Tile {};
template<typename PixFmt, typename Mode>
struct ImageAccessor {};
template<typename PixFmt>
struct ImageAccessor<PixFmt, Fill> {
typedef agg::image_accessor_clone<PixFmt> type;
};
template<typename PixFmt>
struct ImageAccessor<PixFmt, Tile> {
typedef agg::image_accessor_wrap<PixFmt,
agg::wrap_mode_repeat, agg::wrap_mode_repeat> type;
};
template<typename FillMode>
struct DrawBitmapGeneric {
static void
Draw(const Painter* painter, PainterAggInterface& aggInterface,
@ -44,7 +63,8 @@ struct DrawBitmapGeneric {
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;
typedef
typename ImageAccessor<pixfmt_image, FillMode>::type source_type;
source_type source(pixf_img);
// clip to the current clipping region's frame

View File

@ -271,6 +271,7 @@ SubInclude HAIKU_TOP src tests servers app statusbar ;
SubInclude HAIKU_TOP src tests servers app stress_test ;
SubInclude HAIKU_TOP src tests servers app text_rendering ;
SubInclude HAIKU_TOP src tests servers app textview ;
SubInclude HAIKU_TOP src tests servers app tiled_bitmap_test ;
SubInclude HAIKU_TOP src tests servers app transformation ;
SubInclude HAIKU_TOP src tests servers app unit_tests ;
SubInclude HAIKU_TOP src tests servers app view_state ;

View File

@ -0,0 +1,17 @@
SubDir HAIKU_TOP src tests servers app tiled_bitmap_test ;
AddSubDirSupportedPlatforms libbe_test ;
UseHeaders [ FDirName os app ] ;
UseHeaders [ FDirName os interface ] ;
Application TiledBitmapTest :
TiledBitmapTest.cpp
: be [ TargetLibstdc++ ] [ TargetLibsupc++ ]
;
if $(TARGET_PLATFORM) = libbe_test {
HaikuInstall install-test-apps : $(HAIKU_APP_TEST_DIR) : TiledBitmapTest
: tests!apps ;
}

View File

@ -0,0 +1,141 @@
/*
* Copyright 2019, Haiku Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Kacper Kasper, kacperkasper@gmail.com
*/
#include <Application.h>
#include <Bitmap.h>
#include <GradientRadial.h>
#include <String.h>
#include <View.h>
#include <Window.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
class View : public BView {
public:
View(BRect rect);
virtual ~View();
virtual void Draw(BRect);
BBitmap* fBitmap;
};
View::View(BRect rect)
: BView(rect, "tiledBitmaps", B_FOLLOW_ALL, B_WILL_DRAW)
{
SetViewColor(make_color(255, 0, 0));
fBitmap = new BBitmap(BRect(0, 0, 5, 5), B_RGBA32);
uint32 data[] = {
0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff,
0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff,
0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff,
0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff,
0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
};
/*uint32 data[] = {
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
};*/
fBitmap->SetBits((void*)data, sizeof(data), 0, B_RGBA32);
}
View::~View()
{
delete fBitmap;
}
void
View::Draw(BRect)
{
DrawTiledBitmap(fBitmap, BRect(10, 10, 60, 60), BPoint(1, 1));
DrawTiledBitmap(fBitmap, BRect(10, 60, 60, 110), BPoint(3, 3));
DrawTiledBitmap(fBitmap, BRect(10, 110, 60, 160));
DrawBitmap(fBitmap, fBitmap->Bounds(), BRect(60, 10, 110, 60));
}
// #pragma mark -
class Window : public BWindow {
public:
Window();
virtual ~Window();
virtual bool QuitRequested();
};
Window::Window()
: BWindow(BRect(100, 100, 300, 300), "TiledBitmap-Test",
B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS)
{
BView *view = new View(Bounds());
AddChild(view);
}
Window::~Window()
{
}
bool
Window::QuitRequested()
{
be_app->PostMessage(B_QUIT_REQUESTED);
return true;
}
// #pragma mark -
class Application : public BApplication {
public:
Application();
virtual void ReadyToRun();
};
Application::Application()
: BApplication("application/x-vnd.haiku-tiled_bitmap_test")
{
}
void
Application::ReadyToRun()
{
Window *window = new Window();
window->Show();
}
// #pragma mark -
int
main(int argc, char **argv)
{
Application app;
app.Run();
return 0;
}