496 changes: 432 additions & 64 deletions src/core/qgspallabeling.cpp

Large diffs are not rendered by default.

127 changes: 123 additions & 4 deletions src/core/qgspallabeling.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

class QFontMetricsF;
class QPainter;
class QPicture;
class QgsGeometry;
class QgsMapRenderer;
class QgsRectangle;
Expand Down Expand Up @@ -134,6 +135,14 @@ class CORE_EXPORT QgsPalLayerSettings
Percent
};

enum ShadowType
{
ShadowLowest = 0,
ShadowText,
ShadowBuffer,
ShadowShape
};

// update mDataDefinedNames QList in constructor when adding/deleting enum value
enum DataDefinedProperties
{
Expand Down Expand Up @@ -226,6 +235,21 @@ class CORE_EXPORT QgsPalLayerSettings
int shapeTransparency;
QPainter::CompositionMode shapeBlendMode;

// drop shadow
bool shadowDraw;
ShadowType shadowUnder;
int shadowOffsetAngle;
double shadowOffsetDist;
SizeUnit shadowOffsetUnits;
bool shadowOffsetGlobal;
double shadowRadius;
SizeUnit shadowRadiusUnits;
bool shadowRadiusAlphaOnly;
int shadowTransparency;
int shadowScale;
QColor shadowColor;
QPainter::CompositionMode shadowBlendMode;

bool formatNumbers;
int decimals;
bool plusSign;
Expand Down Expand Up @@ -302,6 +326,8 @@ class CORE_EXPORT QgsPalLayerSettings
int mFeatsSendingToPal; // total features tested for sending into PAL (relative to maxNumLabels)
int mFeatsRegPal; // number of features registered in PAL, when using limitNumLabels

bool showingShadowRects; // whether to show debug rectangles for drop shadows

private:
void readDataDefinedPropertyMap( QgsVectorLayer* layer,
QMap < QgsPalLayerSettings::DataDefinedProperties,
Expand Down Expand Up @@ -335,6 +361,91 @@ class CORE_EXPORT QgsLabelCandidate
double cost;
};

/** \ingroup core
* Maintains current state of more grainular and temporal values when creating/painting
* component parts of an individual label (e.g. buffer, background, shadow, etc.).
*/
class CORE_EXPORT QgsLabelComponent
{
public:
QgsLabelComponent(): mText( QString() )
, mOrigin( QgsPoint() )
, mUseOrigin( false )
, mRotation( 0.0 )
, mRotationOffset( 0.0 )
, mUseRotation( false )
, mCenter( QgsPoint() )
, mUseCenter( false )
, mSize( QgsPoint() )
, mOffset( QgsPoint() )
{}

const QString& text() { return mText; }
void setText( const QString& text ) { mText = text; }

const QgsPoint& origin() { return mOrigin; }
void setOrigin( QgsPoint point ) { mOrigin = point; }

bool useOrigin() const { return mUseOrigin; }
void setUseOrigin( bool use ) { mUseOrigin = use; }

double rotation() const { return mRotation; }
void setRotation( double rotation ) { mRotation = rotation; }

double rotationOffset() const { return mRotationOffset; }
void setRotationOffset( double rotation ) { mRotationOffset = rotation; }

bool useRotation() const { return mUseRotation; }
void setUseRotation( bool use ) { mUseRotation = use; }

const QgsPoint& center() { return mCenter; }
void setCenter( QgsPoint point ) { mCenter = point; }

bool useCenter() const { return mUseCenter; }
void setUseCenter( bool use ) { mUseCenter = use; }

const QgsPoint& size() { return mSize; }
void setSize( QgsPoint point ) { mSize = point; }

const QgsPoint& offset() { return mOffset; }
void setOffset( QgsPoint point ) { mOffset = point; }

const QPicture* picture() { return mPicture; }
void setPicture( QPicture* picture ) { mPicture = picture; }

double pictureBuffer() const { return mPictureBuffer; }
void setPictureBuffer( double buffer ) { mPictureBuffer = buffer; }

private:
// current label component text,
// e.g. single line in a multi-line label or charcater in curved labeling
QString mText;
// current origin point for painting (generally current painter rotation point)
QgsPoint mOrigin;
// whether to translate the painter to supplied origin
bool mUseOrigin;
// any rotation to be applied to painter (in radians)
double mRotation;
// any rotation to be applied to painter (in radians) after initial rotation
double mRotationOffset;
// whether to use the rotation to rotate the painter
bool mUseRotation;
// current center point of label compnent, after rotation
QgsPoint mCenter;
// whether to translate the painter to supplied origin based upon center
bool mUseCenter;
// width and height of label component, transformed and ready for painting
QgsPoint mSize;
// any translation offsets to be applied before painting, transformed and ready for painting
QgsPoint mOffset;

// a stored QPicture of painting for the component
QPicture* mPicture;
// buffer for component to accommodate graphic items ignored by QPicture,
// e.g. half-width of an applied QPen, which would extend beyond boundingRect() of QPicture
double mPictureBuffer;
};

class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
{
public:
Expand Down Expand Up @@ -364,6 +475,9 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
void setShowingCandidates( bool showing ) { mShowingCandidates = showing; }
const QList<QgsLabelCandidate>& candidates() { return mCandidates; }

bool isShowingShadowRectangles() const { return mShowingShadowRects; }
void setShowingShadowRectangles( bool showing ) { mShowingShadowRects = showing; }

bool isShowingAllLabels() const { return mShowingAllLabels; }
void setShowingAllLabels( bool showing ) { mShowingAllLabels = showing; }

Expand Down Expand Up @@ -398,12 +512,18 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
//! @note not available in python bindings
void drawLabel( pal::LabelPosition* label, QgsRenderContext& context, QgsPalLayerSettings& tmpLyr, DrawLabelType drawType );

static void drawLabelBuffer( QgsRenderContext& context, QString text, const QgsPalLayerSettings& tmpLyr );
static void drawLabelBuffer( QgsRenderContext& context,
QgsLabelComponent component,
const QgsPalLayerSettings& tmpLyr );

static void drawLabelBackground( QgsRenderContext& context,
const QgsPoint& centerPt, double labelRotation, double labelWidth, double labelHeight,
QgsLabelComponent component,
const QgsPalLayerSettings& tmpLyr );

static void drawLabelShadow( QgsRenderContext& context,
QgsLabelComponent component,
const QgsPalLayerSettings& tmpLyr );

//! load/save engine settings to project file
//! @note added in QGIS 1.9
void loadEngineSettings();
Expand All @@ -428,10 +548,9 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
// list of candidates from last labeling
QList<QgsLabelCandidate> mCandidates;
bool mShowingCandidates;

bool mShowingAllLabels; // whether to avoid collisions or not

bool mSavedWithProject; // whether engine settings have been read from project file
bool mShowingShadowRects; // whether to show debugging rectangles for drop shadows

QgsLabelSearchTree* mLabelSearchTree;
};
Expand Down
76 changes: 76 additions & 0 deletions src/core/symbology-ng/qgssymbollayerv2utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2629,6 +2629,82 @@ void QgsSymbolLayerV2Utils::multiplyImageOpacity( QImage* image, qreal alpha )
}
}

