1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-28 02:30: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

@ -68,7 +68,13 @@ public:
Call this on the public key object to encode some data, then use the matching
private key object to decode it.
Returns false if the operation failed, e.g. if this object isn't a valid key.
Returns false if the operation couldn't be completed, e.g. if this key hasn't been
initialised correctly.
NOTE: This method dumbly applies this key to this data. If you encode some data
and then try to decode it with a key that doesn't match, this method will still
happily do its job and return true, but the result won't be what you were expecting.
It's your responsibility to check that the result is what you wanted.
*/
bool applyToValue (BitArray& value) const throw();

View file

@ -80,7 +80,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,

View file

@ -171,7 +171,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;
}

View file

@ -70,7 +70,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,

View file

@ -27,24 +27,20 @@
BEGIN_JUCE_NAMESPACE
#include "juce_EdgeTable.h"
#include "../geometry/juce_PathIterator.h"
const int juce_edgeTableDefaultEdgesPerLine = 32;
//==============================================================================
EdgeTable::EdgeTable (const int top_,
const int height_,
const OversamplingLevel oversampling_,
const int expectedEdgesPerLine) throw()
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()
@ -61,11 +57,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);
@ -85,10 +79,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;
@ -121,7 +114,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];
@ -159,39 +152,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;
@ -199,45 +181,32 @@ 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

View file

