| @@ -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; | |||
| y1 += step; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| 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); | |||
| /*void EdgeTable::clipToRectangle (const Rectangle& r) throw() | |||
| { | |||
| } | |||
| if (--num >= 0) | |||
| { | |||
| line += 2; | |||
| *line = -abs (*line); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| 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; | |||
| } | |||
| @@ -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,85 +152,61 @@ 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; | |||
| } | |||
| else | |||
| { | |||
| winding = -windingAmount; | |||
| winding = 1; | |||
| } | |||
| jassert (y1 < y2); | |||
| if (y1 < 0) | |||
| y1 = 0; | |||
| 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; | |||
| y1 += step; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| 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; | |||
| /*void EdgeTable::clipToRectangle (const Rectangle& r) throw() | |||
| { | |||
| } | |||
| while (--num >= 0) | |||
| { | |||
| line += 2; | |||
| *line = abs (*line); | |||
| if (--num >= 0) | |||
| { | |||
| line += 2; | |||
| *line = -abs (*line); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| 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 x = subPixelXOffset + *++line; | |||
| int level = *++line; | |||
| int levelAccumulator = 0; | |||
| int num = *l; | |||
| jassert (num >= 0); | |||
| iterationCallback.setEdgeTableYPos (y); | |||
| if (num > 0) | |||
| while (--numPoints >= 0) | |||
| { | |||
| if (numMergedPoints + num >= mergedLineAllocation) | |||
| { | |||
| mergedLineAllocation = (numMergedPoints + num + 0x100) & ~0xff; | |||
| temp.setSize (mergedLineAllocation * (2 * sizeof (int)), false); | |||
| mergedLine = (int*) temp.getData(); | |||
| } | |||
| while (--num >= 0) | |||
| int correctedLevel = abs (level); | |||
| if (correctedLevel >> 8) | |||
| { | |||
| const int x = *++l; | |||
| const int winding = *++l; | |||
| int n = numMergedPoints << 1; | |||
| while (n > 0) | |||
| if (nonZeroWinding) | |||
| { | |||
| const int cx = mergedLine [n - 2]; | |||
| if (cx <= x) | |||
| break; | |||
| mergedLine [n] = cx; | |||
| --n; | |||
| mergedLine [n + 2] = mergedLine [n]; | |||
| --n; | |||
| correctedLevel = 0xff; | |||
| } | |||
| else | |||
| { | |||
| correctedLevel &= 511; | |||
| if (correctedLevel >> 8) | |||
| correctedLevel = 511 - correctedLevel; | |||
| } | |||
| mergedLine [n] = x; | |||
| mergedLine [n + 1] = winding; | |||
| ++numMergedPoints; | |||
| } | |||
| } | |||
| } | |||
| if (--numMergedPoints > 0) | |||
| { | |||
| const int* line = mergedLine; | |||
| int x = subPixelXOffset + *line; | |||
| int level = *++line; | |||
| int levelAccumulator = 0; | |||
| iterationCallback.setEdgeTableYPos (y); | |||
| while (--numMergedPoints >= 0) | |||
| { | |||
| 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; | |||
| } | |||