Use custom class for storing distance fields instead of QImage.

Distance fields are stored using a one-byte alpha component per pixel, a
format that QImage doesn't currently support. The Indexed8 format was used
instead, limiting what could be done with the QImage.
This patch introduces a new private class, QDistanceField, with a similar
API to QImage and using the Alpha8 pixel format. Unlike QImage which
aligns scanlines on a 4-byte boundary, QDistanceField tightly packs
scanlines together.

Task-number: QTBUG-30908
Task-number: QTBUG-32861
Change-Id: Ic273259ea07dfbd2b81a6358c0ca11a2330eb749
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>
This commit is contained in:
Yoann Lopes 2013-08-21 13:54:07 +02:00 committed by The Qt Project
parent f3a53eae80
commit 5971e09187
2 changed files with 307 additions and 30 deletions

View File

@ -487,15 +487,19 @@ static void drawPolygons(qint32 *bits, int width, int height, const QPoint *vert
}
}
static QImage makeDistanceField(int imgWidth, int imgHeight, const QPainterPath &path, int dfScale, int offs)
static void makeDistanceField(QDistanceFieldData *data, const QPainterPath &path, int dfScale, int offs)
{
QImage image(imgWidth, imgHeight, QImage::Format_Indexed8);
if (!data || !data->data)
return;
if (path.isEmpty()) {
image.fill(0);
return image;
memset(data->data, 0, data->nbytes);
return;
}
int imgWidth = data->width;
int imgHeight = data->height;
QTransform transform;
transform.translate(offs, offs);
transform.scale(qreal(1) / dfScale, qreal(1) / dfScale);
@ -521,8 +525,8 @@ static QImage makeDistanceField(int imgWidth, int imgHeight, const QPainterPath
QVarLengthArray<bool> isConvex;
QVarLengthArray<bool> needsClipping;
drawPolygons(bits.data(), imgWidth, imgHeight, pathVertices.data(), indices, pathIndices.size(),
interiorColor);
drawPolygons(bits.data(), imgWidth, imgHeight, pathVertices.data(),
indices, pathIndices.size(), interiorColor);
int index = 0;
@ -681,15 +685,11 @@ static QImage makeDistanceField(int imgWidth, int imgHeight, const QPainterPath
}
const qint32 *inLine = bits.data();
uchar *outLine = image.bits();
int padding = image.bytesPerLine() - image.width();
uchar *outLine = data->data;
for (int y = 0; y < imgHeight; ++y) {
for (int x = 0; x < imgWidth; ++x, ++inLine, ++outLine)
*outLine = uchar((0x7f80 - *inLine) >> 8);
outLine += padding;
}
return image;
}
static bool imageHasNarrowOutlines(const QImage &im)
@ -769,31 +769,96 @@ bool qt_fontHasNarrowOutlines(const QRawFont &f)
QRawFont::PixelAntialiasing));
}
static QImage renderDistanceFieldPath(const QPainterPath &path, bool doubleResolution)
QDistanceFieldData::QDistanceFieldData(const QDistanceFieldData &other)
: QSharedData(other)
, glyph(other.glyph)
, width(other.width)
, height(other.height)
, nbytes(other.nbytes)
{
if (nbytes && other.data)
data = (uchar *)memcpy(malloc(nbytes), other.data, nbytes);
else
data = 0;
}
QDistanceFieldData::~QDistanceFieldData()
{
free(data);
}
QDistanceFieldData *QDistanceFieldData::create(const QSize &size)
{
QDistanceFieldData *data = new QDistanceFieldData;
if (size.isValid()) {
data->width = size.width();
data->height = size.height();
// pixel data stored as a 1-byte alpha value
data->nbytes = data->width * data->height; // tightly packed
data->data = (uchar *)malloc(data->nbytes);
}
return data;
}
QDistanceFieldData *QDistanceFieldData::create(const QPainterPath &path, bool doubleResolution)
{
int dfMargin = QT_DISTANCEFIELD_RADIUS(doubleResolution) / QT_DISTANCEFIELD_SCALE(doubleResolution);
int glyphWidth = qCeil(path.boundingRect().width() / QT_DISTANCEFIELD_SCALE(doubleResolution)) + dfMargin * 2;
QImage im = makeDistanceField(glyphWidth,
QT_DISTANCEFIELD_TILESIZE(doubleResolution),
path,
QT_DISTANCEFIELD_SCALE(doubleResolution),
QT_DISTANCEFIELD_RADIUS(doubleResolution) / QT_DISTANCEFIELD_SCALE(doubleResolution));
return im;
QDistanceFieldData *data = create(QSize(glyphWidth, QT_DISTANCEFIELD_TILESIZE(doubleResolution)));
makeDistanceField(data,
path,
QT_DISTANCEFIELD_SCALE(doubleResolution),
QT_DISTANCEFIELD_RADIUS(doubleResolution) / QT_DISTANCEFIELD_SCALE(doubleResolution));
return data;
}
QImage qt_renderDistanceFieldGlyph(QFontEngine *fe, glyph_t glyph, bool doubleResolution)
QDistanceField::QDistanceField()
: d(new QDistanceFieldData)
{
QFixedPoint position;
QPainterPath path;
fe->addGlyphsToPath(&glyph, &position, 1, &path, 0);
path.translate(-path.boundingRect().topLeft());
path.setFillRule(Qt::WindingFill);
return renderDistanceFieldPath(path, doubleResolution);
}
QImage qt_renderDistanceFieldGlyph(const QRawFont &font, glyph_t glyph, bool doubleResolution)
QDistanceField::QDistanceField(int width, int height)
: d(QDistanceFieldData::create(QSize(width, height)))
{
}
QDistanceField::QDistanceField(const QDistanceField &other)
{
d = other.d;
}
QDistanceField::QDistanceField(const QRawFont &font, glyph_t glyph, bool doubleResolution)
{
setGlyph(font, glyph, doubleResolution);
}
QDistanceField::QDistanceField(QFontEngine *fontEngine, glyph_t glyph, bool doubleResolution)
{
setGlyph(fontEngine, glyph, doubleResolution);
}
QDistanceField::QDistanceField(QDistanceFieldData *data)
: d(data)
{
}
bool QDistanceField::isNull() const
{
return !d->data;
}
glyph_t QDistanceField::glyph() const
{
return d->glyph;
}
void QDistanceField::setGlyph(const QRawFont &font, glyph_t glyph, bool doubleResolution)
{
QRawFont renderFont = font;
renderFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(doubleResolution) * QT_DISTANCEFIELD_SCALE(doubleResolution));
@ -802,7 +867,158 @@ QImage qt_renderDistanceFieldGlyph(const QRawFont &font, glyph_t glyph, bool dou
path.translate(-path.boundingRect().topLeft());
path.setFillRule(Qt::WindingFill);
return renderDistanceFieldPath(path, doubleResolution);
d = QDistanceFieldData::create(path, doubleResolution);
d->glyph = glyph;
}
void QDistanceField::setGlyph(QFontEngine *fontEngine, glyph_t glyph, bool doubleResolution)
{
QFixedPoint position;
QPainterPath path;
fontEngine->addGlyphsToPath(&glyph, &position, 1, &path, 0);
path.translate(-path.boundingRect().topLeft());
path.setFillRule(Qt::WindingFill);
d = QDistanceFieldData::create(path, doubleResolution);
d->glyph = glyph;
}
int QDistanceField::width() const
{
return d->width;
}
int QDistanceField::height() const
{
return d->height;
}
QDistanceField QDistanceField::copy(const QRect &r) const
{
if (isNull())
return QDistanceField();
if (r.isNull())
return QDistanceField(new QDistanceFieldData(*d));
int x = r.x();
int y = r.y();
int w = r.width();
int h = r.height();
int dx = 0;
int dy = 0;
if (w <= 0 || h <= 0)
return QDistanceField();
QDistanceField df(w, h);
if (df.isNull())
return df;
if (x < 0 || y < 0 || x + w > d->width || y + h > d->height) {
memset(df.d->data, 0, df.d->nbytes);
if (x < 0) {
dx = -x;
x = 0;
}
if (y < 0) {
dy = -y;
y = 0;
}
}
int pixels_to_copy = qMax(w - dx, 0);
if (x > d->width)
pixels_to_copy = 0;
else if (pixels_to_copy > d->width - x)
pixels_to_copy = d->width - x;
int lines_to_copy = qMax(h - dy, 0);
if (y > d->height)
lines_to_copy = 0;
else if (lines_to_copy > d->height - y)
lines_to_copy = d->height - y;
const uchar *src = d->data + x + y * d->width;
uchar *dest = df.d->data + dx + dy * df.d->width;
for (int i = 0; i < lines_to_copy; ++i) {
memcpy(dest, src, pixels_to_copy);
src += d->width;
dest += df.d->width;
}
df.d->glyph = d->glyph;
return df;
}
uchar *QDistanceField::bits()
{
return d->data;
}
const uchar *QDistanceField::bits() const
{
return d->data;
}
const uchar *QDistanceField::constBits() const
{
return d->data;
}
uchar *QDistanceField::scanLine(int i)
{
if (isNull())
return 0;
Q_ASSERT(i >= 0 && i < d->height);
return d->data + i * d->width;
}
const uchar *QDistanceField::scanLine(int i) const
{
if (isNull())
return 0;
Q_ASSERT(i >= 0 && i < d->height);
return d->data + i * d->width;
}
const uchar *QDistanceField::constScanLine(int i) const
{
if (isNull())
return 0;
Q_ASSERT(i >= 0 && i < d->height);
return d->data + i * d->width;
}
QImage QDistanceField::toImage(QImage::Format format) const
{
if (isNull())
return QImage();
QImage image(d->width, d->height, format == QImage::Format_Indexed8 ?
format : QImage::Format_ARGB32_Premultiplied);
if (image.isNull())
return image;
if (format == QImage::Format_Indexed8) {
for (int y = 0; y < d->height; ++y)
memcpy(image.scanLine(y), scanLine(y), d->width);
} else {
for (int y = 0; y < d->height; ++y) {
for (int x = 0; x < d->width; ++x) {
uint alpha = *(d->data + x + y * d->width);
image.setPixel(x, y, alpha << 24);
}
}
if (image.format() != format)
image = image.convertToFormat(format);
}
return image;
}
QT_END_NAMESPACE