void QgsSymbolLayerV2Utils::blurImageInPlace( QImage& image, const QRect& rect, int radius, bool alphaOnly )
{
// culled from Qt's qpixmapfilter.cpp, see: http://www.qtcentre.org/archive/index.php/t-26534.html
int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
int alpha = ( radius < 1 ) ? 16 : ( radius > 17 ) ? 1 : tab[radius-1];

if ( image.format() != QImage::Format_ARGB32_Premultiplied
&& image.format() != QImage::Format_RGB32 )
{
image = image.convertToFormat( QImage::Format_ARGB32_Premultiplied );
}

int r1 = rect.top();
int r2 = rect.bottom();
int c1 = rect.left();
int c2 = rect.right();

int bpl = image.bytesPerLine();
int rgba[4];
unsigned char* p;

int i1 = 0;
int i2 = 3;

if ( alphaOnly ) // this seems to only work right for a black color
i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );

for ( int col = c1; col <= c2; col++ )
{
p = image.scanLine( r1 ) + col * 4;
for ( int i = i1; i <= i2; i++ )
rgba[i] = p[i] << 4;

p += bpl;
for ( int j = r1; j < r2; j++, p += bpl )
for ( int i = i1; i <= i2; i++ )
p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
}

for ( int row = r1; row <= r2; row++ )
{
p = image.scanLine( row ) + c1 * 4;
for ( int i = i1; i <= i2; i++ )
rgba[i] = p[i] << 4;

p += 4;
for ( int j = c1; j < c2; j++, p += 4 )
for ( int i = i1; i <= i2; i++ )
p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
}

