| @@ -85,19 +85,17 @@ public: | |||
| void setOrigin (int x, int y) | |||
| { | |||
| currentState->origin.addXY (x, y); | |||
| addTransform (AffineTransform::translation ((float) x, (float) y)); | |||
| } | |||
| void addTransform (const AffineTransform& /*transform*/) | |||
| void addTransform (const AffineTransform& transform) | |||
| { | |||
| //xxx todo | |||
| jassertfalse; | |||
| currentState->transform = transform.followedBy (currentState->transform); | |||
| } | |||
| float getScaleFactor() | |||
| { | |||
| //xxx | |||
| return 1.0f; | |||
| return currentState->transform.getScaleFactor(); | |||
| } | |||
| bool clipToRectangle (const Rectangle<int>& r) | |||
| @@ -119,24 +117,23 @@ public: | |||
| void clipToPath (const Path& path, const AffineTransform& transform) | |||
| { | |||
| currentState->clipToPath (pathToPathGeometry (path, transform, currentState->origin)); | |||
| currentState->clipToPath (pathToPathGeometry (path, transform)); | |||
| } | |||
| void clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform) | |||
| { | |||
| currentState->clipToImage (sourceImage,transform); | |||
| currentState->clipToImage (sourceImage, transform); | |||
| } | |||
| bool clipRegionIntersects (const Rectangle<int>& r) | |||
| { | |||
| const Rectangle<int> r2 (r + currentState->origin); | |||
| return currentState->clipRect.intersects (r2); | |||
| return currentState->clipRect.intersects (r.toFloat().transformed (currentState->transform).getSmallestIntegerContainer()); | |||
| } | |||
| Rectangle<int> getClipBounds() const | |||
| { | |||
| // xxx could this take into account complex clip regions? | |||
| return currentState->clipRect - currentState->origin; | |||
| return currentState->clipRect.toFloat().transformed (currentState->transform.inverted()).getSmallestIntegerContainer(); | |||
| } | |||
| bool isClipEmpty() const | |||
| @@ -183,14 +180,16 @@ public: | |||
| void fillRect (const Rectangle<int>& r, bool /*replaceExistingContents*/) | |||
| { | |||
| renderingTarget->SetTransform (transformToMatrix (currentState->transform)); | |||
| currentState->createBrush(); | |||
| renderingTarget->FillRectangle (rectangleToRectF (r + currentState->origin), currentState->currentBrush); | |||
| renderingTarget->FillRectangle (rectangleToRectF (r), currentState->currentBrush); | |||
| renderingTarget->SetTransform (D2D1::IdentityMatrix()); | |||
| } | |||
| void fillPath (const Path& p, const AffineTransform& transform) | |||
| { | |||
| currentState->createBrush(); | |||
| ComSmartPtr <ID2D1Geometry> geometry (pathToPathGeometry (p, transform, currentState->origin)); | |||
| ComSmartPtr <ID2D1Geometry> geometry (pathToPathGeometry (p, transform.followedBy (currentState->transform))); | |||
| if (renderingTarget != nullptr) | |||
| renderingTarget->FillGeometry (geometry, currentState->currentBrush); | |||
| @@ -198,10 +197,7 @@ public: | |||
| void drawImage (const Image& image, const AffineTransform& transform) | |||
| { | |||
| const int x = currentState->origin.getX(); | |||
| const int y = currentState->origin.getY(); | |||
| renderingTarget->SetTransform (transformToMatrix (transform) * D2D1::Matrix3x2F::Translation ((FLOAT) x, (FLOAT) y)); | |||
| renderingTarget->SetTransform (transformToMatrix (transform.followedBy (currentState->transform))); | |||
| D2D1_SIZE_U size; | |||
| size.width = image.getWidth(); | |||
| @@ -227,40 +223,37 @@ public: | |||
| void drawLine (const Line <float>& line) | |||
| { | |||
| // xxx doesn't seem to be correctly aligned, may need nudging by 0.5 to match the software renderer's behaviour | |||
| const Line<float> l (line.getStart() + currentState->origin.toFloat(), | |||
| line.getEnd() + currentState->origin.toFloat()); | |||
| renderingTarget->SetTransform (transformToMatrix (currentState->transform)); | |||
| currentState->createBrush(); | |||
| renderingTarget->DrawLine (D2D1::Point2F (l.getStartX(), l.getStartY()), | |||
| D2D1::Point2F (l.getEndX(), l.getEndY()), | |||
| renderingTarget->DrawLine (D2D1::Point2F (line.getStartX(), line.getStartY()), | |||
| D2D1::Point2F (line.getEndX(), line.getEndY()), | |||
| currentState->currentBrush); | |||
| renderingTarget->SetTransform (D2D1::IdentityMatrix()); | |||
| } | |||
| void drawVerticalLine (int x, float top, float bottom) | |||
| { | |||
| // xxx doesn't seem to be correctly aligned, may need nudging by 0.5 to match the software renderer's behaviour | |||
| renderingTarget->SetTransform (transformToMatrix (currentState->transform)); | |||
| currentState->createBrush(); | |||
| x += currentState->origin.getX(); | |||
| const int y = currentState->origin.getY(); | |||
| renderingTarget->DrawLine (D2D1::Point2F ((FLOAT) x, y + top), | |||
| D2D1::Point2F ((FLOAT) x, y + bottom), | |||
| renderingTarget->DrawLine (D2D1::Point2F ((FLOAT) x, top), | |||
| D2D1::Point2F ((FLOAT) x, bottom), | |||
| currentState->currentBrush); | |||
| renderingTarget->SetTransform (D2D1::IdentityMatrix()); | |||
| } | |||
| void drawHorizontalLine (int y, float left, float right) | |||
| { | |||
| // xxx doesn't seem to be correctly aligned, may need nudging by 0.5 to match the software renderer's behaviour | |||
| renderingTarget->SetTransform (transformToMatrix (currentState->transform)); | |||
| currentState->createBrush(); | |||
| y += currentState->origin.getY(); | |||
| const int x = currentState->origin.getX(); | |||
| renderingTarget->DrawLine (D2D1::Point2F (x + left, (FLOAT) y), | |||
| D2D1::Point2F (x + right, (FLOAT) y), | |||
| renderingTarget->DrawLine (D2D1::Point2F (left, (FLOAT) y), | |||
| D2D1::Point2F (right, (FLOAT) y), | |||
| currentState->currentBrush); | |||
| renderingTarget->SetTransform (D2D1::IdentityMatrix()); | |||
| } | |||
| void setFont (const Font& newFont) | |||
| @@ -275,15 +268,14 @@ public: | |||
| void drawGlyph (int glyphNumber, const AffineTransform& transform) | |||
| { | |||
| const float x = (float) currentState->origin.getX(); | |||
| const float y = (float) currentState->origin.getY(); | |||
| currentState->createBrush(); | |||
| currentState->createFont(); | |||
| float hScale = currentState->font.getHorizontalScale(); | |||
| renderingTarget->SetTransform (D2D1::Matrix3x2F::Scale (hScale, 1) * transformToMatrix (transform) * D2D1::Matrix3x2F::Translation (x, y)); | |||
| renderingTarget->SetTransform (transformToMatrix (AffineTransform::scale (hScale, 1.0f) | |||
| .followedBy (transform) | |||
| .followedBy (currentState->transform))); | |||
| const UINT16 glyphIndices = (UINT16) glyphNumber; | |||
| const FLOAT glyphAdvances = 0; | |||
| @@ -305,6 +297,18 @@ public: | |||
| renderingTarget->SetTransform (D2D1::IdentityMatrix()); | |||
| } | |||
| bool drawTextLayout (const AttributedString& text, const Rectangle<float>& area) | |||
| { | |||
| renderingTarget->SetTransform (transformToMatrix (currentState->transform)); | |||
| const Direct2DFactories& factories = Direct2DFactories::getInstance(); | |||
| DirectWriteTypeLayout::drawToD2DContext (text, area, renderingTarget, factories.directWriteFactory, | |||
| factories.d2dFactory, factories.systemFonts); | |||
| renderingTarget->SetTransform (D2D1::IdentityMatrix()); | |||
| return true; | |||
| } | |||
| //============================================================================== | |||
| class SavedState | |||
| { | |||
| @@ -323,8 +327,8 @@ public: | |||
| // bottleneck.. Can the same internal objects be shared by multiple state objects, maybe using copy-on-write? | |||
| setFill (owner.currentState->fillType); | |||
| currentBrush = owner.currentState->currentBrush; | |||
| origin = owner.currentState->origin; | |||
| clipRect = owner.currentState->clipRect; | |||
| transform = owner.currentState->transform; | |||
| font = owner.currentState->font; | |||
| currentFontFace = owner.currentState->currentFontFace; | |||
| @@ -357,7 +361,7 @@ public: | |||
| void clipToRectangle (const Rectangle<int>& r) | |||
| { | |||
| clearClip(); | |||
| clipRect = r + origin; | |||
| clipRect = r.toFloat().transformed (transform).getSmallestIntegerContainer(); | |||
| shouldClipRect = true; | |||
| pushClips(); | |||
| } | |||
| @@ -569,9 +573,6 @@ public: | |||
| { | |||
| if (currentBrush == 0) | |||
| { | |||
| const int x = origin.getX(); | |||
| const int y = origin.getY(); | |||
| if (fillType.isColour()) | |||
| { | |||
| D2D1_COLOR_F colour = colourToD2D (fillType.colour); | |||
| @@ -610,7 +611,7 @@ public: | |||
| D2D1_BRUSH_PROPERTIES brushProps; | |||
| brushProps.opacity = fillType.getOpacity(); | |||
| brushProps.transform = transformToMatrix (fillType.transform); | |||
| brushProps.transform = transformToMatrix (fillType.transform.followedBy (transform)); | |||
| const int numColors = fillType.gradient->getNumColours(); | |||
| @@ -633,7 +634,7 @@ public: | |||
| float r = p1.getDistanceFrom (p2); | |||
| D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES props = | |||
| D2D1::RadialGradientBrushProperties (D2D1::Point2F (p1.getX() + x, p1.getY() + y), | |||
| D2D1::RadialGradientBrushProperties (D2D1::Point2F (p1.x, p1.y), | |||
| D2D1::Point2F (0, 0), | |||
| r, r); | |||
| @@ -648,8 +649,8 @@ public: | |||
| const Point<float>& p2 = fillType.gradient->point2; | |||
| D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES props = | |||
| D2D1::LinearGradientBrushProperties (D2D1::Point2F (p1.getX() + x, p1.getY() + y), | |||
| D2D1::Point2F (p2.getX() + x, p2.getY() + y)); | |||
| D2D1::LinearGradientBrushProperties (D2D1::Point2F (p1.x, p1.y), | |||
| D2D1::Point2F (p2.x, p2.y)); | |||
| owner.renderingTarget->CreateLinearGradientBrush (props, brushProps, gradientStops, linearGradient.resetAndGetPointerAddress()); | |||
| @@ -664,7 +665,7 @@ public: | |||
| Direct2DLowLevelGraphicsContext& owner; | |||
| Point<int> origin; | |||
| AffineTransform transform; | |||
| Font font; | |||
| float fontHeightToEmSizeFactor; | |||
| @@ -722,12 +723,12 @@ private: | |||
| return D2D1::RectF ((float) r.getX(), (float) r.getY(), (float) r.getRight(), (float) r.getBottom()); | |||
| } | |||
| static const D2D1_COLOR_F colourToD2D (const Colour& c) | |||
| static D2D1_COLOR_F colourToD2D (const Colour& c) | |||
| { | |||
| return D2D1::ColorF::ColorF (c.getFloatRed(), c.getFloatGreen(), c.getFloatBlue(), c.getFloatAlpha()); | |||
| } | |||
| static const D2D1_POINT_2F pointTransformed (int x, int y, const AffineTransform& transform = AffineTransform::identity) | |||
| static D2D1_POINT_2F pointTransformed (int x, int y, const AffineTransform& transform) | |||
| { | |||
| transform.transformPoint (x, y); | |||
| return D2D1::Point2F ((FLOAT) x, (FLOAT) y); | |||
| @@ -758,7 +759,7 @@ private: | |||
| return p; | |||
| } | |||
| static void pathToGeometrySink (const Path& path, ID2D1GeometrySink* sink, const AffineTransform& transform, int x, int y) | |||
| static void pathToGeometrySink (const Path& path, ID2D1GeometrySink* sink, const AffineTransform& transform) | |||
| { | |||
| Path::Iterator it (path); | |||
| @@ -771,13 +772,13 @@ private: | |||
| D2D1_BEZIER_SEGMENT seg; | |||
| transform.transformPoint (it.x1, it.y1); | |||
| seg.point1 = D2D1::Point2F (it.x1 + x, it.y1 + y); | |||
| seg.point1 = D2D1::Point2F (it.x1, it.y1); | |||
| transform.transformPoint (it.x2, it.y2); | |||
| seg.point2 = D2D1::Point2F (it.x2 + x, it.y2 + y); | |||
| seg.point2 = D2D1::Point2F (it.x2, it.y2); | |||
| transform.transformPoint(it.x3, it.y3); | |||
| seg.point3 = D2D1::Point2F (it.x3 + x, it.y3 + y); | |||
| seg.point3 = D2D1::Point2F (it.x3, it.y3); | |||
| sink->AddBezier (seg); | |||
| break; | |||
| @@ -786,7 +787,7 @@ private: | |||
| case Path::Iterator::lineTo: | |||
| { | |||
| transform.transformPoint (it.x1, it.y1); | |||
| sink->AddLine (D2D1::Point2F (it.x1 + x, it.y1 + y)); | |||
| sink->AddLine (D2D1::Point2F (it.x1, it.y1)); | |||
| break; | |||
| } | |||
| @@ -795,10 +796,10 @@ private: | |||
| D2D1_QUADRATIC_BEZIER_SEGMENT seg; | |||
| transform.transformPoint (it.x1, it.y1); | |||
| seg.point1 = D2D1::Point2F (it.x1 + x, it.y1 + y); | |||
| seg.point1 = D2D1::Point2F (it.x1, it.y1); | |||
| transform.transformPoint (it.x2, it.y2); | |||
| seg.point2 = D2D1::Point2F (it.x2 + x, it.y2 + y); | |||
| seg.point2 = D2D1::Point2F (it.x2, it.y2); | |||
| sink->AddQuadraticBezier (seg); | |||
| break; | |||
| @@ -813,14 +814,14 @@ private: | |||
| case Path::Iterator::startNewSubPath: | |||
| { | |||
| transform.transformPoint (it.x1, it.y1); | |||
| sink->BeginFigure (D2D1::Point2F (it.x1 + x, it.y1 + y), D2D1_FIGURE_BEGIN_FILLED); | |||
| sink->BeginFigure (D2D1::Point2F (it.x1, it.y1), D2D1_FIGURE_BEGIN_FILLED); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| static ID2D1PathGeometry* pathToPathGeometry (const Path& path, const AffineTransform& transform, const Point<int>& point) | |||
| static ID2D1PathGeometry* pathToPathGeometry (const Path& path, const AffineTransform& transform) | |||
| { | |||
| ID2D1PathGeometry* p = nullptr; | |||
| Direct2DFactories::getInstance().d2dFactory->CreatePathGeometry (&p); | |||
| @@ -829,13 +830,13 @@ private: | |||
| HRESULT hr = p->Open (sink.resetAndGetPointerAddress()); | |||
| sink->SetFillMode (D2D1_FILL_MODE_WINDING); // xxx need to check Path::isUsingNonZeroWinding() | |||
| pathToGeometrySink (path, sink, transform, point.getX(), point.getY()); | |||
| pathToGeometrySink (path, sink, transform); | |||
| hr = sink->Close(); | |||
| return p; | |||
| } | |||
| static const D2D1::Matrix3x2F transformToMatrix (const AffineTransform& transform) | |||
| static D2D1::Matrix3x2F transformToMatrix (const AffineTransform& transform) | |||
| { | |||
| D2D1::Matrix3x2F matrix; | |||
| matrix._11 = transform.mat00; | |||