@ -29,9 +29,8 @@
#include "../geometry/juce_AffineTransform.h"
#include "../../../containers/juce_MemoryBlock.h"
class Path;
static const int juce_edgeTableDefaultEdgesPerLine = 10;
class Image;
class Rectangle;
//==============================================================================
/**
@ -43,21 +42,6 @@ class JUCE_API EdgeTable
{
public:
//==============================================================================
/** Indicates the quality at which the edge table should be generated.
Higher values will have better quality anti-aliasing, but will take
longer to generate the edge table and to render it.
*/
enum OversamplingLevel
{
Oversampling_none = 0, /**< No vertical anti-aliasing at all. */
Oversampling_4times = 2, /**< Anti-aliased with 4 levels of grey - good enough for normal use. */
Oversampling_16times = 4, /**< Anti-aliased with 16 levels of grey - very good quality. */
Oversampling_32times = 5, /**< Anti-aliased with 32 levels of grey - very good quality but slower. */
Oversampling_256times = 8 /**< Anti-aliased with 256 levels of grey - best quality, but too slow for
normal user-interface use. */
};
/** Creates an empty edge table ready to have paths added.
A table is created with a fixed vertical size, and only sections of paths
@ -65,14 +49,8 @@ public:
@param topY the lowest y co-ordinate that the table can contain
@param height the number of horizontal lines it can contain
@param verticalOversampling the amount of oversampling used for anti-aliasing
@param expectedEdgesPerLine used to optimise the table's internal data usage - it's not
worth changing this except for very special purposes
*/
EdgeTable (const int topY,
const int height,
const OversamplingLevel verticalOversampling = Oversampling_4times,
const int expectedEdgesPerLine = juce_edgeTableDefaultEdgesPerLine) throw();
EdgeTable (const int topY, const int height) throw();
/** Creates a copy of another edge table. */
EdgeTable (const EdgeTable& other) throw();
@ -96,6 +74,10 @@ public:
void addPath (const Path& path,
const AffineTransform& transform) throw();
/*void clipToRectangle (const Rectangle& r) throw();
void intersectWith (const EdgeTable& other);
void generateFromImageAlpha (Image& image, int x, int y) throw();*/
/** Reduces the amount of space the table has allocated.
This will shrink the table down to use as little memory as possible - useful for
@ -125,11 +107,9 @@ public:
*/
template <class EdgeTableIterationCallback>
void iterate (EdgeTableIterationCallback& iterationCallback,
const int clipLeft,
int clipTop,
const int clipRight,
int clipBottom,
const int subPixelXOffset) const
const int clipLeft, int clipTop,
const int clipRight, int clipBottom,
const int subPixelXOffset) const throw()
{
if (clipTop < top)
clipTop = top;
@ -137,108 +117,68 @@ public:
if (clipBottom > top + height)
clipBottom = top + height;
const int* singleLine = table + lineStrideElements
* ((clipTop - top) << (int) oversampling);
int mergedLineAllocation = 128;
MemoryBlock temp (mergedLineAllocation * (2 * sizeof (int)));
int* mergedLine = (int*) temp.getData();
const int timesOverSampling = 1 << (int) oversampling;
const int* lineStart = table + lineStrideElements * (clipTop - top);
for (int y = clipTop; y < clipBottom; ++y)
{
int numMergedPoints = 0;
const int* line = lineStart;
lineStart += lineStrideElements;
int numPoints = line[0];
// sort all the oversampled lines into a single merged line ready to draw..
for (int over = timesOverSampling; --over >= 0;)
if (--numPoints > 0)
{
const int* l = singleLine;
singleLine += lineStrideElements;
int num = *l;
jassert (num >= 0);
if (num > 0)
{
if (numMergedPoints + num >= mergedLineAllocation)
{
mergedLineAllocation = (numMergedPoints + num + 0x100) & ~0xff;
temp.setSize (mergedLineAllocation * (2 * sizeof (int)), false);
mergedLine = (int*) temp.getData();
}
while (--num >= 0)
{
const int x = *++l;
const int winding = *++l;
int n = numMergedPoints << 1;
while (n > 0)
{
const int cx = mergedLine [n - 2];
if (cx <= x)
break;
mergedLine [n] = cx;
--n;
mergedLine [n + 2] = mergedLine [n];
--n;
}
mergedLine [n] = x;
mergedLine [n + 1] = winding;
++numMergedPoints;
}
}
}
if (--numMergedPoints > 0)
{
const int* line = mergedLine;
int x = subPixelXOffset + *line;
int x = subPixelXOffset + *++line;
int level = *++line;
int levelAccumulator = 0;
iterationCallback.setEdgeTableYPos (y);
while (--numMergedPoints >= 0)
while (--numPoints >= 0)
{
int correctedLevel = abs (level);
if (correctedLevel >> 8)
{
if (nonZeroWinding)
{
correctedLevel = 0xff;
}
else
{
correctedLevel &= 511;
if (correctedLevel >> 8)
correctedLevel = 511 - correctedLevel;
}
}
const int endX = subPixelXOffset + *++line;
jassert (endX >= x);
const int absLevel = abs (level);
int endOfRun = (endX >> 8);
if (endOfRun == (x >> 8))
{
// small segment within the same pixel, so just save it for the next
// time round..
levelAccumulator += (endX - x) * absLevel;
levelAccumulator += (endX - x) * correctedLevel;
}
else
{
// plot the fist pixel of this segment, including any accumulated
// levels from smaller segments that haven't been drawn yet
levelAccumulator += (0xff - (x & 0xff)) * absLevel;
levelAccumulator >>= 8;
if (levelAccumulator > 0xff)
levelAccumulator = 0xff;
levelAccumulator += (0xff - (x & 0xff)) * correctedLevel;
x >>= 8;
if (x >= clipRight)
{
levelAccumulator = 0;
break;
}
if (x >= clipLeft && x < clipRight && levelAccumulator > 0)
iterationCallback.handleEdgeTablePixel (x, levelAccumulator);
if (x >= clipLeft)
{
levelAccumulator >>= 8;
if (levelAccumulator > 0)
iterationCallback.handleEdgeTablePixel (x, jmin (0xff, levelAccumulator));
}
if (++x >= clipRight)
{
@ -247,7 +187,7 @@ public:
}
// if there's a segment of solid pixels, do it all in one go..
if (absLevel > 0 && endOfRun > x)
if (correctedLevel > 0 && endOfRun > x)
{
if (x < clipLeft)
x = clipLeft;
@ -259,11 +199,11 @@ public:
if (numPix > 0)
iterationCallback.handleEdgeTableLine (x, numPix,
jmin (absLevel, 0xff));
jmin (correctedLevel, 0xff));
}
// save the bit at the end to be drawn next time round the loop.
levelAccumulator = (endX & 0xff) * absLevel;
levelAccumulator = (endX & 0xff) * correctedLevel;
}
level += *++line;
@ -273,7 +213,7 @@ public:
if (levelAccumulator > 0)
{
levelAccumulator >>= 8;
if (levelAccumulator > 0xff)
if (levelAccumulator >> 8)
levelAccumulator = 0xff;
x >>= 8;
@ -284,7 +224,6 @@ public:
}
}
//==============================================================================
juce_UseDebuggingNewOperator
@ -292,12 +231,9 @@ private:
// table line format: number of points; point0 x, point0 levelDelta, point1 x, point1 levelDelta, etc
int* table;
int top, height, maxEdgesPerLine, lineStrideElements;
OversamplingLevel oversampling;
bool nonZeroWinding;
// this will assume that the y co-ord is within bounds, and will avoid checking
// this for speed.
void addEdgePoint (const int x, const int y, const int winding) throw();
void remapTableForNumEdges (const int newNumEdgesPerLine) throw();
};

View file

@ -450,7 +450,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);
}

View file

@ -697,6 +697,33 @@ public:
/** @internal */
LowLevelGraphicsContext* getInternalContext() const throw() { return context; }
//==============================================================================
/*class FillType
{
public:
FillType (const Colour& colour) throw();
FillType (const ColourGradient& gradient) throw();
FillType (Image* image, int x, int y) throw();
FillType (const FillType& other) throw();
const FillType& operator= (const FillType& other) throw();
~FillType() throw();
bool isColour() const throw() { return gradient == 0 && image == 0; }
bool isGradient() const throw() { return gradient != 0; }
bool isTiledImage() const throw() { return image != 0; }
void setColour (const Colour& newColour) throw();
void setGradient (const ColourGradient& newGradient) throw();
void setTiledImage (Image* image, const int imageX, const int imageY) throw();
Colour colour;
ColourGradient* gradient;
Image* image;
int imageX, imageY;
juce_UseDebuggingNewOperator
};*/
private:
//==============================================================================
LowLevelGraphicsContext* const context;

