| @@ -85,19 +85,17 @@ public: | |||||
| void setOrigin (int x, int y) | 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() | float getScaleFactor() | ||||
| { | { | ||||
| //xxx | |||||
| return 1.0f; | |||||
| return currentState->transform.getScaleFactor(); | |||||
| } | } | ||||
| bool clipToRectangle (const Rectangle<int>& r) | bool clipToRectangle (const Rectangle<int>& r) | ||||
| @@ -119,24 +117,23 @@ public: | |||||
| void clipToPath (const Path& path, const AffineTransform& transform) | 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) | void clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform) | ||||
| { | { | ||||
| currentState->clipToImage (sourceImage,transform); | |||||
| currentState->clipToImage (sourceImage, transform); | |||||
| } | } | ||||
| bool clipRegionIntersects (const Rectangle<int>& r) | 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 | Rectangle<int> getClipBounds() const | ||||
| { | { | ||||
| // xxx could this take into account complex clip regions? | // 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 | bool isClipEmpty() const | ||||
| @@ -183,14 +180,16 @@ public: | |||||
| void fillRect (const Rectangle<int>& r, bool /*replaceExistingContents*/) | void fillRect (const Rectangle<int>& r, bool /*replaceExistingContents*/) | ||||
| { | { | ||||
| renderingTarget->SetTransform (transformToMatrix (currentState->transform)); | |||||
| currentState->createBrush(); | 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) | void fillPath (const Path& p, const AffineTransform& transform) | ||||
| { | { | ||||
| currentState->createBrush(); | currentState->createBrush(); | ||||
| ComSmartPtr <ID2D1Geometry> geometry (pathToPathGeometry (p, transform, currentState->origin)); | |||||
| ComSmartPtr <ID2D1Geometry> geometry (pathToPathGeometry (p, transform.followedBy (currentState->transform))); | |||||
| if (renderingTarget != nullptr) | if (renderingTarget != nullptr) | ||||
| renderingTarget->FillGeometry (geometry, currentState->currentBrush); | renderingTarget->FillGeometry (geometry, currentState->currentBrush); | ||||
| @@ -198,10 +197,7 @@ public: | |||||
| void drawImage (const Image& image, const AffineTransform& transform) | 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; | D2D1_SIZE_U size; | ||||
| size.width = image.getWidth(); | size.width = image.getWidth(); | ||||
| @@ -227,40 +223,37 @@ public: | |||||
| void drawLine (const Line <float>& line) | 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 | // 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(); | 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); | currentState->currentBrush); | ||||
| renderingTarget->SetTransform (D2D1::IdentityMatrix()); | |||||
| } | } | ||||
| void drawVerticalLine (int x, float top, float bottom) | 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 | // 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(); | 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); | currentState->currentBrush); | ||||
| renderingTarget->SetTransform (D2D1::IdentityMatrix()); | |||||
| } | } | ||||
| void drawHorizontalLine (int y, float left, float right) | 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 | // 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(); | 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); | currentState->currentBrush); | ||||
| renderingTarget->SetTransform (D2D1::IdentityMatrix()); | |||||
| } | } | ||||
| void setFont (const Font& newFont) | void setFont (const Font& newFont) | ||||
| @@ -275,15 +268,14 @@ public: | |||||
| void drawGlyph (int glyphNumber, const AffineTransform& transform) | void drawGlyph (int glyphNumber, const AffineTransform& transform) | ||||
| { | { | ||||
| const float x = (float) currentState->origin.getX(); | |||||
| const float y = (float) currentState->origin.getY(); | |||||
| currentState->createBrush(); | currentState->createBrush(); | ||||
| currentState->createFont(); | currentState->createFont(); | ||||
| float hScale = currentState->font.getHorizontalScale(); | 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 UINT16 glyphIndices = (UINT16) glyphNumber; | ||||
| const FLOAT glyphAdvances = 0; | const FLOAT glyphAdvances = 0; | ||||
| @@ -305,6 +297,18 @@ public: | |||||
| renderingTarget->SetTransform (D2D1::IdentityMatrix()); | 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 | class SavedState | ||||
| { | { | ||||
| @@ -323,8 +327,8 @@ public: | |||||
| // bottleneck.. Can the same internal objects be shared by multiple state objects, maybe using copy-on-write? | // bottleneck.. Can the same internal objects be shared by multiple state objects, maybe using copy-on-write? | ||||
| setFill (owner.currentState->fillType); | setFill (owner.currentState->fillType); | ||||
| currentBrush = owner.currentState->currentBrush; | currentBrush = owner.currentState->currentBrush; | ||||
| origin = owner.currentState->origin; | |||||
| clipRect = owner.currentState->clipRect; | clipRect = owner.currentState->clipRect; | ||||
| transform = owner.currentState->transform; | |||||
| font = owner.currentState->font; | font = owner.currentState->font; | ||||
| currentFontFace = owner.currentState->currentFontFace; | currentFontFace = owner.currentState->currentFontFace; | ||||
| @@ -357,7 +361,7 @@ public: | |||||
| void clipToRectangle (const Rectangle<int>& r) | void clipToRectangle (const Rectangle<int>& r) | ||||
| { | { | ||||
| clearClip(); | clearClip(); | ||||
| clipRect = r + origin; | |||||
| clipRect = r.toFloat().transformed (transform).getSmallestIntegerContainer(); | |||||
| shouldClipRect = true; | shouldClipRect = true; | ||||
| pushClips(); | pushClips(); | ||||
| } | } | ||||
| @@ -569,9 +573,6 @@ public: | |||||
| { | { | ||||
| if (currentBrush == 0) | if (currentBrush == 0) | ||||
| { | { | ||||
| const int x = origin.getX(); | |||||
| const int y = origin.getY(); | |||||
| if (fillType.isColour()) | if (fillType.isColour()) | ||||
| { | { | ||||
| D2D1_COLOR_F colour = colourToD2D (fillType.colour); | D2D1_COLOR_F colour = colourToD2D (fillType.colour); | ||||
| @@ -610,7 +611,7 @@ public: | |||||
| D2D1_BRUSH_PROPERTIES brushProps; | D2D1_BRUSH_PROPERTIES brushProps; | ||||
| brushProps.opacity = fillType.getOpacity(); | brushProps.opacity = fillType.getOpacity(); | ||||
| brushProps.transform = transformToMatrix (fillType.transform); | |||||
| brushProps.transform = transformToMatrix (fillType.transform.followedBy (transform)); | |||||
| const int numColors = fillType.gradient->getNumColours(); | const int numColors = fillType.gradient->getNumColours(); | ||||
| @@ -633,7 +634,7 @@ public: | |||||
| float r = p1.getDistanceFrom (p2); | float r = p1.getDistanceFrom (p2); | ||||
| D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES props = | 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), | D2D1::Point2F (0, 0), | ||||
| r, r); | r, r); | ||||
| @@ -648,8 +649,8 @@ public: | |||||
| const Point<float>& p2 = fillType.gradient->point2; | const Point<float>& p2 = fillType.gradient->point2; | ||||
| D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES props = | 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()); | owner.renderingTarget->CreateLinearGradientBrush (props, brushProps, gradientStops, linearGradient.resetAndGetPointerAddress()); | ||||
| @@ -664,7 +665,7 @@ public: | |||||
| Direct2DLowLevelGraphicsContext& owner; | Direct2DLowLevelGraphicsContext& owner; | ||||
| Point<int> origin; | |||||
| AffineTransform transform; | |||||
| Font font; | Font font; | ||||
| float fontHeightToEmSizeFactor; | float fontHeightToEmSizeFactor; | ||||
| @@ -722,12 +723,12 @@ private: | |||||
| return D2D1::RectF ((float) r.getX(), (float) r.getY(), (float) r.getRight(), (float) r.getBottom()); | 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()); | 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); | transform.transformPoint (x, y); | ||||
| return D2D1::Point2F ((FLOAT) x, (FLOAT) y); | return D2D1::Point2F ((FLOAT) x, (FLOAT) y); | ||||
| @@ -758,7 +759,7 @@ private: | |||||
| return p; | 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); | Path::Iterator it (path); | ||||
| @@ -771,13 +772,13 @@ private: | |||||
| D2D1_BEZIER_SEGMENT seg; | D2D1_BEZIER_SEGMENT seg; | ||||
| transform.transformPoint (it.x1, it.y1); | 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); | 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); | 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); | sink->AddBezier (seg); | ||||
| break; | break; | ||||
| @@ -786,7 +787,7 @@ private: | |||||
| case Path::Iterator::lineTo: | case Path::Iterator::lineTo: | ||||
| { | { | ||||
| transform.transformPoint (it.x1, it.y1); | 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; | break; | ||||
| } | } | ||||
| @@ -795,10 +796,10 @@ private: | |||||
| D2D1_QUADRATIC_BEZIER_SEGMENT seg; | D2D1_QUADRATIC_BEZIER_SEGMENT seg; | ||||
| transform.transformPoint (it.x1, it.y1); | 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); | 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); | sink->AddQuadraticBezier (seg); | ||||
| break; | break; | ||||
| @@ -813,14 +814,14 @@ private: | |||||
| case Path::Iterator::startNewSubPath: | case Path::Iterator::startNewSubPath: | ||||
| { | { | ||||
| transform.transformPoint (it.x1, it.y1); | 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; | 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; | ID2D1PathGeometry* p = nullptr; | ||||
| Direct2DFactories::getInstance().d2dFactory->CreatePathGeometry (&p); | Direct2DFactories::getInstance().d2dFactory->CreatePathGeometry (&p); | ||||
| @@ -829,13 +830,13 @@ private: | |||||
| HRESULT hr = p->Open (sink.resetAndGetPointerAddress()); | HRESULT hr = p->Open (sink.resetAndGetPointerAddress()); | ||||
| sink->SetFillMode (D2D1_FILL_MODE_WINDING); // xxx need to check Path::isUsingNonZeroWinding() | 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(); | hr = sink->Close(); | ||||
| return p; | return p; | ||||
| } | } | ||||
| static const D2D1::Matrix3x2F transformToMatrix (const AffineTransform& transform) | |||||
| static D2D1::Matrix3x2F transformToMatrix (const AffineTransform& transform) | |||||
| { | { | ||||
| D2D1::Matrix3x2F matrix; | D2D1::Matrix3x2F matrix; | ||||
| matrix._11 = transform.mat00; | matrix._11 = transform.mat00; | ||||