2 * Copyright 2001-2015, Haiku.
3 * Distributed under the terms of the MIT License.
6 * Marc Flerackers (mflerackers@androme.be)
7 * Stefano Ceccherini (stefano.ceccherini@gmail.com)
8 * Marcus Overhagen <marcus@overhagen.de>
9 * Julian Harnath <julian.harnath@rwth-aachen.de>
12 #include "PictureBoundingBoxPlayer.h"
17 #include "DrawState.h"
18 #include "FontManager.h"
20 #include "ServerApp.h"
21 #include "ServerBitmap.h"
22 #include "ServerFont.h"
23 #include "ServerPicture.h"
24 #include "ServerTokenSpace.h"
31 #include <ObjectListPrivate.h>
32 #include <PicturePlayer.h>
33 #include <PictureProtocol.h>
37 //#define DEBUG_TRACE_BB
39 # define TRACE_BB(text, ...) debug_printf("PBBP: " text, ##__VA_ARGS__)
41 # define TRACE_BB(text, ...)
45 typedef PictureBoundingBoxPlayer::State BoundingBoxState
;
48 // #pragma mark - PictureBoundingBoxPlayer::State
51 class PictureBoundingBoxPlayer::State
{
53 State(const DrawState
* drawState
, BRect
* boundingBox
)
55 fDrawState(drawState
->Squash()),
56 fBoundingBox(boundingBox
)
58 fBoundingBox
->Set(INT_MAX
, INT_MAX
, INT_MIN
, INT_MIN
);
66 DrawState
* GetDrawState()
73 DrawState
* nextState
= fDrawState
->PushState();
74 if (nextState
!= NULL
)
75 fDrawState
= nextState
;
80 if (fDrawState
->PreviousState() != NULL
)
81 fDrawState
= fDrawState
->PopState();
84 SimpleTransform
PenToLocalTransform() const
86 SimpleTransform transform
;
87 fDrawState
->Transform(transform
);
91 void IncludeRect(BRect
& rect
)
93 _AffineTransformRect(rect
);
94 *fBoundingBox
= (*fBoundingBox
) | rect
;
98 void _AffineTransformRect(BRect
& rect
)
100 BAffineTransform transform
= fDrawState
->CombinedTransform();
101 if (transform
.IsIdentity())
104 BPoint transformedShape
[4];
105 transformedShape
[0] = rect
.LeftTop();
106 transformedShape
[1] = rect
.LeftBottom();
107 transformedShape
[2] = rect
.RightTop();
108 transformedShape
[3] = rect
.RightBottom();
110 transform
.Apply(&transformedShape
[0], 4);
112 float minX
= INT_MAX
;
113 float minY
= INT_MAX
;
114 float maxX
= INT_MIN
;
115 float maxY
= INT_MIN
;
117 for (uint32 i
= 0; i
< 4; i
++) {
118 if (transformedShape
[i
].x
< minX
)
119 minX
= transformedShape
[i
].x
;
120 else if (transformedShape
[i
].x
> maxX
)
121 maxX
= transformedShape
[i
].x
;
122 if (transformedShape
[i
].y
< minY
)
123 minY
= transformedShape
[i
].y
;
124 else if (transformedShape
[i
].y
> maxY
)
125 maxY
= transformedShape
[i
].y
;
128 rect
.Set(minX
, minY
, maxX
, maxY
);
133 DrawState
* fDrawState
;
138 // #pragma mark - Picture playback hooks
142 get_polygon_frame(const BPoint
* points
, int32 numPoints
, BRect
* frame
)
144 ASSERT(numPoints
> 0);
146 float left
= points
->x
;
147 float top
= points
->y
;
154 while (numPoints
--) {
155 if (points
->x
< left
)
157 if (points
->x
> right
)
161 if (points
->y
> bottom
)
166 frame
->Set(left
, top
, right
, bottom
);
170 template<class RectType
>
172 expand_rect_for_pen_size(BoundingBoxState
* state
, RectType
& rect
)
174 float penInset
= -((state
->GetDrawState()->PenSize() / 2.0f
) + 1.0f
);
175 rect
.InsetBy(penInset
, penInset
);
180 move_pen_by(void* _state
, const BPoint
& delta
)
182 TRACE_BB("%p move pen by %.2f %.2f\n", _state
, delta
.x
, delta
.y
);
183 BoundingBoxState
* const state
=
184 reinterpret_cast<BoundingBoxState
*>(_state
);
186 state
->GetDrawState()->SetPenLocation(
187 state
->GetDrawState()->PenLocation() + delta
);
192 determine_bounds_stroke_line(void* _state
, const BPoint
& _start
,
195 TRACE_BB("%p stroke line %.2f %.2f -> %.2f %.2f\n", _state
,
196 _start
.x
, _start
.y
, _end
.x
, _end
.y
);
197 BoundingBoxState
* const state
=
198 reinterpret_cast<BoundingBoxState
*>(_state
);
200 BPoint start
= _start
;
203 const SimpleTransform transform
= state
->PenToLocalTransform();
204 transform
.Apply(&start
);
205 transform
.Apply(&end
);
208 if (start
.x
<= end
.x
) {
213 rect
.right
= start
.x
;
215 if (start
.y
<= end
.y
) {
220 rect
.bottom
= start
.y
;
223 expand_rect_for_pen_size(state
, rect
);
224 state
->IncludeRect(rect
);
226 state
->GetDrawState()->SetPenLocation(_end
);
231 determine_bounds_draw_rect(void* _state
, const BRect
& _rect
, bool fill
)
233 TRACE_BB("%p draw rect fill=%d %.2f %.2f %.2f %.2f\n", _state
, fill
,
234 _rect
.left
, _rect
.top
, _rect
.right
, _rect
.bottom
);
235 BoundingBoxState
* const state
=
236 reinterpret_cast<BoundingBoxState
*>(_state
);
239 state
->PenToLocalTransform().Apply(&rect
);
241 expand_rect_for_pen_size(state
, rect
);
242 state
->IncludeRect(rect
);
247 determine_bounds_draw_round_rect(void* _state
, const BRect
& _rect
,
248 const BPoint
&, bool fill
)
250 determine_bounds_draw_rect(_state
, _rect
, fill
);
255 determine_bounds_bezier(BoundingBoxState
* state
, const BPoint
* viewPoints
,
258 // Note: this is an approximation which results in a rectangle which
259 // encloses all four control points. That will always enclose the curve,
260 // although not necessarily tightly, but it's good enough for the purpose.
261 // The exact bounding box of a bezier curve is not trivial to determine,
262 // (need to calculate derivative of the curve) and we're going for
265 state
->PenToLocalTransform().Apply(points
, viewPoints
, 4);
266 BPoint topLeft
= points
[0];
267 BPoint bottomRight
= points
[0];
268 for (uint32 index
= 1; index
< 4; index
++) {
269 if (points
[index
].x
< topLeft
.x
|| points
[index
].y
< topLeft
.y
)
270 topLeft
= points
[index
];
271 if (points
[index
].x
> topLeft
.x
|| points
[index
].y
> topLeft
.y
)
272 bottomRight
= points
[index
];
274 outRect
.SetLeftTop(topLeft
);
275 outRect
.SetRightBottom(bottomRight
);
280 determine_bounds_draw_bezier(void* _state
, size_t numPoints
,
281 const BPoint viewPoints
[], bool fill
)
283 TRACE_BB("%p draw bezier fill=%d (%.2f %.2f) (%.2f %.2f) "
284 "(%.2f %.2f) (%.2f %.2f)\n",
287 viewPoints
[0].x
, viewPoints
[0].y
,
288 viewPoints
[1].x
, viewPoints
[1].y
,
289 viewPoints
[2].x
, viewPoints
[2].y
,
290 viewPoints
[3].x
, viewPoints
[3].y
);
291 BoundingBoxState
* const state
=
292 reinterpret_cast<BoundingBoxState
*>(_state
);
294 const size_t kSupportedPoints
= 4;
295 if (numPoints
!= kSupportedPoints
)
299 determine_bounds_bezier(state
, viewPoints
, rect
);
301 expand_rect_for_pen_size(state
, rect
);
302 state
->IncludeRect(rect
);
307 determine_bounds_draw_ellipse(void* _state
, const BRect
& _rect
, bool fill
)
309 TRACE_BB("%p draw ellipse fill=%d (%.2f %.2f) (%.2f %.2f)\n", _state
, fill
,
310 _rect
.left
, _rect
.top
, _rect
.right
, _rect
.bottom
);
311 BoundingBoxState
* const state
=
312 reinterpret_cast<BoundingBoxState
*>(_state
);
315 state
->PenToLocalTransform().Apply(&rect
);
317 expand_rect_for_pen_size(state
, rect
);
318 state
->IncludeRect(rect
);
323 determine_bounds_draw_arc(void* _state
, const BPoint
& center
,
324 const BPoint
& radii
, float, float, bool fill
)
326 BRect
rect(center
.x
- radii
.x
, center
.y
- radii
.y
,
327 center
.x
+ radii
.x
- 1, center
.y
+ radii
.y
- 1);
328 determine_bounds_draw_ellipse(_state
, rect
, fill
);
333 determine_bounds_polygon(BoundingBoxState
* state
, int32 numPoints
,
334 const BPoint
* viewPoints
, BRect
& outRect
)
339 if (numPoints
<= 200) {
340 // fast path: no malloc/free, also avoid
341 // constructor/destructor calls
342 char data
[200 * sizeof(BPoint
)];
343 BPoint
* points
= (BPoint
*)data
;
345 state
->PenToLocalTransform().Apply(points
, viewPoints
, numPoints
);
346 get_polygon_frame(points
, numPoints
, &outRect
);
349 // avoid constructor/destructor calls by
350 // using malloc instead of new []
351 BPoint
* points
= (BPoint
*)malloc(numPoints
* sizeof(BPoint
));
355 state
->PenToLocalTransform().Apply(points
, viewPoints
, numPoints
);
356 get_polygon_frame(points
, numPoints
, &outRect
);
364 determine_bounds_draw_polygon(void* _state
, size_t numPoints
,
365 const BPoint viewPoints
[], bool, bool fill
)
367 TRACE_BB("%p draw polygon fill=%d (%ld points)\n", _state
, fill
, numPoints
);
368 BoundingBoxState
* const state
=
369 reinterpret_cast<BoundingBoxState
*>(_state
);
372 determine_bounds_polygon(state
, numPoints
, viewPoints
, rect
);
374 expand_rect_for_pen_size(state
, rect
);
375 state
->IncludeRect(rect
);
380 determine_bounds_draw_shape(void* _state
, const BShape
& shape
, bool fill
)
382 BRect rect
= shape
.Bounds();
384 TRACE_BB("%p stroke shape (bounds %.2f %.2f %.2f %.2f)\n", _state
,
385 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
386 BoundingBoxState
* const state
=
387 reinterpret_cast<BoundingBoxState
*>(_state
);
389 state
->PenToLocalTransform().Apply(&rect
);
391 expand_rect_for_pen_size(state
, rect
);
392 state
->IncludeRect(rect
);
397 determine_bounds_draw_string(void* _state
, const char* string
, size_t _length
,
398 float deltaSpace
, float deltaNonSpace
)
400 TRACE_BB("%p string '%s'\n", _state
, string
);
401 BoundingBoxState
* const state
=
402 reinterpret_cast<BoundingBoxState
*>(_state
);
404 ServerFont font
= state
->GetDrawState()->Font();
406 escapement_delta delta
= { deltaSpace
, deltaNonSpace
};
408 int32 length
= _length
;
409 font
.GetBoundingBoxesForStrings((char**)&string
, &length
, 1, &rect
,
410 B_SCREEN_METRIC
, &delta
);
412 BPoint location
= state
->GetDrawState()->PenLocation();
414 state
->PenToLocalTransform().Apply(&location
);
415 rect
.OffsetBy(location
);
416 state
->IncludeRect(rect
);
418 state
->PenToLocalTransform().Apply(&location
);
419 state
->GetDrawState()->SetPenLocation(location
);
424 determine_bounds_draw_pixels(void* _state
, const BRect
&, const BRect
& _dest
,
425 uint32
, uint32
, size_t, color_space
, uint32
, const void*, size_t)
427 TRACE_BB("%p pixels (dest %.2f %.2f %.2f %.2f)\n", _state
,
428 _dest
.left
, _dest
.top
, _dest
.right
, _dest
.bottom
);
429 BoundingBoxState
* const state
=
430 reinterpret_cast<BoundingBoxState
*>(_state
);
433 state
->PenToLocalTransform().Apply(&dest
);
434 state
->IncludeRect(dest
);
439 draw_picture(void* _state
, const BPoint
& where
, int32 token
)
441 TRACE_BB("%p picture (unimplemented)\n", _state
);
451 set_clipping_rects(void* _state
, size_t numRects
, const BRect rects
[])
453 TRACE_BB("%p cliping rects (%ld rects)\n", _state
, numRects
);
463 clip_to_picture(void* _state
, int32 pictureToken
, const BPoint
& where
,
466 TRACE_BB("%p clip to picture (unimplemented)\n", _state
);
473 push_state(void* _state
)
475 TRACE_BB("%p push state\n", _state
);
476 BoundingBoxState
* const state
=
477 reinterpret_cast<BoundingBoxState
*>(_state
);
479 state
->PushDrawState();
484 pop_state(void* _state
)
486 TRACE_BB("%p pop state\n", _state
);
487 BoundingBoxState
* const state
=
488 reinterpret_cast<BoundingBoxState
*>(_state
);
490 state
->PopDrawState();
495 enter_state_change(void*)
501 exit_state_change(void*)
507 enter_font_state(void*)
513 exit_font_state(void*)
519 set_origin(void* _state
, const BPoint
& pt
)
521 TRACE_BB("%p set origin %.2f %.2f\n", _state
, pt
.x
, pt
.y
);
522 BoundingBoxState
* const state
=
523 reinterpret_cast<BoundingBoxState
*>(_state
);
524 state
->GetDrawState()->SetOrigin(pt
);
529 set_pen_location(void* _state
, const BPoint
& pt
)
531 TRACE_BB("%p set pen location %.2f %.2f\n", _state
, pt
.x
, pt
.y
);
532 BoundingBoxState
* const state
=
533 reinterpret_cast<BoundingBoxState
*>(_state
);
534 state
->GetDrawState()->SetPenLocation(pt
);
539 set_drawing_mode(void*, drawing_mode
)
545 set_line_mode(void* _state
, cap_mode capMode
, join_mode joinMode
,
548 BoundingBoxState
* const state
=
549 reinterpret_cast<BoundingBoxState
*>(_state
);
551 DrawState
* drawState
= state
->GetDrawState();
552 drawState
->SetLineCapMode(capMode
);
553 drawState
->SetLineJoinMode(joinMode
);
554 drawState
->SetMiterLimit(miterLimit
);
559 set_pen_size(void* _state
, float size
)
561 TRACE_BB("%p set pen size %.2f\n", _state
, size
);
562 BoundingBoxState
* const state
=
563 reinterpret_cast<BoundingBoxState
*>(_state
);
565 state
->GetDrawState()->SetPenSize(size
);
570 set_fore_color(void* _state
, const rgb_color
& color
)
572 BoundingBoxState
* const state
=
573 reinterpret_cast<BoundingBoxState
*>(_state
);
575 state
->GetDrawState()->SetHighColor(color
);
580 set_back_color(void* _state
, const rgb_color
& color
)
582 BoundingBoxState
* const state
=
583 reinterpret_cast<BoundingBoxState
*>(_state
);
585 state
->GetDrawState()->SetLowColor(color
);
590 set_stipple_pattern(void* _state
, const pattern
& _pattern
)
592 BoundingBoxState
* const state
=
593 reinterpret_cast<BoundingBoxState
*>(_state
);
595 state
->GetDrawState()->SetPattern(Pattern(_pattern
));
600 set_scale(void* _state
, float scale
)
602 BoundingBoxState
* const state
=
603 reinterpret_cast<BoundingBoxState
*>(_state
);
605 state
->GetDrawState()->SetScale(scale
);
610 set_font_family(void* _state
, const char* _family
, size_t length
)
612 BoundingBoxState
* const state
=
613 reinterpret_cast<BoundingBoxState
*>(_state
);
615 BString
family(_family
, length
);
616 FontStyle
* fontStyle
= gFontManager
->GetStyleByIndex(family
, 0);
618 font
.SetStyle(fontStyle
);
619 state
->GetDrawState()->SetFont(font
, B_FONT_FAMILY_AND_STYLE
);
624 set_font_style(void* _state
, const char* _style
, size_t length
)
626 BoundingBoxState
* const state
=
627 reinterpret_cast<BoundingBoxState
*>(_state
);
629 BString
style(_style
, length
);
630 ServerFont
font(state
->GetDrawState()->Font());
631 FontStyle
* fontStyle
= gFontManager
->GetStyle(font
.Family(), style
);
632 font
.SetStyle(fontStyle
);
633 state
->GetDrawState()->SetFont(font
, B_FONT_FAMILY_AND_STYLE
);
638 set_font_spacing(void* _state
, uint8 spacing
)
640 BoundingBoxState
* const state
=
641 reinterpret_cast<BoundingBoxState
*>(_state
);
644 font
.SetSpacing(spacing
);
645 state
->GetDrawState()->SetFont(font
, B_FONT_SPACING
);
650 set_font_size(void* _state
, float size
)
652 BoundingBoxState
* const state
=
653 reinterpret_cast<BoundingBoxState
*>(_state
);
657 state
->GetDrawState()->SetFont(font
, B_FONT_SIZE
);
662 set_font_rotate(void* _state
, float rotation
)
664 BoundingBoxState
* const state
=
665 reinterpret_cast<BoundingBoxState
*>(_state
);
668 font
.SetRotation(rotation
);
669 state
->GetDrawState()->SetFont(font
, B_FONT_ROTATION
);
674 set_font_encoding(void* _state
, uint8 encoding
)
676 BoundingBoxState
* const state
=
677 reinterpret_cast<BoundingBoxState
*>(_state
);
680 font
.SetEncoding(encoding
);
681 state
->GetDrawState()->SetFont(font
, B_FONT_ENCODING
);
686 set_font_flags(void* _state
, uint32 flags
)
688 BoundingBoxState
* const state
=
689 reinterpret_cast<BoundingBoxState
*>(_state
);
692 font
.SetFlags(flags
);
693 state
->GetDrawState()->SetFont(font
, B_FONT_FLAGS
);
698 set_font_shear(void* _state
, float shear
)
700 BoundingBoxState
* const state
=
701 reinterpret_cast<BoundingBoxState
*>(_state
);
704 font
.SetShear(shear
);
705 state
->GetDrawState()->SetFont(font
, B_FONT_SHEAR
);
710 set_font_face(void* _state
, uint16 face
)
712 BoundingBoxState
* const state
=
713 reinterpret_cast<BoundingBoxState
*>(_state
);
717 state
->GetDrawState()->SetFont(font
, B_FONT_FACE
);
722 set_blending_mode(void*, source_alpha
, alpha_function
)
728 set_transform(void* _state
, const BAffineTransform
& transform
)
730 TRACE_BB("%p transform\n", _state
);
731 BoundingBoxState
* const state
=
732 reinterpret_cast<BoundingBoxState
*>(_state
);
733 state
->GetDrawState()->SetTransform(transform
);
738 translate_by(void* _state
, double x
, double y
)
740 TRACE_BB("%p translate\n", _state
);
741 BoundingBoxState
* const state
=
742 reinterpret_cast<BoundingBoxState
*>(_state
);
743 BAffineTransform transform
= state
->GetDrawState()->Transform();
744 transform
.PreTranslateBy(x
, y
);
745 state
->GetDrawState()->SetTransform(transform
);
750 scale_by(void* _state
, double x
, double y
)
752 TRACE_BB("%p scale\n", _state
);
753 BoundingBoxState
* const state
=
754 reinterpret_cast<BoundingBoxState
*>(_state
);
755 BAffineTransform transform
= state
->GetDrawState()->Transform();
756 transform
.PreScaleBy(x
, y
);
757 state
->GetDrawState()->SetTransform(transform
);
762 rotate_by(void* _state
, double angleRadians
)
764 TRACE_BB("%p rotate\n", _state
);
765 BoundingBoxState
* const state
=
766 reinterpret_cast<BoundingBoxState
*>(_state
);
767 BAffineTransform transform
= state
->GetDrawState()->Transform();
768 transform
.PreRotateBy(angleRadians
);
769 state
->GetDrawState()->SetTransform(transform
);
774 determine_bounds_nested_layer(void* _state
, Layer
* layer
)
776 TRACE_BB("%p nested layer\n", _state
);
777 BoundingBoxState
* const state
=
778 reinterpret_cast<BoundingBoxState
*>(_state
);
781 PictureBoundingBoxPlayer::Play(layer
, state
->GetDrawState(), &boundingBox
);
782 if (boundingBox
.IsValid())
783 state
->IncludeRect(boundingBox
);
787 static const BPrivate::picture_player_callbacks
788 kPictureBoundingBoxPlayerCallbacks
= {
790 determine_bounds_stroke_line
,
791 determine_bounds_draw_rect
,
792 determine_bounds_draw_round_rect
,
793 determine_bounds_draw_bezier
,
794 determine_bounds_draw_arc
,
795 determine_bounds_draw_ellipse
,
796 determine_bounds_draw_polygon
,
797 determine_bounds_draw_shape
,
798 determine_bounds_draw_string
,
799 determine_bounds_draw_pixels
,
832 determine_bounds_nested_layer
836 // #pragma mark - PictureBoundingBoxPlayer
840 PictureBoundingBoxPlayer::Play(ServerPicture
* picture
,
841 const DrawState
* drawState
, BRect
* outBoundingBox
)
843 State
state(drawState
, outBoundingBox
);
845 BMallocIO
* mallocIO
= dynamic_cast<BMallocIO
*>(picture
->fData
);
846 if (mallocIO
== NULL
)
849 BPrivate::PicturePlayer
player(mallocIO
->Buffer(),
850 mallocIO
->BufferLength(), ServerPicture::PictureList::Private(
851 picture
->fPictures
).AsBList());
852 player
.Play(kPictureBoundingBoxPlayerCallbacks
,
853 sizeof(kPictureBoundingBoxPlayerCallbacks
), &state
);