View File

@ -55,6 +55,7 @@
#include <qrawfont.h>
#include <private/qfontengine_p.h>
#include <QtCore/qshareddata.h>
QT_BEGIN_NAMESPACE
@ -78,9 +79,69 @@ QT_BEGIN_NAMESPACE
QT_DISTANCEFIELD_DEFAULT_RADIUS)
bool Q_GUI_EXPORT qt_fontHasNarrowOutlines(const QRawFont &f);
QImage Q_GUI_EXPORT qt_renderDistanceFieldGlyph(const QRawFont &font, glyph_t glyph, bool doubleResolution);
bool Q_GUI_EXPORT qt_fontHasNarrowOutlines(QFontEngine *fontEngine);
QImage Q_GUI_EXPORT qt_renderDistanceFieldGlyph(QFontEngine *fontEngine, glyph_t glyph, bool doubleResolution);
class Q_GUI_EXPORT QDistanceFieldData : public QSharedData
{
public:
QDistanceFieldData() : glyph(0), width(0), height(0), nbytes(0), data(0) {}
QDistanceFieldData(const QDistanceFieldData &other);
~QDistanceFieldData();
static QDistanceFieldData *create(const QSize &size);
static QDistanceFieldData *create(const QPainterPath &path, bool doubleResolution);
glyph_t glyph;
int width;
int height;
int nbytes;
uchar *data;
};
class Q_GUI_EXPORT QDistanceField
{
public:
QDistanceField();
QDistanceField(int width, int height);
QDistanceField(const QRawFont &font, glyph_t glyph, bool doubleResolution = false);
QDistanceField(QFontEngine *fontEngine, glyph_t glyph, bool doubleResolution = false);
QDistanceField(const QDistanceField &other);
bool isNull() const;
glyph_t glyph() const;
void setGlyph(const QRawFont &font, glyph_t glyph, bool doubleResolution = false);
void setGlyph(QFontEngine *fontEngine, glyph_t glyph, bool doubleResolution = false);
int width() const;
int height() const;
QDistanceField copy(const QRect &rect = QRect()) const;
inline QDistanceField copy(int x, int y, int w, int h) const
{ return copy(QRect(x, y, w, h)); }
uchar *bits();
const uchar *bits() const;
const uchar *constBits() const;
uchar *scanLine(int);
const uchar *scanLine(int) const;
const uchar *constScanLine(int) const;
QImage toImage(QImage::Format format = QImage::Format_ARGB32_Premultiplied) const;
private:
QDistanceField(QDistanceFieldData *data);
QSharedDataPointer<QDistanceFieldData> d;
friend class QDistanceFieldData;
};
inline QImage Q_GUI_EXPORT qt_renderDistanceFieldGlyph(const QRawFont &f, glyph_t g, bool d)
{ return QDistanceField(f, g, d).toImage(QImage::Format_Indexed8); }
inline QImage Q_GUI_EXPORT qt_renderDistanceFieldGlyph(QFontEngine *fe, glyph_t g, bool d)
{ return QDistanceField(fe, g, d).toImage(QImage::Format_Indexed8); }
QT_END_NAMESPACE