/* For general Scribus (>=1.3.2) copyright and licensing information please refer to the COPYING file provided with the program. Following this notice may exist a copyright and/or license notice that predates the release of Scribus 1.3.2 for which a new license (GPL+exception) is in place. */ /* This file is part of the KDE project. Copyright (C) 2001, 2002, 2003 The Karbon Developers This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Adapted for Scribus 22.08.2003 by Franz Schmid */ // kopainter/libart wrapper #ifdef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN #endif #include #include #include "scconfig.h" #include using namespace Gdiplus; #include "scribusdoc.h" #include "scpainterex_gdiplus.h" #include "sccolorengine.h" #include "util.h" #include #include #include #include #include using namespace std; #include ScPainterEx_GDIPlus::ScPainterEx_GDIPlus( HDC hDC, QRect& rect, ScribusDoc* doc, bool gray ) : ScPainterExBase() { m_doc = doc; m_width = rect.width(); m_height = rect.height(); m_strokeColor = ScColorShade( QColor(0,0,0), 100 ); m_fillColor = ScColorShade( QColor(0,0,0), 100 ); m_fillTrans = 1.0; m_strokeTrans = 1.0; m_fillRule = true; m_fillMode = 1; m_lineWidth = 1.0; m_offset = 0; m_lineEnd = Qt::SquareCap; m_lineJoin = Qt::RoundJoin; m_fillGradient = VGradientEx(VGradientEx::linear); m_strokeGradient = VGradientEx(VGradientEx::linear); // Grayscale conversion parameter m_convertToGray = gray; // Initialization of Windows GDI data m_dc = hDC; m_pathIsClosed = true; m_drawingClosedPath = false; m_positionX = m_positionY = 0; m_graphics = new Gdiplus::Graphics( m_dc ); m_graphics->SetCompositingMode( CompositingModeSourceOver ); m_graphics->SetCompositingQuality( CompositingQualityHighQuality ); m_graphics->SetSmoothingMode( SmoothingModeHighQuality ); m_graphics->SetPageUnit( UnitPoint ); m_graphicsPath = NULL; clear(); } ScPainterEx_GDIPlus::~ScPainterEx_GDIPlus() { if ( m_graphicsPath ) delete m_graphicsPath; if ( m_graphics ) delete m_graphics; } QColor ScPainterEx_GDIPlus::transformColor( ScColorShade& colorShade, double trans ) { QColor c, color; c = ScColorEngine::getShadeColor(colorShade.color, m_doc, colorShade.shade); color = qRgba( qRed(c.rgb()), qGreen(c.rgb()), qBlue(c.rgb()), qAlpha(trans * 255)); if ( m_convertToGray ) { int grayLevel; QColor grayColor; grayLevel = 0.3 * color.red() + 0.59 * color.green() + 0.11 * color.blue(); grayColor = qRgba( grayLevel, grayLevel, grayLevel, qAlpha(color.rgb())); color = grayColor; } return color; } void ScPainterEx_GDIPlus::transformImage( QImage* image, uchar* data, int scan) { int rgb, grey; int imageWidth = image->width(); int imageHeight = image->height(); QRgb* imageBits = (QRgb *) image->bits(); QRgb* pscani; uchar *pscand; pscand = data; for ( int j = 0; j < imageHeight; j++ ) { pscani = (QRgb *) image->scanLine(j); for (int i = 0; i < imageWidth; i++ ) { rgb = pscani[i]; grey = 0.3 * qRed(rgb) + 0.59 * qGreen(rgb) + 0.11 * qBlue(rgb); pscand[i] = grey; } pscand += scan; } } void ScPainterEx_GDIPlus::begin() { } void ScPainterEx_GDIPlus::end() { m_graphics->ResetTransform(); } void ScPainterEx_GDIPlus::clear() { SolidBrush brush( Color(255,255,255) ); m_graphics->FillRectangle(&brush, 0, 0, m_width, m_height); } void ScPainterEx_GDIPlus::clear( ScColorShade &c ) { QColor color = transformColor( c, 1.0 ); QRgb cs = color.rgb(); SolidBrush brush( Color(qAlpha(cs), qRed(cs), qGreen(cs),qBlue(cs)) ); m_graphics->FillRectangle(&brush, 0, 0, m_width, m_height); } const QMatrix ScPainterEx_GDIPlus::worldMatrix() { return m_matrix; } void ScPainterEx_GDIPlus::setWorldMatrix( const QMatrix &mat ) { m_matrix = mat; } void ScPainterEx_GDIPlus::transformPoint( const FPoint& in, FPoint& out ) { double x, y; x = in.x() * m_matrix.m11() + in.y() * m_matrix.m21() + m_matrix.dx(); y = in.x() * m_matrix.m12() + in.y() * m_matrix.m22() + m_matrix.dy(); out.setX( x ); out.setY( y ); } void ScPainterEx_GDIPlus::transformPoints( const FPoint* ArrayIn, FPoint* ArrayOut, uint length ) { for( uint i = 0; i < length; i++ ) { transformPoint( ArrayIn[i], ArrayOut[i] ); } } void ScPainterEx_GDIPlus::translate( double x, double y ) { m_matrix.translate(x, y); } void ScPainterEx_GDIPlus::rotate( double r ) { m_matrix.rotate(r); } void ScPainterEx_GDIPlus::scale( double x, double y ) { m_matrix.scale(x, y); } void ScPainterEx_GDIPlus::moveTo( const double &x, const double &y ) { FPoint pp( x, y ); transformPoint( pp, pp ); m_positionX = pp.x(); m_positionY = pp.y(); } void ScPainterEx_GDIPlus::lineTo( const double &x, const double &y ) { FPoint pp( x, y ); transformPoint( pp, pp ); m_graphicsPath->AddLine( (REAL) m_positionX, (REAL) m_positionY, pp.x(), pp.y() ); m_positionX = pp.x(); m_positionY = pp.y(); } void ScPainterEx_GDIPlus::curveTo( FPoint p1, FPoint p2, FPoint p3 ) { FPoint fpoints[3]; fpoints[0].setXY( p1.x(), p1.y() ); fpoints[1].setXY( p2.x(), p2.y() ); fpoints[2].setXY( p3.x(), p3.y() ); transformPoints( fpoints, fpoints, 3 ); double p1x = fpoints[0].x(); double p1y = fpoints[0].y(); double p2x = fpoints[1].x(); double p2y = fpoints[1].y(); double p3x = fpoints[2].x(); double p3y = fpoints[2].y(); m_graphicsPath->AddBezier( (REAL) m_positionX, (REAL) m_positionY, p1x, p1y, p2x, p2y, p3x, p3y ); m_positionX = p3x; m_positionY = p3y; } void ScPainterEx_GDIPlus::newPath() { if( m_graphicsPath ) delete m_graphicsPath; m_graphicsPath = new Gdiplus::GraphicsPath(); m_pathIsClosed = false; m_drawingClosedPath = false; } void ScPainterEx_GDIPlus::setFillRule( bool fillRule ) { m_fillRule = fillRule; } void ScPainterEx_GDIPlus::setFillMode( int fill ) { m_fillMode = fill; } void ScPainterEx_GDIPlus::setGradient(VGradientEx::Type mode, FPoint orig, FPoint vec, FPoint foc) { m_fillGradient.setType(mode); m_fillGradient.setOrigin(orig); m_fillGradient.setVector(vec); m_fillGradient.setFocalPoint(foc); } void ScPainterEx_GDIPlus::setPattern( ScPattern* pattern, QMatrix& patternTransform ) { m_pattern = pattern; m_patternTransform = patternTransform; } void ScPainterEx_GDIPlus::fillTextPath() { drawVPath( 0 ); } void ScPainterEx_GDIPlus::strokeTextPath() { if( m_lineWidth == 0 ) return; drawVPath( 1 ); } void ScPainterEx_GDIPlus::fillPath() { m_pathIsClosed = true; if( m_fillMode != 0) drawVPath( 0 ); } void ScPainterEx_GDIPlus::strokePath() { if( m_lineWidth == 0 ) return; m_pathIsClosed = true; drawVPath( 1 ); } ScColorShade ScPainterEx_GDIPlus::pen() { return m_strokeColor; } ScColorShade ScPainterEx_GDIPlus::brush() { return m_fillColor; } void ScPainterEx_GDIPlus::setPen( const ScColorShade &c ) { m_strokeColor = c; } void ScPainterEx_GDIPlus::setPen( const ScColorShade &c, double w, Qt::PenStyle st, Qt::PenCapStyle ca, Qt::PenJoinStyle jo ) { m_strokeColor = c; m_lineWidth = w; m_lineEnd = ca; m_lineJoin = jo; // double Dt = qMax(1*w, 1.0); // double Sp = qMax(2*w, 1.0); // double Da = qMax(4*w, 1.0); // QList tmp; m_array.clear(); m_offset = 0; getDashArray(st, w, m_array); /* switch (st) { case Qt::SolidLine: break; case Qt::DashLine: m_array.append(Da); m_array.append(Sp); break; case Qt::DotLine: m_array.append(Dt); m_array.append(Sp); #ifdef SC_USE_GDIPLUS m_array.append(Dt); #endif break; case Qt::DashDotLine: m_array.append(Da); m_array.append(Sp); m_array.append(Dt); m_array.append(Sp); break; case Qt::DashDotDotLine: m_array.append(Da); m_array.append(Sp); m_array.append(Dt); m_array.append(Sp); m_array.append(Dt); m_array.append(Sp); break; default: break; } */ } void ScPainterEx_GDIPlus::setLineWidth( double w ) { m_lineWidth = w; } void ScPainterEx_GDIPlus::setPenOpacity( double op ) { m_strokeTrans = op; } void ScPainterEx_GDIPlus::setDash(const QList& array, double ofs) { m_array = array; m_offset = ofs; } void ScPainterEx_GDIPlus::setBrush( const ScColorShade &c ) { m_fillColor = c; } void ScPainterEx_GDIPlus::setBrushOpacity( double op ) { m_fillTrans = op; } void ScPainterEx_GDIPlus::setOpacity( double op ) { m_fillTrans = op; m_strokeTrans = op; } void ScPainterEx_GDIPlus::setFont( const QFont &f) { m_font = f; } QFont ScPainterEx_GDIPlus::font() { return m_font; } void ScPainterEx_GDIPlus::save() { m_stack.push( m_matrix ); m_gStates.push( m_graphics->Save() ); } void ScPainterEx_GDIPlus::restore() { m_matrix = m_stack.pop(); m_graphics->Restore( m_gStates.pop() ); } void ScPainterEx_GDIPlus::setRasterOp( int ) { } void ScPainterEx_GDIPlus::drawVPath( int mode ) { save(); if (mode == 0) { if( m_fillRule ) m_graphicsPath->SetFillMode( FillModeAlternate); else m_graphicsPath->SetFillMode( FillModeWinding); if (m_fillMode == ScPainterExBase::Gradient) { drawGradient( m_fillGradient ); } else { QColor fillColor = transformColor(m_fillColor, 1.0); SolidBrush fill_brush( Color(m_fillTrans * 255, fillColor.red(), fillColor.green(), fillColor.blue()) ); m_graphics->FillPath( &fill_brush, m_graphicsPath ); } } else { double m11 = m_matrix.m11(); double m12 = m_matrix.m12(); double m21 = m_matrix.m21(); double m22 = m_matrix.m22(); double norm2 = m11 * m11 + m12 * m12 + m21 * m21 + m22 * m22; double penScale = sqrt(norm2 / 2.0); double penWidth = m_lineWidth * penScale; QColor strokeColor = transformColor( m_strokeColor, 1.0 ); SolidBrush stroke_brush( Color(m_strokeTrans * 255, strokeColor.red(), strokeColor.green(), strokeColor.blue()) ); Pen stroke_pen( &stroke_brush, penWidth ); REAL *dashes = NULL; if( m_array.count() > 0 ) { dashes = new REAL[ m_array.count() ]; for( int i = 0; i < m_array.count();++ i ) { dashes[i] = (REAL) ( m_array[i] / (double) m_lineWidth ); // The following lines are needed so that gdi+ rendering matches the libart one if( m_lineEnd == Qt::RoundCap || m_lineEnd == Qt::SquareCap ) { if( (i % 2) == 0 ) dashes[i] = dashes[i] + 1.0; else dashes[i] = dashes[i] - 1.0; } } // The following two lines are needed so that gdi+ rendering matches the libart one if( m_lineEnd == Qt::SquareCap && m_array.count() >= 2) stroke_pen.SetDashOffset( dashes[1]/2 ); stroke_pen.SetDashPattern( dashes, m_array.count() ); } if( m_lineEnd == Qt::RoundCap ) stroke_pen.SetLineCap( LineCapRound, LineCapRound, DashCapRound ); else if( m_lineEnd == Qt::SquareCap ) stroke_pen.SetLineCap( LineCapSquare, LineCapSquare, DashCapFlat ); else if( m_lineEnd == Qt::FlatCap && m_array.count() == 0 && m_drawingClosedPath ) stroke_pen.SetLineCap( LineCapSquare, LineCapSquare, DashCapFlat ); else if( m_lineEnd == Qt::FlatCap ) stroke_pen.SetLineCap( LineCapFlat, LineCapFlat, DashCapFlat ); if( m_lineJoin == Qt::RoundJoin ) stroke_pen.SetLineJoin( LineJoinRound ); else if( m_lineJoin == Qt::BevelJoin ) stroke_pen.SetLineJoin( LineJoinBevel ); else if( m_lineJoin == Qt::MiterJoin ) stroke_pen.SetLineJoin( LineJoinMiter ); m_graphics->DrawPath( &stroke_pen, m_graphicsPath ); if( dashes ) delete [] dashes; } restore(); } void ScPainterEx_GDIPlus::setClipPath() { m_graphics->SetClip( m_graphicsPath, CombineModeIntersect ); } void ScPainterEx_GDIPlus::drawImage( ScImage *image, ScPainterExBase::ImageMode mode ) { FPoint ulp( 0, 0 ); FPoint urp( image->width(), 0 ); FPoint blp( 0, image->height() ); transformPoint( ulp, ulp ); transformPoint( blp, blp ); transformPoint( urp, urp ); Point destinationPoint[] = { Point(ulp.x(), ulp.y()), Point(urp.x(), urp.y()), Point(blp.x(), blp.y()) }; ColorMatrix colorMatrix = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, m_fillTrans, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}; if ( m_convertToGray ) { colorMatrix.m[0][0] = colorMatrix.m[0][1] = colorMatrix.m[0][2] = (REAL) 0.3; colorMatrix.m[1][0] = colorMatrix.m[1][1] = colorMatrix.m[1][2] = (REAL) 0.59; colorMatrix.m[2][0] = colorMatrix.m[2][1] = colorMatrix.m[2][2] = (REAL) 0.11; } ImageAttributes imageAtt; imageAtt.SetColorMatrix(&colorMatrix, ColorMatrixFlagsDefault, ColorAdjustTypeBitmap); Bitmap bitmap( image->width(), image->height(), 4 * image->width(), PixelFormat32bppARGB, (uchar*) image->qImage().bits() ); m_graphics->DrawImage( &bitmap, destinationPoint, 3, 0, 0, image->width(), image->height(), UnitPixel, &imageAtt ); } void ScPainterEx_GDIPlus::setupPolygon(FPointArray *points, bool closed) { bool nPath = true, first = true; FPoint np, np1, np2, np3; if (points->size() > 3) { newPath(); for (uint poi=0; poisize()-3; poi += 4) { if (points->point(poi).x() > 900000) { nPath = true; continue; } if (nPath) { np = points->point(poi); if (!first && closed) m_graphicsPath->CloseFigure(); m_graphicsPath->StartFigure(); moveTo( np.x(), np.y() ); nPath = first = false; } np = points->point(poi); np1 = points->point(poi+1); np2 = points->point(poi+3); np3 = points->point(poi+2); if ((np == np1) && (np2 == np3)) lineTo( np3.x(), np3.y() ); else curveTo(np1, np2, np3); } if (closed) { m_graphicsPath->CloseFigure(); m_pathIsClosed = true; m_drawingClosedPath = true; } } } void ScPainterEx_GDIPlus::drawPolygon() { fillPath(); } void ScPainterEx_GDIPlus::drawPolyLine() { strokePath(); } void ScPainterEx_GDIPlus::drawLine(FPoint start, FPoint end) { newPath(); moveTo(start.x(), start.y()); lineTo(end.x(), end.y()); strokePath(); } void ScPainterEx_GDIPlus::drawRect(double x, double y, double w, double h) { newPath(); moveTo( x, y ); lineTo( x+w, y ); lineTo( x+w, y+h ); lineTo( x, y+h ); lineTo( x, y ); m_pathIsClosed = true; m_drawingClosedPath = true; fillPath(); strokePath(); } void ScPainterEx_GDIPlus::drawGradient( VGradientEx& gradient ) { QRect clipPathRect; save(); setClipPath(); getClipPathDimensions( clipPathRect ); if ( gradient.type() == VGradientEx::linear ) drawLinearGradient( gradient, clipPathRect ); else if ( gradient.type() == VGradientEx::radial ) drawCircularGradient( gradient, clipPathRect ); restore(); } void ScPainterEx_GDIPlus::drawLinearGradient( VGradientEx& gradient, const QRect& rect ) { Matrix matrix; int index = 0; int numElements; int r, g, b, a; double cosa, sina; double length, scale; double x1, y1, x2, y2, dx, dy; int clipBoxWidth, clipBoxHeight, maxDim; QList colorStops = gradient.colorStops(); VColorStopEx stop( *colorStops[0] ); FPoint p1, p1a, p2, p2a; QColor color; if ( gradient.Stops() < 2 ) return; clipBoxWidth = rect.width(); clipBoxHeight = rect.height(); maxDim = sqrt( (double) (clipBoxWidth * clipBoxWidth + clipBoxHeight * clipBoxHeight) ) * 2; if ( maxDim <= 0 ) return; x1 = m_matrix.dx() + gradient.origin().x() * m_matrix.m11(); y1 = m_matrix.dy() + gradient.origin().y() * m_matrix.m22(); x2 = m_matrix.dx() + gradient.vector().x() * m_matrix.m11(); y2 = m_matrix.dy() + gradient.vector().y() * m_matrix.m22(); p1.setXY( x1, y1 ); p2.setXY( x2, y2 ); dx = ( p2.x() - p1.x() ); dy = ( p2.y() - p1.y() ); length = sqrt(( dx * dx + dy * dy )); if ( length == 0.0 ) scale = 1.0; else scale = 1.0 / length; cosa = dx * scale; sina = dy * scale; p1a.setXY( p1.x() - cosa * maxDim, p1.y() - sina * maxDim ); p2a.setXY( p2.x() + cosa * maxDim, p2.y() + sina * maxDim ); numElements = gradient.Stops() + 2; Color *colors = new Color[numElements]; REAL *positions = new REAL[numElements]; stop = *colorStops[0]; color = transformColor( ScColorShade(stop.color, stop.shade), 1.0 ); colors[0] = Color(stop.opacity * 255, color.red(), color.green(), color.blue()); positions[0] = 0.0; stop = *colorStops[gradient.Stops() - 1]; color = transformColor( ScColorShade(stop.color, stop.shade), 1.0 ); colors[numElements - 1] = Color(stop.opacity * 255, color.red(), color.green(), color.blue()); positions[numElements - 1] = 1.0; for( uint index = 0 ; index < gradient.Stops(); index++) { stop = *colorStops[index]; color = transformColor( ScColorShade(stop.color, stop.shade), 1.0 ); r = color.red(); g = color.green(); b = color.blue(); a = stop.opacity * 255; colors[index + 1] = Color(a, r, g, b); positions[index + 1] = ( stop.rampPoint * length + maxDim ) / ( length + 2 * maxDim ); } LinearGradientBrush gradientBrush( PointF( p1a.x(), p1a.y() ) , PointF( p2a.x(), p2a.y() ), colors[0], colors[numElements - 1] ); gradientBrush.SetInterpolationColors(colors, positions, numElements); m_graphics->FillPath( &gradientBrush, m_graphicsPath ); delete[] colors; delete[] positions; } void ScPainterEx_GDIPlus::drawCircularGradient( VGradientEx& gradient, const QRect& rect ) { int offset = 0; int numElements; int r, g, b, a; int clipBoxWidth, clipBoxHeight, maxDim; QList colorStops = gradient.colorStops(); VColorStopEx stop( *colorStops[0] ); QColor color; clipBoxWidth = rect.width(); clipBoxHeight = rect.height(); maxDim = sqrt( (double) (clipBoxWidth * clipBoxWidth + clipBoxHeight * clipBoxHeight) ) * 2; if ( maxDim <= 0 ) return; FPoint pc( gradient.origin().x(), gradient.origin().y() ); FPoint pcz( gradient.origin().x(), gradient.origin().y() ); FPoint pf( gradient.focalPoint().x(), gradient.focalPoint().y() ); FPoint pfz( gradient.focalPoint().x(), gradient.focalPoint().y() ); FPoint pv( gradient.vector().x(), gradient.vector().y() ); transformPoint( pc, pc ); transformPoint( pf, pf ); transformPoint( pv, pv ); transformPoint( pcz, pcz ); transformPoint( pfz, pfz ); double cx = pcz.x(); double cy = pcz.y(); double fx = pfz.x(); double fy = pfz.y(); double rad = sqrt( pow(pv.x() - pc.x(), 2) + pow(pv.y() - pc.y(), 2) ); numElements = gradient.Stops() + 2; Color *colors = new Color[numElements]; REAL *positions = new REAL[numElements]; stop = *colorStops[ 0 ]; color = transformColor( ScColorShade(stop.color, stop.shade), stop.opacity); colors[numElements - 1] = Color(stop.opacity * 255, color.red(), color.green(), color.blue()); positions[numElements - 1] = 1.0; stop = *colorStops[ gradient.Stops() - 1 ]; color = transformColor( ScColorShade(stop.color, stop.shade), stop.opacity); colors[0] = Color(stop.opacity * 255, color.red(), color.green(), color.blue()); positions[0] = 0.0; for( uint index = 0 ; index < gradient.Stops() ; index++) { stop = *colorStops[index]; color = transformColor( ScColorShade(stop.color, stop.shade), 1.0 ); r = color.red(); g = color.green(); b = color.blue(); a = stop.opacity * 255; colors[gradient.Stops() - index] = Color(a, r, g, b); positions[gradient.Stops() - index] = 1 - 2 * (stop.rampPoint * rad) / (double) maxDim; } GraphicsPath gradientPath; gradientPath.AddEllipse( cx - maxDim/2, cy - maxDim/2, maxDim, maxDim ); PathGradientBrush gradientBrush( &gradientPath ); gradientBrush.SetCenterPoint( PointF(cx, cy) ); gradientBrush.SetCenterColor( colors[numElements - 1] ); gradientBrush.SetInterpolationColors( colors, positions, numElements ); m_graphics->FillPath( &gradientBrush, m_graphicsPath ); delete[] colors; delete[] positions; } void ScPainterEx_GDIPlus::getClipPathDimensions( QRect& r ) { Rect rect(0, 0, 0, 0); Status result = m_graphics->GetClipBounds( &rect ); if( result == Ok ) { r.setLeft( rect.GetLeft() ); r.setRight( rect.GetRight() ); r.setBottom( rect.GetBottom() ); r.setTop( rect.GetTop() ); } }