1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-30 02:50:05 +00:00

Rewrote EdgeTable so that it now always renders in perfect quality - this makes the old oversampling quality options defunct and I've removed them.

This commit is contained in:
Julian Storer 2009-11-09 22:46:18 +00:00
parent 405d934e68
commit 4d000ff593
20 changed files with 1050 additions and 984 deletions

View file

@ -77531,7 +77531,7 @@ void GradientBrush::paintPath (LowLevelGraphicsContext& context,
const Path& path, const AffineTransform& transform) throw()
{
context.setGradient (gradient);
context.fillPath (path, transform, EdgeTable::Oversampling_4times);
context.fillPath (path, transform);
}
void GradientBrush::paintRectangle (LowLevelGraphicsContext& context,
@ -77699,7 +77699,7 @@ void ImageBrush::paintPath (LowLevelGraphicsContext& context,
while (x < right)
{
context.fillPathWithImage (path, transform, *image, x, y, EdgeTable::Oversampling_4times);
context.fillPathWithImage (path, transform, *image, x, y);
x += iw;
}
@ -77792,7 +77792,7 @@ void SolidColourBrush::paintPath (LowLevelGraphicsContext& context,
const Path& path, const AffineTransform& transform) throw()
{
context.setColour (colour);
context.fillPath (path, transform, EdgeTable::Oversampling_4times);
context.fillPath (path, transform);
}
void SolidColourBrush::paintRectangle (LowLevelGraphicsContext& context,
@ -78823,18 +78823,16 @@ END_JUCE_NAMESPACE
BEGIN_JUCE_NAMESPACE
EdgeTable::EdgeTable (const int top_,
const int height_,
const OversamplingLevel oversampling_,
const int expectedEdgesPerLine) throw()
const int juce_edgeTableDefaultEdgesPerLine = 32;
EdgeTable::EdgeTable (const int top_, const int height_) throw()
: top (top_),
height (height_),
maxEdgesPerLine (expectedEdgesPerLine),
lineStrideElements ((expectedEdgesPerLine << 1) + 1),
oversampling (oversampling_)
maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine),
lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1),
nonZeroWinding (true)
{
table = (int*) juce_calloc ((height << (int)oversampling_)
* lineStrideElements * sizeof (int));
table = (int*) juce_calloc (height * lineStrideElements * sizeof (int));
}
EdgeTable::EdgeTable (const EdgeTable& other) throw()
@ -78851,11 +78849,9 @@ const EdgeTable& EdgeTable::operator= (const EdgeTable& other) throw()
height = other.height;
maxEdgesPerLine = other.maxEdgesPerLine;
lineStrideElements = other.lineStrideElements;
oversampling = other.oversampling;
const int tableSize = (height << (int)oversampling)
* lineStrideElements * sizeof (int);
nonZeroWinding = other.nonZeroWinding;
const int tableSize = height * lineStrideElements * sizeof (int);
table = (int*) juce_malloc (tableSize);
memcpy (table, other.table, tableSize);
@ -78874,10 +78870,9 @@ void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine) throw()
maxEdgesPerLine = newNumEdgesPerLine;
const int newLineStrideElements = maxEdgesPerLine * 2 + 1;
int* const newTable = (int*) juce_malloc ((height << (int) oversampling)
* newLineStrideElements * sizeof (int));
int* const newTable = (int*) juce_malloc (height * newLineStrideElements * sizeof (int));
for (int i = 0; i < (height << (int) oversampling); ++i)
for (int i = 0; i < height; ++i)
{
const int* srcLine = table + lineStrideElements * i;
int* dstLine = newTable + newLineStrideElements * i;
@ -78909,7 +78904,7 @@ void EdgeTable::optimiseTable() throw()
void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw()
{
jassert (y >= 0 && y < (height << oversampling))
jassert (y >= 0 && y < height)
int* lineStart = table + lineStrideElements * y;
int n = lineStart[0];
@ -78946,39 +78941,28 @@ void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw
void EdgeTable::addPath (const Path& path,
const AffineTransform& transform) throw()
{
const int windingAmount = 256 / (1 << (int) oversampling);
const float timesOversampling = (float) (1 << (int) oversampling);
const int bottomLimit = (height << (int) oversampling);
nonZeroWinding = path.isUsingNonZeroWinding();
const int bottomLimit = height << 8;
PathFlatteningIterator iter (path, transform);
while (iter.next())
{
int y1 = roundFloatToInt (iter.y1 * timesOversampling) - (top << (int) oversampling);
int y2 = roundFloatToInt (iter.y2 * timesOversampling) - (top << (int) oversampling);
int y1 = roundFloatToInt (iter.y1 * 256.0f) - (top << 8);
int y2 = roundFloatToInt (iter.y2 * 256.0f) - (top << 8);
if (y1 != y2)
{
const double x1 = 256.0 * iter.x1;
const double x2 = 256.0 * iter.x2;
const double multiplier = (x2 - x1) / (y2 - y1);
const int oldY1 = y1;
int winding;
int winding = -1;
if (y1 > y2)
{
swapVariables (y1, y2);
winding = windingAmount;
winding = 1;
}
else
{
winding = -windingAmount;
}
jassert (y1 < y2);
if (y1 < 0)
y1 = 0;
@ -78986,46 +78970,34 @@ void EdgeTable::addPath (const Path& path,
if (y2 > bottomLimit)
y2 = bottomLimit;
const int oldY1 = y1;
const int stepSize = jlimit (1, 256, 256 / (1 + abs ((int) multiplier)));
while (y1 < y2)
{
const int step = jmin (stepSize, y2 - y1, 256 - (y1 & 255));
addEdgePoint (roundDoubleToInt (x1 + multiplier * (y1 - oldY1)),
y1,
winding);
y1 >> 8, winding * step);
++y1;
}
}
}
if (! path.isUsingNonZeroWinding())
{
// if it's an alternate-winding path, we need to go through and
// make sure all the windings are alternating.
int* lineStart = table;
for (int i = height << (int) oversampling; --i >= 0;)
{
int* line = lineStart;
lineStart += lineStrideElements;
int num = *line;
while (--num >= 0)
{
line += 2;
*line = abs (*line);
if (--num >= 0)
{
line += 2;
*line = -abs (*line);
}
y1 += step;
}
}
}
}
/*void EdgeTable::clipToRectangle (const Rectangle& r) throw()
{
}
void EdgeTable::intersectWith (const EdgeTable& other)
{
}
void EdgeTable::generateFromImageAlpha (Image& image, int x, int y) throw()
{
}*/
END_JUCE_NAMESPACE
/********* End of inlined file: juce_EdgeTable.cpp *********/
@ -79436,7 +79408,7 @@ void Graphics::fillPath (const Path& path,
if ((! context->isClipEmpty()) && ! path.isEmpty())
{
if (state->brush == 0)
context->fillPath (path, transform, EdgeTable::Oversampling_4times);
context->fillPath (path, transform);
else
state->brush->paintPath (*context, path, transform);
}
@ -80439,13 +80411,12 @@ void LowLevelGraphicsPostScriptRenderer::fillRect (int x, int y, int w, int h, c
{
Path p;
p.addRectangle ((float) x, (float) y, (float) w, (float) h);
fillPath (p, AffineTransform::identity, EdgeTable::Oversampling_256times);
fillPath (p, AffineTransform::identity);
}
}
void LowLevelGraphicsPostScriptRenderer::fillPath (const Path& path, const AffineTransform& t,
EdgeTable::OversamplingLevel /*quality*/)
void LowLevelGraphicsPostScriptRenderer::fillPath (const Path& path, const AffineTransform& t)
{
if (gradient == 0)
{
@ -80488,8 +80459,7 @@ void LowLevelGraphicsPostScriptRenderer::fillPath (const Path& path, const Affin
void LowLevelGraphicsPostScriptRenderer::fillPathWithImage (const Path& path, const AffineTransform& transform,
const Image& sourceImage,
int imageX, int imageY,
EdgeTable::OversamplingLevel /*quality*/)
int imageX, int imageY)
{
writeClip();
@ -80651,7 +80621,7 @@ void LowLevelGraphicsPostScriptRenderer::drawLine (double x1, double y1, double
{
Path p;
p.addLineSegment ((float) x1, (float) y1, (float) x2, (float) y2, 1.0f);
fillPath (p, AffineTransform::identity, EdgeTable::Oversampling_256times);
fillPath (p, AffineTransform::identity);
}
void LowLevelGraphicsPostScriptRenderer::drawVerticalLine (const int x, double top, double bottom)
@ -81702,6 +81672,79 @@ static void renderAlphaMap (DestPixelType* destPixels,
}
}
/*class ClippingPath
{
public:
ClippingPath (const Rectangle& r) throw()
: rectangles (r)
{
}
ClippingPath (const ClippingPath& other) throw()
: rectangles (other.rectangles), mask (other.mask)
{
}
~ClippingPath() throw()
{
delete mask;
}
bool reduce (int x, int y, int w, int h) throw()
{
return clip.clipTo (Rectangle (x, y, w, h));
}
bool exclude (int x, int y, int w, int h) throw()
{
return clip.subtract (Rectangle (x, y, w, h));
}
bool reduce (const Path& p, const AffineTransform& transform)
{
float px, py, pw, ph;
p.getBoundsTransformed (transform, px, py, pw, ph);
Rectangle pathBounds ((int) px - 1, (int) py - 1, (int) pw + 3, (int) ph + 3);
if (clip.clipTo (pathBounds))
{
}
}
bool reduce (Image& image, int x, int y)
{
}
bool reduce (Image& image, const AffineTransform& transform)
{
}
class MaskImage : public ReferenceCountedObject
{
public:
MaskImage (int x_, int y_, int w, int h) throw()
: x (x_), y (y_)
{
image = new Image (Image::SingleChannel, w, h, true);
}
~MaskImage() throw()
{
delete image;
}
Image* image;
int x, y;
};
RectangleList clip;
ReferenceCountedObjectPtr<MaskImage> mask;
private:
};
*/
LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image_)
: image (image_),
xOffset (0),
@ -81846,7 +81889,7 @@ void LowLevelGraphicsSoftwareRenderer::fillRect (int x, int y, int w, int h, con
Path p;
p.addRectangle ((float) x, (float) y, (float) w, (float) h);
fillPath (p, AffineTransform::identity, EdgeTable::Oversampling_none);
fillPath (p, AffineTransform::identity);
}
else
{
@ -81907,26 +81950,26 @@ bool LowLevelGraphicsSoftwareRenderer::getPathBounds (int clipX, int clipY, int
return Rectangle::intersectRectangles (x, y, w, h, clipX, clipY, clipW, clipH);
}
void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality)
void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform)
{
for (RectangleList::Iterator i (*clip); i.next();)
{
const Rectangle& r = *i.getRectangle();
clippedFillPath (r.getX(), r.getY(), r.getWidth(), r.getHeight(),
path, transform, quality);
path, transform);
}
}
void LowLevelGraphicsSoftwareRenderer::clippedFillPath (int clipX, int clipY, int clipW, int clipH, const Path& path,
const AffineTransform& t, EdgeTable::OversamplingLevel quality)
const AffineTransform& t)
{
const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset));
int cx, cy, cw, ch;
if (getPathBounds (clipX, clipY, clipW, clipH, path, transform, cx, cy, cw, ch))
{
EdgeTable edgeTable (0, ch, quality);
EdgeTable edgeTable (0, ch);
edgeTable.addPath (path, transform.translated ((float) -cx, (float) -cy));
int stride, pixelStride;
@ -82034,7 +82077,7 @@ void LowLevelGraphicsSoftwareRenderer::clippedFillPath (int clipX, int clipY, in
}
void LowLevelGraphicsSoftwareRenderer::fillPathWithImage (const Path& path, const AffineTransform& transform,
const Image& sourceImage, int imageX, int imageY, EdgeTable::OversamplingLevel quality)
const Image& sourceImage, int imageX, int imageY)
{
imageX += xOffset;
imageY += yOffset;
@ -82045,16 +82088,16 @@ void LowLevelGraphicsSoftwareRenderer::fillPathWithImage (const Path& path, cons
clippedFillPathWithImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(),
path, transform, sourceImage, imageX, imageY,
colour.getFloatAlpha(), quality);
colour.getFloatAlpha());
}
}
void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithImage (int x, int y, int w, int h, const Path& path, const AffineTransform& transform,
const Image& sourceImage, int imageX, int imageY, float opacity, EdgeTable::OversamplingLevel quality)
const Image& sourceImage, int imageX, int imageY, float opacity)
{
if (Rectangle::intersectRectangles (x, y, w, h, imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight()))
{
EdgeTable edgeTable (0, h, quality);
EdgeTable edgeTable (0, h);
edgeTable.addPath (path, transform.translated ((float) (xOffset - x), (float) (yOffset - y)));
int stride, pixelStride;
@ -85980,27 +86023,27 @@ class FontGlyphAlphaMap
public:
FontGlyphAlphaMap() throw()
: glyph (0), lastAccessCount (0),
bitmap1 (0), bitmap2 (0)
: glyph (0), lastAccessCount (0)
{
bitmap[0] = bitmap[1] = 0;
}
~FontGlyphAlphaMap() throw()
{
delete bitmap1;
delete bitmap2;
delete bitmap[0];
delete bitmap[1];
}
void draw (LowLevelGraphicsContext& g, float x, const float y) const throw()
{
if (bitmap1 != 0)
if (bitmap[0] != 0)
{
x += xOrigin;
const float xFloor = floorf (x);
const int intX = (int) xFloor;
const int bitmapToUse = ((x - xFloor) >= 0.5f && bitmap[1] != 0) ? 1 : 0;
g.fillAlphaChannel (((x - xFloor) >= 0.5f && bitmap2 != 0) ? *bitmap2 : *bitmap1,
intX, (int) floorf (y + yOrigin));
g.fillAlphaChannel (*bitmap [bitmapToUse],
xOrigin [bitmapToUse] + (int) xFloor,
yOrigin [bitmapToUse] + (int) floorf (y));
}
}
@ -86009,8 +86052,8 @@ public:
font = font_;
glyph = glyph_;
deleteAndZero (bitmap1);
deleteAndZero (bitmap2);
deleteAndZero (bitmap[0]);
deleteAndZero (bitmap[1]);
Path glyphPath;
font.getTypeface()->getOutlineForGlyph (glyph_, glyphPath);
@ -86019,15 +86062,19 @@ public:
{
const float fontHeight = font.getHeight();
const float fontHScale = fontHeight * font.getHorizontalScale();
AffineTransform transform (AffineTransform::scale (fontHScale, fontHeight));
Rectangle clip (-2048, -2048, 4096, 4096), pos;
bitmap1 = createAlphaMapFromPath (glyphPath, xOrigin, yOrigin, fontHScale, fontHeight, 0.0f);
bitmap[0] = glyphPath.createMaskBitmap (transform, clip, pos);
xOrigin[0] = pos.getX();
yOrigin[0] = pos.getY();
if (fontHScale < 24.0f)
bitmap2 = createAlphaMapFromPath (glyphPath, xOrigin, yOrigin, fontHScale, fontHeight, 0.5f);
}
else
{
xOrigin = yOrigin = 0;
if (fontHScale < 30.0f)
{
bitmap[1] = glyphPath.createMaskBitmap (transform.translated (0.5f, 0.0f), clip, pos);
xOrigin[1] = pos.getX();
yOrigin[1] = pos.getY();
}
}
}
@ -86037,78 +86084,8 @@ public:
juce_UseDebuggingNewOperator
private:
Image* bitmap1;
Image* bitmap2;
float xOrigin, yOrigin;
class AlphaBitmapRenderer
{
public:
AlphaBitmapRenderer (uint8* const data_, const int stride_) throw()
: data (data_), stride (stride_)
{
}
forcedinline void setEdgeTableYPos (const int y) throw()
{
lineStart = data + (stride * y);
}
forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw()
{
lineStart [x] = (uint8) alphaLevel;
}
forcedinline void handleEdgeTableLine (const int x, int width, const int alphaLevel) const throw()
{
uint8* d = lineStart + x;
while (--width >= 0)
*d++ = (uint8) alphaLevel;
}
private:
uint8* const data;
const int stride;
uint8* lineStart;
AlphaBitmapRenderer (const AlphaBitmapRenderer&);
const AlphaBitmapRenderer& operator= (const AlphaBitmapRenderer&);
};
Image* createAlphaMapFromPath (const Path& path,
float& topLeftX, float& topLeftY,
float xScale, float yScale,
const float subPixelOffsetX) throw()
{
Image* im = 0;
float px, py, pw, ph;
path.getBounds (px, py, pw, ph);
topLeftX = floorf (px * xScale);
topLeftY = floorf (py * yScale);
const int bitmapWidth = roundFloatToInt (pw * xScale) + 2;
const int bitmapHeight = roundFloatToInt (ph * yScale) + 2;
im = new Image (Image::SingleChannel, bitmapWidth, bitmapHeight, true);
EdgeTable edgeTable (0, bitmapHeight, EdgeTable::Oversampling_16times);
edgeTable.addPath (path, AffineTransform::scale (xScale, yScale)
.translated (subPixelOffsetX - topLeftX, -topLeftY));
int stride, pixelStride;
uint8* const pixels = (uint8*) im->lockPixelDataReadWrite (0, 0, bitmapWidth, bitmapHeight, stride, pixelStride);
jassert (pixelStride == 1);
AlphaBitmapRenderer renderer (pixels, stride);
edgeTable.iterate (renderer, 0, 0, bitmapWidth, bitmapHeight, 0);
im->releasePixelDataReadWrite (pixels);
return im;
}
Image* bitmap[2];
int xOrigin[2], yOrigin[2];
FontGlyphAlphaMap (const FontGlyphAlphaMap&);
const FontGlyphAlphaMap& operator= (const FontGlyphAlphaMap&);
@ -86215,8 +86192,7 @@ void Font::renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, c
getTypeface()->getOutlineForGlyph (glyphNumber, p);
g.fillPath (p, AffineTransform::scale (font->height * font->horizontalScale, font->height)
.followedBy (transform),
EdgeTable::Oversampling_16times);
.followedBy (transform));
}
END_JUCE_NAMESPACE
@ -89928,6 +89904,73 @@ bool Path::Iterator::next()
return false;
}
class MaskBitmapRenderer
{
public:
MaskBitmapRenderer (uint8* const data_, const int stride_) throw()
: data (data_), stride (stride_)
{
}
forcedinline void setEdgeTableYPos (const int y) throw()
{
lineStart = data + (stride * y);
}
forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw()
{
lineStart [x] = (uint8) alphaLevel;
}
forcedinline void handleEdgeTableLine (const int x, int width, const int alphaLevel) const throw()
{
uint8* d = lineStart + x;
while (--width >= 0)
*d++ = (uint8) alphaLevel;
}
private:
uint8* const data;
const int stride;
uint8* lineStart;
MaskBitmapRenderer (const MaskBitmapRenderer&);
const MaskBitmapRenderer& operator= (const MaskBitmapRenderer&);
};
Image* Path::createMaskBitmap (const AffineTransform& transform,
const Rectangle& clipRegion,
Rectangle& imagePosition) const throw()
{
if (isEmpty())
return 0;
float px, py, pw, ph;
getBoundsTransformed (transform, px, py, pw, ph);
imagePosition = clipRegion.getIntersection (Rectangle ((int) floorf (px), (int) floorf (py),
roundFloatToInt (pw) + 2, roundFloatToInt (ph) + 2));
if (imagePosition.isEmpty())
return 0;
Image* im = new Image (Image::SingleChannel, imagePosition.getWidth(), imagePosition.getHeight(), true);
EdgeTable edgeTable (0, imagePosition.getHeight());
edgeTable.addPath (*this, transform.translated (-imagePosition.getX(), -imagePosition.getY()));
int stride, pixelStride;
uint8* const pixels = (uint8*) im->lockPixelDataReadWrite (0, 0, imagePosition.getWidth(), imagePosition.getHeight(), stride, pixelStride);
jassert (pixelStride == 1);
MaskBitmapRenderer renderer (pixels, stride);
edgeTable.iterate (renderer, 0, 0, imagePosition.getWidth(), imagePosition.getHeight(), 0);
im->releasePixelDataReadWrite (pixels);
return im;
}
END_JUCE_NAMESPACE
/********* End of inlined file: juce_Path.cpp *********/
@ -263700,7 +263743,7 @@ public:
~WindowedGLContext()
{
makeInactive();
[renderContext setView: nil];
[renderContext clearDrawable];
delete viewHolder;
}
@ -266034,7 +266077,7 @@ public:
}
}
void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality)
void fillPath (const Path& path, const AffineTransform& transform)
{
CGContextSaveGState (context);
@ -266058,7 +266101,7 @@ public:
}
void fillPathWithImage (const Path& path, const AffineTransform& transform,
const Image& image, int imageX, int imageY, EdgeTable::OversamplingLevel quality)
const Image& image, int imageX, int imageY)
{
CGContextSaveGState (context);
createPath (path, transform);
@ -268731,7 +268774,7 @@ public:
~WindowedGLContext()
{
makeInactive();
[renderContext setView: nil];
[renderContext clearDrawable];
delete viewHolder;
}