/* * Copyright 2001-2018, Haiku. * Distributed under the terms of the MIT License. * * Authors: * DarkWyrm * Adi Oanca * Stephan Aßmus * Axel Dörfler, axeld@pinc-software.de * Michael Pfeiffer * Julian Harnath * Joseph Groover */ //! Data classes for working with BView states and draw parameters #include "DrawState.h" #include #include #include #include #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, AppFontManager* fontManager) { uint16 mask; link.Read(&mask); if ((mask & B_FONT_FAMILY_AND_STYLE) != 0) { uint32 fontID; link.Read(&fontID); fFont.SetFamilyAndStyle(fontID, fontManager); } if ((mask & B_FONT_SIZE) != 0) { float size; link.Read(&size); fUnscaledFontSize = size; fFont.SetSize(fUnscaledFontSize * fCombinedScale); } if ((mask & B_FONT_SHEAR) != 0) { float shear; link.Read(&shear); fFont.SetShear(shear); } if ((mask & B_FONT_ROTATION) != 0) { float rotation; link.Read(&rotation); fFont.SetRotation(rotation); } if ((mask & B_FONT_FALSE_BOLD_WIDTH) != 0) { float falseBoldWidth; link.Read(&falseBoldWidth); fFont.SetFalseBoldWidth(falseBoldWidth); } if ((mask & B_FONT_SPACING) != 0) { uint8 spacing; link.Read(&spacing); fFont.SetSpacing(spacing); } if ((mask & B_FONT_ENCODING) != 0) { uint8 encoding; link.Read(&encoding); fFont.SetEncoding(encoding); } if ((mask & B_FONT_FACE) != 0) { uint16 face; link.Read(&face); fFont.SetFace(face); } if ((mask & B_FONT_FLAGS) != 0) { uint32 flags; link.Read(&flags); fFont.SetFlags(flags); } return mask; } void DrawState::ReadFromLink(BPrivate::LinkReceiver& link) { ViewSetStateInfo info; link.Read(&info); // BAffineTransform is transmitted as a double array double transform[6]; link.Read(&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(&clipRectCount); if (clipRectCount >= 0) { BRegion region; BRect rect; for (int32 i = 0; i < clipRectCount; i++) { link.Read(&rect); region.Include(rect); } SetClippingRegion(®ion); } 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(info); // BAffineTransform is transmitted as a double array double transform[6]; if (fTransform.Flatten(transform, sizeof(transform)) != B_OK) return; link.Attach(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(clippingRectCount); for (int i = 0; i < clippingRectCount; i++) link.Attach(fClippingRegion->RectAt(i)); } else { // no client clipping link.Attach(-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()) { if (!inverse) { if (!fClippingRegion.IsSet()) fClippingRegion.SetTo(new(nothrow) BRegion()); else fClippingRegion->MakeEmpty(); } 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 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()); }