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:
parent
f3a53eae80
commit
5971e09187
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user