View file

@ -73,6 +73,9 @@ public:
/** Cliping co-ords are relative to the origin. */
virtual bool reduceClipRegion (const RectangleList& clipRegion) = 0;
//virtual bool clipToPath (const Path& path) = 0;
//virtual bool clipToImageAlpha (Image& image, int imageX, int imageY) = 0;
/** Cliping co-ords are relative to the origin. */
virtual void excludeClipRegion (int x, int y, int w, int h) = 0;
@ -91,10 +94,10 @@ public:
//==============================================================================
virtual void fillRect (int x, int y, int w, int h, const bool replaceExistingContents) = 0;
virtual void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality) = 0;
virtual void fillPath (const Path& path, const AffineTransform& transform) = 0;
virtual void fillPathWithImage (const Path& path, const AffineTransform& transform,
const Image& image, int imageX, int imageY, EdgeTable::OversamplingLevel quality) = 0;
const Image& image, int imageX, int imageY) = 0;
virtual void fillAlphaChannel (const Image& alphaImage, int alphaImageX, int alphaImageY) = 0;
virtual void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY,

View file

@ -374,14 +374,13 @@ 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)
{
@ -424,8 +423,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();
@ -593,7 +591,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)

View file

@ -69,10 +69,10 @@ public:
//==============================================================================
void fillRect (int x, int y, int w, int h, const bool replaceExistingContents);
void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality);
void fillPath (const Path& path, const AffineTransform& transform);
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);
void fillAlphaChannel (const Image& alphaImage, int imageX, int imageY);
void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY,

View file

@ -1058,6 +1058,80 @@ 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_),
@ -1207,7 +1281,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
{
@ -1269,26 +1343,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;
@ -1396,7 +1470,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;
@ -1407,16 +1481,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;

View file

@ -68,10 +68,10 @@ public:
//==============================================================================
void fillRect (int x, int y, int w, int h, const bool replaceExistingContents);
void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality);
void fillPath (const Path& path, const AffineTransform& transform);
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);
void fillAlphaChannel (const Image& alphaImage, int imageX, int imageY);
void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY,
@ -141,9 +141,9 @@ protected:
void clippedFillRectWithColour (const Rectangle& clipRect, int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents);
void clippedFillPath (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality);
void clippedFillPath (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform);
void clippedFillPathWithImage (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform,
const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality);
const Image& image, int imageX, int imageY, float alpha);
void clippedFillAlphaChannel (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY);
void clippedFillAlphaChannelWithImage (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY,

View file

@ -435,27 +435,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));
}
}
@ -464,8 +464,8 @@ public:
font = font_;
glyph = glyph_;
deleteAndZero (bitmap1);
deleteAndZero (bitmap2);
deleteAndZero (bitmap[0]);
deleteAndZero (bitmap[1]);
Path glyphPath;
font.getTypeface()->getOutlineForGlyph (glyph_, glyphPath);
@ -474,15 +474,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();
}
}
}
@ -493,78 +497,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&);
@ -677,8 +611,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));
}

View file

@ -31,6 +31,7 @@ BEGIN_JUCE_NAMESPACE
#include "juce_PathIterator.h"
#include "juce_Line.h"
#include "../../../io/streams/juce_MemoryInputStream.h"
#include "../imaging/juce_Image.h"
// tests that some co-ords aren't NaNs
#define CHECK_COORDS_ARE_VALID(x, y) \
@ -1548,5 +1549,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

View file

@ -28,11 +28,13 @@
#include "juce_AffineTransform.h"
#include "juce_Point.h"
#include "juce_Rectangle.h"
#include "../contexts/juce_Justification.h"
#include "../contexts/juce_EdgeTable.h"
#include "../../../containers/juce_Array.h"
#include "../../../io/streams/juce_InputStream.h"
#include "../../../io/streams/juce_OutputStream.h"
class Image;
//==============================================================================
/**
@ -614,6 +616,22 @@ public:
*/
void restoreFromString (const String& stringVersion);
//==============================================================================
/** Creates a single-channel bitmap containing a mask of this path.
The smallest bitmap that contains the path will be created, and on return, the
imagePosition rectangle indicates the position of the newly created image, relative
to the path's origin.
Only the intersection of the path's bounds with the specified clipRegion rectangle
will be rendered.
If the path is empty or doesn't intersect the clip region, this may return 0.
*/
Image* createMaskBitmap (const AffineTransform& transform,
const Rectangle& clipRegion,
Rectangle& imagePosition) const throw();
//==============================================================================
juce_UseDebuggingNewOperator

View file

@ -187,7 +187,7 @@ public:
}
}
void fillPath (const Path& path, const AffineTransform& transform, EdgeTable::OversamplingLevel quality)
void fillPath (const Path& path, const AffineTransform& transform)
{
CGContextSaveGState (context);
@ -211,7 +211,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);

View file

@ -177,7 +177,7 @@ public:
~WindowedGLContext()
{
makeInactive();
[renderContext setView: nil];
[renderContext clearDrawable];
delete viewHolder;
}