for ( int col = c1; col <= c2; col++ )
{
p = image.scanLine( r2 ) + col * 4;
for ( int i = i1; i <= i2; i++ )
rgba[i] = p[i] << 4;

p -= bpl;
for ( int j = r1; j < r2; j++, p -= bpl )
for ( int i = i1; i <= i2; i++ )
p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
}

for ( int row = r1; row <= r2; row++ )
{
p = image.scanLine( row ) + c2 * 4;
for ( int i = i1; i <= i2; i++ )
rgba[i] = p[i] << 4;

p -= 4;
for ( int j = c1; j < c2; j++, p -= 4 )
for ( int i = i1; i <= i2; i++ )
p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
}
}

#if 0
static bool _QVariantLessThan( const QVariant& lhs, const QVariant& rhs )
{
Expand Down
5 changes: 5 additions & 0 deletions src/core/symbology-ng/qgssymbollayerv2utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ class CORE_EXPORT QgsSymbolLayerV2Utils
/**Multiplies opacity of image pixel values with a (global) transparency value*/
static void multiplyImageOpacity( QImage* image, qreal alpha );

/** Blurs an image in place, e.g. creating Qt-independent drop shadows
* @note added in 1.9
*/
static void blurImageInPlace( QImage& image, const QRect& rect, int radius, bool alphaOnly );

/**Sorts the passed list in requested order*/
static void sortVariantList( QList<QVariant>& list, Qt::SortOrder order );
/**Returns a point on the line from startPoint to directionPoint that is a certain distance away from the starting point*/
Expand Down
69 changes: 38 additions & 31 deletions src/ui/qgsengineconfigdialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>336</width>
<height>352</height>
<height>367</height>
</rect>
</property>
<property name="minimumSize">
Expand Down Expand Up @@ -231,6 +231,39 @@
</property>
</spacer>
</item>
<item row="0" column="0" colspan="3">
<widget class="QCheckBox" name="chkShowAllLabels">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Show all labels and features for all layers</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QCheckBox" name="mSaveWithProjectChkBox">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Save settings with project</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QCheckBox" name="chkShowCandidates">
<property name="text">
<string>Show candidates (for debugging)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
Expand All @@ -251,42 +284,16 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="3">
<widget class="QCheckBox" name="chkShowAllLabels">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Show all labels and features for all layers</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QCheckBox" name="chkShowCandidates">
<property name="text">
<string>Show candidates (for debugging)</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QCheckBox" name="mSaveWithProjectChkBox">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<widget class="QCheckBox" name="mShadowDebugRectChkBox">
<property name="text">
<string>Save settings with project</string>
</property>
<property name="checked">
<bool>false</bool>
<string>Show shadow rectangles (for debugging)</string>
</property>
</widget>
</item>
Expand All @@ -300,7 +307,7 @@
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<height>0</height>
</size>
</property>
</spacer>
Expand Down
392 changes: 244 additions & 148 deletions src/ui/qgslabelingguibase.ui

Large diffs are not rendered by default.