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:
parent
405d934e68
commit
4d000ff593
20 changed files with 1050 additions and 984 deletions
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ public:
|
|||
~WindowedGLContext()
|
||||
{
|
||||
makeInactive();
|
||||
[renderContext setView: nil];
|
||||
[renderContext clearDrawable];
|
||||
delete viewHolder;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue