haiku/src/servers/app/DrawState.cpp

844 lines
18 KiB
C++

/*
* Copyright 2001-2018, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* DarkWyrm <bpmagic@columbus.rr.com>
* Adi Oanca <adioanca@mymail.ro>
* Stephan Aßmus <superstippi@gmx.de>
* Axel Dörfler, axeld@pinc-software.de
* Michael Pfeiffer <laplace@users.sourceforge.net>
* Julian Harnath <julian.harnath@rwth-aachen.de>
* Joseph Groover <looncraz@looncraz.net>
*/
//! Data classes for working with BView states and draw parameters
#include "DrawState.h"
#include <new>
#include <stdio.h>
#include <Region.h>
#include <ShapePrivate.h>
#include "AlphaMask.h"
#include "LinkReceiver.h"
#include "LinkSender.h"
#include "ServerProtocolStructs.h"
using std::nothrow;
DrawState::DrawState()
:
fOrigin(0.0f, 0.0f),
fCombinedOrigin(0.0f, 0.0f),
fScale(1.0f),
fCombinedScale(1.0f),
fTransform(),
fCombinedTransform(),
fAlphaMask(NULL),
fHighColor((rgb_color){ 0, 0, 0, 255 }),
fLowColor((rgb_color){ 255, 255, 255, 255 }),
fWhichHighColor(B_NO_COLOR),
fWhichLowColor(B_NO_COLOR),
fWhichHighColorTint(B_NO_TINT),
fWhichLowColorTint(B_NO_TINT),
fPattern(kSolidHigh),
fDrawingMode(B_OP_COPY),
fAlphaSrcMode(B_PIXEL_ALPHA),
fAlphaFncMode(B_ALPHA_OVERLAY),
fDrawingModeLocked(false),
fPenLocation(0.0f, 0.0f),
fPenSize(1.0f),
fFontAliasing(false),
fSubPixelPrecise(false),
fLineCapMode(B_BUTT_CAP),
fLineJoinMode(B_MITER_JOIN),
fMiterLimit(B_DEFAULT_MITER_LIMIT),
fFillRule(B_NONZERO)
{
fUnscaledFontSize = fFont.Size();
}
DrawState::DrawState(const DrawState& other)
:
fOrigin(other.fOrigin),
fCombinedOrigin(other.fCombinedOrigin),
fScale(other.fScale),
fCombinedScale(other.fCombinedScale),
fTransform(other.fTransform),
fCombinedTransform(other.fCombinedTransform),
fClippingRegion(NULL),
fAlphaMask(NULL),
fHighColor(other.fHighColor),
fLowColor(other.fLowColor),
fWhichHighColor(other.fWhichHighColor),
fWhichLowColor(other.fWhichLowColor),
fWhichHighColorTint(other.fWhichHighColorTint),
fWhichLowColorTint(other.fWhichLowColorTint),
fPattern(other.fPattern),
fDrawingMode(other.fDrawingMode),
fAlphaSrcMode(other.fAlphaSrcMode),
fAlphaFncMode(other.fAlphaFncMode),
fDrawingModeLocked(other.fDrawingModeLocked),
fPenLocation(other.fPenLocation),
fPenSize(other.fPenSize),
fFont(other.fFont),
fFontAliasing(other.fFontAliasing),
fSubPixelPrecise(other.fSubPixelPrecise),
fLineCapMode(other.fLineCapMode),
fLineJoinMode(other.fLineJoinMode),
fMiterLimit(other.fMiterLimit),
fFillRule(other.fFillRule),
// Since fScale is reset to 1.0, the unscaled
// font size is the current size of the font
// (which is from->fUnscaledFontSize * from->fCombinedScale)
fUnscaledFontSize(other.fUnscaledFontSize),
fPreviousState(NULL)
{
}
DrawState::~DrawState()
{
}
DrawState*
DrawState::PushState()
{
DrawState* next = new (nothrow) DrawState(*this);
if (next != NULL) {
// Prepare state as derived from this state
next->fOrigin = BPoint(0.0, 0.0);
next->fScale = 1.0;
next->fTransform.Reset();
next->fPreviousState.SetTo(this);
next->SetAlphaMask(fAlphaMask);
}
return next;
}
DrawState*
DrawState::PopState()
{
return fPreviousState.Detach();
}
uint16
DrawState::ReadFontFromLink(BPrivate::LinkReceiver& link)
{
uint16 mask;
link.Read<uint16>(&mask);
if ((mask & B_FONT_FAMILY_AND_STYLE) != 0) {
uint32 fontID;
link.Read<uint32>(&fontID);
fFont.SetFamilyAndStyle(fontID);
}
if ((mask & B_FONT_SIZE) != 0) {
float size;
link.Read<float>(&size);
fUnscaledFontSize = size;
fFont.SetSize(fUnscaledFontSize * fCombinedScale);
}
if ((mask & B_FONT_SHEAR) != 0) {
float shear;
link.Read<float>(&shear);
fFont.SetShear(shear);
}
if ((mask & B_FONT_ROTATION) != 0) {
float rotation;
link.Read<float>(&rotation);
fFont.SetRotation(rotation);
}
if ((mask & B_FONT_FALSE_BOLD_WIDTH) != 0) {
float falseBoldWidth;
link.Read<float>(&falseBoldWidth);
fFont.SetFalseBoldWidth(falseBoldWidth);
}
if ((mask & B_FONT_SPACING) != 0) {
uint8 spacing;
link.Read<uint8>(&spacing);
fFont.SetSpacing(spacing);
}
if ((mask & B_FONT_ENCODING) != 0) {
uint8 encoding;
link.Read<uint8>(&encoding);
fFont.SetEncoding(encoding);
}
if ((mask & B_FONT_FACE) != 0) {
uint16 face;
link.Read<uint16>(&face);
fFont.SetFace(face);
}
if ((mask & B_FONT_FLAGS) != 0) {
uint32 flags;
link.Read<uint32>(&flags);
fFont.SetFlags(flags);
}
return mask;
}
void
DrawState::ReadFromLink(BPrivate::LinkReceiver& link)
{
ViewSetStateInfo info;
link.Read<ViewSetStateInfo>(&info);
// BAffineTransform is transmitted as a double array
double transform[6];
link.Read<double[6]>(&transform);
if (fTransform.Unflatten(B_AFFINE_TRANSFORM_TYPE, transform,
sizeof(transform)) != B_OK) {
return;
}
fPenLocation = info.penLocation;
fPenSize = info.penSize;
fHighColor = info.highColor;
fLowColor = info.lowColor;
fWhichHighColor = info.whichHighColor;
fWhichLowColor = info.whichLowColor;
fWhichHighColorTint = info.whichHighColorTint;
fWhichLowColorTint = info.whichLowColorTint;
fPattern = info.pattern;
fDrawingMode = info.drawingMode;
fOrigin = info.origin;
fScale = info.scale;
fLineJoinMode = info.lineJoin;
fLineCapMode = info.lineCap;
fMiterLimit = info.miterLimit;
fFillRule = info.fillRule;
fAlphaSrcMode = info.alphaSourceMode;
fAlphaFncMode = info.alphaFunctionMode;
fFontAliasing = info.fontAntialiasing;
if (fPreviousState.IsSet()) {
fCombinedOrigin = fPreviousState->fCombinedOrigin + fOrigin;
fCombinedScale = fPreviousState->fCombinedScale * fScale;
fCombinedTransform = fPreviousState->fCombinedTransform * fTransform;
} else {
fCombinedOrigin = fOrigin;
fCombinedScale = fScale;
fCombinedTransform = fTransform;
}
// read clipping
// TODO: This could be optimized, but the user clipping regions are rarely
// used, so it's low priority...
int32 clipRectCount;
link.Read<int32>(&clipRectCount);
if (clipRectCount >= 0) {
BRegion region;
BRect rect;
for (int32 i = 0; i < clipRectCount; i++) {
link.Read<BRect>(&rect);
region.Include(rect);
}
SetClippingRegion(&region);
} else {
// No user clipping used
SetClippingRegion(NULL);
}
}
void
DrawState::WriteToLink(BPrivate::LinkSender& link) const
{
// Attach font state
ViewGetStateInfo info;
info.fontID = fFont.GetFamilyAndStyle();
info.fontSize = fFont.Size();
info.fontShear = fFont.Shear();
info.fontRotation = fFont.Rotation();
info.fontFalseBoldWidth = fFont.FalseBoldWidth();
info.fontSpacing = fFont.Spacing();
info.fontEncoding = fFont.Encoding();
info.fontFace = fFont.Face();
info.fontFlags = fFont.Flags();
// Attach view state
info.viewStateInfo.penLocation = fPenLocation;
info.viewStateInfo.penSize = fPenSize;
info.viewStateInfo.highColor = fHighColor;
info.viewStateInfo.lowColor = fLowColor;
info.viewStateInfo.whichHighColor = fWhichHighColor;
info.viewStateInfo.whichLowColor = fWhichLowColor;
info.viewStateInfo.whichHighColorTint = fWhichHighColorTint;
info.viewStateInfo.whichLowColorTint = fWhichLowColorTint;
info.viewStateInfo.pattern = (::pattern)fPattern.GetPattern();
info.viewStateInfo.drawingMode = fDrawingMode;
info.viewStateInfo.origin = fOrigin;
info.viewStateInfo.scale = fScale;
info.viewStateInfo.lineJoin = fLineJoinMode;
info.viewStateInfo.lineCap = fLineCapMode;
info.viewStateInfo.miterLimit = fMiterLimit;
info.viewStateInfo.fillRule = fFillRule;
info.viewStateInfo.alphaSourceMode = fAlphaSrcMode;
info.viewStateInfo.alphaFunctionMode = fAlphaFncMode;
info.viewStateInfo.fontAntialiasing = fFontAliasing;
link.Attach<ViewGetStateInfo>(info);
// BAffineTransform is transmitted as a double array
double transform[6];
if (fTransform.Flatten(transform, sizeof(transform)) != B_OK)
return;
link.Attach<double[6]>(transform);
// TODO: Could be optimized, but is low prio, since most views do not
// use a custom clipping region...
if (fClippingRegion.IsSet()) {
int32 clippingRectCount = fClippingRegion->CountRects();
link.Attach<int32>(clippingRectCount);
for (int i = 0; i < clippingRectCount; i++)
link.Attach<BRect>(fClippingRegion->RectAt(i));
} else {
// no client clipping
link.Attach<int32>(-1);
}
}
void
DrawState::SetOrigin(BPoint origin)
{
fOrigin = origin;
// NOTE: the origins of earlier states are never expected to
// change, only the topmost state ever changes
if (fPreviousState.IsSet()) {
fCombinedOrigin.x = fPreviousState->fCombinedOrigin.x
+ fOrigin.x * fPreviousState->fCombinedScale;
fCombinedOrigin.y = fPreviousState->fCombinedOrigin.y
+ fOrigin.y * fPreviousState->fCombinedScale;
} else {
fCombinedOrigin = fOrigin;
}
}
void
DrawState::SetScale(float scale)
{
if (fScale == scale)
return;
fScale = scale;
// NOTE: the scales of earlier states are never expected to
// change, only the topmost state ever changes
if (fPreviousState.IsSet())
fCombinedScale = fPreviousState->fCombinedScale * fScale;
else
fCombinedScale = fScale;
// update font size
// NOTE: This is what makes the call potentially expensive,
// hence the introductory check
fFont.SetSize(fUnscaledFontSize * fCombinedScale);
}
void
DrawState::SetTransform(BAffineTransform transform)
{
if (fTransform == transform)
return;
fTransform = transform;
// NOTE: the transforms of earlier states are never expected to
// change, only the topmost state ever changes
if (fPreviousState.IsSet())
fCombinedTransform = fPreviousState->fCombinedTransform * fTransform;
else
fCombinedTransform = fTransform;
}
/* Can be used to temporarily disable all BAffineTransforms in the state
stack, and later reenable them.
*/
void
DrawState::SetTransformEnabled(bool enabled)
{
if (enabled) {
BAffineTransform temp = fTransform;
SetTransform(BAffineTransform());
SetTransform(temp);
}
else
fCombinedTransform = BAffineTransform();
}
DrawState*
DrawState::Squash() const
{
DrawState* const squashedState = new(nothrow) DrawState(*this);
return squashedState->PushState();
}
void
DrawState::SetClippingRegion(const BRegion* region)
{
if (region) {
if (fClippingRegion.IsSet())
*fClippingRegion.Get() = *region;
else
fClippingRegion.SetTo(new(nothrow) BRegion(*region));
} else {
fClippingRegion.Unset();
}
}
bool
DrawState::HasClipping() const
{
if (fClippingRegion.IsSet())
return true;
if (fPreviousState.IsSet())
return fPreviousState->HasClipping();
return false;
}
bool
DrawState::HasAdditionalClipping() const
{
return fClippingRegion.IsSet();
}
bool
DrawState::GetCombinedClippingRegion(BRegion* region) const
{
if (fClippingRegion.IsSet()) {
BRegion localTransformedClipping(*fClippingRegion.Get());
SimpleTransform penTransform;
Transform(penTransform);
penTransform.Apply(&localTransformedClipping);
if (fPreviousState.IsSet()
&& fPreviousState->GetCombinedClippingRegion(region)) {
localTransformedClipping.IntersectWith(region);
}
*region = localTransformedClipping;
return true;
} else {
if (fPreviousState.IsSet())
return fPreviousState->GetCombinedClippingRegion(region);
}
return false;
}
bool
DrawState::ClipToRect(BRect rect, bool inverse)
{
if (!rect.IsValid())
return false;
if (!fCombinedTransform.IsIdentity()) {
if (fCombinedTransform.IsDilation()) {
BPoint points[2] = { rect.LeftTop(), rect.RightBottom() };
fCombinedTransform.Apply(&points[0], 2);
rect.Set(points[0].x, points[0].y, points[1].x, points[1].y);
} else {
uint32 ops[] = {
OP_MOVETO | OP_LINETO | 3,
OP_CLOSE
};
BPoint points[4] = {
BPoint(rect.left, rect.top),
BPoint(rect.right, rect.top),
BPoint(rect.right, rect.bottom),
BPoint(rect.left, rect.bottom)
};
shape_data rectShape;
rectShape.opList = &ops[0];
rectShape.opCount = 2;
rectShape.opSize = sizeof(uint32) * 2;
rectShape.ptList = &points[0];
rectShape.ptCount = 4;
rectShape.ptSize = sizeof(BPoint) * 4;
ClipToShape(&rectShape, inverse);
return true;
}
}
if (inverse) {
if (!fClippingRegion.IsSet()) {
fClippingRegion.SetTo(new(nothrow) BRegion(BRect(
-(1 << 16), -(1 << 16), (1 << 16), (1 << 16))));
// TODO: we should have a definition for a rect (or region)
// with "infinite" area. For now, this region size should do...
}
fClippingRegion->Exclude(rect);
} else {
if (!fClippingRegion.IsSet())
fClippingRegion.SetTo(new(nothrow) BRegion(rect));
else {
BRegion rectRegion(rect);
fClippingRegion->IntersectWith(&rectRegion);
}
}
return false;
}
void
DrawState::ClipToShape(shape_data* shape, bool inverse)
{
if (shape->ptCount == 0)
return;
if (!fCombinedTransform.IsIdentity())
fCombinedTransform.Apply(shape->ptList, shape->ptCount);
BReference<AlphaMask> const mask(ShapeAlphaMask::Create(GetAlphaMask(), *shape,
BPoint(0, 0), inverse), true);
SetAlphaMask(mask);
}
void
DrawState::SetAlphaMask(AlphaMask* mask)
{
// NOTE: In BeOS, it wasn't possible to clip to a BPicture and keep
// regular custom clipping to a BRegion at the same time.
fAlphaMask.SetTo(mask);
}
AlphaMask*
DrawState::GetAlphaMask() const
{
return fAlphaMask.Get();
}
// #pragma mark -
void
DrawState::Transform(SimpleTransform& transform) const
{
transform.AddOffset(fCombinedOrigin.x, fCombinedOrigin.y);
transform.SetScale(fCombinedScale);
}
void
DrawState::InverseTransform(SimpleTransform& transform) const
{
transform.AddOffset(-fCombinedOrigin.x, -fCombinedOrigin.y);
if (fCombinedScale != 0.0)
transform.SetScale(1.0 / fCombinedScale);
}
// #pragma mark -
void
DrawState::SetHighColor(rgb_color color)
{
fHighColor = color;
}
void
DrawState::SetLowColor(rgb_color color)
{
fLowColor = color;
}
void
DrawState::SetHighUIColor(color_which which, float tint)
{
fWhichHighColor = which;
fWhichHighColorTint = tint;
}
color_which
DrawState::HighUIColor(float* tint) const
{
if (tint != NULL)
*tint = fWhichHighColorTint;
return fWhichHighColor;
}
void
DrawState::SetLowUIColor(color_which which, float tint)
{
fWhichLowColor = which;
fWhichLowColorTint = tint;
}
color_which
DrawState::LowUIColor(float* tint) const
{
if (tint != NULL)
*tint = fWhichLowColorTint;
return fWhichLowColor;
}
void
DrawState::SetPattern(const Pattern& pattern)
{
fPattern = pattern;
}
bool
DrawState::SetDrawingMode(drawing_mode mode)
{
if (!fDrawingModeLocked) {
fDrawingMode = mode;
return true;
}
return false;
}
bool
DrawState::SetBlendingMode(source_alpha srcMode, alpha_function fncMode)
{
if (!fDrawingModeLocked) {
fAlphaSrcMode = srcMode;
fAlphaFncMode = fncMode;
return true;
}
return false;
}
void
DrawState::SetDrawingModeLocked(bool locked)
{
fDrawingModeLocked = locked;
}
void
DrawState::SetPenLocation(BPoint location)
{
fPenLocation = location;
}
BPoint
DrawState::PenLocation() const
{
return fPenLocation;
}
void
DrawState::SetPenSize(float size)
{
fPenSize = size;
}
//! returns the scaled pen size
float
DrawState::PenSize() const
{
float penSize = fPenSize * fCombinedScale;
// NOTE: As documented in the BeBook,
// pen size is never smaller than 1.0.
// This is supposed to be the smallest
// possible device size.
if (penSize < 1.0)
penSize = 1.0;
return penSize;
}
//! returns the unscaled pen size
float
DrawState::UnscaledPenSize() const
{
// NOTE: As documented in the BeBook,
// pen size is never smaller than 1.0.
// This is supposed to be the smallest
// possible device size.
return max_c(fPenSize, 1.0);
}
//! sets the font to be already scaled by fScale
void
DrawState::SetFont(const ServerFont& font, uint32 flags)
{
if (flags == B_FONT_ALL) {
fFont = font;
fUnscaledFontSize = font.Size();
fFont.SetSize(fUnscaledFontSize * fCombinedScale);
} else {
// family & style
if ((flags & B_FONT_FAMILY_AND_STYLE) != 0)
fFont.SetFamilyAndStyle(font.GetFamilyAndStyle());
// size
if ((flags & B_FONT_SIZE) != 0) {
fUnscaledFontSize = font.Size();
fFont.SetSize(fUnscaledFontSize * fCombinedScale);
}
// shear
if ((flags & B_FONT_SHEAR) != 0)
fFont.SetShear(font.Shear());
// rotation
if ((flags & B_FONT_ROTATION) != 0)
fFont.SetRotation(font.Rotation());
// spacing
if ((flags & B_FONT_SPACING) != 0)
fFont.SetSpacing(font.Spacing());
// encoding
if ((flags & B_FONT_ENCODING) != 0)
fFont.SetEncoding(font.Encoding());
// face
if ((flags & B_FONT_FACE) != 0)
fFont.SetFace(font.Face());
// flags
if ((flags & B_FONT_FLAGS) != 0)
fFont.SetFlags(font.Flags());
}
}
void
DrawState::SetForceFontAliasing(bool aliasing)
{
fFontAliasing = aliasing;
}
void
DrawState::SetSubPixelPrecise(bool precise)
{
fSubPixelPrecise = precise;
}
void
DrawState::SetLineCapMode(cap_mode mode)
{
fLineCapMode = mode;
}
void
DrawState::SetLineJoinMode(join_mode mode)
{
fLineJoinMode = mode;
}
void
DrawState::SetMiterLimit(float limit)
{
fMiterLimit = limit;
}
void
DrawState::SetFillRule(int32 fillRule)
{
fFillRule = fillRule;
}
void
DrawState::PrintToStream() const
{
printf("\t Origin: (%.1f, %.1f)\n", fOrigin.x, fOrigin.y);
printf("\t Scale: %.2f\n", fScale);
printf("\t Transform: %.2f, %.2f, %.2f, %.2f, %.2f, %.2f\n",
fTransform.sx, fTransform.shy, fTransform.shx,
fTransform.sy, fTransform.tx, fTransform.ty);
printf("\t Pen Location and Size: (%.1f, %.1f) - %.2f (%.2f)\n",
fPenLocation.x, fPenLocation.y, PenSize(), fPenSize);
printf("\t HighColor: r=%d g=%d b=%d a=%d\n",
fHighColor.red, fHighColor.green, fHighColor.blue, fHighColor.alpha);
printf("\t LowColor: r=%d g=%d b=%d a=%d\n",
fLowColor.red, fLowColor.green, fLowColor.blue, fLowColor.alpha);
printf("\t WhichHighColor: %i\n", fWhichHighColor);
printf("\t WhichLowColor: %i\n", fWhichLowColor);
printf("\t WhichHighColorTint: %.3f\n", fWhichHighColorTint);
printf("\t WhichLowColorTint: %.3f\n", fWhichLowColorTint);
printf("\t Pattern: %" B_PRIu64 "\n", fPattern.GetInt64());
printf("\t DrawMode: %" B_PRIu32 "\n", (uint32)fDrawingMode);
printf("\t AlphaSrcMode: %" B_PRId32 "\t AlphaFncMode: %" B_PRId32 "\n",
(int32)fAlphaSrcMode, (int32)fAlphaFncMode);
printf("\t LineCap: %d\t LineJoin: %d\t MiterLimit: %.2f\n",
(int16)fLineCapMode, (int16)fLineJoinMode, fMiterLimit);
if (fClippingRegion.IsSet())
fClippingRegion->PrintToStream();
printf("\t ===== Font Data =====\n");
printf("\t Style: CURRENTLY NOT SET\n"); // ???
printf("\t Size: %.1f (%.1f)\n", fFont.Size(), fUnscaledFontSize);
printf("\t Shear: %.2f\n", fFont.Shear());
printf("\t Rotation: %.2f\n", fFont.Rotation());
printf("\t Spacing: %" B_PRId32 "\n", fFont.Spacing());
printf("\t Encoding: %" B_PRId32 "\n", fFont.Encoding());
printf("\t Face: %d\n", fFont.Face());
printf("\t Flags: %" B_PRIu32 "\n", fFont.Flags());
}