From 6fdde63a634a2dfef3d74cf5167a052433b52187 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Thu, 26 Nov 2009 21:36:45 +0000 Subject: [PATCH] Big rewrite of the LowLevelGraphicsSoftwareRenderer class, adding internal support for complex clipping regions - this will temporarily make font rendering quite slow, until it gets re-optimised for this new design. Changed the Image class to remove the lockPixelData methods, and replaced these with an object Image::BitmapData, which is easier to use. --- extras/juce demo/src/demos/OpenGLDemo.cpp | 6 +- juce_amalgamated.cpp | 3796 +++++++---------- juce_amalgamated.h | 337 +- .../special/juce_ColourSelector.cpp | 7 +- src/gui/graphics/colour/juce_PixelFormats.h | 139 + src/gui/graphics/contexts/juce_EdgeTable.cpp | 476 ++- src/gui/graphics/contexts/juce_EdgeTable.h | 34 +- ...uce_LowLevelGraphicsPostScriptRenderer.cpp | 8 +- .../juce_LowLevelGraphicsSoftwareRenderer.cpp | 2660 +++++------- .../juce_LowLevelGraphicsSoftwareRenderer.h | 41 +- .../effects/juce_DropShadowEffect.cpp | 21 +- src/gui/graphics/geometry/juce_Path.cpp | 17 +- .../image_file_formats/juce_GIFLoader.cpp | 13 +- .../image_file_formats/juce_JPEGLoader.cpp | 21 +- .../image_file_formats/juce_PNGLoader.cpp | 23 +- src/gui/graphics/imaging/juce_Image.cpp | 165 +- src/gui/graphics/imaging/juce_Image.h | 72 +- .../imaging/juce_ImageConvolutionKernel.cpp | 26 +- src/native/linux/juce_linux_Windowing.cpp | 9 +- src/native/mac/juce_mac_CameraDevice.mm | 25 +- .../mac/juce_mac_CoreGraphicsContext.mm | 26 +- src/native/mac/juce_mac_MouseCursor.mm | 16 +- .../mac/juce_mac_NSViewComponentPeer.mm | 29 +- .../windows/juce_win32_CameraDevice.cpp | 14 +- src/native/windows/juce_win32_Windowing.cpp | 19 +- 25 files changed, 3542 insertions(+), 4458 deletions(-) diff --git a/extras/juce demo/src/demos/OpenGLDemo.cpp b/extras/juce demo/src/demos/OpenGLDemo.cpp index 853cf20eed..2baa0e0552 100644 --- a/extras/juce demo/src/demos/OpenGLDemo.cpp +++ b/extras/juce demo/src/demos/OpenGLDemo.cpp @@ -122,13 +122,11 @@ public: glPixelStorei (GL_UNPACK_ALIGNMENT, 4); - int stride, pixStride; - const void* pixels = image->lockPixelDataReadOnly (0, 0, image->getWidth(), image->getHeight(), stride, pixStride); + Image::BitmapData srcData (*image, 0, 0, image->getWidth(), image->getHeight()); glTexImage2D (GL_TEXTURE_2D, 0, 4, image->getWidth(), image->getHeight(), 0, GL_RGB, - GL_UNSIGNED_BYTE, pixels); - image->releasePixelDataReadOnly (pixels); + GL_UNSIGNED_BYTE, srcData.data); glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); glHint (GL_POINT_SMOOTH_HINT, GL_NICEST); diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 4c32c5643d..ab5b21a9ba 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -730,6 +730,8 @@ public: the rest of the codebase. */ +#define USE_COREGRAPHICS_RENDERING 0 + #if JUCE_IPHONE #import #import @@ -771,6 +773,10 @@ public: #include #endif +#if ! CGFLOAT_DEFINED + #define CGFloat float +#endif + #endif // __JUCE_MAC_NATIVEINCLUDES_JUCEHEADER__ /********* End of inlined file: juce_mac_NativeIncludes.h *********/ @@ -11300,6 +11306,21 @@ String& String::operator<< (const short number) throw() return *this; } +String& String::operator<< (const unsigned short number) throw() +{ + return operator<< ((unsigned int) number); +} + +String& String::operator<< (const long number) throw() +{ + return operator<< ((int) number); +} + +String& String::operator<< (const unsigned long number) throw() +{ + return operator<< ((unsigned int) number); +} + String& String::operator<< (const double number) throw() { operator+= (String (number)); @@ -35216,6 +35237,9 @@ void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& m for (i = 0; i < buffer.getNumChannels(); ++i) buffer.copyFrom (i, 0, currentAudioOutputBuffer, i, 0, numSamples); + + midiMessages.clear(); + midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0); } const String AudioProcessorGraph::getInputChannelName (const int channelIndex) const @@ -72086,8 +72110,7 @@ public: const int height = getHeight() / 2; colours = new Image (Image::RGB, width, height, false); - int ls, ps; - char* data = (char*) colours->lockPixelDataReadWrite (0, 0, width, height, ls, ps); + Image::BitmapData pixels (*colours, 0, 0, width, height, true); for (int y = 0; y < height; ++y) { @@ -72098,12 +72121,10 @@ public: const float s = x / (float) width; const Colour col (h, s, v, 1.0f); - PixelRGB* const pix = (PixelRGB*) (data + ls * y + ps * x); + PixelRGB* const pix = (PixelRGB*) pixels.getPixelPointer (x, y); pix->set (col.getPixelARGB()); } } - - colours->releasePixelDataReadWrite (data); } g.setOpacity (1.0f); @@ -78894,6 +78915,16 @@ BEGIN_JUCE_NAMESPACE const int juce_edgeTableDefaultEdgesPerLine = 32; +static void copyEdgeTableData (int* dest, const int destLineStride, const int* src, const int srcLineStride, int numLines) throw() +{ + while (--numLines >= 0) + { + memcpy (dest, src, (src[0] * 2 + 1) * sizeof (int)); + src += srcLineStride; + dest += destLineStride; + } +} + EdgeTable::EdgeTable (const Rectangle& bounds_, const Path& path, const AffineTransform& transform) throw() : bounds (bounds_), @@ -78964,19 +78995,36 @@ EdgeTable::EdgeTable (const Rectangle& bounds_, } } - if (! path.isUsingNonZeroWinding()) + // Convert the table from relative windings to absolute levels.. + int* lineStart = table; + + for (int i = bounds.getHeight(); --i >= 0;) { - int* lineStart = table; + int* line = lineStart; + lineStart += lineStrideElements; - for (int i = bounds.getHeight(); --i >= 0;) + int num = *line; + if (num == 0) + continue; + + int level = 0; + + if (path.isUsingNonZeroWinding()) { - int* line = lineStart; - lineStart += lineStrideElements; - int num = *line; - int level = 0; - int lastCorrected = 0; + while (--num > 0) + { + line += 2; + level += *line; + int corrected = abs (level); + if (corrected >> 8) + corrected = 255; - while (--num >= 0) + *line = corrected; + } + } + else + { + while (--num > 0) { line += 2; level += *line; @@ -78988,10 +79036,11 @@ EdgeTable::EdgeTable (const Rectangle& bounds_, corrected = 511 - corrected; } - *line = corrected - lastCorrected; - lastCorrected = corrected; + *line = corrected; } } + + line[2] = 0; // force the last level to 0, just in case something went wrong in creating the table } } @@ -79003,21 +79052,95 @@ EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) throw() table = (int*) juce_malloc (jmax (1, bounds.getHeight()) * lineStrideElements * sizeof (int)); *table = 0; - const int x1 = rectangleToAdd.getX(); - const int x2 = rectangleToAdd.getRight(); + const int x1 = rectangleToAdd.getX() << 8; + const int x2 = rectangleToAdd.getRight() << 8; int* t = table; for (int i = rectangleToAdd.getHeight(); --i >= 0;) { t[0] = 2; t[1] = x1; - t[2] = 256; + t[2] = 255; t[3] = x2; - t[4] = -256; + t[4] = 0; t += lineStrideElements; } } +EdgeTable::EdgeTable (const float x, const float y, const float w, const float h) throw() + : bounds (Rectangle ((int) floorf (x), roundFloatToInt (y * 256.0f) >> 8, 2 + (int) w, 2 + (int) h)), + maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), + lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1) +{ + jassert (w > 0 && h > 0); + table = (int*) juce_malloc (jmax (1, bounds.getHeight()) * lineStrideElements * sizeof (int)); + *table = 0; + + const int x1 = roundFloatToInt (x * 256.0f); + const int x2 = roundFloatToInt ((x + w) * 256.0f); + + int y1 = roundFloatToInt (y * 256.0f) - (bounds.getY() << 8); + jassert (y1 < 256); + int y2 = roundFloatToInt ((y + h) * 256.0f) - (bounds.getY() << 8); + + if (x2 <= x1 || y2 <= y1) + { + bounds.setHeight (0); + return; + } + + int lineY = 0; + int* t = table; + + if ((y1 >> 8) == (y2 >> 8)) + { + t[0] = 2; + t[1] = x1; + t[2] = y2 - y1; + t[3] = x2; + t[4] = 0; + ++lineY; + t += lineStrideElements; + } + else + { + t[0] = 2; + t[1] = x1; + t[2] = 255 - (y1 & 255); + t[3] = x2; + t[4] = 0; + ++lineY; + t += lineStrideElements; + + while (lineY < (y2 >> 8)) + { + t[0] = 2; + t[1] = x1; + t[2] = 255; + t[3] = x2; + t[4] = 0; + ++lineY; + t += lineStrideElements; + } + + jassert (lineY < bounds.getHeight()); + t[0] = 2; + t[1] = x1; + t[2] = y2 & 255; + t[3] = x2; + t[4] = 0; + ++lineY; + t += lineStrideElements; + } + + while (lineY < bounds.getHeight()) + { + t[0] = 0; + t += lineStrideElements; + ++lineY; + } +} + EdgeTable::EdgeTable (const EdgeTable& other) throw() : table (0) { @@ -79034,8 +79157,7 @@ const EdgeTable& EdgeTable::operator= (const EdgeTable& other) throw() const int tableSize = jmax (1, bounds.getHeight()) * lineStrideElements * sizeof (int); table = (int*) juce_malloc (tableSize); - memcpy (table, other.table, tableSize); - + copyEdgeTableData (table, lineStrideElements, other.table, lineStrideElements, bounds.getHeight()); return *this; } @@ -79054,18 +79176,7 @@ void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine) throw() const int newLineStrideElements = maxEdgesPerLine * 2 + 1; int* const newTable = (int*) juce_malloc (bounds.getHeight() * newLineStrideElements * sizeof (int)); - for (int i = 0; i < bounds.getHeight(); ++i) - { - const int* srcLine = table + lineStrideElements * i; - int* dstLine = newTable + newLineStrideElements * i; - - int num = *srcLine++; - *dstLine++ = num; - - num <<= 1; - while (--num >= 0) - *dstLine++ = *srcLine++; - } + copyEdgeTableData (newTable, newLineStrideElements, table, lineStrideElements, bounds.getHeight()); juce_free (table); table = newTable; @@ -79089,7 +79200,7 @@ void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw int* line = table + lineStrideElements * y; const int numPoints = line[0]; - int n = line[0] << 1; + int n = numPoints << 1; if (n > 0) { @@ -79126,104 +79237,124 @@ void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw line[0]++; } -void EdgeTable::clearLineSection (const int y, int minX, int maxX) throw() -{ - jassert (y >= 0 && y < bounds.getHeight()); - jassert (maxX > minX); - - int* lineStart = table + lineStrideElements * y; - const int totalPoints = *lineStart; - int* line = lineStart; - int level = 0; - int num = totalPoints; - - while (--num >= 0) - { - int x = *++line; - - if (x < minX) - { - level += *++line; - continue; - } - - if (x > maxX) - { - if (level != 0) - { - addEdgePoint (minX, y, -level); - addEdgePoint (maxX, y, level); - } - - return; - } - - if (level != 0) - { - const int oldLevel = level; - level += line[1]; - line[0] = minX; - line[1] = -oldLevel; - line += 2; - } - else - { - ++num; - } - - int* cutPoint = line; - int numToDelete = 0; - - while (--num >= 0) - { - x = *line++; - - if (x <= maxX) - { - level += *line++; - ++numToDelete; - continue; - } - - break; - } - - if (num < 0) - { - lineStart[0] -= numToDelete; - } - else - { - if (--numToDelete > 0) - { - char* startOfSrcSection = (char*) (cutPoint + numToDelete * 2); - memmove (cutPoint, startOfSrcSection, - ((char*) (lineStart + (1 + 2 * totalPoints))) - startOfSrcSection); - - lineStart[0] -= numToDelete; - } - - if (numToDelete < 0) - { - addEdgePoint (maxX, y, level); - } - else - { - cutPoint[0] = maxX; - cutPoint[1] = level; - } - } - - break; - } -} - void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) throw() { -// int otherNum = otherLine[0]; + jassert (y >= 0 && y < bounds.getHeight()); + + int* dest = table + lineStrideElements * y; + if (dest[0] == 0) + return; + + int otherNumPoints = *otherLine; + if (otherNumPoints == 0) + { + *dest = 0; + return; + } + + ++otherLine; + const int lineSizeBytes = (dest[0] * 2 + 1) * sizeof (int); + int* temp = (int*) alloca (lineSizeBytes); + memcpy (temp, dest, lineSizeBytes); + + const int* src1 = temp; + int srcNum1 = *src1++; + int x1 = *src1++; + + const int* src2 = otherLine; + int srcNum2 = otherNumPoints; + int x2 = *src2++; + + int destIndex = 0, destTotal = 0; + int level1 = 0, level2 = 0; + int lastX = INT_MIN, lastLevel = 0; + const int right = bounds.getRight() << 8; + + while (srcNum1 > 0 && srcNum2 > 0) + { + int nextX; + + if (x1 < x2) + { + nextX = x1; + level1 = *src1++; + x1 = *src1++; + --srcNum1; + } + else if (x1 == x2) + { + nextX = x1; + level1 = *src1++; + level2 = *src2++; + x1 = *src1++; + x2 = *src2++; + --srcNum1; + --srcNum2; + } + else + { + nextX = x2; + level2 = *src2++; + x2 = *src2++; + --srcNum2; + } + + if (nextX > lastX) + { + if (nextX >= right) + break; + + lastX = nextX; + + const int nextLevel = (level1 * (level2 + 1)) >> 8; + jassert (((unsigned int) nextLevel) < (unsigned int) 256); + + if (nextLevel != lastLevel) + { + if (destTotal >= maxEdgesPerLine) + { + dest[0] = destTotal; + remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); + dest = table + lineStrideElements * y; + } + + ++destTotal; + lastLevel = nextLevel; + dest[++destIndex] = nextX; + dest[++destIndex] = nextLevel; + } + } + } + + if (lastLevel > 0) + { + if (destTotal >= maxEdgesPerLine) + { + dest[0] = destTotal; + remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); + dest = table + lineStrideElements * y; + } + + ++destTotal; + dest[++destIndex] = right; + dest[++destIndex] = 0; + } + + dest[0] = destTotal; + +#if JUCE_DEBUG + int last = INT_MIN; + for (int i = 0; i < dest[0]; ++i) + { + jassert (dest[i * 2 + 1] > last); + last = dest[i * 2 + 1]; + } + + jassert (dest [dest[0] * 2] == 0); +#endif } -/*void EdgeTable::clipToRectangle (const Rectangle& r) throw() +void EdgeTable::clipToRectangle (const Rectangle& r) throw() { const Rectangle clipped (r.getIntersection (bounds)); @@ -79246,8 +79377,12 @@ void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) t table [lineStrideElements * i] = 0; if (clipped.getX() > bounds.getX()) + { + const int rectLine[] = { 2, clipped.getX() << 8, 255, clipped.getRight() << 8, 0 }; + for (int i = top; i < bottom; ++i) - clearLineSection (i, bounds.getX(), clipped.getX()); + intersectWithEdgeTableLine (i, rectLine); + } } } @@ -79260,8 +79395,12 @@ void EdgeTable::excludeRectangle (const Rectangle& r) throw() const int top = clipped.getY() - bounds.getY(); const int bottom = clipped.getBottom() - bounds.getY(); + //XXX optimise here by shortening the table if it fills top or bottom + + const int rectLine[] = { 4, INT_MIN, 255, clipped.getX() << 8, 0, clipped.getRight() << 8, 255, INT_MAX, 0 }; + for (int i = top; i < bottom; ++i) - clearLineSection (i, clipped.getX(), clipped.getRight()); + intersectWithEdgeTableLine (i, rectLine); } } @@ -79287,7 +79426,7 @@ void EdgeTable::clipToEdgeTable (const EdgeTable& other) for (int i = top; --i >= 0;) table [lineStrideElements * i] = 0; - const int* otherLine = other.table + other.lineStrideElements * (top + (bounds.getY() - other.bounds.getY())); + const int* otherLine = other.table + other.lineStrideElements * (clipped.getY() - other.bounds.getY()); for (int i = top; i < bottom; ++i) { @@ -79297,10 +79436,108 @@ void EdgeTable::clipToEdgeTable (const EdgeTable& other) } } -void EdgeTable::clipToImageAlpha (Image& image, int x, int y) throw() +void EdgeTable::clipToImageAlpha (const Image& image, int x, int y) throw() { + const Rectangle clipped (bounds.getIntersection (Rectangle (x, y, image.getWidth(), image.getHeight()))); + + if (clipped.isEmpty()) + { + bounds.setHeight (0); + } + else + { + if (! image.hasAlphaChannel()) + { + clipToRectangle (clipped); + return; + } + + const int top = clipped.getY() - bounds.getY(); + const int bottom = clipped.getBottom() - bounds.getY(); + + if (bottom < bounds.getHeight()) + bounds.setHeight (bottom); + + if (clipped.getRight() < bounds.getRight()) + bounds.setRight (clipped.getRight()); + + for (int i = top; --i >= 0;) + table [lineStrideElements * i] = 0; + + int imageX = clipped.getX() - x; + int imageY = clipped.getY() - y; + + int* temp = (int*) alloca ((clipped.getWidth() * 2 + 4) * sizeof (int)); + + Image::BitmapData srcData (image, imageX, imageY, clipped.getWidth(), clipped.getHeight()); + + for (int i = 0; i < clipped.getHeight(); ++i) + { + int destIndex = 0, lastLevel = 0; + const uint8* pixels = srcData.getLinePointer (i); + + if (image.getFormat() == Image::ARGB) + { + for (int px = 0; px < clipped.getWidth(); ++px) + { + const int alpha = ((const PixelARGB*) pixels)->getAlpha(); + pixels += srcData.pixelStride; + + if (alpha != lastLevel) + { + temp[++destIndex] = (clipped.getX() + px) << 8; + temp[++destIndex] = alpha; + lastLevel = alpha; + } + } + } + else + { + jassert (image.getFormat() == Image::SingleChannel); + + for (int px = 0; px < clipped.getWidth(); ++px) + { + const int alpha = *pixels; + pixels += srcData.pixelStride; + + if (alpha != lastLevel) + { + temp[++destIndex] = (clipped.getX() + px) << 8; + temp[++destIndex] = alpha; + lastLevel = alpha; + } + } + } + + if (lastLevel > 0) + { + temp[++destIndex] = clipped.getRight() << 8; + temp[++destIndex] = 0; + } + + temp[0] = destIndex >> 1; + + intersectWithEdgeTableLine (top + i, temp); + ++y; + } + } } -*/ + +bool EdgeTable::isEmpty() const throw() +{ + int* t = table; + + for (int i = bounds.getHeight(); --i >= 0;) + { + if (t[0] > 1) + return false; + + t += lineStrideElements; + } + + return true; +} + END_JUCE_NAMESPACE /********* End of inlined file: juce_EdgeTable.cpp *********/ @@ -80823,16 +81060,14 @@ void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im, const int h = jmin (maxH, im.getHeight()); int charsOnLine = 0; - int lineStride, pixelStride; - const uint8* data = im.lockPixelDataReadOnly (0, 0, w, h, lineStride, pixelStride); - + const Image::BitmapData srcData (im, 0, 0, w, h); Colour pixel; for (int y = h; --y >= 0;) { for (int x = 0; x < w; ++x) { - const uint8* pixelData = data + lineStride * y + pixelStride * x; + const uint8* pixelData = srcData.getPixelPointer (x, y); if (x >= sx && y >= sy) { @@ -80870,8 +81105,6 @@ void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im, } } - im.releasePixelDataReadOnly (data); - out << "\n>}\n"; } @@ -80963,101 +81196,16 @@ BEGIN_JUCE_NAMESPACE #define JUCE_USE_SSE_INSTRUCTIONS 1 #endif -#if JUCE_DEBUG && JUCE_MSVC - #pragma warning (disable: 4714) +#if JUCE_MSVC && JUCE_DEBUG + #pragma warning (disable: 4714) // warning about forcedinline methods not being inlined #endif -#define MINIMUM_COORD -0x3fffffff -#define MAXIMUM_COORD 0x3fffffff +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4127) // "expression is constant" warning +#endif -#undef ASSERT_COORDS_ARE_SENSIBLE_NUMBERS -#define ASSERT_COORDS_ARE_SENSIBLE_NUMBERS(x, y, w, h) \ - jassert ((int) x >= MINIMUM_COORD \ - && (int) x <= MAXIMUM_COORD \ - && (int) y >= MINIMUM_COORD \ - && (int) y <= MAXIMUM_COORD \ - && (int) w >= 0 \ - && (int) w < MAXIMUM_COORD \ - && (int) h >= 0 \ - && (int) h < MAXIMUM_COORD); - -static void replaceRectRGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() -{ - const PixelARGB blendColour (colour.getPixelARGB()); - - if (w < 32) - { - while (--h >= 0) - { - PixelRGB* dest = (PixelRGB*) pixels; - - for (int i = w; --i >= 0;) - (dest++)->set (blendColour); - - pixels += stride; - } - } - else - { - // for wider fills, it's worth using some optimisations.. - - const uint8 r = blendColour.getRed(); - const uint8 g = blendColour.getGreen(); - const uint8 b = blendColour.getBlue(); - - if (r == g && r == b) // if all the component values are the same, we can cheat.. - { - while (--h >= 0) - { - memset (pixels, r, w * 3); - pixels += stride; - } - } - else - { - PixelRGB filler [4]; - filler[0].set (blendColour); - filler[1].set (blendColour); - filler[2].set (blendColour); - filler[3].set (blendColour); - const int* const intFiller = (const int*) filler; - - while (--h >= 0) - { - uint8* dest = (uint8*) pixels; - - int i = w; - - while ((i > 8) && (((pointer_sized_int) dest & 7) != 0)) - { - ((PixelRGB*) dest)->set (blendColour); - dest += 3; - --i; - } - - while (i >= 4) - { - ((int*) dest) [0] = intFiller[0]; - ((int*) dest) [1] = intFiller[1]; - ((int*) dest) [2] = intFiller[2]; - - dest += 12; - i -= 4; - } - - while (--i >= 0) - { - ((PixelRGB*) dest)->set (blendColour); - dest += 3; - } - - pixels += stride; - } - } - } -} - -static void replaceRectARGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() +/*static void replaceRectARGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() { const PixelARGB blendColour (colour.getPixelARGB()); @@ -81173,14 +81321,14 @@ static void blendRectRGB (uint8* pixels, const int w, int h, const int stride, c "\tjg .lineLoop2 \n" "\tpop %%ebx \n" "\temms \n" - : /* No output registers */ - : [aaaa] "m" (aaaa), /* Input registers */ + : // No output registers + : [aaaa] "m" (aaaa), // Input registers [rgb0] "m" (rgb0), [w] "m" (w), "c" (h), [stride] "D" (stride), [pixels] "S" (pixels) - : "cc", "eax", "edx", "memory" /* Clobber list */ + : "cc", "eax", "edx", "memory" ); #endif } @@ -81199,120 +81347,38 @@ static void blendRectRGB (uint8* pixels, const int w, int h, const int stride, c } } } +*/ -static void blendRectARGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() -{ - if (colour.isOpaque()) - { - replaceRectARGB (pixels, w, h, stride, colour); - } - else - { - const PixelARGB blendColour (colour.getPixelARGB()); - const int alpha = blendColour.getAlpha(); - - if (alpha <= 0) - return; - - while (--h >= 0) - { - PixelARGB* dest = (PixelARGB*) pixels; - - for (int i = w; --i >= 0;) - (dest++)->blend (blendColour); - - pixels += stride; - } - } -} - -static void blendAlphaMapARGB (uint8* destPixel, const int imageStride, - const uint8* alphaValues, const int w, int h, - const int pixelStride, const int lineStride, - const Colour& colour) throw() -{ - const PixelARGB srcPix (colour.getPixelARGB()); - - while (--h >= 0) - { - PixelARGB* dest = (PixelARGB*) destPixel; - const uint8* src = alphaValues; - - int i = w; - while (--i >= 0) - { - unsigned int srcAlpha = *src; - src += pixelStride; - - if (srcAlpha > 0) - dest->blend (srcPix, srcAlpha); - - ++dest; - } - - alphaValues += lineStride; - destPixel += imageStride; - } -} - -static void blendAlphaMapRGB (uint8* destPixel, const int imageStride, - const uint8* alphaValues, int const width, int height, - const int pixelStride, const int lineStride, - const Colour& colour) throw() -{ - const PixelARGB srcPix (colour.getPixelARGB()); - - while (--height >= 0) - { - PixelRGB* dest = (PixelRGB*) destPixel; - const uint8* src = alphaValues; - - int i = width; - while (--i >= 0) - { - unsigned int srcAlpha = *src; - src += pixelStride; - - if (srcAlpha > 0) - dest->blend (srcPix, srcAlpha); - - ++dest; - } - - alphaValues += lineStride; - destPixel += imageStride; - } -} - -template +template class SolidColourEdgeTableRenderer { - uint8* const data; - const int stride; - PixelType* linePixels; - PixelARGB sourceColour; - - SolidColourEdgeTableRenderer (const SolidColourEdgeTableRenderer&); - const SolidColourEdgeTableRenderer& operator= (const SolidColourEdgeTableRenderer&); - public: - SolidColourEdgeTableRenderer (uint8* const data_, - const int stride_, - const Colour& colour) throw() + SolidColourEdgeTableRenderer (const Image::BitmapData& data_, const PixelARGB& colour) throw() : data (data_), - stride (stride_), - sourceColour (colour.getPixelARGB()) + sourceColour (colour) { + if (sizeof (PixelType) == 3) + { + areRGBComponentsEqual = sourceColour.getRed() == sourceColour.getGreen() + && sourceColour.getGreen() == sourceColour.getBlue(); + filler[0].set (sourceColour); + filler[1].set (sourceColour); + filler[2].set (sourceColour); + filler[3].set (sourceColour); + } } forcedinline void setEdgeTableYPos (const int y) throw() { - linePixels = (PixelType*) (data + stride * y); + linePixels = (PixelType*) data.getLinePointer (y); } forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw() { - linePixels[x].blend (sourceColour, alphaLevel); + if (replaceExisting) + linePixels[x].set (sourceColour); + else + linePixels[x].blend (sourceColour, alphaLevel); } forcedinline void handleEdgeTableLine (const int x, int width, const int alphaLevel) const throw() @@ -81322,77 +81388,87 @@ public: PixelType* dest = linePixels + x; - if (p.getAlpha() < 0xff) - { - do - { - dest->blend (p); - ++dest; + if (replaceExisting || p.getAlpha() >= 0xff) + replaceLine (dest, p, width); + else + blendLine (dest, p, width); + } - } while (--width > 0); +private: + const Image::BitmapData& data; + PixelType* linePixels; + PixelARGB sourceColour; + PixelRGB filler [4]; + bool areRGBComponentsEqual; + + forcedinline void blendLine (PixelType* dest, const PixelARGB& colour, int width) const throw() + { + do + { + dest->blend (colour); + ++dest; + + } while (--width > 0); + } + + forcedinline void replaceLine (PixelRGB* dest, const PixelARGB& colour, int width) const throw() + { + if (areRGBComponentsEqual) // if all the component values are the same, we can cheat.. + { + memset (dest, colour.getRed(), width * 3); } else { - do + if (width >> 5) { - dest->set (p); - ++dest; + const int* const intFiller = (const int*) filler; - } while (--width > 0); + while (width > 8 && (((pointer_sized_int) dest) & 7) != 0) + { + dest->set (colour); + ++dest; + --width; + } + + while (width > 4) + { + ((int*) dest) [0] = intFiller[0]; + ((int*) dest) [1] = intFiller[1]; + ((int*) dest) [2] = intFiller[2]; + dest = (PixelRGB*) (((uint8*) dest) + 12); + width -= 4; + } + } + + while (--width >= 0) + { + dest->set (colour); + ++dest; + } } } + + forcedinline void replaceLine (PixelAlpha* dest, const PixelARGB& colour, int width) const throw() + { + memset (dest, colour.getAlpha(), width); + } + + forcedinline void replaceLine (PixelARGB* dest, const PixelARGB& colour, int width) const throw() + { + do + { + dest->set (colour); + ++dest; + + } while (--width > 0); + } + + SolidColourEdgeTableRenderer (const SolidColourEdgeTableRenderer&); + const SolidColourEdgeTableRenderer& operator= (const SolidColourEdgeTableRenderer&); }; -class AlphaBitmapRenderer -{ - uint8* data; - int stride; - uint8* lineStart; - - AlphaBitmapRenderer (const AlphaBitmapRenderer&); - const AlphaBitmapRenderer& operator= (const 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; - } -}; - -static const int numScaleBits = 12; - class LinearGradientPixelGenerator { - const PixelARGB* const lookupTable; - const int numEntries; - PixelARGB linePix; - int start, scale; - double grad, yTerm; - bool vertical, horizontal; - - LinearGradientPixelGenerator (const LinearGradientPixelGenerator&); - const LinearGradientPixelGenerator& operator= (const LinearGradientPixelGenerator&); - public: LinearGradientPixelGenerator (const ColourGradient& gradient, const PixelARGB* const lookupTable_, const int numEntries_) @@ -81429,19 +81505,19 @@ public: if (vertical) { - scale = roundDoubleToInt ((numEntries << numScaleBits) / (double) (y2 - y1)); + scale = roundDoubleToInt ((numEntries << (int) numScaleBits) / (double) (y2 - y1)); start = roundDoubleToInt (y1 * scale); } else if (horizontal) { - scale = roundDoubleToInt ((numEntries << numScaleBits) / (double) (x2 - x1)); + scale = roundDoubleToInt ((numEntries << (int) numScaleBits) / (double) (x2 - x1)); start = roundDoubleToInt (x1 * scale); } else { grad = (y2 - y1) / (double) (x1 - x2); yTerm = y1 - x1 / grad; - scale = roundDoubleToInt ((numEntries << numScaleBits) / (yTerm * grad - (y2 * grad - x2))); + scale = roundDoubleToInt ((numEntries << (int) numScaleBits) / (yTerm * grad - (y2 * grad - x2))); grad *= scale; } } @@ -81449,7 +81525,7 @@ public: forcedinline void setY (const int y) throw() { if (vertical) - linePix = lookupTable [jlimit (0, numEntries, (y * scale - start) >> numScaleBits)]; + linePix = lookupTable [jlimit (0, numEntries, (y * scale - start) >> (int) numScaleBits)]; else if (! horizontal) start = roundDoubleToInt ((y - yTerm) * grad); } @@ -81459,22 +81535,24 @@ public: if (vertical) return linePix; - return lookupTable [jlimit (0, numEntries, (x * scale - start) >> numScaleBits)]; + return lookupTable [jlimit (0, numEntries, (x * scale - start) >> (int) numScaleBits)]; } + +private: + const PixelARGB* const lookupTable; + const int numEntries; + PixelARGB linePix; + int start, scale; + double grad, yTerm; + bool vertical, horizontal; + enum { numScaleBits = 12 }; + + LinearGradientPixelGenerator (const LinearGradientPixelGenerator&); + const LinearGradientPixelGenerator& operator= (const LinearGradientPixelGenerator&); }; class RadialGradientPixelGenerator { -protected: - const PixelARGB* const lookupTable; - const int numEntries; - const double gx1, gy1; - double maxDist, invScale; - double dy; - - RadialGradientPixelGenerator (const RadialGradientPixelGenerator&); - const RadialGradientPixelGenerator& operator= (const RadialGradientPixelGenerator&); - public: RadialGradientPixelGenerator (const ColourGradient& gradient, const PixelARGB* const lookupTable_, const int numEntries_) throw() @@ -81507,16 +81585,19 @@ public: else return lookupTable [jmin (numEntries, roundDoubleToInt (sqrt (x) * invScale))]; } + +protected: + const PixelARGB* const lookupTable; + const int numEntries; + const double gx1, gy1; + double maxDist, invScale, dy; + + RadialGradientPixelGenerator (const RadialGradientPixelGenerator&); + const RadialGradientPixelGenerator& operator= (const RadialGradientPixelGenerator&); }; class TransformedRadialGradientPixelGenerator : public RadialGradientPixelGenerator { - double tM10, tM00, lineYM01, lineYM11; - AffineTransform inverseTransform; - - TransformedRadialGradientPixelGenerator (const TransformedRadialGradientPixelGenerator&); - const TransformedRadialGradientPixelGenerator& operator= (const TransformedRadialGradientPixelGenerator&); - public: TransformedRadialGradientPixelGenerator (const ColourGradient& gradient, const PixelARGB* const lookupTable_, const int numEntries_) throw() @@ -81546,32 +81627,29 @@ public: else return lookupTable [jmin (numEntries, roundDoubleToInt (sqrt (x) * invScale))]; } + +private: + double tM10, tM00, lineYM01, lineYM11; + const AffineTransform inverseTransform; + + TransformedRadialGradientPixelGenerator (const TransformedRadialGradientPixelGenerator&); + const TransformedRadialGradientPixelGenerator& operator= (const TransformedRadialGradientPixelGenerator&); }; template class GradientEdgeTableRenderer : public GradientType { - uint8* const data; - const int stride; - PixelType* linePixels; - - GradientEdgeTableRenderer (const GradientEdgeTableRenderer&); - const GradientEdgeTableRenderer& operator= (const GradientEdgeTableRenderer&); - public: - GradientEdgeTableRenderer (uint8* const data_, - const int stride_, - const ColourGradient& gradient, + GradientEdgeTableRenderer (const Image::BitmapData& destData_, const ColourGradient& gradient, const PixelARGB* const lookupTable, const int numEntries) throw() : GradientType (gradient, lookupTable, numEntries - 1), - data (data_), - stride (stride_) + destData (destData_) { } forcedinline void setEdgeTableYPos (const int y) throw() { - linePixels = (PixelType*) (data + stride * y); + linePixels = (PixelType*) destData.getLinePointer (y); GradientType::setY (y); } @@ -81601,40 +81679,32 @@ public: } while (--width > 0); } } + +private: + const Image::BitmapData& destData; + PixelType* linePixels; + + GradientEdgeTableRenderer (const GradientEdgeTableRenderer&); + const GradientEdgeTableRenderer& operator= (const GradientEdgeTableRenderer&); }; template class ImageFillEdgeTableRenderer { - uint8* const destImageData; - const uint8* srcImageData; - int stride, srcStride, extraAlpha; - - DestPixelType* linePixels; - SrcPixelType* sourceLineStart; - - ImageFillEdgeTableRenderer (const ImageFillEdgeTableRenderer&); - const ImageFillEdgeTableRenderer& operator= (const ImageFillEdgeTableRenderer&); - public: - ImageFillEdgeTableRenderer (uint8* const destImageData_, - const int stride_, - const uint8* srcImageData_, - const int srcStride_, - int extraAlpha_, - SrcPixelType*) throw() // dummy param to avoid compiler error - : destImageData (destImageData_), - srcImageData (srcImageData_), - stride (stride_), - srcStride (srcStride_), - extraAlpha (extraAlpha_) + ImageFillEdgeTableRenderer (const Image::BitmapData& destData_, + const Image::BitmapData& srcData_, + const int extraAlpha_) throw() + : destData (destData_), + srcData (srcData_), + extraAlpha (extraAlpha_ + 1) { } forcedinline void setEdgeTableYPos (int y) throw() { - linePixels = (DestPixelType*) (destImageData + stride * y); - sourceLineStart = (SrcPixelType*) (srcImageData + srcStride * y); + linePixels = (DestPixelType*) destData.getLinePointer (y); + sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y); } forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) const throw() @@ -81658,411 +81728,819 @@ public: } while (--width > 0); } else + { + copyRow (dest, sourceLineStart + x, width); + } + } + +private: + const Image::BitmapData& destData; + const Image::BitmapData& srcData; + const int extraAlpha; + DestPixelType* linePixels; + SrcPixelType* sourceLineStart; + + template + forcedinline static void copyRow (PixelType1* dest, PixelType2* src, int width) throw() + { + do + { + dest++ ->blend (*src++); + + } while (--width > 0); + } + + forcedinline static void copyRow (PixelRGB* dest, PixelRGB* src, int width) throw() + { + memcpy (dest, src, width * sizeof (PixelRGB)); + } + + ImageFillEdgeTableRenderer (const ImageFillEdgeTableRenderer&); + const ImageFillEdgeTableRenderer& operator= (const ImageFillEdgeTableRenderer&); +}; + +template +class TransformedImageFillEdgeTableRenderer +{ +public: + TransformedImageFillEdgeTableRenderer (const Image::BitmapData& destData_, + const Image::BitmapData& srcData_, + const AffineTransform& transform, + const int extraAlpha_, + const bool betterQuality_) throw() + : interpolator (transform), + destData (destData_), + srcData (srcData_), + extraAlpha (extraAlpha_ + 1), + betterQuality (betterQuality_), + pixelOffset (betterQuality_ ? 0.5f : 0.0f), + pixelOffsetInt (betterQuality_ ? -128 : 0), + maxX (srcData_.width - 1), + maxY (srcData_.height - 1) + { + } + + forcedinline void setEdgeTableYPos (const int newY) throw() + { + y = newY; + linePixels = (DestPixelType*) destData.getLinePointer (newY); + } + + forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) throw() + { + alphaLevel *= extraAlpha; + alphaLevel >>= 8; + + SrcPixelType p; + generate (&p, x, 1); + + linePixels[x].blend (p, alphaLevel); + } + + forcedinline void handleEdgeTableLine (const int x, int width, int alphaLevel) throw() + { + SrcPixelType* span = (SrcPixelType*) alloca (sizeof (SrcPixelType) * width); + generate (span, x, width); + + DestPixelType* dest = linePixels + x; + alphaLevel *= extraAlpha; + alphaLevel >>= 8; + + if (alphaLevel < 0xfe) { do { - dest++ ->blend (sourceLineStart [x++]); - + dest++ ->blend (*span++, alphaLevel); + } while (--width > 0); + } + else + { + do + { + dest++ ->blend (*span++); } while (--width > 0); } } + +private: + + void generate (PixelARGB* dest, const int x, int numPixels) throw() + { + this->interpolator.setStartOfLine (x + pixelOffset, y + pixelOffset, numPixels); + + do + { + int hiResX, hiResY; + this->interpolator.next (hiResX, hiResY); + hiResX += pixelOffsetInt; + hiResY += pixelOffsetInt; + + int loResX = hiResX >> 8; + int loResY = hiResY >> 8; + + if (betterQuality + && ((unsigned int) loResX) < (unsigned int) maxX + && ((unsigned int) loResY) < (unsigned int) maxY) + { + uint32 c[4] = { 256 * 128, 256 * 128, 256 * 128, 256 * 128 }; + hiResX &= 255; + hiResY &= 255; + + const uint8* src = this->srcData.getPixelPointer (loResX, loResY); + + uint32 weight = (256 - hiResX) * (256 - hiResY); + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + c[3] += weight * src[3]; + + weight = hiResX * (256 - hiResY); + c[0] += weight * src[4]; + c[1] += weight * src[5]; + c[2] += weight * src[6]; + c[3] += weight * src[7]; + + src += this->srcData.lineStride; + + weight = (256 - hiResX) * hiResY; + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + c[3] += weight * src[3]; + + weight = hiResX * hiResY; + c[0] += weight * src[4]; + c[1] += weight * src[5]; + c[2] += weight * src[6]; + c[3] += weight * src[7]; + + dest->setARGB ((uint8) (c[3] >> 16), + (uint8) (c[2] >> 16), + (uint8) (c[1] >> 16), + (uint8) (c[0] >> 16)); + } + else + { + // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + + dest->set (*(const PixelARGB*) this->srcData.getPixelPointer (loResX, loResY)); + } + + ++dest; + + } while (--numPixels > 0); + } + + void generate (PixelRGB* dest, const int x, int numPixels) throw() + { + this->interpolator.setStartOfLine (x + pixelOffset, y + pixelOffset, numPixels); + + do + { + int hiResX, hiResY; + this->interpolator.next (hiResX, hiResY); + hiResX += pixelOffsetInt; + hiResY += pixelOffsetInt; + int loResX = hiResX >> 8; + int loResY = hiResY >> 8; + + if (betterQuality + && ((unsigned int) loResX) < (unsigned int) maxX + && ((unsigned int) loResY) < (unsigned int) maxY) + { + uint32 c[3] = { 256 * 128, 256 * 128, 256 * 128 }; + hiResX &= 255; + hiResY &= 255; + + const uint8* src = this->srcData.getPixelPointer (loResX, loResY); + + unsigned int weight = (256 - hiResX) * (256 - hiResY); + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + + weight = hiResX * (256 - hiResY); + c[0] += weight * src[3]; + c[1] += weight * src[4]; + c[2] += weight * src[5]; + + src += this->srcData.lineStride; + + weight = (256 - hiResX) * hiResY; + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + + weight = hiResX * hiResY; + c[0] += weight * src[3]; + c[1] += weight * src[4]; + c[2] += weight * src[5]; + + dest->setARGB ((uint8) 255, + (uint8) (c[0] >> 16), + (uint8) (c[1] >> 16), + (uint8) (c[2] >> 16)); + } + else + { + // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + + dest->set (*(const PixelRGB*) this->srcData.getPixelPointer (loResX, loResY)); + } + + ++dest; + + } while (--numPixels > 0); + } + + void generate (PixelAlpha* dest, const int x, int numPixels) throw() + { + this->interpolator.setStartOfLine (x + pixelOffset, y + pixelOffset, numPixels); + + do + { + int hiResX, hiResY; + this->interpolator.next (hiResX, hiResY); + hiResX += pixelOffsetInt; + hiResY += pixelOffsetInt; + int loResX = hiResX >> 8; + int loResY = hiResY >> 8; + + if (betterQuality + && ((unsigned int) loResX) < (unsigned int) maxX + && ((unsigned int) loResY) < (unsigned int) maxY) + { + hiResX &= 255; + hiResY &= 255; + + uint32 c = 256 * 128; + const uint8* src = this->srcData.getPixelPointer (loResX, loResY); + c += src[0] * ((256 - hiResX) * (256 - hiResY)); + c += src[1] * (hiResX * (256 - hiResY)); + src += this->srcData.lineStride; + c += src[0] * ((256 - hiResX) * hiResY); + c += src[1] * (hiResX * hiResY); + + *((uint8*) dest) = (uint8) c; + } + else + { + // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + + *((uint8*) dest) = *(this->srcData.getPixelPointer (loResX, loResY)); + } + + ++dest; + + } while (--numPixels > 0); + } + + class TransformedImageSpanInterpolator + { + public: + TransformedImageSpanInterpolator (const AffineTransform& transform) throw() + : inverseTransform (transform.inverted()) + {} + + void setStartOfLine (float x, float y, const int numPixels) throw() + { + float x1 = x, y1 = y; + inverseTransform.transformPoint (x1, y1); + x += numPixels; + inverseTransform.transformPoint (x, y); + + xBresenham.set ((int) (x1 * 256.0f), (int) (x * 256.0f), numPixels); + yBresenham.set ((int) (y1 * 256.0f), (int) (y * 256.0f), numPixels); + } + + void next (int& x, int& y) throw() + { + x = xBresenham.n; + xBresenham.stepToNext(); + y = yBresenham.n; + yBresenham.stepToNext(); + } + + private: + class BresenhamInterpolator + { + public: + BresenhamInterpolator() throw() {} + + void set (const int n1, const int n2, const int numSteps_) throw() + { + numSteps = jmax (1, numSteps_); + step = (n2 - n1) / numSteps; + remainder = modulo = (n2 - n1) % numSteps; + n = n1; + + if (modulo <= 0) + { + modulo += numSteps; + remainder += numSteps; + --step; + } + + modulo -= numSteps; + } + + forcedinline void stepToNext() throw() + { + modulo += remainder; + n += step; + + if (modulo > 0) + { + modulo -= numSteps; + ++n; + } + } + + int n; + + private: + int numSteps, step, modulo, remainder; + }; + + const AffineTransform inverseTransform; + BresenhamInterpolator xBresenham, yBresenham; + + TransformedImageSpanInterpolator (const TransformedImageSpanInterpolator&); + const TransformedImageSpanInterpolator& operator= (const TransformedImageSpanInterpolator&); + }; + + TransformedImageSpanInterpolator interpolator; + const Image::BitmapData& destData; + const Image::BitmapData& srcData; + const int extraAlpha; + const bool betterQuality; + const float pixelOffset; + const int pixelOffsetInt, maxX, maxY; + int y; + DestPixelType* linePixels; + + TransformedImageFillEdgeTableRenderer (const TransformedImageFillEdgeTableRenderer&); + const TransformedImageFillEdgeTableRenderer& operator= (const TransformedImageFillEdgeTableRenderer&); }; -static void blendRowOfPixels (PixelARGB* dst, - const PixelRGB* src, - int width) throw() -{ - while (--width >= 0) - (dst++)->set (*src++); -} - -static void blendRowOfPixels (PixelRGB* dst, - const PixelRGB* src, - int width) throw() -{ - memcpy (dst, src, 3 * width); -} - -static void blendRowOfPixels (PixelRGB* dst, - const PixelARGB* src, - int width) throw() -{ - while (--width >= 0) - (dst++)->blend (*src++); -} - -static void blendRowOfPixels (PixelARGB* dst, - const PixelARGB* src, - int width) throw() -{ - while (--width >= 0) - (dst++)->blend (*src++); -} - -static void blendRowOfPixels (PixelARGB* dst, - const PixelRGB* src, - int width, - const uint8 alpha) throw() -{ - while (--width >= 0) - (dst++)->blend (*src++, alpha); -} - -static void blendRowOfPixels (PixelRGB* dst, - const PixelRGB* src, - int width, - const uint8 alpha) throw() -{ - uint8* d = (uint8*) dst; - const uint8* s = (const uint8*) src; - const int inverseAlpha = 0xff - alpha; - - while (--width >= 0) - { - d[0] = (uint8) (s[0] + (((d[0] - s[0]) * inverseAlpha) >> 8)); - d[1] = (uint8) (s[1] + (((d[1] - s[1]) * inverseAlpha) >> 8)); - d[2] = (uint8) (s[2] + (((d[2] - s[2]) * inverseAlpha) >> 8)); - - d += 3; - s += 3; - } -} - -static void blendRowOfPixels (PixelRGB* dst, - const PixelARGB* src, - int width, - const uint8 alpha) throw() -{ - while (--width >= 0) - (dst++)->blend (*src++, alpha); -} - -static void blendRowOfPixels (PixelARGB* dst, - const PixelARGB* src, - int width, - const uint8 alpha) throw() -{ - while (--width >= 0) - (dst++)->blend (*src++, alpha); -} - -template -static void overlayImage (DestPixelType* dest, - const int destStride, - const SrcPixelType* src, - const int srcStride, - const int width, - int height, - const uint8 alpha) throw() -{ - if (alpha < 0xff) - { - while (--height >= 0) - { - blendRowOfPixels (dest, src, width, alpha); - - dest = (DestPixelType*) (((uint8*) dest) + destStride); - src = (const SrcPixelType*) (((const uint8*) src) + srcStride); - } - } - else - { - while (--height >= 0) - { - blendRowOfPixels (dest, src, width); - - dest = (DestPixelType*) (((uint8*) dest) + destStride); - src = (const SrcPixelType*) (((const uint8*) src) + srcStride); - } - } -} - -template -static void transformedImageRender (Image& destImage, - const Image& sourceImage, - const int destClipX, const int destClipY, - const int destClipW, const int destClipH, - const int srcClipX, const int srcClipY, - const int srcClipWidth, const int srcClipHeight, - double srcX, double srcY, - const double lineDX, const double lineDY, - const double pixelDX, const double pixelDY, - const uint8 alpha, - const Graphics::ResamplingQuality quality, - DestPixelType*, - SrcPixelType*) throw() // forced by a compiler bug to include dummy - // parameters of the templated classes to - // make it use the correct instance of this function.. -{ - int destStride, destPixelStride; - uint8* const destPixels = destImage.lockPixelDataReadWrite (destClipX, destClipY, destClipW, destClipH, destStride, destPixelStride); - - int srcStride, srcPixelStride; - const uint8* const srcPixels = sourceImage.lockPixelDataReadOnly (srcClipX, srcClipY, srcClipWidth, srcClipHeight, srcStride, srcPixelStride); - - if (quality == Graphics::lowResamplingQuality) // nearest-neighbour.. - { - if (alpha == 255) - { - for (int y = 0; y < destClipH; ++y) - { - double sx = srcX; - double sy = srcY; - - DestPixelType* dest = (DestPixelType*) (destPixels + destStride * y); - - for (int x = destClipW; --x >= 0;) - { - const int ix = ((int) sx) - srcClipX; - - if (((unsigned int) ix) < (unsigned int) srcClipWidth) - { - const int iy = ((int) sy) - srcClipY; - - if (((unsigned int) iy) < (unsigned int) srcClipHeight) - { - const SrcPixelType* const src = (const SrcPixelType*) (srcPixels + srcStride * iy + srcPixelStride * ix); - dest->blend (*src); - } - } - - ++dest; - sx += pixelDX; - sy += pixelDY; - } - - srcX += lineDX; - srcY += lineDY; - } - } - else - { - for (int y = 0; y < destClipH; ++y) - { - double sx = srcX; - double sy = srcY; - - DestPixelType* dest = (DestPixelType*) (destPixels + destStride * y); - - for (int x = destClipW; --x >= 0;) - { - const int ix = ((int) sx) - srcClipX; - - if (((unsigned int) ix) < (unsigned int) srcClipWidth) - { - const int iy = ((int) sy) - srcClipY; - - if (((unsigned int) iy) < (unsigned int) srcClipHeight) - { - const SrcPixelType* const src = (const SrcPixelType*) (srcPixels + srcStride * iy + srcPixelStride * ix); - dest->blend (*src, alpha); - } - } - - ++dest; - sx += pixelDX; - sy += pixelDY; - } - - srcX += lineDX; - srcY += lineDY; - } - } - } - else - { - jassert (quality == Graphics::mediumResamplingQuality); // (only bilinear is implemented, so that's what you'll get here..) - - for (int y = 0; y < destClipH; ++y) - { - double sx = srcX - (srcClipWidth == destClipW ? 0.0 : 0.5); - double sy = srcY - (srcClipHeight == destClipH ? 0.0 : 0.5); - DestPixelType* dest = (DestPixelType*) (destPixels + destStride * y); - - for (int x = 0; x < destClipW; ++x) - { - const double fx = floor (sx); - const double fy = floor (sy); - const int ix = roundDoubleToInt (fx) - srcClipX; - const int iy = roundDoubleToInt (fy) - srcClipY; - - if (ix < srcClipWidth && iy < srcClipHeight) - { - PixelARGB p1 (0), p2 (0), p3 (0), p4 (0); - - const SrcPixelType* src = (const SrcPixelType*) (srcPixels + srcStride * iy + srcPixelStride * ix); - - if (iy >= 0) - { - if (ix >= 0) - p1.set (src[0]); - - if (((unsigned int) (ix + 1)) < (unsigned int) srcClipWidth) - p2.set (src[1]); - } - - if (((unsigned int) (iy + 1)) < (unsigned int) srcClipHeight) - { - src = (const SrcPixelType*) (((const uint8*) src) + srcStride); - - if (ix >= 0) - p3.set (src[0]); - - if (((unsigned int) (ix + 1)) < (unsigned int) srcClipWidth) - p4.set (src[1]); - } - - const int dx = roundDoubleToInt ((sx - fx) * 255.0); - p1.tween (p2, dx); - p3.tween (p4, dx); - p1.tween (p3, roundDoubleToInt ((sy - fy) * 255.0)); - - if (p1.getAlpha() > 0) - dest->blend (p1, alpha); - } - - ++dest; - sx += pixelDX; - sy += pixelDY; - } - - srcX += lineDX; - srcY += lineDY; - } - } - - destImage.releasePixelDataReadWrite (destPixels); - sourceImage.releasePixelDataReadOnly (srcPixels); -} - -template -static void renderAlphaMap (DestPixelType* destPixels, - int destStride, - SrcPixelType* srcPixels, - int srcStride, - const uint8* alphaValues, - const int lineStride, const int pixelStride, - int width, int height, - const int extraAlpha) throw() -{ - while (--height >= 0) - { - SrcPixelType* srcPix = srcPixels; - srcPixels = (SrcPixelType*) (((const uint8*) srcPixels) + srcStride); - - DestPixelType* destPix = destPixels; - destPixels = (DestPixelType*) (((uint8*) destPixels) + destStride); - - const uint8* alpha = alphaValues; - alphaValues += lineStride; - - if (extraAlpha < 0x100) - { - for (int i = width; --i >= 0;) - { - destPix++ ->blend (*srcPix++, (extraAlpha * *alpha) >> 8); - alpha += pixelStride; - } - } - else - { - for (int i = width; --i >= 0;) - { - destPix++ ->blend (*srcPix++, *alpha); - alpha += pixelStride; - } - } - } -} - -/*class ClippingPath +class LLGCSavedState { public: - ClippingPath (const Rectangle& r) throw() - : rectangles (r) + LLGCSavedState (const Rectangle& clip_, + const int xOffset_, const int yOffset_, + const Font& font_, const Colour& colour_, ColourGradient* const gradient_, + const Graphics::ResamplingQuality interpolationQuality_) throw() + : edgeTable (new EdgeTableHolder (EdgeTable (clip_))), + xOffset (xOffset_), + yOffset (yOffset_), + font (font_), + colour (colour_), + gradient (gradient_), + interpolationQuality (interpolationQuality_) { } - ClippingPath (const ClippingPath& other) throw() - : rectangles (other.rectangles), mask (other.mask) + LLGCSavedState (const LLGCSavedState& other) throw() + : edgeTable (other.edgeTable), + xOffset (other.xOffset), + yOffset (other.yOffset), + font (other.font), + colour (other.colour), + gradient (other.gradient), + interpolationQuality (other.interpolationQuality) { + if (gradient != 0) + gradient = new ColourGradient (*gradient); } - ~ClippingPath() throw() + ~LLGCSavedState() throw() { - delete mask; + delete gradient; } bool reduce (int x, int y, int w, int h) throw() { - return clip.clipTo (Rectangle (x, y, w, h)); + dupeEdgeTableIfMultiplyReferenced(); + edgeTable->edgeTable.clipToRectangle (Rectangle (x, y, w, h)); + return ! edgeTable->edgeTable.isEmpty(); + } + + bool reduce (const RectangleList& r) throw() + { + dupeEdgeTableIfMultiplyReferenced(); + RectangleList totalArea (edgeTable->edgeTable.getMaximumBounds()); + totalArea.subtract (r); + + for (RectangleList::Iterator i (totalArea); i.next();) + edgeTable->edgeTable.excludeRectangle (*i.getRectangle()); + + return ! edgeTable->edgeTable.isEmpty(); } bool exclude (int x, int y, int w, int h) throw() { - return clip.subtract (Rectangle (x, y, w, h)); + dupeEdgeTableIfMultiplyReferenced(); + edgeTable->edgeTable.excludeRectangle (Rectangle (x, y, w, h)); + return ! edgeTable->edgeTable.isEmpty(); } - bool reduce (const Path& p, const AffineTransform& transform) + bool reduce (const Path& p, const AffineTransform& transform) throw() { - float px, py, pw, ph; - p.getBoundsTransformed (transform, px, py, pw, ph); + dupeEdgeTableIfMultiplyReferenced(); + EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); + edgeTable->edgeTable.clipToEdgeTable (et); + return ! edgeTable->edgeTable.isEmpty(); + } - Rectangle pathBounds ((int) px - 1, (int) py - 1, (int) pw + 3, (int) ph + 3); + bool reduce (const Image& image, int x, int y) throw() + { + dupeEdgeTableIfMultiplyReferenced(); + edgeTable->edgeTable.clipToImageAlpha (image, x, y); + return ! edgeTable->edgeTable.isEmpty(); + } - if (clip.clipTo (pathBounds)) + bool reduce (const Image& image, const AffineTransform& transform) throw() + { + jassertfalse + return true; + } + + void fillEdgeTable (Image& image, EdgeTable& et, const bool replaceContents = false) throw() + { + et.clipToEdgeTable (edgeTable->edgeTable); + + Image::BitmapData destData (image, 0, 0, image.getWidth(), image.getHeight(), true); + + if (gradient != 0) { + jassert (! replaceContents); // that option is just for solid colours + + ColourGradient g2 (*gradient); + + const bool isIdentity = g2.transform.isOnlyTranslation(); + if (isIdentity) + { + // If our translation doesn't involve any distortion, we can speed it up.. + const float tx = g2.transform.getTranslationX() + xOffset; + const float ty = g2.transform.getTranslationY() + yOffset; + + g2.x1 += tx; + g2.x2 += tx; + g2.y1 += ty; + g2.y2 += ty; + } + else + { + g2.transform = g2.transform.translated ((float) xOffset, + (float) yOffset); + } + + int numLookupEntries; + PixelARGB* const lookupTable = g2.createLookupTable (numLookupEntries); + jassert (numLookupEntries > 0); + + if (image.getFormat() == Image::RGB) + { + jassert (destData.pixelStride == 3); + + if (g2.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + else + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + else + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + else if (image.getFormat() == Image::ARGB) + { + jassert (destData.pixelStride == 4); + + if (g2.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + else + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + else + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + else if (image.getFormat() == Image::SingleChannel) + { + jassert (destData.pixelStride == 4); + + if (g2.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + else + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + else + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + + juce_free (lookupTable); + } + else + { + const PixelARGB fillColour (colour.getPixelARGB()); + + if (replaceContents) + { + if (image.getFormat() == Image::RGB) + { + jassert (destData.pixelStride == 3); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + else if (image.getFormat() == Image::ARGB) + { + jassert (destData.pixelStride == 4); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + else if (image.getFormat() == Image::SingleChannel) + { + jassert (destData.pixelStride == 1); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + } + else + { + if (image.getFormat() == Image::RGB) + { + jassert (destData.pixelStride == 3); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + else if (image.getFormat() == Image::ARGB) + { + jassert (destData.pixelStride == 4); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + else if (image.getFormat() == Image::SingleChannel) + { + jassert (destData.pixelStride == 1); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + } } } - bool reduce (Image& image, int x, int y) + void renderImage (Image& destImage, const Image& sourceImage, + int srcClipX, int srcClipY, int srcClipW, int srcClipH, + const AffineTransform& t) throw() { + if (t.isSingularity()) + return; + + const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); + const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); + + if (transform.isOnlyTranslation()) + { + // If our translation doesn't involve any distortion, just use a simple blit.. + const int tx = (int) (t.getTranslationX() * 256.0f); + const int ty = (int) (t.getTranslationY() * 256.0f); + + if ((! betterQuality) || ((tx | ty) & 224) == 0) + { + renderImage (destImage, sourceImage, (tx + 128) >> 8, (ty + 128) >> 8); + return; + } + } + + Path p; + p.addRectangle ((float) srcClipX, (float) srcClipY, (float) srcClipW, (float) srcClipH); + + EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); + et.clipToEdgeTable (edgeTable->edgeTable); + + Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true); + Image::BitmapData srcData (sourceImage, 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); + + const int extraAlpha = colour.getAlpha(); + + switch (sourceImage.getFormat()) + { + case Image::ARGB: + if (destImage.getFormat() == Image::ARGB) + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + else if (destImage.getFormat() == Image::RGB) + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + else + { + jassert (destImage.getFormat() == Image::SingleChannel) + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + break; + + case Image::RGB: + if (destImage.getFormat() == Image::ARGB) + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + else if (destImage.getFormat() == Image::RGB) + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + else + { + jassert (destImage.getFormat() == Image::SingleChannel) + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + break; + + default: + jassert (sourceImage.getFormat() == Image::SingleChannel); + + if (destImage.getFormat() == Image::ARGB) + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + else if (destImage.getFormat() == Image::RGB) + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + else + { + jassert (destImage.getFormat() == Image::SingleChannel) + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + break; + } } - bool reduce (Image& image, const AffineTransform& transform) + void renderImage (Image& destImage, const Image& sourceImage, int imageX, int imageY) throw() { + EdgeTable et (Rectangle (imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight()) + .getIntersection (Rectangle (0, 0, destImage.getWidth(), destImage.getHeight()))); + et.clipToEdgeTable (edgeTable->edgeTable); + + if (et.isEmpty()) + return; + + Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true); + Image::BitmapData srcData (sourceImage, 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); + srcData.data = srcData.getPixelPointer (-imageX, -imageY); + + const int alpha = colour.getAlpha(); + + switch (destImage.getFormat()) + { + case Image::RGB: + if (sourceImage.getFormat() == Image::RGB) + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + else if (sourceImage.getFormat() == Image::ARGB) + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + else + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + break; + + case Image::ARGB: + if (sourceImage.getFormat() == Image::RGB) + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + else if (sourceImage.getFormat() == Image::ARGB) + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + else + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + break; + + default: + jassert (destImage.getFormat() == Image::SingleChannel); + if (sourceImage.getFormat() == Image::RGB) + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + else if (sourceImage.getFormat() == Image::ARGB) + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + else + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + break; + } } - class MaskImage : public ReferenceCountedObject + class EdgeTableHolder : 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); - } + EdgeTableHolder (const EdgeTable& e) throw() + : edgeTable (e) + {} - ~MaskImage() throw() - { - delete image; - } - - Image* image; - int x, y; + EdgeTable edgeTable; }; - RectangleList clip; - ReferenceCountedObjectPtr mask; + ReferenceCountedObjectPtr edgeTable; + int xOffset, yOffset; + Font font; + Colour colour; + ColourGradient* gradient; + Graphics::ResamplingQuality interpolationQuality; private: + const LLGCSavedState& operator= (const LLGCSavedState&); + + void dupeEdgeTableIfMultiplyReferenced() throw() + { + if (edgeTable->getReferenceCount() > 1) + edgeTable = new EdgeTableHolder (edgeTable->edgeTable); + } }; -*/ LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image_) : image (image_), - xOffset (0), - yOffset (0), - stateStack (20), - colour (0xff000000), - gradient (0) + stateStack (20) { - clip = new RectangleList (Rectangle (0, 0, image_.getWidth(), image_.getHeight())); + currentState = new LLGCSavedState (Rectangle (0, 0, image_.getWidth(), image_.getHeight()), + 0, 0, Font(), Colours::black, 0, Graphics::mediumResamplingQuality); } LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() { - delete clip; - delete gradient; + delete currentState; } bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const @@ -82072,26 +82550,26 @@ bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y) { - xOffset += x; - yOffset += y; + currentState->xOffset += x; + currentState->yOffset += y; } bool LowLevelGraphicsSoftwareRenderer::reduceClipRegion (int x, int y, int w, int h) { - return clip->clipTo (Rectangle (x + xOffset, y + yOffset, w, h)); + return currentState->reduce (x + currentState->xOffset, y + currentState->yOffset, w, h); } bool LowLevelGraphicsSoftwareRenderer::reduceClipRegion (const RectangleList& clipRegion) { RectangleList temp (clipRegion); - temp.offsetAll (xOffset, yOffset); + temp.offsetAll (currentState->xOffset, currentState->yOffset); - return clip->clipTo (temp); + return currentState->reduce (temp); } void LowLevelGraphicsSoftwareRenderer::excludeClipRegion (int x, int y, int w, int h) { - clip->subtract (Rectangle (x + xOffset, y + yOffset, w, h)); + currentState->exclude (x + currentState->xOffset, y + currentState->yOffset, w, h); } void LowLevelGraphicsSoftwareRenderer::clipToPath (const Path& path, const AffineTransform& transform) @@ -82104,61 +82582,34 @@ void LowLevelGraphicsSoftwareRenderer::clipToImage (Image& image, int imageX, in bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (int x, int y, int w, int h) { - return clip->intersectsRectangle (Rectangle (x + xOffset, y + yOffset, w, h)); + return currentState->edgeTable->edgeTable.getMaximumBounds() + .intersects (Rectangle (x + currentState->xOffset, y + currentState->yOffset, w, h)); } const Rectangle LowLevelGraphicsSoftwareRenderer::getClipBounds() const { - return clip->getBounds().translated (-xOffset, -yOffset); + return currentState->edgeTable->edgeTable.getMaximumBounds().translated (-currentState->xOffset, -currentState->yOffset); } bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const { - return clip->isEmpty(); -} - -LowLevelGraphicsSoftwareRenderer::SavedState::SavedState (RectangleList* const clip_, - const int xOffset_, const int yOffset_, - const Font& font_, const Colour& colour_, ColourGradient* gradient_, - Graphics::ResamplingQuality interpolationQuality_) - : clip (clip_), - xOffset (xOffset_), - yOffset (yOffset_), - font (font_), - colour (colour_), - gradient (gradient_), - interpolationQuality (interpolationQuality_) -{ -} - -LowLevelGraphicsSoftwareRenderer::SavedState::~SavedState() -{ - delete clip; - delete gradient; + return currentState->edgeTable->edgeTable.isEmpty(); } void LowLevelGraphicsSoftwareRenderer::saveState() { - stateStack.add (new SavedState (new RectangleList (*clip), xOffset, yOffset, - font, colour, gradient != 0 ? new ColourGradient (*gradient) : 0, - interpolationQuality)); + stateStack.add (new LLGCSavedState (*currentState)); } void LowLevelGraphicsSoftwareRenderer::restoreState() { - SavedState* const top = stateStack.getLast(); + LLGCSavedState* const top = stateStack.getLast(); if (top != 0) { - swapVariables (clip, top->clip); - xOffset = top->xOffset; - yOffset = top->yOffset; - font = top->font; - colour = top->colour; - swapVariables (gradient, top->gradient); - interpolationQuality = top->interpolationQuality; - - stateStack.removeLast(); + delete currentState; + currentState = top; + stateStack.removeLast (1, false); } else { @@ -82168,1066 +82619,142 @@ void LowLevelGraphicsSoftwareRenderer::restoreState() void LowLevelGraphicsSoftwareRenderer::setColour (const Colour& colour_) { - deleteAndZero (gradient); - colour = colour_; + deleteAndZero (currentState->gradient); + currentState->colour = colour_; } void LowLevelGraphicsSoftwareRenderer::setGradient (const ColourGradient& gradient_) { - delete gradient; - gradient = new ColourGradient (gradient_); + delete currentState->gradient; + currentState->gradient = new ColourGradient (gradient_); } void LowLevelGraphicsSoftwareRenderer::setOpacity (float opacity) { - colour = colour.withAlpha (opacity); + currentState->colour = currentState->colour.withAlpha (opacity); } void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality) { - interpolationQuality = quality; + currentState->interpolationQuality = quality; } void LowLevelGraphicsSoftwareRenderer::fillRect (int x, int y, int w, int h, const bool replaceExistingContents) { - if (gradient != 0) + x += currentState->xOffset; + y += currentState->yOffset; + + if (Rectangle::intersectRectangles (x, y, w, h, 0, 0, image.getWidth(), image.getHeight())) { - if (replaceExistingContents && ! gradient->isOpaque()) - { - for (RectangleList::Iterator i (*clip); i.next();) - clippedFillRectWithColour (*i.getRectangle(), x + xOffset, y + yOffset, w, h, Colours::transparentBlack, true); - } - - Path p; - p.addRectangle ((float) x, (float) y, (float) w, (float) h); - fillPath (p, AffineTransform::identity); + EdgeTable et (Rectangle (x, y, w, h)); + currentState->fillEdgeTable (image, et, replaceExistingContents); } - else - { - x += xOffset; - y += yOffset; - - for (RectangleList::Iterator i (*clip); i.next();) - clippedFillRectWithColour (*i.getRectangle(), x, y, w, h, colour, replaceExistingContents); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedFillRectWithColour (const Rectangle& clipRect, - int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents) -{ - if (clipRect.intersectRectangle (x, y, w, h)) - { - int stride, pixelStride; - uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); - - if (image.getFormat() == Image::RGB) - { - if (replaceExistingContents) - replaceRectRGB (pixels, w, h, stride, colour); - else - blendRectRGB (pixels, w, h, stride, colour); - } - else if (image.getFormat() == Image::ARGB) - { - if (replaceExistingContents) - replaceRectARGB (pixels, w, h, stride, colour); - else - blendRectARGB (pixels, w, h, stride, colour); - } - else - { - jassertfalse // not done! - } - - image.releasePixelDataReadWrite (pixels); - } -} - -bool LowLevelGraphicsSoftwareRenderer::getPathBounds (int clipX, int clipY, int clipW, int clipH, - const Path& path, const AffineTransform& transform, - int& x, int& y, int& w, int& h) const -{ - float tx, ty, tw, th; - path.getBoundsTransformed (transform, tx, ty, tw, th); - - x = roundDoubleToInt (tx) - 1; - y = roundDoubleToInt (ty) - 1; - w = roundDoubleToInt (tw) + 2; - h = roundDoubleToInt (th) + 2; - - // seems like this operation is using some crazy out-of-range numbers.. - ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, w, h); - - return Rectangle::intersectRectangles (x, y, w, h, clipX, clipY, clipW, clipH); } void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform) { - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); + EdgeTable et (currentState->edgeTable->edgeTable.getMaximumBounds(), + path, transform.translated ((float) currentState->xOffset, + (float) currentState->yOffset)); - clippedFillPath (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - path, transform); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedFillPath (int clipX, int clipY, int clipW, int clipH, const Path& path, - 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 (Rectangle (0, 0, cw, ch), path, transform.translated ((float) -cx, (float) -cy)); - - int stride, pixelStride; - uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (cx, cy, cw, ch, stride, pixelStride); - - if (gradient != 0) - { - ColourGradient g2 (*gradient); - - const bool isIdentity = g2.transform.isIdentity(); - if (isIdentity) - { - g2.x1 += xOffset - cx; - g2.x2 += xOffset - cx; - g2.y1 += yOffset - cy; - g2.y2 += yOffset - cy; - } - else - { - g2.transform = g2.transform.translated ((float) (xOffset - cx), - (float) (yOffset - cy)); - } - - int numLookupEntries; - PixelARGB* const lookupTable = g2.createLookupTable (numLookupEntries); - jassert (numLookupEntries > 0); - - if (image.getFormat() == Image::RGB) - { - jassert (pixelStride == 3); - - if (g2.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer); - } - } - else if (image.getFormat() == Image::ARGB) - { - jassert (pixelStride == 4); - - if (g2.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer); - } - } - else if (image.getFormat() == Image::SingleChannel) - { - jassertfalse // not done! - } - - juce_free (lookupTable); - } - else - { - if (image.getFormat() == Image::RGB) - { - jassert (pixelStride == 3); - SolidColourEdgeTableRenderer renderer (pixels, stride, colour); - edgeTable.iterate (renderer); - } - else if (image.getFormat() == Image::ARGB) - { - jassert (pixelStride == 4); - SolidColourEdgeTableRenderer renderer (pixels, stride, colour); - edgeTable.iterate (renderer); - } - else if (image.getFormat() == Image::SingleChannel) - { - jassert (pixelStride == 1); - AlphaBitmapRenderer renderer (pixels, stride); - edgeTable.iterate (renderer); - } - } - - image.releasePixelDataReadWrite (pixels); - } + currentState->fillEdgeTable (image, et); } void LowLevelGraphicsSoftwareRenderer::fillPathWithImage (const Path& path, const AffineTransform& transform, const Image& sourceImage, int imageX, int imageY) { - imageX += xOffset; - imageY += yOffset; + imageX += currentState->xOffset; + imageY += currentState->yOffset; - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedFillPathWithImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - path, transform, sourceImage, imageX, imageY, - 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) -{ - if (Rectangle::intersectRectangles (x, y, w, h, imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight())) - { - EdgeTable edgeTable (Rectangle (0, 0, w, h), path, - transform.translated ((float) (xOffset - x), (float) (yOffset - y))); - - int stride, pixelStride; - uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); - - int srcStride, srcPixelStride; - const uint8* const srcPix = (const uint8*) sourceImage.lockPixelDataReadOnly (x - imageX, y - imageY, w, h, srcStride, srcPixelStride); - - const int alpha = jlimit (0, 255, roundDoubleToInt (opacity * 255.0f)); - - if (image.getFormat() == Image::RGB) - { - if (sourceImage.getFormat() == Image::RGB) - { - ImageFillEdgeTableRenderer renderer (pixels, stride, - srcPix, srcStride, - alpha, (PixelRGB*) 0); - edgeTable.iterate (renderer); - } - else if (sourceImage.getFormat() == Image::ARGB) - { - ImageFillEdgeTableRenderer renderer (pixels, stride, - srcPix, srcStride, - alpha, (PixelARGB*) 0); - edgeTable.iterate (renderer); - } - else - { - jassertfalse // not done! - } - } - else if (image.getFormat() == Image::ARGB) - { - if (sourceImage.getFormat() == Image::RGB) - { - ImageFillEdgeTableRenderer renderer (pixels, stride, - srcPix, srcStride, - alpha, (PixelRGB*) 0); - edgeTable.iterate (renderer); - } - else if (sourceImage.getFormat() == Image::ARGB) - { - ImageFillEdgeTableRenderer renderer (pixels, stride, - srcPix, srcStride, - alpha, (PixelARGB*) 0); - edgeTable.iterate (renderer); - } - else - { - jassertfalse // not done! - } - } - else - { - jassertfalse // not done! - } - - sourceImage.releasePixelDataReadOnly (srcPix); - image.releasePixelDataReadWrite (pixels); - } + saveState(); + currentState->reduce (path, transform.translated ((float) currentState->xOffset, (float) currentState->yOffset)); + currentState->renderImage (image, sourceImage, imageX, imageY); + restoreState(); } void LowLevelGraphicsSoftwareRenderer::fillAlphaChannel (const Image& clipImage, int x, int y) { - x += xOffset; - y += yOffset; + x += currentState->xOffset; + y += currentState->yOffset; - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); + Rectangle maxBounds (currentState->edgeTable->edgeTable.getMaximumBounds()); + EdgeTable et (maxBounds.getIntersection (Rectangle (x, y, clipImage.getWidth(), clipImage.getHeight()))); + et.clipToImageAlpha (clipImage, x, y); - clippedFillAlphaChannel (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - clipImage, x, y); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannel (int clipX, int clipY, int clipW, int clipH, const Image& clipImage, int x, int y) -{ - if (gradient != 0) - { - if (Rectangle::intersectRectangles (clipX, clipY, clipW, clipH, x, y, clipImage.getWidth(), clipImage.getHeight())) - { - ColourGradient g2 (*gradient); - g2.x1 += xOffset - clipX; - g2.x2 += xOffset - clipX; - g2.y1 += yOffset - clipY; - g2.y2 += yOffset - clipY; - - Image temp (g2.isOpaque() ? Image::RGB : Image::ARGB, clipW, clipH, true); - LowLevelGraphicsSoftwareRenderer tempG (temp); - tempG.setGradient (g2); - tempG.fillRect (0, 0, clipW, clipH, false); - - clippedFillAlphaChannelWithImage (clipX, clipY, clipW, clipH, - clipImage, x, y, - temp, clipX, clipY, 1.0f); - } - } - else - { - int w = clipImage.getWidth(); - int h = clipImage.getHeight(); - int sx = 0; - int sy = 0; - - if (x < clipX) - { - sx = clipX - x; - w -= clipX - x; - x = clipX; - } - - if (y < clipY) - { - sy = clipY - y; - h -= clipY - y; - y = clipY; - } - - if (x + w > clipX + clipW) - w = clipX + clipW - x; - - if (y + h > clipY + clipH) - h = clipY + clipH - y; - - if (w > 0 && h > 0) - { - int stride, alphaStride, pixelStride; - uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); - - const uint8* const alphaValues - = clipImage.lockPixelDataReadOnly (sx, sy, w, h, alphaStride, pixelStride); - -#if JUCE_BIG_ENDIAN - const uint8* const alphas = alphaValues; -#else - const uint8* const alphas = alphaValues + (clipImage.getFormat() == Image::ARGB ? 3 : 0); -#endif - - if (image.getFormat() == Image::RGB) - { - blendAlphaMapRGB (pixels, stride, - alphas, w, h, - pixelStride, alphaStride, - colour); - } - else if (image.getFormat() == Image::ARGB) - { - blendAlphaMapARGB (pixels, stride, - alphas, w, h, - pixelStride, alphaStride, - colour); - } - else - { - jassertfalse // not done! - } - - clipImage.releasePixelDataReadOnly (alphaValues); - image.releasePixelDataReadWrite (pixels); - } - } + currentState->fillEdgeTable (image, et); } void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, const Image& fillerImage, int fillerImageX, int fillerImageY) { - alphaImageX += xOffset; - alphaImageY += yOffset; + alphaImageX += currentState->xOffset; + alphaImageY += currentState->yOffset; + fillerImageX += currentState->xOffset; + fillerImageY += currentState->yOffset; - fillerImageX += xOffset; - fillerImageY += yOffset; - - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedFillAlphaChannelWithImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - alphaImage, alphaImageX, alphaImageY, - fillerImage, fillerImageX, fillerImageY, - colour.getFloatAlpha()); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannelWithImage (int x, int y, int w, int h, const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float opacity) -{ - if (Rectangle::intersectRectangles (x, y, w, h, alphaImageX, alphaImageY, alphaImage.getWidth(), alphaImage.getHeight()) - && Rectangle::intersectRectangles (x, y, w, h, fillerImageX, fillerImageY, fillerImage.getWidth(), fillerImage.getHeight())) - { - int dstStride, dstPixStride; - uint8* const dstPix = image.lockPixelDataReadWrite (x, y, w, h, dstStride, dstPixStride); - - int srcStride, srcPixStride; - const uint8* const srcPix = fillerImage.lockPixelDataReadOnly (x - fillerImageX, y - fillerImageY, w, h, srcStride, srcPixStride); - - int maskStride, maskPixStride; - const uint8* const alpha - = alphaImage.lockPixelDataReadOnly (x - alphaImageX, y - alphaImageY, w, h, maskStride, maskPixStride); - -#if JUCE_BIG_ENDIAN - const uint8* const alphaValues = alpha; -#else - const uint8* const alphaValues = alpha + (alphaImage.getFormat() == Image::ARGB ? 3 : 0); -#endif - - const int extraAlpha = jlimit (0, 0x100, roundDoubleToInt (opacity * 256.0f)); - - if (image.getFormat() == Image::RGB) - { - if (fillerImage.getFormat() == Image::RGB) - { - renderAlphaMap ((PixelRGB*) dstPix, dstStride, (const PixelRGB*) srcPix, srcStride, alphaValues, maskStride, maskPixStride, w, h, extraAlpha); - } - else if (fillerImage.getFormat() == Image::ARGB) - { - renderAlphaMap ((PixelRGB*) dstPix, dstStride, (const PixelARGB*) srcPix, srcStride, alphaValues, maskStride, maskPixStride, w, h, extraAlpha); - } - else - { - jassertfalse // not done! - } - } - else if (image.getFormat() == Image::ARGB) - { - if (fillerImage.getFormat() == Image::RGB) - { - renderAlphaMap ((PixelARGB*) dstPix, dstStride, (const PixelRGB*) srcPix, srcStride, alphaValues, maskStride, maskPixStride, w, h, extraAlpha); - } - else if (fillerImage.getFormat() == Image::ARGB) - { - renderAlphaMap ((PixelARGB*) dstPix, dstStride, (const PixelARGB*) srcPix, srcStride, alphaValues, maskStride, maskPixStride, w, h, extraAlpha); - } - else - { - jassertfalse // not done! - } - } - else - { - jassertfalse // not done! - } - - alphaImage.releasePixelDataReadOnly (alphaValues); - fillerImage.releasePixelDataReadOnly (srcPix); - image.releasePixelDataReadWrite (dstPix); - } + saveState(); + currentState->reduce (alphaImage, alphaImageX, alphaImageY); + currentState->renderImage (image, fillerImage, fillerImageX, fillerImageY); + restoreState(); } void LowLevelGraphicsSoftwareRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy) { - dx += xOffset; - dy += yOffset; + dx += currentState->xOffset; + dy += currentState->yOffset; - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedBlendImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - sourceImage, dx, dy, dw, dh, sx, sy); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedBlendImage (int clipX, int clipY, int clipW, int clipH, - const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy) -{ - if (dx < clipX) - { - sx += clipX - dx; - dw -= clipX - dx; - dx = clipX; - } - - if (dy < clipY) - { - sy += clipY - dy; - dh -= clipY - dy; - dy = clipY; - } - - if (dx + dw > clipX + clipW) - dw = clipX + clipW - dx; - - if (dy + dh > clipY + clipH) - dh = clipY + clipH - dy; - - if (dw <= 0 || dh <= 0) - return; - - const uint8 alpha = (uint8) colour.getAlpha(); - - if (alpha == 0) - return; - - int dstStride, dstPixelStride; - uint8* const dstPixels = image.lockPixelDataReadWrite (dx, dy, dw, dh, dstStride, dstPixelStride); - - int srcStride, srcPixelStride; - const uint8* const srcPixels = sourceImage.lockPixelDataReadOnly (sx, sy, dw, dh, srcStride, srcPixelStride); - - if (image.getFormat() == Image::ARGB) - { - if (sourceImage.getFormat() == Image::ARGB) - { - overlayImage ((PixelARGB*) dstPixels, dstStride, - (PixelARGB*) srcPixels, srcStride, - dw, dh, alpha); - } - else if (sourceImage.getFormat() == Image::RGB) - { - overlayImage ((PixelARGB*) dstPixels, dstStride, - (PixelRGB*) srcPixels, srcStride, - dw, dh, alpha); - } - else - { - jassertfalse - } - } - else if (image.getFormat() == Image::RGB) - { - if (sourceImage.getFormat() == Image::ARGB) - { - overlayImage ((PixelRGB*) dstPixels, dstStride, - (PixelARGB*) srcPixels, srcStride, - dw, dh, alpha); - } - else if (sourceImage.getFormat() == Image::RGB) - { - overlayImage ((PixelRGB*) dstPixels, dstStride, - (PixelRGB*) srcPixels, srcStride, - dw, dh, alpha); - } - else - { - jassertfalse - } - } - else - { - jassertfalse - } - - image.releasePixelDataReadWrite (dstPixels); - sourceImage.releasePixelDataReadOnly (srcPixels); + saveState(); + currentState->reduce (dx, dy, dw, dh); + currentState->renderImage (image, sourceImage, dx - sx, dy - sy); + restoreState(); } void LowLevelGraphicsSoftwareRenderer::blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, const AffineTransform& t) { - const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); - - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedBlendImageWarping (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - sourceImage, srcClipX, srcClipY, srcClipW, srcClipH, - transform); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, int destClipY, int destClipW, int destClipH, - const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform) -{ - if ((! colour.isTransparent()) && destClipW > 0 && destClipH > 0 && ! transform.isSingularity()) - { - Rectangle::intersectRectangles (srcClipX, srcClipY, srcClipW, srcClipH, - 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); - - if (srcClipW <= 0 || srcClipH <= 0) - return; - - jassert (srcClipX >= 0 && srcClipY >= 0); - - Path imageBounds; - imageBounds.addRectangle ((float) srcClipX, (float) srcClipY, (float) srcClipW, (float) srcClipH); - imageBounds.applyTransform (transform); - float imX, imY, imW, imH; - imageBounds.getBounds (imX, imY, imW, imH); - - if (Rectangle::intersectRectangles (destClipX, destClipY, destClipW, destClipH, - (int) floorf (imX), - (int) floorf (imY), - 1 + roundDoubleToInt (imW), - 1 + roundDoubleToInt (imH))) - { - const uint8 alpha = (uint8) colour.getAlpha(); - - float srcX1 = (float) destClipX; - float srcY1 = (float) destClipY; - float srcX2 = (float) (destClipX + destClipW); - float srcY2 = srcY1; - float srcX3 = srcX1; - float srcY3 = (float) (destClipY + destClipH); - - AffineTransform inverse (transform.inverted()); - inverse.transformPoint (srcX1, srcY1); - inverse.transformPoint (srcX2, srcY2); - inverse.transformPoint (srcX3, srcY3); - - const double lineDX = (double) (srcX3 - srcX1) / destClipH; - const double lineDY = (double) (srcY3 - srcY1) / destClipH; - const double pixelDX = (double) (srcX2 - srcX1) / destClipW; - const double pixelDY = (double) (srcY2 - srcY1) / destClipW; - - if (image.getFormat() == Image::ARGB) - { - if (sourceImage.getFormat() == Image::ARGB) - { - transformedImageRender (image, sourceImage, - destClipX, destClipY, destClipW, destClipH, - srcClipX, srcClipY, srcClipW, srcClipH, - srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, interpolationQuality, (PixelARGB*)0, (PixelARGB*)0); - } - else if (sourceImage.getFormat() == Image::RGB) - { - transformedImageRender (image, sourceImage, - destClipX, destClipY, destClipW, destClipH, - srcClipX, srcClipY, srcClipW, srcClipH, - srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, interpolationQuality, (PixelARGB*)0, (PixelRGB*)0); - } - else - { - jassertfalse - } - } - else if (image.getFormat() == Image::RGB) - { - if (sourceImage.getFormat() == Image::ARGB) - { - transformedImageRender (image, sourceImage, - destClipX, destClipY, destClipW, destClipH, - srcClipX, srcClipY, srcClipW, srcClipH, - srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, interpolationQuality, (PixelRGB*)0, (PixelARGB*)0); - } - else if (sourceImage.getFormat() == Image::RGB) - { - transformedImageRender (image, sourceImage, - destClipX, destClipY, destClipW, destClipH, - srcClipX, srcClipY, srcClipW, srcClipH, - srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, interpolationQuality, (PixelRGB*)0, (PixelRGB*)0); - } - else - { - jassertfalse - } - } - else - { - jassertfalse - } - } - } + currentState->renderImage (image, sourceImage, srcClipX, srcClipY, srcClipW, srcClipH, t); } void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2, double y2) { - x1 += xOffset; - y1 += yOffset; - x2 += xOffset; - y2 += yOffset; - - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedDrawLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - x1, y1, x2, y2); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2) -{ - if (clipW > 0 && clipH > 0) - { - if (x1 == x2) - { - if (y2 < y1) - swapVariables (y1, y2); - - clippedDrawVerticalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (x1), y1, y2); - } - else if (y1 == y2) - { - if (x2 < x1) - swapVariables (x1, x2); - - clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (y1), x1, x2); - } - else - { - double gradient = (y2 - y1) / (x2 - x1); - - if (fabs (gradient) > 1.0) - { - gradient = 1.0 / gradient; - - int y = roundDoubleToInt (y1); - const int startY = y; - int endY = roundDoubleToInt (y2); - - if (y > endY) - swapVariables (y, endY); - - while (y < endY) - { - const double x = x1 + gradient * (y - startY); - clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, y, x, x + 1.0); - ++y; - } - } - else - { - int x = roundDoubleToInt (x1); - const int startX = x; - int endX = roundDoubleToInt (x2); - - if (x > endX) - swapVariables (x, endX); - - while (x < endX) - { - const double y = y1 + gradient * (x - startX); - clippedDrawVerticalLine (clipX, clipY, clipW, clipH, x, y, y + 1.0); - ++x; - } - } - } - } + Path p; + p.addLineSegment ((float) x1, (float) y1, (float) x2, (float) y2, 1.0f); + fillPath (p, AffineTransform::identity); } void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, double top, double bottom) { - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedDrawVerticalLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - x + xOffset, top + yOffset, bottom + yOffset); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, - const int x, double top, double bottom) -{ - jassert (top <= bottom); - - if (((unsigned int) (x - clipX)) < (unsigned int) clipW - && top < clipY + clipH - && bottom > clipY - && clipW > 0) - { - if (top < clipY) - top = clipY; - - if (bottom > clipY + clipH) - bottom = clipY + clipH; - - if (bottom > top) - drawVertical (x, top, bottom); - } + EdgeTable et ((float) (x + currentState->xOffset), (float) (top + currentState->yOffset), 1.0f, (float) (bottom - top)); + currentState->fillEdgeTable (image, et); } void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double left, double right) { - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedDrawHorizontalLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - y + yOffset, left + xOffset, right + xOffset); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, - const int y, double left, double right) -{ - jassert (left <= right); - - if (((unsigned int) (y - clipY)) < (unsigned int) clipH - && left < clipX + clipW - && right > clipX - && clipW > 0) - { - if (left < clipX) - left = clipX; - - if (right > clipX + clipW) - right = clipX + clipW; - - if (right > left) - drawHorizontal (y, left, right); - } -} - -void LowLevelGraphicsSoftwareRenderer::drawVertical (const int x, - const double top, - const double bottom) -{ - int wholeStart = (int) top; - const int wholeEnd = (int) bottom; - - const int lastAlpha = roundDoubleToInt (255.0 * (bottom - wholeEnd)); - const int totalPixels = (wholeEnd - wholeStart) + (lastAlpha > 0 ? 1 : 0); - - if (totalPixels <= 0) - return; - - int lineStride, dstPixelStride; - uint8* const dstPixels = image.lockPixelDataReadWrite (x, wholeStart, 1, totalPixels, lineStride, dstPixelStride); - uint8* dest = dstPixels; - - PixelARGB colour (this->colour.getPixelARGB()); - - if (wholeEnd == wholeStart) - { - if (image.getFormat() == Image::ARGB) - ((PixelARGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (bottom - top))); - else if (image.getFormat() == Image::RGB) - ((PixelRGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (bottom - top))); - else - { - jassertfalse - } - } - else - { - if (image.getFormat() == Image::ARGB) - { - ((PixelARGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (1.0 - (top - wholeStart)))); - ++wholeStart; - dest += lineStride; - - if (colour.getAlpha() == 0xff) - { - while (wholeEnd > wholeStart) - { - ((PixelARGB*) dest)->set (colour); - ++wholeStart; - dest += lineStride; - } - } - else - { - while (wholeEnd > wholeStart) - { - ((PixelARGB*) dest)->blend (colour); - ++wholeStart; - dest += lineStride; - } - } - - if (lastAlpha > 0) - { - ((PixelARGB*) dest)->blend (colour, lastAlpha); - } - } - else if (image.getFormat() == Image::RGB) - { - ((PixelRGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (1.0 - (top - wholeStart)))); - ++wholeStart; - dest += lineStride; - - if (colour.getAlpha() == 0xff) - { - while (wholeEnd > wholeStart) - { - ((PixelRGB*) dest)->set (colour); - ++wholeStart; - dest += lineStride; - } - } - else - { - while (wholeEnd > wholeStart) - { - ((PixelRGB*) dest)->blend (colour); - ++wholeStart; - dest += lineStride; - } - } - - if (lastAlpha > 0) - { - ((PixelRGB*) dest)->blend (colour, lastAlpha); - } - } - else - { - jassertfalse - } - } - - image.releasePixelDataReadWrite (dstPixels); -} - -void LowLevelGraphicsSoftwareRenderer::drawHorizontal (const int y, - const double top, - const double bottom) -{ - int wholeStart = (int) top; - const int wholeEnd = (int) bottom; - - const int lastAlpha = roundDoubleToInt (255.0 * (bottom - wholeEnd)); - const int totalPixels = (wholeEnd - wholeStart) + (lastAlpha > 0 ? 1 : 0); - - if (totalPixels <= 0) - return; - - int lineStride, dstPixelStride; - uint8* const dstPixels = image.lockPixelDataReadWrite (wholeStart, y, totalPixels, 1, lineStride, dstPixelStride); - uint8* dest = dstPixels; - - PixelARGB colour (this->colour.getPixelARGB()); - - if (wholeEnd == wholeStart) - { - if (image.getFormat() == Image::ARGB) - ((PixelARGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (bottom - top))); - else if (image.getFormat() == Image::RGB) - ((PixelRGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (bottom - top))); - else - { - jassertfalse - } - } - else - { - if (image.getFormat() == Image::ARGB) - { - ((PixelARGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (1.0 - (top - wholeStart)))); - dest += dstPixelStride; - ++wholeStart; - - if (colour.getAlpha() == 0xff) - { - while (wholeEnd > wholeStart) - { - ((PixelARGB*) dest)->set (colour); - dest += dstPixelStride; - ++wholeStart; - } - } - else - { - while (wholeEnd > wholeStart) - { - ((PixelARGB*) dest)->blend (colour); - dest += dstPixelStride; - ++wholeStart; - } - } - - if (lastAlpha > 0) - { - ((PixelARGB*) dest)->blend (colour, lastAlpha); - } - } - else if (image.getFormat() == Image::RGB) - { - ((PixelRGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (1.0 - (top - wholeStart)))); - dest += dstPixelStride; - ++wholeStart; - - if (colour.getAlpha() == 0xff) - { - while (wholeEnd > wholeStart) - { - ((PixelRGB*) dest)->set (colour); - dest += dstPixelStride; - ++wholeStart; - } - } - else - { - while (wholeEnd > wholeStart) - { - ((PixelRGB*) dest)->blend (colour); - dest += dstPixelStride; - ++wholeStart; - } - } - - if (lastAlpha > 0) - { - ((PixelRGB*) dest)->blend (colour, lastAlpha); - } - } - else - { - jassertfalse - } - } - - image.releasePixelDataReadWrite (dstPixels); + EdgeTable et ((float) (left + currentState->xOffset), (float) (y + currentState->yOffset), + (float) (right - left), 1.0f); + currentState->fillEdgeTable (image, et); } void LowLevelGraphicsSoftwareRenderer::setFont (const Font& newFont) { - font = newFont; + currentState->font = newFont; } void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, float x, float y) { - font.renderGlyphIndirectly (*this, glyphNumber, x, y); + currentState->font.renderGlyphIndirectly (*this, glyphNumber, x, y); } void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform) { - font.renderGlyphIndirectly (*this, glyphNumber, transform); + currentState->font.renderGlyphIndirectly (*this, glyphNumber, transform); } +#if JUCE_MSVC + #pragma warning (pop) +#endif + END_JUCE_NAMESPACE /********* End of inlined file: juce_LowLevelGraphicsSoftwareRenderer.cpp *********/ @@ -85817,12 +85344,10 @@ void DropShadowEffect::applyEffect (Image& image, Graphics& g) const int w = image.getWidth(); const int h = image.getHeight(); - int lineStride, pixelStride; - const PixelARGB* srcPixels = (const PixelARGB*) image.lockPixelDataReadOnly (0, 0, image.getWidth(), image.getHeight(), lineStride, pixelStride); - Image shadowImage (Image::SingleChannel, w, h, false); - int destStride, destPixelStride; - uint8* const shadowChannel = (uint8*) shadowImage.lockPixelDataReadWrite (0, 0, w, h, destStride, destPixelStride); + + const Image::BitmapData srcData (image, 0, 0, w, h); + const Image::BitmapData destData (shadowImage, 0, 0, w, h, true); const int filter = roundFloatToInt (63.0f / radius); const int radiusMinus1 = roundFloatToInt ((radius - 1.0f) * 63.0f); @@ -85831,23 +85356,23 @@ void DropShadowEffect::applyEffect (Image& image, Graphics& g) { int shadowAlpha = 0; - const PixelARGB* src = srcPixels + x; - uint8* shadowPix = shadowChannel + x; + const PixelARGB* src = ((const PixelARGB*) srcData.data) + x; + uint8* shadowPix = destData.data + x; for (int y = h; --y >= 0;) { shadowAlpha = ((shadowAlpha * radiusMinus1 + (src->getAlpha() << 6)) * filter) >> 12; *shadowPix = (uint8) shadowAlpha; - src = (const PixelARGB*) (((const uint8*) src) + lineStride); - shadowPix += destStride; + src = (const PixelARGB*) (((const uint8*) src) + srcData.lineStride); + shadowPix += destData.lineStride; } } for (int y = h; --y >= 0;) { int shadowAlpha = 0; - uint8* shadowPix = shadowChannel + y * destStride; + uint8* shadowPix = destData.getLinePointer (y); for (int x = w; --x >= 0;) { @@ -85856,9 +85381,6 @@ void DropShadowEffect::applyEffect (Image& image, Graphics& g) } } - image.releasePixelDataReadOnly (srcPixels); - shadowImage.releasePixelDataReadWrite (shadowChannel); - g.setColour (Colours::black.withAlpha (opacity)); g.drawImageAt (&shadowImage, offsetX, offsetY, true); @@ -90214,14 +89736,14 @@ bool Path::Iterator::next() class MaskBitmapRenderer { public: - MaskBitmapRenderer (uint8* const data_, const int stride_) throw() - : data (data_), stride (stride_) + MaskBitmapRenderer (const Image::BitmapData& destData_) throw() + : destData (destData_) { } forcedinline void setEdgeTableYPos (const int y) throw() { - lineStart = data + (stride * y); + lineStart = destData.getLinePointer (y); } forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw() @@ -90238,8 +89760,7 @@ public: } private: - uint8* const data; - const int stride; + const Image::BitmapData& destData; uint8* lineStart; MaskBitmapRenderer (const MaskBitmapRenderer&); @@ -90267,14 +89788,12 @@ Image* Path::createMaskBitmap (const AffineTransform& transform, EdgeTable edgeTable (Rectangle (0, 0, imagePosition.getWidth(), imagePosition.getHeight()), *this, transform.translated ((float) -imagePosition.getX(), (float) -imagePosition.getY())); - int stride, pixelStride; - uint8* const pixels = (uint8*) im->lockPixelDataReadWrite (0, 0, imagePosition.getWidth(), imagePosition.getHeight(), stride, pixelStride); + const Image::BitmapData destData (*im, 0, 0, imagePosition.getWidth(), imagePosition.getHeight(), true); - jassert (pixelStride == 1); - MaskBitmapRenderer renderer (pixels, stride); + jassert (destData.pixelStride == 1); + MaskBitmapRenderer renderer (destData); edgeTable.iterate (renderer); - im->releasePixelDataReadWrite (pixels); return im; } @@ -92473,10 +91992,8 @@ Image::Image (const Image& other) imageData = (uint8*) juce_malloc (dataSize); - int ls, ps; - const uint8* srcData = other.lockPixelDataReadOnly (0, 0, imageWidth, imageHeight, ls, ps); - setPixelData (0, 0, imageWidth, imageHeight, srcData, ls); - other.releasePixelDataReadOnly (srcData); + BitmapData srcData (other, 0, 0, imageWidth, imageHeight); + setPixelData (0, 0, imageWidth, imageHeight, srcData.data, srcData.lineStride); } Image::~Image() @@ -92489,33 +92006,27 @@ LowLevelGraphicsContext* Image::createLowLevelContext() return new LowLevelGraphicsSoftwareRenderer (*this); } -uint8* Image::lockPixelDataReadWrite (int x, int y, int w, int h, int& ls, int& ps) +Image::BitmapData::BitmapData (Image& image, int x, int y, int w, int h, const bool /*makeWritable*/) throw() + : data (image.imageData + image.lineStride * y + image.pixelStride * x), + lineStride (image.lineStride), + pixelStride (image.pixelStride), + width (w), + height (h) { - jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= imageWidth && y + h <= imageHeight); - w = w; - h = h; - - ls = lineStride; - ps = pixelStride; - return imageData + x * pixelStride + y * lineStride; + jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= image.getWidth() && y + h <= image.getHeight()); } -void Image::releasePixelDataReadWrite (void*) +Image::BitmapData::BitmapData (const Image& image, int x, int y, int w, int h) throw() + : data (image.imageData + image.lineStride * y + image.pixelStride * x), + lineStride (image.lineStride), + pixelStride (image.pixelStride), + width (w), + height (h) { + jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= image.getWidth() && y + h <= image.getHeight()); } -const uint8* Image::lockPixelDataReadOnly (int x, int y, int w, int h, int& ls, int& ps) const -{ - jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= imageWidth && y + h <= imageHeight); - w = w; - h = h; - - ls = lineStride; - ps = pixelStride; - return imageData + x * pixelStride + y * lineStride; -} - -void Image::releasePixelDataReadOnly (const void*) const +Image::BitmapData::~BitmapData() throw() { } @@ -92526,17 +92037,14 @@ void Image::setPixelData (int x, int y, int w, int h, if (Rectangle::intersectRectangles (x, y, w, h, 0, 0, imageWidth, imageHeight)) { - int ls, ps; - uint8* dest = lockPixelDataReadWrite (x, y, w, h, ls, ps); + const BitmapData dest (*this, x, y, w, h, true); for (int i = 0; i < h; ++i) { - memcpy (dest + ls * i, + memcpy (dest.getLinePointer(i), sourcePixelData + sourceLineStride * i, - w * pixelStride); + w * dest.pixelStride); } - - releasePixelDataReadWrite (dest); } } @@ -92545,21 +92053,20 @@ void Image::clear (int dx, int dy, int dw, int dh, { const PixelARGB col (colourToClearTo.getPixelARGB()); - int ls, ps; - uint8* dstData = lockPixelDataReadWrite (dx, dy, dw, dh, ls, ps); - uint8* dest = dstData; + const BitmapData destData (*this, dx, dy, dw, dh, true); + uint8* dest = destData.data; while (--dh >= 0) { uint8* line = dest; - dest += ls; + dest += destData.lineStride; if (isARGB()) { for (int x = dw; --x >= 0;) { ((PixelARGB*) line)->set (col); - line += ps; + line += destData.pixelStride; } } else if (isRGB()) @@ -92567,7 +92074,7 @@ void Image::clear (int dx, int dy, int dw, int dh, for (int x = dw; --x >= 0;) { ((PixelRGB*) line)->set (col); - line += ps; + line += destData.pixelStride; } } else @@ -92575,12 +92082,10 @@ void Image::clear (int dx, int dy, int dw, int dh, for (int x = dw; --x >= 0;) { *line = col.getAlpha(); - line += ps; + line += destData.pixelStride; } } } - - releasePixelDataReadWrite (dstData); } Image* Image::createCopy (int newWidth, int newHeight, @@ -92617,16 +92122,13 @@ Image* Image::createCopyOfAlphaChannel() const } else { - int dls, dps; - uint8* dstData = newImage->lockPixelDataReadWrite (0, 0, imageWidth, imageHeight, dls, dps); - - int sls, sps; - const uint8* srcData = lockPixelDataReadOnly (0, 0, imageWidth, imageHeight, sls, sps); + const BitmapData destData (*newImage, 0, 0, imageWidth, imageHeight, true); + const BitmapData srcData (*this, 0, 0, imageWidth, imageHeight); for (int y = 0; y < imageHeight; ++y) { - const PixelARGB* src = (const PixelARGB*) (srcData + y * sls); - uint8* dst = dstData + y * dls; + const PixelARGB* src = (const PixelARGB*) srcData.getLinePointer(y); + uint8* dst = destData.getLinePointer (y); for (int x = imageWidth; --x >= 0;) { @@ -92634,9 +92136,6 @@ Image* Image::createCopyOfAlphaChannel() const ++src; } } - - releasePixelDataReadOnly (srcData); - newImage->releasePixelDataReadWrite (dstData); } return newImage; @@ -92649,21 +92148,18 @@ const Colour Image::getPixelAt (const int x, const int y) const if (((unsigned int) x) < (unsigned int) imageWidth && ((unsigned int) y) < (unsigned int) imageHeight) { - int ls, ps; - const uint8* const pixels = lockPixelDataReadOnly (x, y, 1, 1, ls, ps); + const BitmapData srcData (*this, x, y, 1, 1); if (isARGB()) { - PixelARGB p (*(const PixelARGB*) pixels); + PixelARGB p (*(const PixelARGB*) srcData.data); p.unpremultiply(); c = Colour (p.getARGB()); } else if (isRGB()) - c = Colour (((const PixelRGB*) pixels)->getARGB()); + c = Colour (((const PixelRGB*) srcData.data)->getARGB()); else - c = Colour ((uint8) 0, (uint8) 0, (uint8) 0, *pixels); - - releasePixelDataReadOnly (pixels); + c = Colour ((uint8) 0, (uint8) 0, (uint8) 0, *(srcData.data)); } return c; @@ -92675,18 +92171,15 @@ void Image::setPixelAt (const int x, const int y, if (((unsigned int) x) < (unsigned int) imageWidth && ((unsigned int) y) < (unsigned int) imageHeight) { - int ls, ps; - uint8* const pixels = lockPixelDataReadWrite (x, y, 1, 1, ls, ps); + const BitmapData destData (*this, x, y, 1, 1, true); const PixelARGB col (colour.getPixelARGB()); if (isARGB()) - ((PixelARGB*) pixels)->set (col); + ((PixelARGB*) destData.data)->set (col); else if (isRGB()) - ((PixelRGB*) pixels)->set (col); + ((PixelRGB*) destData.data)->set (col); else - *pixels = col.getAlpha(); - - releasePixelDataReadWrite (pixels); + *(destData.data) = col.getAlpha(); } } @@ -92697,15 +92190,12 @@ void Image::multiplyAlphaAt (const int x, const int y, && ((unsigned int) y) < (unsigned int) imageHeight && hasAlphaChannel()) { - int ls, ps; - uint8* const pixels = lockPixelDataReadWrite (x, y, 1, 1, ls, ps); + const BitmapData destData (*this, x, y, 1, 1, true); if (isARGB()) - ((PixelARGB*) pixels)->multiplyAlpha (multiplier); + ((PixelARGB*) destData.data)->multiplyAlpha (multiplier); else - *pixels = (uint8) (*pixels * multiplier); - - releasePixelDataReadWrite (pixels); + *(destData.data) = (uint8) (*(destData.data) * multiplier); } } @@ -92713,19 +92203,18 @@ void Image::multiplyAllAlphas (const float amountToMultiplyBy) { if (hasAlphaChannel()) { - int ls, ps; - uint8* const pixels = lockPixelDataReadWrite (0, 0, getWidth(), getHeight(), ls, ps); + const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), true); if (isARGB()) { for (int y = 0; y < imageHeight; ++y) { - uint8* p = pixels + y * ls; + uint8* p = destData.getLinePointer (y); for (int x = 0; x < imageWidth; ++x) { ((PixelARGB*) p)->multiplyAlpha (amountToMultiplyBy); - p += ps; + p += destData.pixelStride; } } } @@ -92733,17 +92222,15 @@ void Image::multiplyAllAlphas (const float amountToMultiplyBy) { for (int y = 0; y < imageHeight; ++y) { - uint8* p = pixels + y * ls; + uint8* p = destData.getLinePointer (y); for (int x = 0; x < imageWidth; ++x) { *p = (uint8) (*p * amountToMultiplyBy); - p += ps; + p += destData.pixelStride; } } } - - releasePixelDataReadWrite (pixels); } else { @@ -92755,19 +92242,18 @@ void Image::desaturate() { if (isARGB() || isRGB()) { - int ls, ps; - uint8* const pixels = lockPixelDataReadWrite (0, 0, getWidth(), getHeight(), ls, ps); + const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), true); if (isARGB()) { for (int y = 0; y < imageHeight; ++y) { - uint8* p = pixels + y * ls; + uint8* p = destData.getLinePointer (y); for (int x = 0; x < imageWidth; ++x) { ((PixelARGB*) p)->desaturate(); - p += ps; + p += destData.pixelStride; } } } @@ -92775,17 +92261,15 @@ void Image::desaturate() { for (int y = 0; y < imageHeight; ++y) { - uint8* p = pixels + y * ls; + uint8* p = destData.getLinePointer (y); for (int x = 0; x < imageWidth; ++x) { ((PixelRGB*) p)->desaturate(); - p += ps; + p += destData.pixelStride; } } } - - releasePixelDataReadWrite (pixels); } } @@ -92796,13 +92280,12 @@ void Image::createSolidAreaMask (RectangleList& result, const float alphaThresho const uint8 threshold = (uint8) jlimit (0, 255, roundFloatToInt (alphaThreshold * 255.0f)); SparseSet pixelsOnRow; - int ls, ps; - const uint8* const pixels = lockPixelDataReadOnly (0, 0, imageWidth, imageHeight, ls, ps); + const BitmapData srcData (*this, 0, 0, getWidth(), getHeight()); for (int y = 0; y < imageHeight; ++y) { pixelsOnRow.clear(); - const uint8* lineData = pixels + ls * y; + const uint8* lineData = srcData.getLinePointer (y); if (isARGB()) { @@ -92811,7 +92294,7 @@ void Image::createSolidAreaMask (RectangleList& result, const float alphaThresho if (((const PixelARGB*) lineData)->getAlpha() >= threshold) pixelsOnRow.addRange (x, 1); - lineData += ps; + lineData += srcData.pixelStride; } } else @@ -92821,7 +92304,7 @@ void Image::createSolidAreaMask (RectangleList& result, const float alphaThresho if (*lineData >= threshold) pixelsOnRow.addRange (x, 1); - lineData += ps; + lineData += srcData.pixelStride; } } @@ -92835,8 +92318,6 @@ void Image::createSolidAreaMask (RectangleList& result, const float alphaThresho result.consolidate(); } - - releasePixelDataReadOnly (pixels); } else { @@ -92887,19 +92368,18 @@ void Image::moveImageSection (int dx, int dy, const int maxX = jmax (dx, sx) + w; const int maxY = jmax (dy, sy) + h; - int ls, ps; - uint8* const pixels = lockPixelDataReadWrite (minX, minY, maxX - minX, maxY - minY, ls, ps); + const BitmapData destData (*this, minX, minY, maxX - minX, maxY - minY, true); - uint8* dst = pixels + ls * (dy - minY) + ps * (dx - minX); - const uint8* src = pixels + ls * (sy - minY) + ps * (sx - minX); + uint8* dst = destData.getPixelPointer (dx - minX, dy - minY); + const uint8* src = destData.getPixelPointer (sx - minX, sy - minY); - const int lineSize = ps * w; + const int lineSize = destData.pixelStride * w; if (dy > sy) { while (--h >= 0) { - const int offset = h * ls; + const int offset = h * destData.lineStride; memmove (dst + offset, src + offset, lineSize); } } @@ -92908,12 +92388,10 @@ void Image::moveImageSection (int dx, int dy, while (--h >= 0) { memmove (dst, src, lineSize); - dst += ls; - src += ls; + dst += destData.lineStride; + src += destData.lineStride; } } - - releasePixelDataReadWrite (pixels); } } @@ -93250,19 +92728,17 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, const int dx2 = dx + dw; const int dy2 = dy + dh; - int lineStride, pixelStride; - uint8* pixels = destImage.lockPixelDataReadWrite (dx, dy, dw, dh, lineStride, pixelStride); - uint8* line = pixels; + const Image::BitmapData destData (destImage, dx, dy, dw, dh, true); + uint8* line = destData.data; - int srcLineStride, srcPixelStride; - const uint8* srcPixels = sourceImage->lockPixelDataReadOnly (0, 0, sourceImage->getWidth(), sourceImage->getHeight(), srcLineStride, srcPixelStride); + const Image::BitmapData srcData (*sourceImage, 0, 0, sourceImage->getWidth(), sourceImage->getHeight()); - if (pixelStride == 4) + if (destData.pixelStride == 4) { for (int y = dy; y < dy2; ++y) { uint8* dest = line; - line += lineStride; + line += destData.lineStride; for (int x = dx; x < dx2; ++x) { @@ -93281,7 +92757,7 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, if (sy >= 0) { int sx = x - (size >> 1); - const uint8* src = srcPixels + srcLineStride * sy + srcPixelStride * sx; + const uint8* src = srcData.getPixelPointer (sx, sy); for (int xx = 0; xx < size; ++xx) { @@ -93313,12 +92789,12 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, } } } - else if (pixelStride == 3) + else if (destData.pixelStride == 3) { for (int y = dy; y < dy2; ++y) { uint8* dest = line; - line += lineStride; + line += destData.lineStride; for (int x = dx; x < dx2; ++x) { @@ -93336,7 +92812,7 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, if (sy >= 0) { int sx = x - (size >> 1); - const uint8* src = srcPixels + srcLineStride * sy + srcPixelStride * sx; + const uint8* src = srcData.getPixelPointer (sx, sy); for (int xx = 0; xx < size; ++xx) { @@ -93367,11 +92843,7 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, } } - sourceImage->releasePixelDataReadOnly (srcPixels); - destImage.releasePixelDataReadWrite (pixels); - - if (imageCreated != 0) - delete imageCreated; + delete imageCreated; } END_JUCE_NAMESPACE @@ -93974,9 +93446,8 @@ bool GIFLoader::readImage (const int width, const int height, int index; int xpos = 0, ypos = 0, pass = 0; - int stride, pixelStride; - uint8* const pixels = image->lockPixelDataReadWrite (0, 0, width, height, stride, pixelStride); - uint8* p = pixels; + const Image::BitmapData destData (*image, 0, 0, width, height, true); + uint8* p = destData.data; const bool hasAlpha = image->hasAlphaChannel(); while ((index = readLZWByte (false, c)) >= 0) @@ -93991,8 +93462,6 @@ bool GIFLoader::readImage (const int width, const int height, paletteEntry[2]); ((PixelARGB*) p)->premultiply(); - - p += pixelStride; } else { @@ -94000,10 +93469,9 @@ bool GIFLoader::readImage (const int width, const int height, paletteEntry[0], paletteEntry[1], paletteEntry[2]); - - p += pixelStride; } + p += destData.pixelStride; ++xpos; if (xpos == width) @@ -94051,14 +93519,13 @@ bool GIFLoader::readImage (const int width, const int height, ++ypos; } - p = pixels + xpos * pixelStride + ypos * stride; + p = destData.getPixelPointer (xpos, ypos); } if (ypos >= height) break; } - image->releasePixelDataReadWrite (pixels); return true; } @@ -185219,7 +184686,7 @@ public: float** dataIn = 0; const int samps = ov_read_float (&ovFile, &dataIn, numToRead, &bitStream); - if (samps == 0) + if (samps <= 0) break; jassert (samps <= numToRead); @@ -208971,14 +208438,14 @@ Image* juce_loadJPEGImageFromStream (InputStream& in) throw() image = Image::createNativeImage (Image::RGB, width, height, false); const bool hasAlphaChan = image->hasAlphaChannel(); + const Image::BitmapData destData (*image, 0, 0, width, height, true); + for (int y = 0; y < height; ++y) { jpeg_read_scanlines (&jpegDecompStruct, buffer, 1); - int stride, pixelStride; - uint8* pixels = image->lockPixelDataReadWrite (0, y, width, 1, stride, pixelStride); const uint8* src = *buffer; - uint8* dest = pixels; + uint8* dest = destData.getLinePointer (y); if (hasAlphaChan) { @@ -208986,7 +208453,7 @@ Image* juce_loadJPEGImageFromStream (InputStream& in) throw() { ((PixelARGB*) dest)->setARGB (0xff, src[0], src[1], src[2]); ((PixelARGB*) dest)->premultiply(); - dest += pixelStride; + dest += destData.pixelStride; src += 3; } } @@ -208995,12 +208462,10 @@ Image* juce_loadJPEGImageFromStream (InputStream& in) throw() for (int i = width; --i >= 0;) { ((PixelRGB*) dest)->setARGB (0xff, src[0], src[1], src[2]); - dest += pixelStride; + dest += destData.pixelStride; src += 3; } } - - image->releasePixelDataReadWrite (pixels); } jpeg_finish_decompress (&jpegDecompStruct); @@ -209107,11 +208572,11 @@ bool juce_writeJPEGImageToStream (const Image& image, JPOOL_IMAGE, strideBytes, 1); + const Image::BitmapData srcData (image, 0, 0, jpegCompStruct.image_width, jpegCompStruct.image_height); + while (jpegCompStruct.next_scanline < jpegCompStruct.image_height) { - int stride, pixelStride; - const uint8* pixels = image.lockPixelDataReadOnly (0, jpegCompStruct.next_scanline, jpegCompStruct.image_width, 1, stride, pixelStride); - const uint8* src = pixels; + const uint8* src = srcData.getLinePointer (jpegCompStruct.next_scanline); uint8* dst = *buffer; for (int i = jpegCompStruct.image_width; --i >= 0;) @@ -209119,11 +208584,10 @@ bool juce_writeJPEGImageToStream (const Image& image, *dst++ = ((const PixelRGB*) src)->getRed(); *dst++ = ((const PixelRGB*) src)->getGreen(); *dst++ = ((const PixelRGB*) src)->getBlue(); - src += pixelStride; + src += srcData.pixelStride; } jpeg_write_scanlines (&jpegCompStruct, buffer, 1); - image.releasePixelDataReadOnly (pixels); } jpeg_finish_compress (&jpegCompStruct); @@ -234661,17 +234125,16 @@ Image* juce_loadPNGImageFromStream (InputStream& in) throw() hasAlphaChan = image->hasAlphaChannel(); // (the native image creator may not give back what we expect) - int stride, pixelStride; - uint8* const pixels = image->lockPixelDataReadWrite (0, 0, width, height, stride, pixelStride); + const Image::BitmapData destData (*image, 0, 0, width, height, true); uint8* srcRow = tempBuffer; - uint8* destRow = pixels; + uint8* destRow = destData.data; for (y = 0; y < (int) height; ++y) { const uint8* src = srcRow; srcRow += (width << 2); uint8* dest = destRow; - destRow += stride; + destRow += destData.lineStride; if (hasAlphaChan) { @@ -234679,7 +234142,7 @@ Image* juce_loadPNGImageFromStream (InputStream& in) throw() { ((PixelARGB*) dest)->setARGB (src[3], src[0], src[1], src[2]); ((PixelARGB*) dest)->premultiply(); - dest += pixelStride; + dest += destData.pixelStride; src += 4; } } @@ -234688,13 +234151,12 @@ Image* juce_loadPNGImageFromStream (InputStream& in) throw() for (int i = width; --i >= 0;) { ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); - dest += pixelStride; + dest += destData.pixelStride; src += 4; } } } - image->releasePixelDataReadWrite (pixels); juce_free (tempBuffer); } @@ -234752,12 +234214,12 @@ bool juce_writePNGImageToStream (const Image& image, OutputStream& out) throw() png_set_shift (pngWriteStruct, &sig_bit); png_set_packing (pngWriteStruct); + const Image::BitmapData srcData (image, 0, 0, width, height); + for (int y = 0; y < height; ++y) { uint8* dst = (uint8*) rowData; - int stride, pixelStride; - const uint8* pixels = image.lockPixelDataReadOnly (0, y, width, 1, stride, pixelStride); - const uint8* src = pixels; + const uint8* src = srcData.getLinePointer (y); if (image.hasAlphaChannel()) { @@ -234770,7 +234232,7 @@ bool juce_writePNGImageToStream (const Image& image, OutputStream& out) throw() *dst++ = p.getGreen(); *dst++ = p.getBlue(); *dst++ = p.getAlpha(); - src += pixelStride; + src += srcData.pixelStride; } } else @@ -234780,12 +234242,11 @@ bool juce_writePNGImageToStream (const Image& image, OutputStream& out) throw() *dst++ = ((const PixelRGB*) src)->getRed(); *dst++ = ((const PixelRGB*) src)->getGreen(); *dst++ = ((const PixelRGB*) src)->getBlue(); - src += pixelStride; + src += srcData.pixelStride; } } png_write_rows (pngWriteStruct, &rowData, 1); - image.releasePixelDataReadOnly (pixels); } juce_free (rowData); @@ -238516,12 +237977,7 @@ private: WindowsBitmapImage* const offscreenImage = offscreenImageGenerator.getImage (transparent, w, h); - LowLevelGraphicsSoftwareRenderer context (*offscreenImage); - - RectangleList* const contextClip = context.getRawClipRegion(); - contextClip->clear(); - - context.setOrigin (-x, -y); + RectangleList contextClip; bool needToPaintAll = true; @@ -238557,7 +238013,7 @@ private: if (cx + cw - x <= w && cy + ch - y <= h) { - contextClip->addWithoutMerging (Rectangle (cx - x, cy - y, cw, ch)); + contextClip.addWithoutMerging (Rectangle (cx - x, cy - y, cw, ch)); } else { @@ -238573,13 +238029,13 @@ private: if (needToPaintAll) { - contextClip->clear(); - contextClip->addWithoutMerging (Rectangle (0, 0, w, h)); + contextClip.clear(); + contextClip.addWithoutMerging (Rectangle (0, 0, w, h)); } if (transparent) { - RectangleList::Iterator i (*contextClip); + RectangleList::Iterator i (contextClip); while (i.next()) { @@ -238593,6 +238049,10 @@ private: updateCurrentModifiers(); + LowLevelGraphicsSoftwareRenderer context (*offscreenImage); + context.reduceClipRegion (contextClip); + context.setOrigin (-x, -y); + handlePaint (context); if (! dontRepaint) @@ -250505,7 +249965,9 @@ public: width (0), height (0), activeUsers (0), - recordNextFrameTime (false) + recordNextFrameTime (false), + activeImage (0), + loadingImage (0) { HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph, CLSCTX_INPROC); if (FAILED (hr)) @@ -250607,7 +250069,9 @@ public: ~DShowCameraDeviceInteral() { - mediaControl->Stop(); + if (mediaControl != 0) + mediaControl->Stop(); + removeGraphFromRot(); for (int i = viewerComps.size(); --i >= 0;) @@ -250650,16 +250114,14 @@ public: } imageSwapLock.enter(); - int ls, ps; const int lineStride = width * 3; - uint8* const dest = loadingImage->lockPixelDataReadWrite (0, 0, width, height, ls, ps); + const Image::BitmapData destData (*loadingImage, 0, 0, width, height, true); for (int i = 0; i < height; ++i) - memcpy (dest + ls * ((height - 1) - i), + memcpy (destData.getLinePointer ((height - 1) - i), buffer + lineStride * i, lineStride); - loadingImage->releasePixelDataReadWrite (dest); imageNeedsFlipping = true; imageSwapLock.exit(); @@ -254821,17 +254283,16 @@ public: const uint32 bShiftL = jmax (0, getShiftNeeded (bMask)); const uint32 bShiftR = jmax (0, -getShiftNeeded (bMask)); - int ls, ps; - const uint8* const pixels = lockPixelDataReadOnly (0, 0, getWidth(), getHeight(), ls, ps); + const Image::BitmapData srcData (*this, 0, 0, getWidth(), getHeight()); for (int y = sy; y < sy + dh; ++y) { - const uint8* p = pixels + y * ls + sx * ps; + const uint8* p = srcData.getPixelPointer (sx, y); for (int x = sx; x < sx + dw; ++x) { const PixelRGB* const pixel = (const PixelRGB*) p; - p += ps; + p += srcData.pixelStride; XPutPixel (xImage, x, y, (((((uint32) pixel->getRed()) << rShiftL) >> rShiftR) & rMask) @@ -254839,8 +254300,6 @@ public: | (((((uint32) pixel->getBlue()) << bShiftL) >> bShiftR) & bMask)); } } - - releasePixelDataReadOnly (pixels); } // blit results to screen. @@ -262804,7 +262263,11 @@ public: Image* Image::createNativeImage (const PixelFormat format, const int imageWidth, const int imageHeight, const bool clearImage) { +#if USE_COREGRAPHICS_RENDERING return new CoreGraphicsImage (format == RGB ? ARGB : format, imageWidth, imageHeight, clearImage); +#else + return new Image (format, imageWidth, imageHeight, clearImage); +#endif } class CoreGraphicsContext : public LowLevelGraphicsContext @@ -263215,10 +262678,6 @@ private: SavedState* state; OwnedArray stateStack; -#if ! CGFLOAT_DEFINED - #define CGFloat float -#endif - static void gradientCallback (void* info, const CGFloat* inData, CGFloat* outData) { const ColourGradient* const g = (const ColourGradient*) info; @@ -263339,27 +262798,21 @@ private: } else { - int lineStride = 0; - int pixelStride = 0; - const uint8* imageData = juceImage.lockPixelDataReadOnly (0, 0, juceImage.getWidth(), juceImage.getHeight(), - lineStride, pixelStride); - - CGDataProviderRef provider = CGDataProviderCreateWithData (0, imageData, lineStride * pixelStride, 0); + const Image::BitmapData srcData (juceImage, 0, 0, juceImage.getWidth(), juceImage.getHeight()); + CGDataProviderRef provider = CGDataProviderCreateWithData (0, srcData.data, srcData.lineStride * srcData.pixelStride, 0); CGColorSpaceRef colourSpace = forAlpha ? greyColourSpace : rgbColourSpace; - CGImageRef imageRef = CGImageCreate (juceImage.getWidth(), juceImage.getHeight(), - 8, pixelStride * 8, lineStride, - colourSpace, - (juceImage.hasAlphaChannel() && ! forAlpha) - ? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little) - : kCGBitmapByteOrderDefault, - provider, - 0, true, kCGRenderingIntentDefault); + CGImageRef imageRef = CGImageCreate (srcData.width, srcData.height, + 8, srcData.pixelStride * 8, srcData.lineStride, + colourSpace, + (juceImage.hasAlphaChannel() && ! forAlpha) + ? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little) + : kCGBitmapByteOrderDefault, + provider, + 0, true, kCGRenderingIntentDefault); CGDataProviderRelease (provider); - - juceImage.releasePixelDataReadOnly (imageData); return imageRef; } } @@ -263563,7 +263016,7 @@ static int getModifierForButtonNumber (const int num) throw() : (num == 2 ? ModifierKeys::middleButtonModifier : 0)); } -static int64 getMouseTime (UIEvent* e) { return (int64) [e timestamp] * 1000.0; } +static int64 getMouseTime (UIEvent* e) { return (int64) ([e timestamp] * 1000.0); } int juce_lastMouseX = 0, juce_lastMouseY = 0; @@ -263583,13 +263036,13 @@ int juce_lastMouseX = 0, juce_lastMouseY = 0; int x, y, w, h; owner->getBounds (x, y, w, h, true); - juce_lastMouseX = p.x + x; - juce_lastMouseY = p.y + y; + juce_lastMouseX = x + (int) p.x; + juce_lastMouseY = y + (int) p.y; - owner->handleMouseMove (p.x, p.y, getMouseTime (event)); + owner->handleMouseMove ((int) p.x, (int) p.y, getMouseTime (event)); if (owner != 0) - owner->handleMouseDown (p.x, p.y, getMouseTime (event)); + owner->handleMouseDown ((int) p.x, (int) p.y, getMouseTime (event)); } default: @@ -263613,10 +263066,10 @@ int juce_lastMouseX = 0, juce_lastMouseY = 0; int x, y, w, h; owner->getBounds (x, y, w, h, true); - juce_lastMouseX = p.x + x; - juce_lastMouseY = p.y + y; + juce_lastMouseX = x + (int) p.x; + juce_lastMouseY = y + (int) p.y; - owner->handleMouseDrag (p.x, p.y, getMouseTime (event)); + owner->handleMouseDrag ((int) p.x, (int) p.y, getMouseTime (event)); } default: @@ -263640,12 +263093,12 @@ int juce_lastMouseX = 0, juce_lastMouseY = 0; int x, y, w, h; owner->getBounds (x, y, w, h, true); - juce_lastMouseX = p.x + x; - juce_lastMouseY = p.y + y; + juce_lastMouseX = x + (int) p.x; + juce_lastMouseY = y + (int) p.y; const int oldMods = currentModifiers; currentModifiers &= ~getModifierForButtonNumber (0); - owner->handleMouseUp (oldMods, p.x, p.y, getMouseTime (event)); + owner->handleMouseUp (oldMods, (int) p.x, (int) p.y, getMouseTime (event)); } default: @@ -264389,27 +263842,30 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) return ! quitMessagePosted; } -static CFRunLoopTimerRef messageTimer = 0; +static CFRunLoopRef runLoop = 0; +static CFRunLoopSourceRef runLoopSource = 0; static Array * pendingMessages = 0; -static void timerCallback (CFRunLoopTimerRef, void*) +static void runLoopSourceCallback (void*) { if (pendingMessages != 0) { - const int numToDispatch = jmin (4, pendingMessages->size()); + int numDispatched = 0; - for (int i = 0; i < numToDispatch; ++i) + do { - void* const nextMessage = (*pendingMessages)[i]; + void* const nextMessage = pendingMessages->remove (0); - if (nextMessage != 0) - MessageManager::getInstance()->deliverMessage (nextMessage); - } + if (nextMessage == 0) + return; - pendingMessages->removeRange (0, numToDispatch); + const ScopedAutoReleasePool pool; + MessageManager::getInstance()->deliverMessage (nextMessage); - if (pendingMessages->size() > 0) - CFRunLoopTimerSetNextFireDate (messageTimer, CFAbsoluteTimeGetCurrent() - 0.5); + } while (++numDispatched <= 4); + + CFRunLoopSourceSignal (runLoopSource); + CFRunLoopWakeUp (runLoop); } } @@ -264417,10 +263873,12 @@ void MessageManager::doPlatformSpecificInitialisation() { pendingMessages = new Array (); - messageTimer = CFRunLoopTimerCreate (kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + 60.0, 60.0, - 0, 0, timerCallback, 0); - - CFRunLoopAddTimer (CFRunLoopGetMain(), messageTimer, kCFRunLoopCommonModes); + runLoop = CFRunLoopGetCurrent(); + CFRunLoopSourceContext sourceContext; + zerostruct (sourceContext); + sourceContext.perform = runLoopSourceCallback; + runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); + CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); if (juceAppDelegate == 0) juceAppDelegate = [[JuceAppDelegate alloc] init]; @@ -264428,10 +263886,9 @@ void MessageManager::doPlatformSpecificInitialisation() void MessageManager::doPlatformSpecificShutdown() { - CFRunLoopTimerInvalidate (messageTimer); - CFRunLoopRemoveTimer (CFRunLoopGetMain(), messageTimer, kCFRunLoopCommonModes); - CFRelease (messageTimer); - messageTimer = 0; + CFRunLoopSourceInvalidate (runLoopSource); + CFRelease (runLoopSource); + runLoopSource = 0; if (pendingMessages != 0) { @@ -264454,7 +263911,8 @@ bool juce_postMessageToSystemQueue (void* message) if (pendingMessages != 0) { pendingMessages->add (message); - CFRunLoopTimerSetNextFireDate (messageTimer, CFAbsoluteTimeGetCurrent() - 1.0); + CFRunLoopSourceSignal (runLoopSource); + CFRunLoopWakeUp (runLoop); } return true; @@ -264965,26 +264423,24 @@ void juce_glViewport (const int w, const int h) static NSImage* juceImageToNSImage (const Image& image) { const ScopedAutoReleasePool pool; - int lineStride, pixelStride; - const uint8* pixels = image.lockPixelDataReadOnly (0, 0, image.getWidth(), image.getHeight(), - lineStride, pixelStride); + + const Image::BitmapData srcData (image, 0, 0, image.getWidth(), image.getHeight()); NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL - pixelsWide: image.getWidth() - pixelsHigh: image.getHeight() + pixelsWide: srcData.width + pixelsHigh: srcData.height bitsPerSample: 8 samplesPerPixel: image.hasAlphaChannel() ? 4 : 3 hasAlpha: image.hasAlphaChannel() isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bitmapFormat: (NSBitmapFormat) 0 - bytesPerRow: lineStride - bitsPerPixel: pixelStride * 8]; + bytesPerRow: srcData.lineStride + bitsPerPixel: srcData.pixelStride * 8]; unsigned char* newData = [rep bitmapData]; - memcpy (newData, pixels, lineStride * image.getHeight()); - image.releasePixelDataReadOnly (pixels); + memcpy (newData, srcData.data, srcData.lineStride * srcData.height); NSImage* im = [[NSImage alloc] init]; [im addRepresentation: rep]; @@ -267233,7 +266689,11 @@ public: Image* Image::createNativeImage (const PixelFormat format, const int imageWidth, const int imageHeight, const bool clearImage) { +#if USE_COREGRAPHICS_RENDERING return new CoreGraphicsImage (format == RGB ? ARGB : format, imageWidth, imageHeight, clearImage); +#else + return new Image (format, imageWidth, imageHeight, clearImage); +#endif } class CoreGraphicsContext : public LowLevelGraphicsContext @@ -267644,10 +267104,6 @@ private: SavedState* state; OwnedArray stateStack; -#if ! CGFLOAT_DEFINED - #define CGFloat float -#endif - static void gradientCallback (void* info, const CGFloat* inData, CGFloat* outData) { const ColourGradient* const g = (const ColourGradient*) info; @@ -267768,27 +267224,21 @@ private: } else { - int lineStride = 0; - int pixelStride = 0; - const uint8* imageData = juceImage.lockPixelDataReadOnly (0, 0, juceImage.getWidth(), juceImage.getHeight(), - lineStride, pixelStride); - - CGDataProviderRef provider = CGDataProviderCreateWithData (0, imageData, lineStride * pixelStride, 0); + const Image::BitmapData srcData (juceImage, 0, 0, juceImage.getWidth(), juceImage.getHeight()); + CGDataProviderRef provider = CGDataProviderCreateWithData (0, srcData.data, srcData.lineStride * srcData.pixelStride, 0); CGColorSpaceRef colourSpace = forAlpha ? greyColourSpace : rgbColourSpace; - CGImageRef imageRef = CGImageCreate (juceImage.getWidth(), juceImage.getHeight(), - 8, pixelStride * 8, lineStride, - colourSpace, - (juceImage.hasAlphaChannel() && ! forAlpha) - ? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little) - : kCGBitmapByteOrderDefault, - provider, - 0, true, kCGRenderingIntentDefault); + CGImageRef imageRef = CGImageCreate (srcData.width, srcData.height, + 8, srcData.pixelStride * 8, srcData.lineStride, + colourSpace, + (juceImage.hasAlphaChannel() && ! forAlpha) + ? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little) + : kCGBitmapByteOrderDefault, + provider, + 0, true, kCGRenderingIntentDefault); CGDataProviderRelease (provider); - - juceImage.releasePixelDataReadOnly (imageData); return imageRef; } } @@ -267843,8 +267293,6 @@ LowLevelGraphicsContext* CoreGraphicsImage::createLowLevelContext() // compiled on its own). #if JUCE_INCLUDED_FILE -#define USE_COREGRAPHICS_RENDERING 1 - class NSViewComponentPeer; END_JUCE_NAMESPACE @@ -268363,27 +267811,21 @@ class JuceNSImage public: JuceNSImage (const int width, const int height, const bool hasAlpha) : juceImage (hasAlpha ? Image::ARGB : Image::RGB, - width, height, hasAlpha) + width, height, hasAlpha), + srcData (juceImage, 0, 0, width, height) { - lineStride = 0; - pixelStride = 0; - imageData = juceImage.lockPixelDataReadWrite (0, 0, width, height, - lineStride, pixelStride); - imageRep = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes: &imageData + initWithBitmapDataPlanes: (unsigned char**) &(srcData.data) pixelsWide: width pixelsHigh: height bitsPerSample: 8 - samplesPerPixel: pixelStride + samplesPerPixel: srcData.pixelStride hasAlpha: hasAlpha isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bitmapFormat: /*NSAlphaFirstBitmapFormat*/ (NSBitmapFormat) 0 - bytesPerRow: lineStride - bitsPerPixel: 8 * pixelStride ]; - - juceImage.releasePixelDataReadWrite (imageData); + bytesPerRow: srcData.lineStride + bitsPerPixel: 8 * srcData.pixelStride ]; } ~JuceNSImage() @@ -268442,23 +267884,22 @@ public: private: Image juceImage; NSBitmapImageRep* imageRep; - uint8* imageData; - int pixelStride, lineStride; + const Image::BitmapData srcData; void swapRGBOrder (const int x, const int y, const int w, int h) const { #if JUCE_BIG_ENDIAN - jassert (pixelStride == 4); + jassert (srcData.pixelStride == 4); #endif jassert (Rectangle (0, 0, juceImage.getWidth(), juceImage.getHeight()) .contains (Rectangle (x, y, w, h))); - uint8* start = imageData + x * pixelStride + y * lineStride; + uint8* start = srcData.getPixelPointer (x, y); while (--h >= 0) { uint8* p = start; - start += lineStride; + start += srcData.lineStride; for (int i = w; --i >= 0;) { @@ -268475,7 +267916,7 @@ private: p[2] = oldp0; #endif - p += pixelStride; + p += srcData.pixelStride; } } } @@ -269245,7 +268686,10 @@ void NSViewComponentPeer::drawRect (NSRect r) CGContextClearRect (cg, CGContextGetClipBoundingBox (cg)); CoreGraphicsContext context (cg, [view frame].size.height); + + insideDrawRect = true; handlePaint (context); + insideDrawRect = false; #else const float y = [view frame].size.height - (r.origin.y + r.size.height); @@ -269449,26 +268893,24 @@ const int KeyPress::rewindKey = 0x30003; static NSImage* juceImageToNSImage (const Image& image) { const ScopedAutoReleasePool pool; - int lineStride, pixelStride; - const uint8* pixels = image.lockPixelDataReadOnly (0, 0, image.getWidth(), image.getHeight(), - lineStride, pixelStride); + + const Image::BitmapData srcData (image, 0, 0, image.getWidth(), image.getHeight()); NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL - pixelsWide: image.getWidth() - pixelsHigh: image.getHeight() + pixelsWide: srcData.width + pixelsHigh: srcData.height bitsPerSample: 8 samplesPerPixel: image.hasAlphaChannel() ? 4 : 3 hasAlpha: image.hasAlphaChannel() isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bitmapFormat: (NSBitmapFormat) 0 - bytesPerRow: lineStride - bitsPerPixel: pixelStride * 8]; + bytesPerRow: srcData.lineStride + bitsPerPixel: srcData.pixelStride * 8]; unsigned char* newData = [rep bitmapData]; - memcpy (newData, pixels, lineStride * image.getHeight()); - image.releasePixelDataReadOnly (pixels); + memcpy (newData, srcData.data, srcData.lineStride * srcData.height); NSImage* im = [[NSImage alloc] init]; [im addRepresentation: rep]; @@ -271659,30 +271101,24 @@ class AppDelegateRedirector public: AppDelegateRedirector() { - CFRunLoopTimerContext context; - zerostruct (context); - context.info = this; - - timer = CFRunLoopTimerCreate (kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + 60.0, 60.0, 0, 0, - AppDelegateRedirector::timerCallbackStatic, &context); - #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 - CFRunLoopAddTimer (CFRunLoopGetMain(), timer, kCFRunLoopCommonModes); + runLoop = CFRunLoopGetMain(); #else - CFRunLoopAddTimer (CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes); + runLoop = CFRunLoopGetCurrent(); #endif + CFRunLoopSourceContext sourceContext; + zerostruct (sourceContext); + sourceContext.info = this; + sourceContext.perform = runLoopSourceCallback; + runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); + CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); } virtual ~AppDelegateRedirector() { - CFRunLoopTimerInvalidate (timer); - -#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 - CFRunLoopRemoveTimer (CFRunLoopGetMain(), timer, kCFRunLoopCommonModes); -#else - CFRunLoopRemoveTimer (CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes); -#endif - CFRelease (timer); + CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes); + CFRunLoopSourceInvalidate (runLoopSource); + CFRelease (runLoopSource); while (messages.size() > 0) delete ((Message*) messages.remove(0)); @@ -271747,34 +271183,38 @@ public: void postMessage (void* m) throw() { messages.add (m); - CFRunLoopTimerSetNextFireDate (timer, CFAbsoluteTimeGetCurrent() - 0.5); + CFRunLoopSourceSignal (runLoopSource); + CFRunLoopWakeUp (runLoop); } private: - CFRunLoopTimerRef timer; + CFRunLoopRef runLoop; + CFRunLoopSourceRef runLoopSource; Array messages; - void timerCallback() throw() + void runLoopCallback() throw() { - const int numToDispatch = jmin (4, messages.size()); + int numDispatched = 0; - for (int i = 0; i < numToDispatch; ++i) + do { - void* const nextMessage = messages[i]; + void* const nextMessage = messages.remove (0); - if (nextMessage != 0) - MessageManager::getInstance()->deliverMessage (nextMessage); - } + if (nextMessage == 0) + return; - messages.removeRange (0, numToDispatch); + const ScopedAutoReleasePool pool; + MessageManager::getInstance()->deliverMessage (nextMessage); - if (messages.size() > 0) - CFRunLoopTimerSetNextFireDate (timer, CFAbsoluteTimeGetCurrent() - 0.5); + } while (++numDispatched <= 4); + + CFRunLoopSourceSignal (runLoopSource); + CFRunLoopWakeUp (runLoop); } - static void timerCallbackStatic (CFRunLoopTimerRef, void* info) + static void runLoopSourceCallback (void* info) { - ((AppDelegateRedirector*) info)->timerCallback(); + ((AppDelegateRedirector*) info)->runLoopCallback(); } }; @@ -272005,14 +271445,13 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) jassert (isThisTheMessageThread()); // must only be called by the message thread uint32 endTime = Time::getMillisecondCounter() + millisecondsToRunFor; - NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001]; + NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001 + 1.0]; while (! quitMessagePosted) { const ScopedAutoReleasePool pool; - [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode - beforeDate: endDate]; + CFRunLoopRunInMode (kCFRunLoopDefaultMode, 0.002, true); NSEvent* e = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: endDate @@ -274547,22 +273986,21 @@ public: static void drawNSBitmapIntoJuceImage (Image& dest, NSBitmapImageRep* source) { const ScopedAutoReleasePool pool; - int lineStride, pixelStride; - uint8* pixels = dest.lockPixelDataReadWrite (0, 0, dest.getWidth(), dest.getHeight(), - lineStride, pixelStride); + + const Image::BitmapData destData (dest, 0, 0, dest.getWidth(), dest.getHeight(), true); NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes: &pixels - pixelsWide: dest.getWidth() - pixelsHigh: dest.getHeight() + initWithBitmapDataPlanes: (unsigned char**) &(destData.data) + pixelsWide: destData.width + pixelsHigh: destData.height bitsPerSample: 8 - samplesPerPixel: pixelStride + samplesPerPixel: destData.pixelStride hasAlpha: dest.hasAlphaChannel() isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bitmapFormat: (NSBitmapFormat) 0 - bytesPerRow: lineStride - bitsPerPixel: pixelStride * 8]; + bytesPerRow: destData.lineStride + bitsPerPixel: destData.pixelStride * 8]; [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithBitmapImageRep: rep]]; @@ -274572,11 +274010,11 @@ public: [[NSGraphicsContext currentContext] flushGraphics]; [NSGraphicsContext restoreGraphicsState]; - uint8* start = pixels; + uint8* start = destData.data; for (int h = dest.getHeight(); --h >= 0;) { uint8* p = start; - start += lineStride; + start += destData.lineStride; for (int i = dest.getWidth(); --i >= 0;) { @@ -274593,11 +274031,9 @@ public: p[2] = oldp0; #endif - p += pixelStride; + p += destData.pixelStride; } } - - dest.releasePixelDataReadWrite (pixels); } void callListeners (NSBitmapImageRep* bitmap) diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 666178b371..b9677cc213 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -1562,10 +1562,16 @@ public: /** Appends a decimal number at the end of this string. */ String& operator<< (const short number) throw(); /** Appends a decimal number at the end of this string. */ + String& operator<< (const unsigned short number) throw(); + /** Appends a decimal number at the end of this string. */ String& operator<< (const int number) throw(); /** Appends a decimal number at the end of this string. */ String& operator<< (const unsigned int number) throw(); /** Appends a decimal number at the end of this string. */ + String& operator<< (const long number) throw(); + /** Appends a decimal number at the end of this string. */ + String& operator<< (const unsigned long number) throw(); + /** Appends a decimal number at the end of this string. */ String& operator<< (const float number) throw(); /** Appends a decimal number at the end of this string. */ String& operator<< (const double number) throw(); @@ -8014,21 +8020,18 @@ private: /** Macro to declare member variables and methods for a singleton class. - To use this, add the line juce_DeclareSingleton (MyClass, allowOnlyOneInstance) + To use this, add the line juce_DeclareSingleton (MyClass, doNotRecreateAfterDeletion) to the class's definition. - If allowOnlyOneInstance == true, it won't allow the object to be created - more than once in the process's lifetime. - Then put a macro juce_ImplementSingleton (MyClass) along with the class's implementation code. - Clients can then call the static MyClass::getInstance() to get a pointer to the - singleton, or MyClass::getInstanceWithoutCreating() which may return 0 if no instance - is currently extant + It's also a very good idea to also add the call clearSingletonInstance() in your class's + destructor, in case it is deleted by other means than deleteInstance() - it's a very good idea to also add the call clearSingletonInstance() to the - destructor of the class, in case it is deleted by other means than deleteInstance() + Clients can then call the static method MyClass::getInstance() to get a pointer + to the singleton, or MyClass::getInstanceWithoutCreating() which will return 0 if + no instance currently exists. e.g. @code @@ -8060,13 +8063,18 @@ private: @endcode + If doNotRecreateAfterDeletion = true, it won't allow the object to be created more + than once during the process's lifetime - i.e. after you've created and deleted the + object, getInstance() will refuse to create another one. This can be useful to stop + objects being accidentally re-created during your app's shutdown code. + If you know that your object will only be created and deleted by a single thread, you can use the slightly more efficient juce_DeclareSingleton_SingleThreaded() macro instead of this one. @see juce_ImplementSingleton, juce_DeclareSingleton_SingleThreaded */ -#define juce_DeclareSingleton(classname, allowOnlyOneInstance) \ +#define juce_DeclareSingleton(classname, doNotRecreateAfterDeletion) \ \ static classname* _singletonInstance; \ static JUCE_NAMESPACE::CriticalSection _singletonLock; \ @@ -8082,7 +8090,7 @@ private: static bool alreadyInside = false; \ static bool createdOnceAlready = false; \ \ - const bool problem = alreadyInside || ((allowOnlyOneInstance) && createdOnceAlready); \ + const bool problem = alreadyInside || ((doNotRecreateAfterDeletion) && createdOnceAlready); \ jassert (! problem); \ if (! problem) \ { \ @@ -8139,13 +8147,18 @@ private: only ever be created or deleted by a single thread, then this is a more efficient version to use. + If doNotRecreateAfterDeletion = true, it won't allow the object to be created more + than once during the process's lifetime - i.e. after you've created and deleted the + object, getInstance() will refuse to create another one. This can be useful to stop + objects being accidentally re-created during your app's shutdown code. + See the documentation for juce_DeclareSingleton for more information about how to use it, the only difference being that you have to use juce_ImplementSingleton_SingleThreaded instead of juce_ImplementSingleton. @see juce_ImplementSingleton_SingleThreaded, juce_DeclareSingleton, juce_DeclareSingleton_SingleThreaded_Minimal */ -#define juce_DeclareSingleton_SingleThreaded(classname, allowOnlyOneInstance) \ +#define juce_DeclareSingleton_SingleThreaded(classname, doNotRecreateAfterDeletion) \ \ static classname* _singletonInstance; \ \ @@ -8156,7 +8169,7 @@ private: static bool alreadyInside = false; \ static bool createdOnceAlready = false; \ \ - const bool problem = alreadyInside || ((allowOnlyOneInstance) && createdOnceAlready); \ + const bool problem = alreadyInside || ((doNotRecreateAfterDeletion) && createdOnceAlready); \ jassert (! problem); \ if (! problem) \ { \ @@ -15116,6 +15129,9 @@ private: String jobName; ThreadPool* pool; bool shouldStop, isActive, shouldBeDeleted; + + ThreadPoolJob (const ThreadPoolJob&); + const ThreadPoolJob& operator= (const ThreadPoolJob&); }; /** @@ -16657,6 +16673,16 @@ public: points. */ bool isOnlyTranslation() const throw(); + /** If this transform is only a translation, this returns the X offset. + @see isOnlyTranslation + */ + float getTranslationX() const throw() { return mat02; } + + /** If this transform is only a translation, this returns the X offset. + @see isOnlyTranslation + */ + float getTranslationY() const throw() { return mat12; } + juce_UseDebuggingNewOperator /* The transform matrix is: @@ -17138,6 +17164,11 @@ public: */ EdgeTable (const Rectangle& rectangleToAdd) throw(); + /** Creates an edge table containing a rectangle. + */ + EdgeTable (const float x, const float y, + const float w, const float h) throw(); + /** Creates a copy of another edge table. */ EdgeTable (const EdgeTable& other) throw(); @@ -17147,10 +17178,12 @@ public: /** Destructor. */ ~EdgeTable() throw(); - /*void clipToRectangle (const Rectangle& r) throw(); + void clipToRectangle (const Rectangle& r) throw(); void excludeRectangle (const Rectangle& r) throw(); void clipToEdgeTable (const EdgeTable& other); - void clipToImageAlpha (Image& image, int x, int y) throw();*/ + void clipToImageAlpha (const Image& image, int x, int y) throw(); + bool isEmpty() const throw(); + const Rectangle& getMaximumBounds() const throw() { return bounds; } /** Reduces the amount of space the table has allocated. @@ -17187,17 +17220,14 @@ public: { int x = *++line; jassert ((x >> 8) >= bounds.getX() && (x >> 8) < bounds.getRight()); - int level = *++line; int levelAccumulator = 0; - iterationCallback.setEdgeTableYPos (y); + iterationCallback.setEdgeTableYPos (bounds.getY() + y); while (--numPoints >= 0) { - int correctedLevel = abs (level); - if (correctedLevel >> 8) - correctedLevel = 0xff; - + const int level = *++line; + jassert (((unsigned int) level) < (unsigned int) 256); const int endX = *++line; jassert (endX >= x); const int endOfRun = (endX >> 8); @@ -17206,13 +17236,13 @@ public: { // small segment within the same pixel, so just save it for the next // time round.. - levelAccumulator += (endX - x) * correctedLevel; + levelAccumulator += (endX - x) * level; } 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)) * correctedLevel; + levelAccumulator += (0xff - (x & 0xff)) * level; levelAccumulator >>= 8; x >>= 8; @@ -17224,21 +17254,20 @@ public: iterationCallback.handleEdgeTablePixel (x, levelAccumulator); } - // if there's a segment of solid pixels, do it all in one go.. - if (correctedLevel > 0) + // if there's a run of similar pixels, do it all in one go.. + if (level > 0) { jassert (endOfRun <= bounds.getRight()); const int numPix = endOfRun - ++x; if (numPix > 0) - iterationCallback.handleEdgeTableLine (x, numPix, correctedLevel); + iterationCallback.handleEdgeTableLine (x, numPix, level); } // save the bit at the end to be drawn next time round the loop. - levelAccumulator = (endX & 0xff) * correctedLevel; + levelAccumulator = (endX & 0xff) * level; } - level += *++line; x = endX; } @@ -17266,7 +17295,6 @@ private: void addEdgePoint (const int x, const int y, const int winding) throw(); void remapTableForNumEdges (const int newNumEdgesPerLine) throw(); - void clearLineSection (const int y, int minX, int maxX) throw(); void intersectWithEdgeTableLine (const int y, const int* otherLine) throw(); }; @@ -18789,6 +18817,8 @@ private: #define PACKED #endif +class PixelRGB; + /** Represents a 32-bit ARGB pixel with premultiplied alpha, and can perform compositing operations with it. @@ -18837,6 +18867,13 @@ public: argb = sargb; } + /** Blends another pixel onto this one. + + This takes into account the opacity of the pixel being overlaid, and blends + it accordingly. + */ + forcedinline void blend (const PixelRGB& src) throw(); + /** Blends another pixel onto this one, applying an extra multiplier to its opacity. The opacity of the pixel being overlaid is scaled by the extraAlpha factor before @@ -19051,6 +19088,12 @@ public: set (src); } + template + forcedinline void blend (const Pixel& src) throw() + { + blend (PixelARGB (src.getARGB())); + } + /** Blends another pixel onto this one, applying an extra multiplier to its opacity. The opacity of the pixel being overlaid is scaled by the extraAlpha factor before @@ -19140,6 +19183,130 @@ private: } PACKED; +forcedinline void PixelARGB::blend (const PixelRGB& src) throw() +{ + set (src); +} + +/** + Represents an 8-bit single-channel pixel, and can perform compositing operations on it. + + This is used internally by the imaging classes. + + @see PixelARGB, PixelRGB +*/ +class JUCE_API PixelAlpha +{ +public: + /** Creates a pixel without defining its colour. */ + PixelAlpha() throw() {} + ~PixelAlpha() throw() {} + + /** Creates a pixel from a 32-bit argb value. + + (The argb format is that used by PixelARGB) + */ + PixelAlpha (const uint32 argb) throw() + { + a = (uint8) (argb >> 24); + } + + forcedinline uint32 getARGB() const throw() { return (((uint32) a) << 24) | (((uint32) a) << 16) | (((uint32) a) << 8) | a; } + forcedinline uint32 getRB() const throw() { return (((uint32) a) << 16) | a; } + forcedinline uint32 getAG() const throw() { return (((uint32) a) << 16) | a; } + + forcedinline uint8 getAlpha() const throw() { return a; } + forcedinline uint8 getRed() const throw() { return 0; } + forcedinline uint8 getGreen() const throw() { return 0; } + forcedinline uint8 getBlue() const throw() { return 0; } + + /** Blends another pixel onto this one. + + This takes into account the opacity of the pixel being overlaid, and blends + it accordingly. + */ + template + forcedinline void blend (const Pixel& src) throw() + { + const int srcA = src.getAlpha(); + a = (uint8) ((a * (0x100 - srcA) >> 8) + srcA); + } + + /** Blends another pixel onto this one, applying an extra multiplier to its opacity. + + The opacity of the pixel being overlaid is scaled by the extraAlpha factor before + being used, so this can blend semi-transparently from a PixelRGB argument. + */ + template + forcedinline void blend (const Pixel& src, uint32 extraAlpha) throw() + { + ++extraAlpha; + const int srcAlpha = (extraAlpha * src.getAlpha()) >> 8; + a = (uint8) ((a * (0x100 - srcAlpha) >> 8) + srcAlpha); + } + + /** Blends another pixel with this one, creating a colour that is somewhere + between the two, as specified by the amount. + */ + template + forcedinline void tween (const Pixel& src, const uint32 amount) throw() + { + a += ((src,getAlpha() - a) * amount) >> 8; + } + + /** Copies another pixel colour over this one. + + This doesn't blend it - this colour is simply replaced by the other one. + */ + template + forcedinline void set (const Pixel& src) throw() + { + a = src.getAlpha(); + } + + /** Replaces the colour's alpha value with another one. */ + forcedinline void setAlpha (const uint8 newAlpha) throw() + { + a = newAlpha; + } + + /** Multiplies the colour's alpha value with another one. */ + forcedinline void multiplyAlpha (int multiplier) throw() + { + ++multiplier; + a = (uint8) ((a * multiplier) >> 8); + } + + forcedinline void multiplyAlpha (const float multiplier) throw() + { + a = (uint8) (a * multiplier); + } + + /** Sets the pixel's colour from individual components. */ + forcedinline void setARGB (const uint8 a_, const uint8 r, const uint8 g, const uint8 b) throw() + { + a = a_; + } + + /** Premultiplies the pixel's RGB values by its alpha. */ + forcedinline void premultiply() throw() + { + } + + /** Unpremultiplies the pixel's RGB values. */ + forcedinline void unpremultiply() throw() + { + } + + forcedinline void desaturate() throw() + { + } + +private: + + uint8 a : 8; +} PACKED; + #if JUCE_MSVC #pragma pack (pop) #endif @@ -39252,55 +39419,44 @@ public: */ virtual void desaturate(); - /** Locks some of the pixels in the image so they can be read and written to. + /** Retrieves a section of an image as raw pixel data, so it can be read or written to. - This returns a pointer to some memory containing the pixels in the given - rectangle. It also returns values for the line and pixel stride used within - the data. The format of the pixel data is the same as that of this image. + You should only use this class as a last resort - messing about with the internals of + an image is only recommended for people who really know what they're doing! - When you've finished reading and changing the data, you must call - releasePixelDataReadWrite() to give the pixels back to the image. + A BitmapData object should be used as a temporary, stack-based object. Don't keep one + hanging around while the image is being used elsewhere. - For images that are stored in memory, this method may just return a direct - pointer to the image's data, but other types of image may be stored elsewhere, - e.g. in video memory, and if so, this lockPixelDataReadWrite() and - releasePixelDataReadWrite() may need to create a temporary copy in main memory. + Depending on the way the image class is implemented, this may create a temporary buffer + which is copied back to the image when the object is deleted, or it may just get a pointer + directly into the image's raw data. - If you only need read-access to the pixel data, use lockPixelDataReadOnly() - instead. - - @see releasePixelDataReadWrite, lockPixelDataReadOnly + You can use the stride and data values in this class directly, but don't alter them! + The actual format of the pixel data depends on the image's format - see Image::getFormat(), + and the PixelRGB, PixelARGB and PixelAlpha classes for more info. */ - virtual uint8* lockPixelDataReadWrite (int x, int y, int w, int h, int& lineStride, int& pixelStride); + class BitmapData + { + public: + BitmapData (Image& image, int x, int y, int w, int h, const bool needsToBeWritable) throw(); + BitmapData (const Image& image, int x, int y, int w, int h) throw(); + ~BitmapData() throw(); - /** Releases a block of memory that was locked with lockPixelDataReadWrite(). - */ - virtual void releasePixelDataReadWrite (void* sourceData); + /** Returns a pointer to the start of a line in the image. + The co-ordinate you provide here isn't checked, so it's the caller's responsibility to make + sure it's not out-of-range. + */ + inline uint8* getLinePointer (const int y) const throw() { return data + y * lineStride; } - /** Locks some of the pixels in the image so they can be read. + /** Returns a pointer to a pixel in the image. + The co-ordinates you give here are not checked, so it's the caller's responsibility to make sure they're + not out-of-range. + */ + inline uint8* getPixelPointer (const int x, const int y) const throw() { return data + y * lineStride + x * pixelStride; } - This returns a pointer to some memory containing the pixels in the given - rectangle. It also returns values for the line and pixel stride used within - the data. The format of the pixel data is the same as that of this image. - - When you've finished reading the data, you must call releasePixelDataReadOnly() - to let the image free the memory if necessary. - - For images that are stored in memory, this method may just return a direct - pointer to the image's data, but other types of image may be stored elsewhere, - e.g. in video memory, and if so, this lockPixelDataReadWrite() and - releasePixelDataReadWrite() may need to create a temporary copy in main memory. - - If you only need to read and write the pixel data, use lockPixelDataReadWrite() - instead. - - @see releasePixelDataReadOnly, lockPixelDataReadWrite - */ - virtual const uint8* lockPixelDataReadOnly (int x, int y, int w, int h, int& lineStride, int& pixelStride) const; - - /** Releases a block of memory that was locked with lockPixelDataReadOnly(). - */ - virtual void releasePixelDataReadOnly (const void* sourceData) const; + uint8* data; + int lineStride, pixelStride, width, height; + }; /** Copies some pixel values to a rectangle of the image. @@ -39335,6 +39491,7 @@ public: virtual LowLevelGraphicsContext* createLowLevelContext(); protected: + friend class BitmapData; const PixelFormat format; const int imageWidth, imageHeight; @@ -39953,6 +40110,8 @@ public: #ifndef __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ #define __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ +class LLGCSavedState; + /** A lowest-common-denominator implementation of LowLevelGraphicsContext that does all its rendering in memory. @@ -40015,48 +40174,18 @@ public: void drawGlyph (int glyphNumber, float x, float y); void drawGlyph (int glyphNumber, const AffineTransform& transform); - RectangleList* getRawClipRegion() throw() { return clip; } - juce_UseDebuggingNewOperator protected: Image& image; - RectangleList* clip; - int xOffset, yOffset; - Font font; - Colour colour; - ColourGradient* gradient; - Graphics::ResamplingQuality interpolationQuality; - struct SavedState - { - SavedState (RectangleList* const clip, const int xOffset, const int yOffset, - const Font& font, const Colour& colour, ColourGradient* gradient, - Graphics::ResamplingQuality interpolationQuality); - ~SavedState(); + LLGCSavedState* currentState; + OwnedArray stateStack; - RectangleList* clip; - const int xOffset, yOffset; - Font font; - Colour colour; - ColourGradient* gradient; - Graphics::ResamplingQuality interpolationQuality; - - private: - SavedState (const SavedState&); - const SavedState& operator= (const SavedState&); - }; - - OwnedArray stateStack; - - void drawVertical (const int x, const double top, const double bottom); +/* void drawVertical (const int x, const double top, const double bottom); void drawHorizontal (const int y, const double top, const double bottom); - bool getPathBounds (int clipX, int clipY, int clipW, int clipH, - const Path& path, const AffineTransform& transform, - int& x, int& y, int& w, int& h) const; - 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); @@ -40077,7 +40206,7 @@ protected: void clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2); void clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom); - void clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom); + void clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom);*/ LowLevelGraphicsSoftwareRenderer (const LowLevelGraphicsSoftwareRenderer& other); const LowLevelGraphicsSoftwareRenderer& operator= (const LowLevelGraphicsSoftwareRenderer&); diff --git a/src/gui/components/special/juce_ColourSelector.cpp b/src/gui/components/special/juce_ColourSelector.cpp index b78c1cf629..70a5042c4f 100644 --- a/src/gui/components/special/juce_ColourSelector.cpp +++ b/src/gui/components/special/juce_ColourSelector.cpp @@ -129,8 +129,7 @@ public: const int height = getHeight() / 2; colours = new Image (Image::RGB, width, height, false); - int ls, ps; - char* data = (char*) colours->lockPixelDataReadWrite (0, 0, width, height, ls, ps); + Image::BitmapData pixels (*colours, 0, 0, width, height, true); for (int y = 0; y < height; ++y) { @@ -141,12 +140,10 @@ public: const float s = x / (float) width; const Colour col (h, s, v, 1.0f); - PixelRGB* const pix = (PixelRGB*) (data + ls * y + ps * x); + PixelRGB* const pix = (PixelRGB*) pixels.getPixelPointer (x, y); pix->set (col.getPixelARGB()); } } - - colours->releasePixelDataReadWrite (data); } g.setOpacity (1.0f); diff --git a/src/gui/graphics/colour/juce_PixelFormats.h b/src/gui/graphics/colour/juce_PixelFormats.h index bb93f16140..9d09a4bb81 100644 --- a/src/gui/graphics/colour/juce_PixelFormats.h +++ b/src/gui/graphics/colour/juce_PixelFormats.h @@ -37,6 +37,8 @@ #define PACKED #endif +class PixelRGB; + /** Represents a 32-bit ARGB pixel with premultiplied alpha, and can perform compositing operations with it. @@ -85,6 +87,13 @@ public: argb = sargb; } + /** Blends another pixel onto this one. + + This takes into account the opacity of the pixel being overlaid, and blends + it accordingly. + */ + forcedinline void blend (const PixelRGB& src) throw(); + /** Blends another pixel onto this one, applying an extra multiplier to its opacity. The opacity of the pixel being overlaid is scaled by the extraAlpha factor before @@ -301,6 +310,12 @@ public: set (src); } + template + forcedinline void blend (const Pixel& src) throw() + { + blend (PixelARGB (src.getARGB())); + } + /** Blends another pixel onto this one, applying an extra multiplier to its opacity. The opacity of the pixel being overlaid is scaled by the extraAlpha factor before @@ -390,6 +405,130 @@ private: } PACKED; +forcedinline void PixelARGB::blend (const PixelRGB& src) throw() +{ + set (src); +} + +//============================================================================== +/** + Represents an 8-bit single-channel pixel, and can perform compositing operations on it. + + This is used internally by the imaging classes. + + @see PixelARGB, PixelRGB +*/ +class JUCE_API PixelAlpha +{ +public: + /** Creates a pixel without defining its colour. */ + PixelAlpha() throw() {} + ~PixelAlpha() throw() {} + + /** Creates a pixel from a 32-bit argb value. + + (The argb format is that used by PixelARGB) + */ + PixelAlpha (const uint32 argb) throw() + { + a = (uint8) (argb >> 24); + } + + forcedinline uint32 getARGB() const throw() { return (((uint32) a) << 24) | (((uint32) a) << 16) | (((uint32) a) << 8) | a; } + forcedinline uint32 getRB() const throw() { return (((uint32) a) << 16) | a; } + forcedinline uint32 getAG() const throw() { return (((uint32) a) << 16) | a; } + + forcedinline uint8 getAlpha() const throw() { return a; } + forcedinline uint8 getRed() const throw() { return 0; } + forcedinline uint8 getGreen() const throw() { return 0; } + forcedinline uint8 getBlue() const throw() { return 0; } + + /** Blends another pixel onto this one. + + This takes into account the opacity of the pixel being overlaid, and blends + it accordingly. + */ + template + forcedinline void blend (const Pixel& src) throw() + { + const int srcA = src.getAlpha(); + a = (uint8) ((a * (0x100 - srcA) >> 8) + srcA); + } + + /** Blends another pixel onto this one, applying an extra multiplier to its opacity. + + The opacity of the pixel being overlaid is scaled by the extraAlpha factor before + being used, so this can blend semi-transparently from a PixelRGB argument. + */ + template + forcedinline void blend (const Pixel& src, uint32 extraAlpha) throw() + { + ++extraAlpha; + const int srcAlpha = (extraAlpha * src.getAlpha()) >> 8; + a = (uint8) ((a * (0x100 - srcAlpha) >> 8) + srcAlpha); + } + + /** Blends another pixel with this one, creating a colour that is somewhere + between the two, as specified by the amount. + */ + template + forcedinline void tween (const Pixel& src, const uint32 amount) throw() + { + a += ((src,getAlpha() - a) * amount) >> 8; + } + + /** Copies another pixel colour over this one. + + This doesn't blend it - this colour is simply replaced by the other one. + */ + template + forcedinline void set (const Pixel& src) throw() + { + a = src.getAlpha(); + } + + /** Replaces the colour's alpha value with another one. */ + forcedinline void setAlpha (const uint8 newAlpha) throw() + { + a = newAlpha; + } + + /** Multiplies the colour's alpha value with another one. */ + forcedinline void multiplyAlpha (int multiplier) throw() + { + ++multiplier; + a = (uint8) ((a * multiplier) >> 8); + } + + forcedinline void multiplyAlpha (const float multiplier) throw() + { + a = (uint8) (a * multiplier); + } + + /** Sets the pixel's colour from individual components. */ + forcedinline void setARGB (const uint8 a_, const uint8 r, const uint8 g, const uint8 b) throw() + { + a = a_; + } + + /** Premultiplies the pixel's RGB values by its alpha. */ + forcedinline void premultiply() throw() + { + } + + /** Unpremultiplies the pixel's RGB values. */ + forcedinline void unpremultiply() throw() + { + } + + forcedinline void desaturate() throw() + { + } + +private: + //============================================================================== + uint8 a : 8; +} PACKED; #if JUCE_MSVC #pragma pack (pop) diff --git a/src/gui/graphics/contexts/juce_EdgeTable.cpp b/src/gui/graphics/contexts/juce_EdgeTable.cpp index 7cb7ebaf5c..baf851ff6e 100644 --- a/src/gui/graphics/contexts/juce_EdgeTable.cpp +++ b/src/gui/graphics/contexts/juce_EdgeTable.cpp @@ -29,9 +29,21 @@ BEGIN_JUCE_NAMESPACE #include "juce_EdgeTable.h" #include "../geometry/juce_PathIterator.h" +#include "../imaging/juce_Image.h" const int juce_edgeTableDefaultEdgesPerLine = 32; +//============================================================================== +static void copyEdgeTableData (int* dest, const int destLineStride, const int* src, const int srcLineStride, int numLines) throw() +{ + while (--numLines >= 0) + { + memcpy (dest, src, (src[0] * 2 + 1) * sizeof (int)); + src += srcLineStride; + dest += destLineStride; + } +} + //============================================================================== EdgeTable::EdgeTable (const Rectangle& bounds_, const Path& path, const AffineTransform& transform) throw() @@ -103,19 +115,36 @@ EdgeTable::EdgeTable (const Rectangle& bounds_, } } - if (! path.isUsingNonZeroWinding()) + // Convert the table from relative windings to absolute levels.. + int* lineStart = table; + + for (int i = bounds.getHeight(); --i >= 0;) { - int* lineStart = table; + int* line = lineStart; + lineStart += lineStrideElements; - for (int i = bounds.getHeight(); --i >= 0;) + int num = *line; + if (num == 0) + continue; + + int level = 0; + + if (path.isUsingNonZeroWinding()) { - int* line = lineStart; - lineStart += lineStrideElements; - int num = *line; - int level = 0; - int lastCorrected = 0; + while (--num > 0) + { + line += 2; + level += *line; + int corrected = abs (level); + if (corrected >> 8) + corrected = 255; - while (--num >= 0) + *line = corrected; + } + } + else + { + while (--num > 0) { line += 2; level += *line; @@ -127,10 +156,11 @@ EdgeTable::EdgeTable (const Rectangle& bounds_, corrected = 511 - corrected; } - *line = corrected - lastCorrected; - lastCorrected = corrected; + *line = corrected; } } + + line[2] = 0; // force the last level to 0, just in case something went wrong in creating the table } } @@ -142,21 +172,95 @@ EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) throw() table = (int*) juce_malloc (jmax (1, bounds.getHeight()) * lineStrideElements * sizeof (int)); *table = 0; - const int x1 = rectangleToAdd.getX(); - const int x2 = rectangleToAdd.getRight(); + const int x1 = rectangleToAdd.getX() << 8; + const int x2 = rectangleToAdd.getRight() << 8; int* t = table; for (int i = rectangleToAdd.getHeight(); --i >= 0;) { t[0] = 2; t[1] = x1; - t[2] = 256; + t[2] = 255; t[3] = x2; - t[4] = -256; + t[4] = 0; t += lineStrideElements; } } +EdgeTable::EdgeTable (const float x, const float y, const float w, const float h) throw() + : bounds (Rectangle ((int) floorf (x), roundFloatToInt (y * 256.0f) >> 8, 2 + (int) w, 2 + (int) h)), + maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), + lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1) +{ + jassert (w > 0 && h > 0); + table = (int*) juce_malloc (jmax (1, bounds.getHeight()) * lineStrideElements * sizeof (int)); + *table = 0; + + const int x1 = roundFloatToInt (x * 256.0f); + const int x2 = roundFloatToInt ((x + w) * 256.0f); + + int y1 = roundFloatToInt (y * 256.0f) - (bounds.getY() << 8); + jassert (y1 < 256); + int y2 = roundFloatToInt ((y + h) * 256.0f) - (bounds.getY() << 8); + + if (x2 <= x1 || y2 <= y1) + { + bounds.setHeight (0); + return; + } + + int lineY = 0; + int* t = table; + + if ((y1 >> 8) == (y2 >> 8)) + { + t[0] = 2; + t[1] = x1; + t[2] = y2 - y1; + t[3] = x2; + t[4] = 0; + ++lineY; + t += lineStrideElements; + } + else + { + t[0] = 2; + t[1] = x1; + t[2] = 255 - (y1 & 255); + t[3] = x2; + t[4] = 0; + ++lineY; + t += lineStrideElements; + + while (lineY < (y2 >> 8)) + { + t[0] = 2; + t[1] = x1; + t[2] = 255; + t[3] = x2; + t[4] = 0; + ++lineY; + t += lineStrideElements; + } + + jassert (lineY < bounds.getHeight()); + t[0] = 2; + t[1] = x1; + t[2] = y2 & 255; + t[3] = x2; + t[4] = 0; + ++lineY; + t += lineStrideElements; + } + + while (lineY < bounds.getHeight()) + { + t[0] = 0; + t += lineStrideElements; + ++lineY; + } +} + EdgeTable::EdgeTable (const EdgeTable& other) throw() : table (0) { @@ -173,8 +277,7 @@ const EdgeTable& EdgeTable::operator= (const EdgeTable& other) throw() const int tableSize = jmax (1, bounds.getHeight()) * lineStrideElements * sizeof (int); table = (int*) juce_malloc (tableSize); - memcpy (table, other.table, tableSize); - + copyEdgeTableData (table, lineStrideElements, other.table, lineStrideElements, bounds.getHeight()); return *this; } @@ -194,18 +297,7 @@ void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine) throw() const int newLineStrideElements = maxEdgesPerLine * 2 + 1; int* const newTable = (int*) juce_malloc (bounds.getHeight() * newLineStrideElements * sizeof (int)); - for (int i = 0; i < bounds.getHeight(); ++i) - { - const int* srcLine = table + lineStrideElements * i; - int* dstLine = newTable + newLineStrideElements * i; - - int num = *srcLine++; - *dstLine++ = num; - - num <<= 1; - while (--num >= 0) - *dstLine++ = *srcLine++; - } + copyEdgeTableData (newTable, newLineStrideElements, table, lineStrideElements, bounds.getHeight()); juce_free (table); table = newTable; @@ -229,7 +321,7 @@ void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw int* line = table + lineStrideElements * y; const int numPoints = line[0]; - int n = line[0] << 1; + int n = numPoints << 1; if (n > 0) { @@ -266,105 +358,125 @@ void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw line[0]++; } -void EdgeTable::clearLineSection (const int y, int minX, int maxX) throw() -{ - jassert (y >= 0 && y < bounds.getHeight()); - jassert (maxX > minX); - - int* lineStart = table + lineStrideElements * y; - const int totalPoints = *lineStart; - int* line = lineStart; - int level = 0; - int num = totalPoints; - - while (--num >= 0) - { - int x = *++line; - - if (x < minX) - { - level += *++line; - continue; - } - - if (x > maxX) - { - if (level != 0) - { - addEdgePoint (minX, y, -level); - addEdgePoint (maxX, y, level); - } - - return; - } - - if (level != 0) - { - const int oldLevel = level; - level += line[1]; - line[0] = minX; - line[1] = -oldLevel; - line += 2; - } - else - { - ++num; - } - - int* cutPoint = line; - int numToDelete = 0; - - while (--num >= 0) - { - x = *line++; - - if (x <= maxX) - { - level += *line++; - ++numToDelete; - continue; - } - - break; - } - - if (num < 0) - { - lineStart[0] -= numToDelete; - } - else - { - if (--numToDelete > 0) - { - char* startOfSrcSection = (char*) (cutPoint + numToDelete * 2); - memmove (cutPoint, startOfSrcSection, - ((char*) (lineStart + (1 + 2 * totalPoints))) - startOfSrcSection); - - lineStart[0] -= numToDelete; - } - - if (numToDelete < 0) - { - addEdgePoint (maxX, y, level); - } - else - { - cutPoint[0] = maxX; - cutPoint[1] = level; - } - } - - break; - } -} - void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) throw() { -// int otherNum = otherLine[0]; + jassert (y >= 0 && y < bounds.getHeight()); + + int* dest = table + lineStrideElements * y; + if (dest[0] == 0) + return; + + int otherNumPoints = *otherLine; + if (otherNumPoints == 0) + { + *dest = 0; + return; + } + + ++otherLine; + const int lineSizeBytes = (dest[0] * 2 + 1) * sizeof (int); + int* temp = (int*) alloca (lineSizeBytes); + memcpy (temp, dest, lineSizeBytes); + + const int* src1 = temp; + int srcNum1 = *src1++; + int x1 = *src1++; + + const int* src2 = otherLine; + int srcNum2 = otherNumPoints; + int x2 = *src2++; + + int destIndex = 0, destTotal = 0; + int level1 = 0, level2 = 0; + int lastX = INT_MIN, lastLevel = 0; + const int right = bounds.getRight() << 8; + + while (srcNum1 > 0 && srcNum2 > 0) + { + int nextX; + + if (x1 < x2) + { + nextX = x1; + level1 = *src1++; + x1 = *src1++; + --srcNum1; + } + else if (x1 == x2) + { + nextX = x1; + level1 = *src1++; + level2 = *src2++; + x1 = *src1++; + x2 = *src2++; + --srcNum1; + --srcNum2; + } + else + { + nextX = x2; + level2 = *src2++; + x2 = *src2++; + --srcNum2; + } + + if (nextX > lastX) + { + if (nextX >= right) + break; + + lastX = nextX; + + const int nextLevel = (level1 * (level2 + 1)) >> 8; + jassert (((unsigned int) nextLevel) < (unsigned int) 256); + + if (nextLevel != lastLevel) + { + if (destTotal >= maxEdgesPerLine) + { + dest[0] = destTotal; + remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); + dest = table + lineStrideElements * y; + } + + ++destTotal; + lastLevel = nextLevel; + dest[++destIndex] = nextX; + dest[++destIndex] = nextLevel; + } + } + } + + if (lastLevel > 0) + { + if (destTotal >= maxEdgesPerLine) + { + dest[0] = destTotal; + remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); + dest = table + lineStrideElements * y; + } + + ++destTotal; + dest[++destIndex] = right; + dest[++destIndex] = 0; + } + + dest[0] = destTotal; + +#if JUCE_DEBUG + int last = INT_MIN; + for (int i = 0; i < dest[0]; ++i) + { + jassert (dest[i * 2 + 1] > last); + last = dest[i * 2 + 1]; + } + + jassert (dest [dest[0] * 2] == 0); +#endif } //============================================================================== -/*void EdgeTable::clipToRectangle (const Rectangle& r) throw() +void EdgeTable::clipToRectangle (const Rectangle& r) throw() { const Rectangle clipped (r.getIntersection (bounds)); @@ -387,8 +499,12 @@ void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) t table [lineStrideElements * i] = 0; if (clipped.getX() > bounds.getX()) + { + const int rectLine[] = { 2, clipped.getX() << 8, 255, clipped.getRight() << 8, 0 }; + for (int i = top; i < bottom; ++i) - clearLineSection (i, bounds.getX(), clipped.getX()); + intersectWithEdgeTableLine (i, rectLine); + } } } @@ -401,8 +517,12 @@ void EdgeTable::excludeRectangle (const Rectangle& r) throw() const int top = clipped.getY() - bounds.getY(); const int bottom = clipped.getBottom() - bounds.getY(); + //XXX optimise here by shortening the table if it fills top or bottom + + const int rectLine[] = { 4, INT_MIN, 255, clipped.getX() << 8, 0, clipped.getRight() << 8, 255, INT_MAX, 0 }; + for (int i = top; i < bottom; ++i) - clearLineSection (i, clipped.getX(), clipped.getRight()); + intersectWithEdgeTableLine (i, rectLine); } } @@ -428,7 +548,7 @@ void EdgeTable::clipToEdgeTable (const EdgeTable& other) for (int i = top; --i >= 0;) table [lineStrideElements * i] = 0; - const int* otherLine = other.table + other.lineStrideElements * (top + (bounds.getY() - other.bounds.getY())); + const int* otherLine = other.table + other.lineStrideElements * (clipped.getY() - other.bounds.getY()); for (int i = top; i < bottom; ++i) { @@ -438,8 +558,106 @@ void EdgeTable::clipToEdgeTable (const EdgeTable& other) } } -void EdgeTable::clipToImageAlpha (Image& image, int x, int y) throw() +void EdgeTable::clipToImageAlpha (const Image& image, int x, int y) throw() { + const Rectangle clipped (bounds.getIntersection (Rectangle (x, y, image.getWidth(), image.getHeight()))); + + if (clipped.isEmpty()) + { + bounds.setHeight (0); + } + else + { + if (! image.hasAlphaChannel()) + { + clipToRectangle (clipped); + return; + } + + const int top = clipped.getY() - bounds.getY(); + const int bottom = clipped.getBottom() - bounds.getY(); + + if (bottom < bounds.getHeight()) + bounds.setHeight (bottom); + + if (clipped.getRight() < bounds.getRight()) + bounds.setRight (clipped.getRight()); + + for (int i = top; --i >= 0;) + table [lineStrideElements * i] = 0; + + int imageX = clipped.getX() - x; + int imageY = clipped.getY() - y; + + int* temp = (int*) alloca ((clipped.getWidth() * 2 + 4) * sizeof (int)); + + Image::BitmapData srcData (image, imageX, imageY, clipped.getWidth(), clipped.getHeight()); + + for (int i = 0; i < clipped.getHeight(); ++i) + { + int destIndex = 0, lastLevel = 0; + const uint8* pixels = srcData.getLinePointer (i); + + if (image.getFormat() == Image::ARGB) + { + for (int px = 0; px < clipped.getWidth(); ++px) + { + const int alpha = ((const PixelARGB*) pixels)->getAlpha(); + pixels += srcData.pixelStride; + + if (alpha != lastLevel) + { + temp[++destIndex] = (clipped.getX() + px) << 8; + temp[++destIndex] = alpha; + lastLevel = alpha; + } + } + } + else + { + jassert (image.getFormat() == Image::SingleChannel); + + for (int px = 0; px < clipped.getWidth(); ++px) + { + const int alpha = *pixels; + pixels += srcData.pixelStride; + + if (alpha != lastLevel) + { + temp[++destIndex] = (clipped.getX() + px) << 8; + temp[++destIndex] = alpha; + lastLevel = alpha; + } + } + } + + if (lastLevel > 0) + { + temp[++destIndex] = clipped.getRight() << 8; + temp[++destIndex] = 0; + } + + temp[0] = destIndex >> 1; + + intersectWithEdgeTableLine (top + i, temp); + ++y; + } + } } -*/ + +bool EdgeTable::isEmpty() const throw() +{ + int* t = table; + + for (int i = bounds.getHeight(); --i >= 0;) + { + if (t[0] > 1) + return false; + + t += lineStrideElements; + } + + return true; +} + END_JUCE_NAMESPACE diff --git a/src/gui/graphics/contexts/juce_EdgeTable.h b/src/gui/graphics/contexts/juce_EdgeTable.h index 026ad3a735..f190297f8f 100644 --- a/src/gui/graphics/contexts/juce_EdgeTable.h +++ b/src/gui/graphics/contexts/juce_EdgeTable.h @@ -60,6 +60,11 @@ public: */ EdgeTable (const Rectangle& rectangleToAdd) throw(); + /** Creates an edge table containing a rectangle. + */ + EdgeTable (const float x, const float y, + const float w, const float h) throw(); + /** Creates a copy of another edge table. */ EdgeTable (const EdgeTable& other) throw(); @@ -70,10 +75,12 @@ public: ~EdgeTable() throw(); //============================================================================== - /*void clipToRectangle (const Rectangle& r) throw(); + void clipToRectangle (const Rectangle& r) throw(); void excludeRectangle (const Rectangle& r) throw(); void clipToEdgeTable (const EdgeTable& other); - void clipToImageAlpha (Image& image, int x, int y) throw();*/ + void clipToImageAlpha (const Image& image, int x, int y) throw(); + bool isEmpty() const throw(); + const Rectangle& getMaximumBounds() const throw() { return bounds; } /** Reduces the amount of space the table has allocated. @@ -112,17 +119,14 @@ public: { int x = *++line; jassert ((x >> 8) >= bounds.getX() && (x >> 8) < bounds.getRight()); - int level = *++line; int levelAccumulator = 0; - iterationCallback.setEdgeTableYPos (y); + iterationCallback.setEdgeTableYPos (bounds.getY() + y); while (--numPoints >= 0) { - int correctedLevel = abs (level); - if (correctedLevel >> 8) - correctedLevel = 0xff; - + const int level = *++line; + jassert (((unsigned int) level) < (unsigned int) 256); const int endX = *++line; jassert (endX >= x); const int endOfRun = (endX >> 8); @@ -131,13 +135,13 @@ public: { // small segment within the same pixel, so just save it for the next // time round.. - levelAccumulator += (endX - x) * correctedLevel; + levelAccumulator += (endX - x) * level; } 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)) * correctedLevel; + levelAccumulator += (0xff - (x & 0xff)) * level; levelAccumulator >>= 8; x >>= 8; @@ -149,21 +153,20 @@ public: iterationCallback.handleEdgeTablePixel (x, levelAccumulator); } - // if there's a segment of solid pixels, do it all in one go.. - if (correctedLevel > 0) + // if there's a run of similar pixels, do it all in one go.. + if (level > 0) { jassert (endOfRun <= bounds.getRight()); const int numPix = endOfRun - ++x; if (numPix > 0) - iterationCallback.handleEdgeTableLine (x, numPix, correctedLevel); + iterationCallback.handleEdgeTableLine (x, numPix, level); } // save the bit at the end to be drawn next time round the loop. - levelAccumulator = (endX & 0xff) * correctedLevel; + levelAccumulator = (endX & 0xff) * level; } - level += *++line; x = endX; } @@ -192,7 +195,6 @@ private: void addEdgePoint (const int x, const int y, const int winding) throw(); void remapTableForNumEdges (const int newNumEdgesPerLine) throw(); - void clearLineSection (const int y, int minX, int maxX) throw(); void intersectWithEdgeTableLine (const int y, const int* otherLine) throw(); }; diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp index 6f3c3f39ff..6f3478999d 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp @@ -487,16 +487,14 @@ void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im, const int h = jmin (maxH, im.getHeight()); int charsOnLine = 0; - int lineStride, pixelStride; - const uint8* data = im.lockPixelDataReadOnly (0, 0, w, h, lineStride, pixelStride); - + const Image::BitmapData srcData (im, 0, 0, w, h); Colour pixel; for (int y = h; --y >= 0;) { for (int x = 0; x < w; ++x) { - const uint8* pixelData = data + lineStride * y + pixelStride * x; + const uint8* pixelData = srcData.getPixelPointer (x, y); if (x >= sx && y >= sy) { @@ -534,8 +532,6 @@ void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im, } } - im.releasePixelDataReadOnly (data); - out << "\n>}\n"; } diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index 3989d0af53..6858198681 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -40,102 +40,17 @@ BEGIN_JUCE_NAMESPACE #define JUCE_USE_SSE_INSTRUCTIONS 1 #endif -#if JUCE_DEBUG && JUCE_MSVC - #pragma warning (disable: 4714) +#if JUCE_MSVC && JUCE_DEBUG + #pragma warning (disable: 4714) // warning about forcedinline methods not being inlined #endif -#define MINIMUM_COORD -0x3fffffff -#define MAXIMUM_COORD 0x3fffffff - -#undef ASSERT_COORDS_ARE_SENSIBLE_NUMBERS -#define ASSERT_COORDS_ARE_SENSIBLE_NUMBERS(x, y, w, h) \ - jassert ((int) x >= MINIMUM_COORD \ - && (int) x <= MAXIMUM_COORD \ - && (int) y >= MINIMUM_COORD \ - && (int) y <= MAXIMUM_COORD \ - && (int) w >= 0 \ - && (int) w < MAXIMUM_COORD \ - && (int) h >= 0 \ - && (int) h < MAXIMUM_COORD); +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4127) // "expression is constant" warning +#endif //============================================================================== -static void replaceRectRGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() -{ - const PixelARGB blendColour (colour.getPixelARGB()); - - if (w < 32) - { - while (--h >= 0) - { - PixelRGB* dest = (PixelRGB*) pixels; - - for (int i = w; --i >= 0;) - (dest++)->set (blendColour); - - pixels += stride; - } - } - else - { - // for wider fills, it's worth using some optimisations.. - - const uint8 r = blendColour.getRed(); - const uint8 g = blendColour.getGreen(); - const uint8 b = blendColour.getBlue(); - - if (r == g && r == b) // if all the component values are the same, we can cheat.. - { - while (--h >= 0) - { - memset (pixels, r, w * 3); - pixels += stride; - } - } - else - { - PixelRGB filler [4]; - filler[0].set (blendColour); - filler[1].set (blendColour); - filler[2].set (blendColour); - filler[3].set (blendColour); - const int* const intFiller = (const int*) filler; - - while (--h >= 0) - { - uint8* dest = (uint8*) pixels; - - int i = w; - - while ((i > 8) && (((pointer_sized_int) dest & 7) != 0)) - { - ((PixelRGB*) dest)->set (blendColour); - dest += 3; - --i; - } - - while (i >= 4) - { - ((int*) dest) [0] = intFiller[0]; - ((int*) dest) [1] = intFiller[1]; - ((int*) dest) [2] = intFiller[2]; - - dest += 12; - i -= 4; - } - - while (--i >= 0) - { - ((PixelRGB*) dest)->set (blendColour); - dest += 3; - } - - pixels += stride; - } - } - } -} - -static void replaceRectARGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() +/*static void replaceRectARGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() { const PixelARGB blendColour (colour.getPixelARGB()); @@ -251,14 +166,14 @@ static void blendRectRGB (uint8* pixels, const int w, int h, const int stride, c "\tjg .lineLoop2 \n" "\tpop %%ebx \n" "\temms \n" - : /* No output registers */ - : [aaaa] "m" (aaaa), /* Input registers */ + : // No output registers + : [aaaa] "m" (aaaa), // Input registers [rgb0] "m" (rgb0), [w] "m" (w), "c" (h), [stride] "D" (stride), [pixels] "S" (pixels) - : "cc", "eax", "edx", "memory" /* Clobber list */ + : "cc", "eax", "edx", "memory" ); #endif } @@ -277,122 +192,39 @@ static void blendRectRGB (uint8* pixels, const int w, int h, const int stride, c } } } - -static void blendRectARGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() -{ - if (colour.isOpaque()) - { - replaceRectARGB (pixels, w, h, stride, colour); - } - else - { - const PixelARGB blendColour (colour.getPixelARGB()); - const int alpha = blendColour.getAlpha(); - - if (alpha <= 0) - return; - - while (--h >= 0) - { - PixelARGB* dest = (PixelARGB*) pixels; - - for (int i = w; --i >= 0;) - (dest++)->blend (blendColour); - - pixels += stride; - } - } -} +*/ //============================================================================== -static void blendAlphaMapARGB (uint8* destPixel, const int imageStride, - const uint8* alphaValues, const int w, int h, - const int pixelStride, const int lineStride, - const Colour& colour) throw() -{ - const PixelARGB srcPix (colour.getPixelARGB()); - - while (--h >= 0) - { - PixelARGB* dest = (PixelARGB*) destPixel; - const uint8* src = alphaValues; - - int i = w; - while (--i >= 0) - { - unsigned int srcAlpha = *src; - src += pixelStride; - - if (srcAlpha > 0) - dest->blend (srcPix, srcAlpha); - - ++dest; - } - - alphaValues += lineStride; - destPixel += imageStride; - } -} - -static void blendAlphaMapRGB (uint8* destPixel, const int imageStride, - const uint8* alphaValues, int const width, int height, - const int pixelStride, const int lineStride, - const Colour& colour) throw() -{ - const PixelARGB srcPix (colour.getPixelARGB()); - - while (--height >= 0) - { - PixelRGB* dest = (PixelRGB*) destPixel; - const uint8* src = alphaValues; - - int i = width; - while (--i >= 0) - { - unsigned int srcAlpha = *src; - src += pixelStride; - - if (srcAlpha > 0) - dest->blend (srcPix, srcAlpha); - - ++dest; - } - - alphaValues += lineStride; - destPixel += imageStride; - } -} - -//============================================================================== -template +template class SolidColourEdgeTableRenderer { - uint8* const data; - const int stride; - PixelType* linePixels; - PixelARGB sourceColour; - - SolidColourEdgeTableRenderer (const SolidColourEdgeTableRenderer&); - const SolidColourEdgeTableRenderer& operator= (const SolidColourEdgeTableRenderer&); - public: - SolidColourEdgeTableRenderer (uint8* const data_, - const int stride_, - const Colour& colour) throw() + SolidColourEdgeTableRenderer (const Image::BitmapData& data_, const PixelARGB& colour) throw() : data (data_), - stride (stride_), - sourceColour (colour.getPixelARGB()) + sourceColour (colour) { + if (sizeof (PixelType) == 3) + { + areRGBComponentsEqual = sourceColour.getRed() == sourceColour.getGreen() + && sourceColour.getGreen() == sourceColour.getBlue(); + filler[0].set (sourceColour); + filler[1].set (sourceColour); + filler[2].set (sourceColour); + filler[3].set (sourceColour); + } } forcedinline void setEdgeTableYPos (const int y) throw() { - linePixels = (PixelType*) (data + stride * y); + linePixels = (PixelType*) data.getLinePointer (y); } forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw() { - linePixels[x].blend (sourceColour, alphaLevel); + if (replaceExisting) + linePixels[x].set (sourceColour); + else + linePixels[x].blend (sourceColour, alphaLevel); } forcedinline void handleEdgeTableLine (const int x, int width, const int alphaLevel) const throw() @@ -402,78 +234,88 @@ public: PixelType* dest = linePixels + x; - if (p.getAlpha() < 0xff) - { - do - { - dest->blend (p); - ++dest; + if (replaceExisting || p.getAlpha() >= 0xff) + replaceLine (dest, p, width); + else + blendLine (dest, p, width); + } - } while (--width > 0); +private: + const Image::BitmapData& data; + PixelType* linePixels; + PixelARGB sourceColour; + PixelRGB filler [4]; + bool areRGBComponentsEqual; + + forcedinline void blendLine (PixelType* dest, const PixelARGB& colour, int width) const throw() + { + do + { + dest->blend (colour); + ++dest; + + } while (--width > 0); + } + + forcedinline void replaceLine (PixelRGB* dest, const PixelARGB& colour, int width) const throw() + { + if (areRGBComponentsEqual) // if all the component values are the same, we can cheat.. + { + memset (dest, colour.getRed(), width * 3); } else { - do + if (width >> 5) { - dest->set (p); - ++dest; + const int* const intFiller = (const int*) filler; - } while (--width > 0); + while (width > 8 && (((pointer_sized_int) dest) & 7) != 0) + { + dest->set (colour); + ++dest; + --width; + } + + while (width > 4) + { + ((int*) dest) [0] = intFiller[0]; + ((int*) dest) [1] = intFiller[1]; + ((int*) dest) [2] = intFiller[2]; + dest = (PixelRGB*) (((uint8*) dest) + 12); + width -= 4; + } + } + + while (--width >= 0) + { + dest->set (colour); + ++dest; + } } } -}; -class AlphaBitmapRenderer -{ - uint8* data; - int stride; - uint8* lineStart; - - AlphaBitmapRenderer (const AlphaBitmapRenderer&); - const AlphaBitmapRenderer& operator= (const AlphaBitmapRenderer&); - -public: - AlphaBitmapRenderer (uint8* const data_, - const int stride_) throw() - : data (data_), - stride (stride_) + forcedinline void replaceLine (PixelAlpha* dest, const PixelARGB& colour, int width) const throw() { + memset (dest, colour.getAlpha(), width); } - forcedinline void setEdgeTableYPos (const int y) throw() + forcedinline void replaceLine (PixelARGB* dest, const PixelARGB& colour, int width) const throw() { - lineStart = data + (stride * y); + do + { + dest->set (colour); + ++dest; + + } while (--width > 0); } - 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; - } + SolidColourEdgeTableRenderer (const SolidColourEdgeTableRenderer&); + const SolidColourEdgeTableRenderer& operator= (const SolidColourEdgeTableRenderer&); }; //============================================================================== -static const int numScaleBits = 12; - class LinearGradientPixelGenerator { - const PixelARGB* const lookupTable; - const int numEntries; - PixelARGB linePix; - int start, scale; - double grad, yTerm; - bool vertical, horizontal; - - LinearGradientPixelGenerator (const LinearGradientPixelGenerator&); - const LinearGradientPixelGenerator& operator= (const LinearGradientPixelGenerator&); - public: LinearGradientPixelGenerator (const ColourGradient& gradient, const PixelARGB* const lookupTable_, const int numEntries_) @@ -510,19 +352,19 @@ public: if (vertical) { - scale = roundDoubleToInt ((numEntries << numScaleBits) / (double) (y2 - y1)); + scale = roundDoubleToInt ((numEntries << (int) numScaleBits) / (double) (y2 - y1)); start = roundDoubleToInt (y1 * scale); } else if (horizontal) { - scale = roundDoubleToInt ((numEntries << numScaleBits) / (double) (x2 - x1)); + scale = roundDoubleToInt ((numEntries << (int) numScaleBits) / (double) (x2 - x1)); start = roundDoubleToInt (x1 * scale); } else { grad = (y2 - y1) / (double) (x1 - x2); yTerm = y1 - x1 / grad; - scale = roundDoubleToInt ((numEntries << numScaleBits) / (yTerm * grad - (y2 * grad - x2))); + scale = roundDoubleToInt ((numEntries << (int) numScaleBits) / (yTerm * grad - (y2 * grad - x2))); grad *= scale; } } @@ -530,7 +372,7 @@ public: forcedinline void setY (const int y) throw() { if (vertical) - linePix = lookupTable [jlimit (0, numEntries, (y * scale - start) >> numScaleBits)]; + linePix = lookupTable [jlimit (0, numEntries, (y * scale - start) >> (int) numScaleBits)]; else if (! horizontal) start = roundDoubleToInt ((y - yTerm) * grad); } @@ -540,22 +382,25 @@ public: if (vertical) return linePix; - return lookupTable [jlimit (0, numEntries, (x * scale - start) >> numScaleBits)]; + return lookupTable [jlimit (0, numEntries, (x * scale - start) >> (int) numScaleBits)]; } -}; -class RadialGradientPixelGenerator -{ -protected: +private: const PixelARGB* const lookupTable; const int numEntries; - const double gx1, gy1; - double maxDist, invScale; - double dy; + PixelARGB linePix; + int start, scale; + double grad, yTerm; + bool vertical, horizontal; + enum { numScaleBits = 12 }; - RadialGradientPixelGenerator (const RadialGradientPixelGenerator&); - const RadialGradientPixelGenerator& operator= (const RadialGradientPixelGenerator&); + LinearGradientPixelGenerator (const LinearGradientPixelGenerator&); + const LinearGradientPixelGenerator& operator= (const LinearGradientPixelGenerator&); +}; +//============================================================================== +class RadialGradientPixelGenerator +{ public: RadialGradientPixelGenerator (const ColourGradient& gradient, const PixelARGB* const lookupTable_, const int numEntries_) throw() @@ -588,16 +433,20 @@ public: else return lookupTable [jmin (numEntries, roundDoubleToInt (sqrt (x) * invScale))]; } + +protected: + const PixelARGB* const lookupTable; + const int numEntries; + const double gx1, gy1; + double maxDist, invScale, dy; + + RadialGradientPixelGenerator (const RadialGradientPixelGenerator&); + const RadialGradientPixelGenerator& operator= (const RadialGradientPixelGenerator&); }; +//============================================================================== class TransformedRadialGradientPixelGenerator : public RadialGradientPixelGenerator { - double tM10, tM00, lineYM01, lineYM11; - AffineTransform inverseTransform; - - TransformedRadialGradientPixelGenerator (const TransformedRadialGradientPixelGenerator&); - const TransformedRadialGradientPixelGenerator& operator= (const TransformedRadialGradientPixelGenerator&); - public: TransformedRadialGradientPixelGenerator (const ColourGradient& gradient, const PixelARGB* const lookupTable_, const int numEntries_) throw() @@ -627,32 +476,30 @@ public: else return lookupTable [jmin (numEntries, roundDoubleToInt (sqrt (x) * invScale))]; } + +private: + double tM10, tM00, lineYM01, lineYM11; + const AffineTransform inverseTransform; + + TransformedRadialGradientPixelGenerator (const TransformedRadialGradientPixelGenerator&); + const TransformedRadialGradientPixelGenerator& operator= (const TransformedRadialGradientPixelGenerator&); }; +//============================================================================== template class GradientEdgeTableRenderer : public GradientType { - uint8* const data; - const int stride; - PixelType* linePixels; - - GradientEdgeTableRenderer (const GradientEdgeTableRenderer&); - const GradientEdgeTableRenderer& operator= (const GradientEdgeTableRenderer&); - public: - GradientEdgeTableRenderer (uint8* const data_, - const int stride_, - const ColourGradient& gradient, + GradientEdgeTableRenderer (const Image::BitmapData& destData_, const ColourGradient& gradient, const PixelARGB* const lookupTable, const int numEntries) throw() : GradientType (gradient, lookupTable, numEntries - 1), - data (data_), - stride (stride_) + destData (destData_) { } forcedinline void setEdgeTableYPos (const int y) throw() { - linePixels = (PixelType*) (data + stride * y); + linePixels = (PixelType*) destData.getLinePointer (y); GradientType::setY (y); } @@ -682,41 +529,33 @@ public: } while (--width > 0); } } + +private: + const Image::BitmapData& destData; + PixelType* linePixels; + + GradientEdgeTableRenderer (const GradientEdgeTableRenderer&); + const GradientEdgeTableRenderer& operator= (const GradientEdgeTableRenderer&); }; //============================================================================== template class ImageFillEdgeTableRenderer { - uint8* const destImageData; - const uint8* srcImageData; - int stride, srcStride, extraAlpha; - - DestPixelType* linePixels; - SrcPixelType* sourceLineStart; - - ImageFillEdgeTableRenderer (const ImageFillEdgeTableRenderer&); - const ImageFillEdgeTableRenderer& operator= (const ImageFillEdgeTableRenderer&); - public: - ImageFillEdgeTableRenderer (uint8* const destImageData_, - const int stride_, - const uint8* srcImageData_, - const int srcStride_, - int extraAlpha_, - SrcPixelType*) throw() // dummy param to avoid compiler error - : destImageData (destImageData_), - srcImageData (srcImageData_), - stride (stride_), - srcStride (srcStride_), - extraAlpha (extraAlpha_) + ImageFillEdgeTableRenderer (const Image::BitmapData& destData_, + const Image::BitmapData& srcData_, + const int extraAlpha_) throw() + : destData (destData_), + srcData (srcData_), + extraAlpha (extraAlpha_ + 1) { } forcedinline void setEdgeTableYPos (int y) throw() { - linePixels = (DestPixelType*) (destImageData + stride * y); - sourceLineStart = (SrcPixelType*) (srcImageData + srcStride * y); + linePixels = (DestPixelType*) destData.getLinePointer (y); + sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y); } forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) const throw() @@ -741,413 +580,824 @@ public: } else { - do - { - dest++ ->blend (sourceLineStart [x++]); - - } while (--width > 0); + copyRow (dest, sourceLineStart + x, width); } } + +private: + const Image::BitmapData& destData; + const Image::BitmapData& srcData; + const int extraAlpha; + DestPixelType* linePixels; + SrcPixelType* sourceLineStart; + + template + forcedinline static void copyRow (PixelType1* dest, PixelType2* src, int width) throw() + { + do + { + dest++ ->blend (*src++); + + } while (--width > 0); + } + + forcedinline static void copyRow (PixelRGB* dest, PixelRGB* src, int width) throw() + { + memcpy (dest, src, width * sizeof (PixelRGB)); + } + + ImageFillEdgeTableRenderer (const ImageFillEdgeTableRenderer&); + const ImageFillEdgeTableRenderer& operator= (const ImageFillEdgeTableRenderer&); }; //============================================================================== -static void blendRowOfPixels (PixelARGB* dst, - const PixelRGB* src, - int width) throw() -{ - while (--width >= 0) - (dst++)->set (*src++); -} - -static void blendRowOfPixels (PixelRGB* dst, - const PixelRGB* src, - int width) throw() -{ - memcpy (dst, src, 3 * width); -} - -static void blendRowOfPixels (PixelRGB* dst, - const PixelARGB* src, - int width) throw() -{ - while (--width >= 0) - (dst++)->blend (*src++); -} - -static void blendRowOfPixels (PixelARGB* dst, - const PixelARGB* src, - int width) throw() -{ - while (--width >= 0) - (dst++)->blend (*src++); -} - -static void blendRowOfPixels (PixelARGB* dst, - const PixelRGB* src, - int width, - const uint8 alpha) throw() -{ - while (--width >= 0) - (dst++)->blend (*src++, alpha); -} - -static void blendRowOfPixels (PixelRGB* dst, - const PixelRGB* src, - int width, - const uint8 alpha) throw() -{ - uint8* d = (uint8*) dst; - const uint8* s = (const uint8*) src; - const int inverseAlpha = 0xff - alpha; - - while (--width >= 0) - { - d[0] = (uint8) (s[0] + (((d[0] - s[0]) * inverseAlpha) >> 8)); - d[1] = (uint8) (s[1] + (((d[1] - s[1]) * inverseAlpha) >> 8)); - d[2] = (uint8) (s[2] + (((d[2] - s[2]) * inverseAlpha) >> 8)); - - d += 3; - s += 3; - } -} - -static void blendRowOfPixels (PixelRGB* dst, - const PixelARGB* src, - int width, - const uint8 alpha) throw() -{ - while (--width >= 0) - (dst++)->blend (*src++, alpha); -} - -static void blendRowOfPixels (PixelARGB* dst, - const PixelARGB* src, - int width, - const uint8 alpha) throw() -{ - while (--width >= 0) - (dst++)->blend (*src++, alpha); -} - template -static void overlayImage (DestPixelType* dest, - const int destStride, - const SrcPixelType* src, - const int srcStride, - const int width, - int height, - const uint8 alpha) throw() -{ - if (alpha < 0xff) - { - while (--height >= 0) - { - blendRowOfPixels (dest, src, width, alpha); - - dest = (DestPixelType*) (((uint8*) dest) + destStride); - src = (const SrcPixelType*) (((const uint8*) src) + srcStride); - } - } - else - { - while (--height >= 0) - { - blendRowOfPixels (dest, src, width); - - dest = (DestPixelType*) (((uint8*) dest) + destStride); - src = (const SrcPixelType*) (((const uint8*) src) + srcStride); - } - } -} - -template -static void transformedImageRender (Image& destImage, - const Image& sourceImage, - const int destClipX, const int destClipY, - const int destClipW, const int destClipH, - const int srcClipX, const int srcClipY, - const int srcClipWidth, const int srcClipHeight, - double srcX, double srcY, - const double lineDX, const double lineDY, - const double pixelDX, const double pixelDY, - const uint8 alpha, - const Graphics::ResamplingQuality quality, - DestPixelType*, - SrcPixelType*) throw() // forced by a compiler bug to include dummy - // parameters of the templated classes to - // make it use the correct instance of this function.. -{ - int destStride, destPixelStride; - uint8* const destPixels = destImage.lockPixelDataReadWrite (destClipX, destClipY, destClipW, destClipH, destStride, destPixelStride); - - int srcStride, srcPixelStride; - const uint8* const srcPixels = sourceImage.lockPixelDataReadOnly (srcClipX, srcClipY, srcClipWidth, srcClipHeight, srcStride, srcPixelStride); - - if (quality == Graphics::lowResamplingQuality) // nearest-neighbour.. - { - if (alpha == 255) - { - for (int y = 0; y < destClipH; ++y) - { - double sx = srcX; - double sy = srcY; - - DestPixelType* dest = (DestPixelType*) (destPixels + destStride * y); - - for (int x = destClipW; --x >= 0;) - { - const int ix = ((int) sx) - srcClipX; - - if (((unsigned int) ix) < (unsigned int) srcClipWidth) - { - const int iy = ((int) sy) - srcClipY; - - if (((unsigned int) iy) < (unsigned int) srcClipHeight) - { - const SrcPixelType* const src = (const SrcPixelType*) (srcPixels + srcStride * iy + srcPixelStride * ix); - dest->blend (*src); - } - } - - ++dest; - sx += pixelDX; - sy += pixelDY; - } - - srcX += lineDX; - srcY += lineDY; - } - } - else - { - for (int y = 0; y < destClipH; ++y) - { - double sx = srcX; - double sy = srcY; - - DestPixelType* dest = (DestPixelType*) (destPixels + destStride * y); - - for (int x = destClipW; --x >= 0;) - { - const int ix = ((int) sx) - srcClipX; - - if (((unsigned int) ix) < (unsigned int) srcClipWidth) - { - const int iy = ((int) sy) - srcClipY; - - if (((unsigned int) iy) < (unsigned int) srcClipHeight) - { - const SrcPixelType* const src = (const SrcPixelType*) (srcPixels + srcStride * iy + srcPixelStride * ix); - dest->blend (*src, alpha); - } - } - - ++dest; - sx += pixelDX; - sy += pixelDY; - } - - srcX += lineDX; - srcY += lineDY; - } - } - } - else - { - jassert (quality == Graphics::mediumResamplingQuality); // (only bilinear is implemented, so that's what you'll get here..) - - for (int y = 0; y < destClipH; ++y) - { - double sx = srcX - (srcClipWidth == destClipW ? 0.0 : 0.5); - double sy = srcY - (srcClipHeight == destClipH ? 0.0 : 0.5); - DestPixelType* dest = (DestPixelType*) (destPixels + destStride * y); - - for (int x = 0; x < destClipW; ++x) - { - const double fx = floor (sx); - const double fy = floor (sy); - const int ix = roundDoubleToInt (fx) - srcClipX; - const int iy = roundDoubleToInt (fy) - srcClipY; - - if (ix < srcClipWidth && iy < srcClipHeight) - { - PixelARGB p1 (0), p2 (0), p3 (0), p4 (0); - - const SrcPixelType* src = (const SrcPixelType*) (srcPixels + srcStride * iy + srcPixelStride * ix); - - if (iy >= 0) - { - if (ix >= 0) - p1.set (src[0]); - - if (((unsigned int) (ix + 1)) < (unsigned int) srcClipWidth) - p2.set (src[1]); - } - - if (((unsigned int) (iy + 1)) < (unsigned int) srcClipHeight) - { - src = (const SrcPixelType*) (((const uint8*) src) + srcStride); - - if (ix >= 0) - p3.set (src[0]); - - if (((unsigned int) (ix + 1)) < (unsigned int) srcClipWidth) - p4.set (src[1]); - } - - const int dx = roundDoubleToInt ((sx - fx) * 255.0); - p1.tween (p2, dx); - p3.tween (p4, dx); - p1.tween (p3, roundDoubleToInt ((sy - fy) * 255.0)); - - if (p1.getAlpha() > 0) - dest->blend (p1, alpha); - } - - ++dest; - sx += pixelDX; - sy += pixelDY; - } - - srcX += lineDX; - srcY += lineDY; - } - } - - destImage.releasePixelDataReadWrite (destPixels); - sourceImage.releasePixelDataReadOnly (srcPixels); -} - -template -static void renderAlphaMap (DestPixelType* destPixels, - int destStride, - SrcPixelType* srcPixels, - int srcStride, - const uint8* alphaValues, - const int lineStride, const int pixelStride, - int width, int height, - const int extraAlpha) throw() -{ - while (--height >= 0) - { - SrcPixelType* srcPix = srcPixels; - srcPixels = (SrcPixelType*) (((const uint8*) srcPixels) + srcStride); - - DestPixelType* destPix = destPixels; - destPixels = (DestPixelType*) (((uint8*) destPixels) + destStride); - - const uint8* alpha = alphaValues; - alphaValues += lineStride; - - if (extraAlpha < 0x100) - { - for (int i = width; --i >= 0;) - { - destPix++ ->blend (*srcPix++, (extraAlpha * *alpha) >> 8); - alpha += pixelStride; - } - } - else - { - for (int i = width; --i >= 0;) - { - destPix++ ->blend (*srcPix++, *alpha); - alpha += pixelStride; - } - } - } -} - -//============================================================================== -/*class ClippingPath +class TransformedImageFillEdgeTableRenderer { public: - ClippingPath (const Rectangle& r) throw() - : rectangles (r) + TransformedImageFillEdgeTableRenderer (const Image::BitmapData& destData_, + const Image::BitmapData& srcData_, + const AffineTransform& transform, + const int extraAlpha_, + const bool betterQuality_) throw() + : interpolator (transform), + destData (destData_), + srcData (srcData_), + extraAlpha (extraAlpha_ + 1), + betterQuality (betterQuality_), + pixelOffset (betterQuality_ ? 0.5f : 0.0f), + pixelOffsetInt (betterQuality_ ? -128 : 0), + maxX (srcData_.width - 1), + maxY (srcData_.height - 1) { } - ClippingPath (const ClippingPath& other) throw() - : rectangles (other.rectangles), mask (other.mask) + forcedinline void setEdgeTableYPos (const int newY) throw() + { + y = newY; + linePixels = (DestPixelType*) destData.getLinePointer (newY); + } + + forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) throw() + { + alphaLevel *= extraAlpha; + alphaLevel >>= 8; + + SrcPixelType p; + generate (&p, x, 1); + + linePixels[x].blend (p, alphaLevel); + } + + forcedinline void handleEdgeTableLine (const int x, int width, int alphaLevel) throw() + { + SrcPixelType* span = (SrcPixelType*) alloca (sizeof (SrcPixelType) * width); + generate (span, x, width); + + DestPixelType* dest = linePixels + x; + alphaLevel *= extraAlpha; + alphaLevel >>= 8; + + if (alphaLevel < 0xfe) + { + do + { + dest++ ->blend (*span++, alphaLevel); + } while (--width > 0); + } + else + { + do + { + dest++ ->blend (*span++); + } while (--width > 0); + } + } + +private: + //============================================================================== + void generate (PixelARGB* dest, const int x, int numPixels) throw() + { + this->interpolator.setStartOfLine (x + pixelOffset, y + pixelOffset, numPixels); + + do + { + int hiResX, hiResY; + this->interpolator.next (hiResX, hiResY); + hiResX += pixelOffsetInt; + hiResY += pixelOffsetInt; + + int loResX = hiResX >> 8; + int loResY = hiResY >> 8; + + if (betterQuality + && ((unsigned int) loResX) < (unsigned int) maxX + && ((unsigned int) loResY) < (unsigned int) maxY) + { + uint32 c[4] = { 256 * 128, 256 * 128, 256 * 128, 256 * 128 }; + hiResX &= 255; + hiResY &= 255; + + const uint8* src = this->srcData.getPixelPointer (loResX, loResY); + + uint32 weight = (256 - hiResX) * (256 - hiResY); + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + c[3] += weight * src[3]; + + weight = hiResX * (256 - hiResY); + c[0] += weight * src[4]; + c[1] += weight * src[5]; + c[2] += weight * src[6]; + c[3] += weight * src[7]; + + src += this->srcData.lineStride; + + weight = (256 - hiResX) * hiResY; + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + c[3] += weight * src[3]; + + weight = hiResX * hiResY; + c[0] += weight * src[4]; + c[1] += weight * src[5]; + c[2] += weight * src[6]; + c[3] += weight * src[7]; + + dest->setARGB ((uint8) (c[3] >> 16), + (uint8) (c[2] >> 16), + (uint8) (c[1] >> 16), + (uint8) (c[0] >> 16)); + } + else + { + // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + + dest->set (*(const PixelARGB*) this->srcData.getPixelPointer (loResX, loResY)); + } + + ++dest; + + } while (--numPixels > 0); + } + + void generate (PixelRGB* dest, const int x, int numPixels) throw() + { + this->interpolator.setStartOfLine (x + pixelOffset, y + pixelOffset, numPixels); + + do + { + int hiResX, hiResY; + this->interpolator.next (hiResX, hiResY); + hiResX += pixelOffsetInt; + hiResY += pixelOffsetInt; + int loResX = hiResX >> 8; + int loResY = hiResY >> 8; + + if (betterQuality + && ((unsigned int) loResX) < (unsigned int) maxX + && ((unsigned int) loResY) < (unsigned int) maxY) + { + uint32 c[3] = { 256 * 128, 256 * 128, 256 * 128 }; + hiResX &= 255; + hiResY &= 255; + + const uint8* src = this->srcData.getPixelPointer (loResX, loResY); + + unsigned int weight = (256 - hiResX) * (256 - hiResY); + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + + weight = hiResX * (256 - hiResY); + c[0] += weight * src[3]; + c[1] += weight * src[4]; + c[2] += weight * src[5]; + + src += this->srcData.lineStride; + + weight = (256 - hiResX) * hiResY; + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + + weight = hiResX * hiResY; + c[0] += weight * src[3]; + c[1] += weight * src[4]; + c[2] += weight * src[5]; + + dest->setARGB ((uint8) 255, + (uint8) (c[0] >> 16), + (uint8) (c[1] >> 16), + (uint8) (c[2] >> 16)); + } + else + { + // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + + dest->set (*(const PixelRGB*) this->srcData.getPixelPointer (loResX, loResY)); + } + + ++dest; + + } while (--numPixels > 0); + } + + void generate (PixelAlpha* dest, const int x, int numPixels) throw() + { + this->interpolator.setStartOfLine (x + pixelOffset, y + pixelOffset, numPixels); + + do + { + int hiResX, hiResY; + this->interpolator.next (hiResX, hiResY); + hiResX += pixelOffsetInt; + hiResY += pixelOffsetInt; + int loResX = hiResX >> 8; + int loResY = hiResY >> 8; + + if (betterQuality + && ((unsigned int) loResX) < (unsigned int) maxX + && ((unsigned int) loResY) < (unsigned int) maxY) + { + hiResX &= 255; + hiResY &= 255; + + uint32 c = 256 * 128; + const uint8* src = this->srcData.getPixelPointer (loResX, loResY); + c += src[0] * ((256 - hiResX) * (256 - hiResY)); + c += src[1] * (hiResX * (256 - hiResY)); + src += this->srcData.lineStride; + c += src[0] * ((256 - hiResX) * hiResY); + c += src[1] * (hiResX * hiResY); + + *((uint8*) dest) = (uint8) c; + } + else + { + // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + + *((uint8*) dest) = *(this->srcData.getPixelPointer (loResX, loResY)); + } + + ++dest; + + } while (--numPixels > 0); + } + + //============================================================================== + class TransformedImageSpanInterpolator + { + public: + TransformedImageSpanInterpolator (const AffineTransform& transform) throw() + : inverseTransform (transform.inverted()) + {} + + void setStartOfLine (float x, float y, const int numPixels) throw() + { + float x1 = x, y1 = y; + inverseTransform.transformPoint (x1, y1); + x += numPixels; + inverseTransform.transformPoint (x, y); + + xBresenham.set ((int) (x1 * 256.0f), (int) (x * 256.0f), numPixels); + yBresenham.set ((int) (y1 * 256.0f), (int) (y * 256.0f), numPixels); + } + + void next (int& x, int& y) throw() + { + x = xBresenham.n; + xBresenham.stepToNext(); + y = yBresenham.n; + yBresenham.stepToNext(); + } + + private: + class BresenhamInterpolator + { + public: + BresenhamInterpolator() throw() {} + + void set (const int n1, const int n2, const int numSteps_) throw() + { + numSteps = jmax (1, numSteps_); + step = (n2 - n1) / numSteps; + remainder = modulo = (n2 - n1) % numSteps; + n = n1; + + if (modulo <= 0) + { + modulo += numSteps; + remainder += numSteps; + --step; + } + + modulo -= numSteps; + } + + forcedinline void stepToNext() throw() + { + modulo += remainder; + n += step; + + if (modulo > 0) + { + modulo -= numSteps; + ++n; + } + } + + int n; + + private: + int numSteps, step, modulo, remainder; + }; + + const AffineTransform inverseTransform; + BresenhamInterpolator xBresenham, yBresenham; + + TransformedImageSpanInterpolator (const TransformedImageSpanInterpolator&); + const TransformedImageSpanInterpolator& operator= (const TransformedImageSpanInterpolator&); + }; + + //============================================================================== + TransformedImageSpanInterpolator interpolator; + const Image::BitmapData& destData; + const Image::BitmapData& srcData; + const int extraAlpha; + const bool betterQuality; + const float pixelOffset; + const int pixelOffsetInt, maxX, maxY; + int y; + DestPixelType* linePixels; + + TransformedImageFillEdgeTableRenderer (const TransformedImageFillEdgeTableRenderer&); + const TransformedImageFillEdgeTableRenderer& operator= (const TransformedImageFillEdgeTableRenderer&); +}; + +//============================================================================== +class LLGCSavedState +{ +public: + LLGCSavedState (const Rectangle& clip_, + const int xOffset_, const int yOffset_, + const Font& font_, const Colour& colour_, ColourGradient* const gradient_, + const Graphics::ResamplingQuality interpolationQuality_) throw() + : edgeTable (new EdgeTableHolder (EdgeTable (clip_))), + xOffset (xOffset_), + yOffset (yOffset_), + font (font_), + colour (colour_), + gradient (gradient_), + interpolationQuality (interpolationQuality_) { } - ~ClippingPath() throw() + LLGCSavedState (const LLGCSavedState& other) throw() + : edgeTable (other.edgeTable), + xOffset (other.xOffset), + yOffset (other.yOffset), + font (other.font), + colour (other.colour), + gradient (other.gradient), + interpolationQuality (other.interpolationQuality) { - delete mask; + if (gradient != 0) + gradient = new ColourGradient (*gradient); + } + + ~LLGCSavedState() throw() + { + delete gradient; } bool reduce (int x, int y, int w, int h) throw() { - return clip.clipTo (Rectangle (x, y, w, h)); + dupeEdgeTableIfMultiplyReferenced(); + edgeTable->edgeTable.clipToRectangle (Rectangle (x, y, w, h)); + return ! edgeTable->edgeTable.isEmpty(); + } + + bool reduce (const RectangleList& r) throw() + { + dupeEdgeTableIfMultiplyReferenced(); + RectangleList totalArea (edgeTable->edgeTable.getMaximumBounds()); + totalArea.subtract (r); + + for (RectangleList::Iterator i (totalArea); i.next();) + edgeTable->edgeTable.excludeRectangle (*i.getRectangle()); + + return ! edgeTable->edgeTable.isEmpty(); } bool exclude (int x, int y, int w, int h) throw() { - return clip.subtract (Rectangle (x, y, w, h)); + dupeEdgeTableIfMultiplyReferenced(); + edgeTable->edgeTable.excludeRectangle (Rectangle (x, y, w, h)); + return ! edgeTable->edgeTable.isEmpty(); } - bool reduce (const Path& p, const AffineTransform& transform) + bool reduce (const Path& p, const AffineTransform& transform) throw() { - float px, py, pw, ph; - p.getBoundsTransformed (transform, px, py, pw, ph); + dupeEdgeTableIfMultiplyReferenced(); + EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); + edgeTable->edgeTable.clipToEdgeTable (et); + return ! edgeTable->edgeTable.isEmpty(); + } - Rectangle pathBounds ((int) px - 1, (int) py - 1, (int) pw + 3, (int) ph + 3); + bool reduce (const Image& image, int x, int y) throw() + { + dupeEdgeTableIfMultiplyReferenced(); + edgeTable->edgeTable.clipToImageAlpha (image, x, y); + return ! edgeTable->edgeTable.isEmpty(); + } - if (clip.clipTo (pathBounds)) + bool reduce (const Image& image, const AffineTransform& transform) throw() + { + jassertfalse + return true; + } + + void fillEdgeTable (Image& image, EdgeTable& et, const bool replaceContents = false) throw() + { + et.clipToEdgeTable (edgeTable->edgeTable); + + Image::BitmapData destData (image, 0, 0, image.getWidth(), image.getHeight(), true); + + if (gradient != 0) { + jassert (! replaceContents); // that option is just for solid colours + + ColourGradient g2 (*gradient); + + const bool isIdentity = g2.transform.isOnlyTranslation(); + if (isIdentity) + { + // If our translation doesn't involve any distortion, we can speed it up.. + const float tx = g2.transform.getTranslationX() + xOffset; + const float ty = g2.transform.getTranslationY() + yOffset; + + g2.x1 += tx; + g2.x2 += tx; + g2.y1 += ty; + g2.y2 += ty; + } + else + { + g2.transform = g2.transform.translated ((float) xOffset, + (float) yOffset); + } + + int numLookupEntries; + PixelARGB* const lookupTable = g2.createLookupTable (numLookupEntries); + jassert (numLookupEntries > 0); + + if (image.getFormat() == Image::RGB) + { + jassert (destData.pixelStride == 3); + + if (g2.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + else + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + else + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + else if (image.getFormat() == Image::ARGB) + { + jassert (destData.pixelStride == 4); + + if (g2.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + else + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + else + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + else if (image.getFormat() == Image::SingleChannel) + { + jassert (destData.pixelStride == 4); + + if (g2.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + else + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + else + { + GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + + juce_free (lookupTable); + } + else + { + const PixelARGB fillColour (colour.getPixelARGB()); + + if (replaceContents) + { + if (image.getFormat() == Image::RGB) + { + jassert (destData.pixelStride == 3); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + else if (image.getFormat() == Image::ARGB) + { + jassert (destData.pixelStride == 4); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + else if (image.getFormat() == Image::SingleChannel) + { + jassert (destData.pixelStride == 1); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + } + else + { + if (image.getFormat() == Image::RGB) + { + jassert (destData.pixelStride == 3); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + else if (image.getFormat() == Image::ARGB) + { + jassert (destData.pixelStride == 4); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + else if (image.getFormat() == Image::SingleChannel) + { + jassert (destData.pixelStride == 1); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + } } } - bool reduce (Image& image, int x, int y) + void renderImage (Image& destImage, const Image& sourceImage, + int srcClipX, int srcClipY, int srcClipW, int srcClipH, + const AffineTransform& t) throw() { + if (t.isSingularity()) + return; + + const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); + const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); + + if (transform.isOnlyTranslation()) + { + // If our translation doesn't involve any distortion, just use a simple blit.. + const int tx = (int) (t.getTranslationX() * 256.0f); + const int ty = (int) (t.getTranslationY() * 256.0f); + + if ((! betterQuality) || ((tx | ty) & 224) == 0) + { + renderImage (destImage, sourceImage, (tx + 128) >> 8, (ty + 128) >> 8); + return; + } + } + + Path p; + p.addRectangle ((float) srcClipX, (float) srcClipY, (float) srcClipW, (float) srcClipH); + + EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); + et.clipToEdgeTable (edgeTable->edgeTable); + + Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true); + Image::BitmapData srcData (sourceImage, 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); + + const int extraAlpha = colour.getAlpha(); + + switch (sourceImage.getFormat()) + { + case Image::ARGB: + if (destImage.getFormat() == Image::ARGB) + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + else if (destImage.getFormat() == Image::RGB) + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + else + { + jassert (destImage.getFormat() == Image::SingleChannel) + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + break; + + case Image::RGB: + if (destImage.getFormat() == Image::ARGB) + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + else if (destImage.getFormat() == Image::RGB) + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + else + { + jassert (destImage.getFormat() == Image::SingleChannel) + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + break; + + default: + jassert (sourceImage.getFormat() == Image::SingleChannel); + + if (destImage.getFormat() == Image::ARGB) + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + else if (destImage.getFormat() == Image::RGB) + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + else + { + jassert (destImage.getFormat() == Image::SingleChannel) + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); + et.iterate (renderer); + } + break; + } } - bool reduce (Image& image, const AffineTransform& transform) + void renderImage (Image& destImage, const Image& sourceImage, int imageX, int imageY) throw() { + EdgeTable et (Rectangle (imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight()) + .getIntersection (Rectangle (0, 0, destImage.getWidth(), destImage.getHeight()))); + et.clipToEdgeTable (edgeTable->edgeTable); + + if (et.isEmpty()) + return; + + Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true); + Image::BitmapData srcData (sourceImage, 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); + srcData.data = srcData.getPixelPointer (-imageX, -imageY); + + const int alpha = colour.getAlpha(); + + switch (destImage.getFormat()) + { + case Image::RGB: + if (sourceImage.getFormat() == Image::RGB) + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + else if (sourceImage.getFormat() == Image::ARGB) + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + else + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + break; + + case Image::ARGB: + if (sourceImage.getFormat() == Image::RGB) + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + else if (sourceImage.getFormat() == Image::ARGB) + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + else + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + break; + + default: + jassert (destImage.getFormat() == Image::SingleChannel); + if (sourceImage.getFormat() == Image::RGB) + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + else if (sourceImage.getFormat() == Image::ARGB) + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + else + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); + et.iterate (renderer); + } + break; + } } - class MaskImage : public ReferenceCountedObject + class EdgeTableHolder : 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); - } + EdgeTableHolder (const EdgeTable& e) throw() + : edgeTable (e) + {} - ~MaskImage() throw() - { - delete image; - } - - Image* image; - int x, y; + EdgeTable edgeTable; }; - RectangleList clip; - ReferenceCountedObjectPtr mask; + ReferenceCountedObjectPtr edgeTable; + int xOffset, yOffset; + Font font; + Colour colour; + ColourGradient* gradient; + Graphics::ResamplingQuality interpolationQuality; private: + const LLGCSavedState& operator= (const LLGCSavedState&); + + void dupeEdgeTableIfMultiplyReferenced() throw() + { + if (edgeTable->getReferenceCount() > 1) + edgeTable = new EdgeTableHolder (edgeTable->edgeTable); + } }; -*/ + //============================================================================== LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image_) : image (image_), - xOffset (0), - yOffset (0), - stateStack (20), - colour (0xff000000), - gradient (0) + stateStack (20) { - clip = new RectangleList (Rectangle (0, 0, image_.getWidth(), image_.getHeight())); + currentState = new LLGCSavedState (Rectangle (0, 0, image_.getWidth(), image_.getHeight()), + 0, 0, Font(), Colours::black, 0, Graphics::mediumResamplingQuality); } LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() { - delete clip; - delete gradient; + delete currentState; } bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const @@ -1158,26 +1408,26 @@ bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const //============================================================================== void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y) { - xOffset += x; - yOffset += y; + currentState->xOffset += x; + currentState->yOffset += y; } bool LowLevelGraphicsSoftwareRenderer::reduceClipRegion (int x, int y, int w, int h) { - return clip->clipTo (Rectangle (x + xOffset, y + yOffset, w, h)); + return currentState->reduce (x + currentState->xOffset, y + currentState->yOffset, w, h); } bool LowLevelGraphicsSoftwareRenderer::reduceClipRegion (const RectangleList& clipRegion) { RectangleList temp (clipRegion); - temp.offsetAll (xOffset, yOffset); + temp.offsetAll (currentState->xOffset, currentState->yOffset); - return clip->clipTo (temp); + return currentState->reduce (temp); } void LowLevelGraphicsSoftwareRenderer::excludeClipRegion (int x, int y, int w, int h) { - clip->subtract (Rectangle (x + xOffset, y + yOffset, w, h)); + currentState->exclude (x + currentState->xOffset, y + currentState->yOffset, w, h); } void LowLevelGraphicsSoftwareRenderer::clipToPath (const Path& path, const AffineTransform& transform) @@ -1190,62 +1440,35 @@ void LowLevelGraphicsSoftwareRenderer::clipToImage (Image& image, int imageX, in bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (int x, int y, int w, int h) { - return clip->intersectsRectangle (Rectangle (x + xOffset, y + yOffset, w, h)); + return currentState->edgeTable->edgeTable.getMaximumBounds() + .intersects (Rectangle (x + currentState->xOffset, y + currentState->yOffset, w, h)); } const Rectangle LowLevelGraphicsSoftwareRenderer::getClipBounds() const { - return clip->getBounds().translated (-xOffset, -yOffset); + return currentState->edgeTable->edgeTable.getMaximumBounds().translated (-currentState->xOffset, -currentState->yOffset); } bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const { - return clip->isEmpty(); + return currentState->edgeTable->edgeTable.isEmpty(); } //============================================================================== -LowLevelGraphicsSoftwareRenderer::SavedState::SavedState (RectangleList* const clip_, - const int xOffset_, const int yOffset_, - const Font& font_, const Colour& colour_, ColourGradient* gradient_, - Graphics::ResamplingQuality interpolationQuality_) - : clip (clip_), - xOffset (xOffset_), - yOffset (yOffset_), - font (font_), - colour (colour_), - gradient (gradient_), - interpolationQuality (interpolationQuality_) -{ -} - -LowLevelGraphicsSoftwareRenderer::SavedState::~SavedState() -{ - delete clip; - delete gradient; -} - void LowLevelGraphicsSoftwareRenderer::saveState() { - stateStack.add (new SavedState (new RectangleList (*clip), xOffset, yOffset, - font, colour, gradient != 0 ? new ColourGradient (*gradient) : 0, - interpolationQuality)); + stateStack.add (new LLGCSavedState (*currentState)); } void LowLevelGraphicsSoftwareRenderer::restoreState() { - SavedState* const top = stateStack.getLast(); + LLGCSavedState* const top = stateStack.getLast(); if (top != 0) { - swapVariables (clip, top->clip); - xOffset = top->xOffset; - yOffset = top->yOffset; - font = top->font; - colour = top->colour; - swapVariables (gradient, top->gradient); - interpolationQuality = top->interpolationQuality; - - stateStack.removeLast(); + delete currentState; + currentState = top; + stateStack.removeLast (1, false); } else { @@ -1256,595 +1479,96 @@ void LowLevelGraphicsSoftwareRenderer::restoreState() //============================================================================== void LowLevelGraphicsSoftwareRenderer::setColour (const Colour& colour_) { - deleteAndZero (gradient); - colour = colour_; + deleteAndZero (currentState->gradient); + currentState->colour = colour_; } void LowLevelGraphicsSoftwareRenderer::setGradient (const ColourGradient& gradient_) { - delete gradient; - gradient = new ColourGradient (gradient_); + delete currentState->gradient; + currentState->gradient = new ColourGradient (gradient_); } void LowLevelGraphicsSoftwareRenderer::setOpacity (float opacity) { - colour = colour.withAlpha (opacity); + currentState->colour = currentState->colour.withAlpha (opacity); } void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality) { - interpolationQuality = quality; + currentState->interpolationQuality = quality; } //============================================================================== void LowLevelGraphicsSoftwareRenderer::fillRect (int x, int y, int w, int h, const bool replaceExistingContents) { - if (gradient != 0) + x += currentState->xOffset; + y += currentState->yOffset; + + if (Rectangle::intersectRectangles (x, y, w, h, 0, 0, image.getWidth(), image.getHeight())) { - if (replaceExistingContents && ! gradient->isOpaque()) - { - for (RectangleList::Iterator i (*clip); i.next();) - clippedFillRectWithColour (*i.getRectangle(), x + xOffset, y + yOffset, w, h, Colours::transparentBlack, true); - } - - Path p; - p.addRectangle ((float) x, (float) y, (float) w, (float) h); - fillPath (p, AffineTransform::identity); + EdgeTable et (Rectangle (x, y, w, h)); + currentState->fillEdgeTable (image, et, replaceExistingContents); } - else - { - x += xOffset; - y += yOffset; - - for (RectangleList::Iterator i (*clip); i.next();) - clippedFillRectWithColour (*i.getRectangle(), x, y, w, h, colour, replaceExistingContents); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedFillRectWithColour (const Rectangle& clipRect, - int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents) -{ - if (clipRect.intersectRectangle (x, y, w, h)) - { - int stride, pixelStride; - uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); - - if (image.getFormat() == Image::RGB) - { - if (replaceExistingContents) - replaceRectRGB (pixels, w, h, stride, colour); - else - blendRectRGB (pixels, w, h, stride, colour); - } - else if (image.getFormat() == Image::ARGB) - { - if (replaceExistingContents) - replaceRectARGB (pixels, w, h, stride, colour); - else - blendRectARGB (pixels, w, h, stride, colour); - } - else - { - jassertfalse // not done! - } - - image.releasePixelDataReadWrite (pixels); - } -} - -//============================================================================== -bool LowLevelGraphicsSoftwareRenderer::getPathBounds (int clipX, int clipY, int clipW, int clipH, - const Path& path, const AffineTransform& transform, - int& x, int& y, int& w, int& h) const -{ - float tx, ty, tw, th; - path.getBoundsTransformed (transform, tx, ty, tw, th); - - x = roundDoubleToInt (tx) - 1; - y = roundDoubleToInt (ty) - 1; - w = roundDoubleToInt (tw) + 2; - h = roundDoubleToInt (th) + 2; - - // seems like this operation is using some crazy out-of-range numbers.. - ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, w, h); - - return Rectangle::intersectRectangles (x, y, w, h, clipX, clipY, clipW, clipH); } void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform) { - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); + EdgeTable et (currentState->edgeTable->edgeTable.getMaximumBounds(), + path, transform.translated ((float) currentState->xOffset, + (float) currentState->yOffset)); - clippedFillPath (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - path, transform); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedFillPath (int clipX, int clipY, int clipW, int clipH, const Path& path, - 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 (Rectangle (0, 0, cw, ch), path, transform.translated ((float) -cx, (float) -cy)); - - int stride, pixelStride; - uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (cx, cy, cw, ch, stride, pixelStride); - - if (gradient != 0) - { - ColourGradient g2 (*gradient); - - const bool isIdentity = g2.transform.isIdentity(); - if (isIdentity) - { - g2.x1 += xOffset - cx; - g2.x2 += xOffset - cx; - g2.y1 += yOffset - cy; - g2.y2 += yOffset - cy; - } - else - { - g2.transform = g2.transform.translated ((float) (xOffset - cx), - (float) (yOffset - cy)); - } - - int numLookupEntries; - PixelARGB* const lookupTable = g2.createLookupTable (numLookupEntries); - jassert (numLookupEntries > 0); - - if (image.getFormat() == Image::RGB) - { - jassert (pixelStride == 3); - - if (g2.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer); - } - } - else if (image.getFormat() == Image::ARGB) - { - jassert (pixelStride == 4); - - if (g2.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); - edgeTable.iterate (renderer); - } - } - else if (image.getFormat() == Image::SingleChannel) - { - jassertfalse // not done! - } - - juce_free (lookupTable); - } - else - { - if (image.getFormat() == Image::RGB) - { - jassert (pixelStride == 3); - SolidColourEdgeTableRenderer renderer (pixels, stride, colour); - edgeTable.iterate (renderer); - } - else if (image.getFormat() == Image::ARGB) - { - jassert (pixelStride == 4); - SolidColourEdgeTableRenderer renderer (pixels, stride, colour); - edgeTable.iterate (renderer); - } - else if (image.getFormat() == Image::SingleChannel) - { - jassert (pixelStride == 1); - AlphaBitmapRenderer renderer (pixels, stride); - edgeTable.iterate (renderer); - } - } - - image.releasePixelDataReadWrite (pixels); - } + currentState->fillEdgeTable (image, et); } void LowLevelGraphicsSoftwareRenderer::fillPathWithImage (const Path& path, const AffineTransform& transform, const Image& sourceImage, int imageX, int imageY) { - imageX += xOffset; - imageY += yOffset; + imageX += currentState->xOffset; + imageY += currentState->yOffset; - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedFillPathWithImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - path, transform, sourceImage, imageX, imageY, - colour.getFloatAlpha()); - } + saveState(); + currentState->reduce (path, transform.translated ((float) currentState->xOffset, (float) currentState->yOffset)); + currentState->renderImage (image, sourceImage, imageX, imageY); + restoreState(); } -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) -{ - if (Rectangle::intersectRectangles (x, y, w, h, imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight())) - { - EdgeTable edgeTable (Rectangle (0, 0, w, h), path, - transform.translated ((float) (xOffset - x), (float) (yOffset - y))); - - int stride, pixelStride; - uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); - - int srcStride, srcPixelStride; - const uint8* const srcPix = (const uint8*) sourceImage.lockPixelDataReadOnly (x - imageX, y - imageY, w, h, srcStride, srcPixelStride); - - const int alpha = jlimit (0, 255, roundDoubleToInt (opacity * 255.0f)); - - if (image.getFormat() == Image::RGB) - { - if (sourceImage.getFormat() == Image::RGB) - { - ImageFillEdgeTableRenderer renderer (pixels, stride, - srcPix, srcStride, - alpha, (PixelRGB*) 0); - edgeTable.iterate (renderer); - } - else if (sourceImage.getFormat() == Image::ARGB) - { - ImageFillEdgeTableRenderer renderer (pixels, stride, - srcPix, srcStride, - alpha, (PixelARGB*) 0); - edgeTable.iterate (renderer); - } - else - { - jassertfalse // not done! - } - } - else if (image.getFormat() == Image::ARGB) - { - if (sourceImage.getFormat() == Image::RGB) - { - ImageFillEdgeTableRenderer renderer (pixels, stride, - srcPix, srcStride, - alpha, (PixelRGB*) 0); - edgeTable.iterate (renderer); - } - else if (sourceImage.getFormat() == Image::ARGB) - { - ImageFillEdgeTableRenderer renderer (pixels, stride, - srcPix, srcStride, - alpha, (PixelARGB*) 0); - edgeTable.iterate (renderer); - } - else - { - jassertfalse // not done! - } - } - else - { - jassertfalse // not done! - } - - sourceImage.releasePixelDataReadOnly (srcPix); - image.releasePixelDataReadWrite (pixels); - } -} - -//============================================================================== void LowLevelGraphicsSoftwareRenderer::fillAlphaChannel (const Image& clipImage, int x, int y) { - x += xOffset; - y += yOffset; + x += currentState->xOffset; + y += currentState->yOffset; - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); + Rectangle maxBounds (currentState->edgeTable->edgeTable.getMaximumBounds()); + EdgeTable et (maxBounds.getIntersection (Rectangle (x, y, clipImage.getWidth(), clipImage.getHeight()))); + et.clipToImageAlpha (clipImage, x, y); - clippedFillAlphaChannel (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - clipImage, x, y); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannel (int clipX, int clipY, int clipW, int clipH, const Image& clipImage, int x, int y) -{ - if (gradient != 0) - { - if (Rectangle::intersectRectangles (clipX, clipY, clipW, clipH, x, y, clipImage.getWidth(), clipImage.getHeight())) - { - ColourGradient g2 (*gradient); - g2.x1 += xOffset - clipX; - g2.x2 += xOffset - clipX; - g2.y1 += yOffset - clipY; - g2.y2 += yOffset - clipY; - - Image temp (g2.isOpaque() ? Image::RGB : Image::ARGB, clipW, clipH, true); - LowLevelGraphicsSoftwareRenderer tempG (temp); - tempG.setGradient (g2); - tempG.fillRect (0, 0, clipW, clipH, false); - - clippedFillAlphaChannelWithImage (clipX, clipY, clipW, clipH, - clipImage, x, y, - temp, clipX, clipY, 1.0f); - } - } - else - { - int w = clipImage.getWidth(); - int h = clipImage.getHeight(); - int sx = 0; - int sy = 0; - - if (x < clipX) - { - sx = clipX - x; - w -= clipX - x; - x = clipX; - } - - if (y < clipY) - { - sy = clipY - y; - h -= clipY - y; - y = clipY; - } - - if (x + w > clipX + clipW) - w = clipX + clipW - x; - - if (y + h > clipY + clipH) - h = clipY + clipH - y; - - if (w > 0 && h > 0) - { - int stride, alphaStride, pixelStride; - uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); - - const uint8* const alphaValues - = clipImage.lockPixelDataReadOnly (sx, sy, w, h, alphaStride, pixelStride); - -#if JUCE_BIG_ENDIAN - const uint8* const alphas = alphaValues; -#else - const uint8* const alphas = alphaValues + (clipImage.getFormat() == Image::ARGB ? 3 : 0); -#endif - - if (image.getFormat() == Image::RGB) - { - blendAlphaMapRGB (pixels, stride, - alphas, w, h, - pixelStride, alphaStride, - colour); - } - else if (image.getFormat() == Image::ARGB) - { - blendAlphaMapARGB (pixels, stride, - alphas, w, h, - pixelStride, alphaStride, - colour); - } - else - { - jassertfalse // not done! - } - - clipImage.releasePixelDataReadOnly (alphaValues); - image.releasePixelDataReadWrite (pixels); - } - } + currentState->fillEdgeTable (image, et); } void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, const Image& fillerImage, int fillerImageX, int fillerImageY) { - alphaImageX += xOffset; - alphaImageY += yOffset; + alphaImageX += currentState->xOffset; + alphaImageY += currentState->yOffset; + fillerImageX += currentState->xOffset; + fillerImageY += currentState->yOffset; - fillerImageX += xOffset; - fillerImageY += yOffset; - - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedFillAlphaChannelWithImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - alphaImage, alphaImageX, alphaImageY, - fillerImage, fillerImageX, fillerImageY, - colour.getFloatAlpha()); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannelWithImage (int x, int y, int w, int h, const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, float opacity) -{ - if (Rectangle::intersectRectangles (x, y, w, h, alphaImageX, alphaImageY, alphaImage.getWidth(), alphaImage.getHeight()) - && Rectangle::intersectRectangles (x, y, w, h, fillerImageX, fillerImageY, fillerImage.getWidth(), fillerImage.getHeight())) - { - int dstStride, dstPixStride; - uint8* const dstPix = image.lockPixelDataReadWrite (x, y, w, h, dstStride, dstPixStride); - - int srcStride, srcPixStride; - const uint8* const srcPix = fillerImage.lockPixelDataReadOnly (x - fillerImageX, y - fillerImageY, w, h, srcStride, srcPixStride); - - int maskStride, maskPixStride; - const uint8* const alpha - = alphaImage.lockPixelDataReadOnly (x - alphaImageX, y - alphaImageY, w, h, maskStride, maskPixStride); - -#if JUCE_BIG_ENDIAN - const uint8* const alphaValues = alpha; -#else - const uint8* const alphaValues = alpha + (alphaImage.getFormat() == Image::ARGB ? 3 : 0); -#endif - - const int extraAlpha = jlimit (0, 0x100, roundDoubleToInt (opacity * 256.0f)); - - if (image.getFormat() == Image::RGB) - { - if (fillerImage.getFormat() == Image::RGB) - { - renderAlphaMap ((PixelRGB*) dstPix, dstStride, (const PixelRGB*) srcPix, srcStride, alphaValues, maskStride, maskPixStride, w, h, extraAlpha); - } - else if (fillerImage.getFormat() == Image::ARGB) - { - renderAlphaMap ((PixelRGB*) dstPix, dstStride, (const PixelARGB*) srcPix, srcStride, alphaValues, maskStride, maskPixStride, w, h, extraAlpha); - } - else - { - jassertfalse // not done! - } - } - else if (image.getFormat() == Image::ARGB) - { - if (fillerImage.getFormat() == Image::RGB) - { - renderAlphaMap ((PixelARGB*) dstPix, dstStride, (const PixelRGB*) srcPix, srcStride, alphaValues, maskStride, maskPixStride, w, h, extraAlpha); - } - else if (fillerImage.getFormat() == Image::ARGB) - { - renderAlphaMap ((PixelARGB*) dstPix, dstStride, (const PixelARGB*) srcPix, srcStride, alphaValues, maskStride, maskPixStride, w, h, extraAlpha); - } - else - { - jassertfalse // not done! - } - } - else - { - jassertfalse // not done! - } - - alphaImage.releasePixelDataReadOnly (alphaValues); - fillerImage.releasePixelDataReadOnly (srcPix); - image.releasePixelDataReadWrite (dstPix); - } + saveState(); + currentState->reduce (alphaImage, alphaImageX, alphaImageY); + currentState->renderImage (image, fillerImage, fillerImageX, fillerImageY); + restoreState(); } //============================================================================== void LowLevelGraphicsSoftwareRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy) { - dx += xOffset; - dy += yOffset; + dx += currentState->xOffset; + dy += currentState->yOffset; - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedBlendImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - sourceImage, dx, dy, dw, dh, sx, sy); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedBlendImage (int clipX, int clipY, int clipW, int clipH, - const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy) -{ - if (dx < clipX) - { - sx += clipX - dx; - dw -= clipX - dx; - dx = clipX; - } - - if (dy < clipY) - { - sy += clipY - dy; - dh -= clipY - dy; - dy = clipY; - } - - if (dx + dw > clipX + clipW) - dw = clipX + clipW - dx; - - if (dy + dh > clipY + clipH) - dh = clipY + clipH - dy; - - if (dw <= 0 || dh <= 0) - return; - - const uint8 alpha = (uint8) colour.getAlpha(); - - if (alpha == 0) - return; - - int dstStride, dstPixelStride; - uint8* const dstPixels = image.lockPixelDataReadWrite (dx, dy, dw, dh, dstStride, dstPixelStride); - - int srcStride, srcPixelStride; - const uint8* const srcPixels = sourceImage.lockPixelDataReadOnly (sx, sy, dw, dh, srcStride, srcPixelStride); - - if (image.getFormat() == Image::ARGB) - { - if (sourceImage.getFormat() == Image::ARGB) - { - overlayImage ((PixelARGB*) dstPixels, dstStride, - (PixelARGB*) srcPixels, srcStride, - dw, dh, alpha); - } - else if (sourceImage.getFormat() == Image::RGB) - { - overlayImage ((PixelARGB*) dstPixels, dstStride, - (PixelRGB*) srcPixels, srcStride, - dw, dh, alpha); - } - else - { - jassertfalse - } - } - else if (image.getFormat() == Image::RGB) - { - if (sourceImage.getFormat() == Image::ARGB) - { - overlayImage ((PixelRGB*) dstPixels, dstStride, - (PixelARGB*) srcPixels, srcStride, - dw, dh, alpha); - } - else if (sourceImage.getFormat() == Image::RGB) - { - overlayImage ((PixelRGB*) dstPixels, dstStride, - (PixelRGB*) srcPixels, srcStride, - dw, dh, alpha); - } - else - { - jassertfalse - } - } - else - { - jassertfalse - } - - image.releasePixelDataReadWrite (dstPixels); - sourceImage.releasePixelDataReadOnly (srcPixels); + saveState(); + currentState->reduce (dx, dy, dw, dh); + currentState->renderImage (image, sourceImage, dx - sx, dy - sy); + restoreState(); } //============================================================================== @@ -1852,476 +1576,48 @@ void LowLevelGraphicsSoftwareRenderer::blendImageWarping (const Image& sourceIma int srcClipX, int srcClipY, int srcClipW, int srcClipH, const AffineTransform& t) { - const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); - - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedBlendImageWarping (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - sourceImage, srcClipX, srcClipY, srcClipW, srcClipH, - transform); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, int destClipY, int destClipW, int destClipH, - const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform) -{ - if ((! colour.isTransparent()) && destClipW > 0 && destClipH > 0 && ! transform.isSingularity()) - { - Rectangle::intersectRectangles (srcClipX, srcClipY, srcClipW, srcClipH, - 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); - - if (srcClipW <= 0 || srcClipH <= 0) - return; - - jassert (srcClipX >= 0 && srcClipY >= 0); - - Path imageBounds; - imageBounds.addRectangle ((float) srcClipX, (float) srcClipY, (float) srcClipW, (float) srcClipH); - imageBounds.applyTransform (transform); - float imX, imY, imW, imH; - imageBounds.getBounds (imX, imY, imW, imH); - - if (Rectangle::intersectRectangles (destClipX, destClipY, destClipW, destClipH, - (int) floorf (imX), - (int) floorf (imY), - 1 + roundDoubleToInt (imW), - 1 + roundDoubleToInt (imH))) - { - const uint8 alpha = (uint8) colour.getAlpha(); - - float srcX1 = (float) destClipX; - float srcY1 = (float) destClipY; - float srcX2 = (float) (destClipX + destClipW); - float srcY2 = srcY1; - float srcX3 = srcX1; - float srcY3 = (float) (destClipY + destClipH); - - AffineTransform inverse (transform.inverted()); - inverse.transformPoint (srcX1, srcY1); - inverse.transformPoint (srcX2, srcY2); - inverse.transformPoint (srcX3, srcY3); - - const double lineDX = (double) (srcX3 - srcX1) / destClipH; - const double lineDY = (double) (srcY3 - srcY1) / destClipH; - const double pixelDX = (double) (srcX2 - srcX1) / destClipW; - const double pixelDY = (double) (srcY2 - srcY1) / destClipW; - - if (image.getFormat() == Image::ARGB) - { - if (sourceImage.getFormat() == Image::ARGB) - { - transformedImageRender (image, sourceImage, - destClipX, destClipY, destClipW, destClipH, - srcClipX, srcClipY, srcClipW, srcClipH, - srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, interpolationQuality, (PixelARGB*)0, (PixelARGB*)0); - } - else if (sourceImage.getFormat() == Image::RGB) - { - transformedImageRender (image, sourceImage, - destClipX, destClipY, destClipW, destClipH, - srcClipX, srcClipY, srcClipW, srcClipH, - srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, interpolationQuality, (PixelARGB*)0, (PixelRGB*)0); - } - else - { - jassertfalse - } - } - else if (image.getFormat() == Image::RGB) - { - if (sourceImage.getFormat() == Image::ARGB) - { - transformedImageRender (image, sourceImage, - destClipX, destClipY, destClipW, destClipH, - srcClipX, srcClipY, srcClipW, srcClipH, - srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, interpolationQuality, (PixelRGB*)0, (PixelARGB*)0); - } - else if (sourceImage.getFormat() == Image::RGB) - { - transformedImageRender (image, sourceImage, - destClipX, destClipY, destClipW, destClipH, - srcClipX, srcClipY, srcClipW, srcClipH, - srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, - alpha, interpolationQuality, (PixelRGB*)0, (PixelRGB*)0); - } - else - { - jassertfalse - } - } - else - { - jassertfalse - } - } - } + currentState->renderImage (image, sourceImage, srcClipX, srcClipY, srcClipW, srcClipH, t); } //============================================================================== void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2, double y2) { - x1 += xOffset; - y1 += yOffset; - x2 += xOffset; - y2 += yOffset; - - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedDrawLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - x1, y1, x2, y2); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2) -{ - if (clipW > 0 && clipH > 0) - { - if (x1 == x2) - { - if (y2 < y1) - swapVariables (y1, y2); - - clippedDrawVerticalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (x1), y1, y2); - } - else if (y1 == y2) - { - if (x2 < x1) - swapVariables (x1, x2); - - clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (y1), x1, x2); - } - else - { - double gradient = (y2 - y1) / (x2 - x1); - - if (fabs (gradient) > 1.0) - { - gradient = 1.0 / gradient; - - int y = roundDoubleToInt (y1); - const int startY = y; - int endY = roundDoubleToInt (y2); - - if (y > endY) - swapVariables (y, endY); - - while (y < endY) - { - const double x = x1 + gradient * (y - startY); - clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, y, x, x + 1.0); - ++y; - } - } - else - { - int x = roundDoubleToInt (x1); - const int startX = x; - int endX = roundDoubleToInt (x2); - - if (x > endX) - swapVariables (x, endX); - - while (x < endX) - { - const double y = y1 + gradient * (x - startX); - clippedDrawVerticalLine (clipX, clipY, clipW, clipH, x, y, y + 1.0); - ++x; - } - } - } - } + Path p; + p.addLineSegment ((float) x1, (float) y1, (float) x2, (float) y2, 1.0f); + fillPath (p, AffineTransform::identity); } void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, double top, double bottom) { - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedDrawVerticalLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - x + xOffset, top + yOffset, bottom + yOffset); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, - const int x, double top, double bottom) -{ - jassert (top <= bottom); - - if (((unsigned int) (x - clipX)) < (unsigned int) clipW - && top < clipY + clipH - && bottom > clipY - && clipW > 0) - { - if (top < clipY) - top = clipY; - - if (bottom > clipY + clipH) - bottom = clipY + clipH; - - if (bottom > top) - drawVertical (x, top, bottom); - } + EdgeTable et ((float) (x + currentState->xOffset), (float) (top + currentState->yOffset), 1.0f, (float) (bottom - top)); + currentState->fillEdgeTable (image, et); } void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double left, double right) { - for (RectangleList::Iterator i (*clip); i.next();) - { - const Rectangle& r = *i.getRectangle(); - - clippedDrawHorizontalLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), - y + yOffset, left + xOffset, right + xOffset); - } -} - -void LowLevelGraphicsSoftwareRenderer::clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, - const int y, double left, double right) -{ - jassert (left <= right); - - if (((unsigned int) (y - clipY)) < (unsigned int) clipH - && left < clipX + clipW - && right > clipX - && clipW > 0) - { - if (left < clipX) - left = clipX; - - if (right > clipX + clipW) - right = clipX + clipW; - - if (right > left) - drawHorizontal (y, left, right); - } -} - -void LowLevelGraphicsSoftwareRenderer::drawVertical (const int x, - const double top, - const double bottom) -{ - int wholeStart = (int) top; - const int wholeEnd = (int) bottom; - - const int lastAlpha = roundDoubleToInt (255.0 * (bottom - wholeEnd)); - const int totalPixels = (wholeEnd - wholeStart) + (lastAlpha > 0 ? 1 : 0); - - if (totalPixels <= 0) - return; - - int lineStride, dstPixelStride; - uint8* const dstPixels = image.lockPixelDataReadWrite (x, wholeStart, 1, totalPixels, lineStride, dstPixelStride); - uint8* dest = dstPixels; - - PixelARGB colour (this->colour.getPixelARGB()); - - if (wholeEnd == wholeStart) - { - if (image.getFormat() == Image::ARGB) - ((PixelARGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (bottom - top))); - else if (image.getFormat() == Image::RGB) - ((PixelRGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (bottom - top))); - else - { - jassertfalse - } - } - else - { - if (image.getFormat() == Image::ARGB) - { - ((PixelARGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (1.0 - (top - wholeStart)))); - ++wholeStart; - dest += lineStride; - - if (colour.getAlpha() == 0xff) - { - while (wholeEnd > wholeStart) - { - ((PixelARGB*) dest)->set (colour); - ++wholeStart; - dest += lineStride; - } - } - else - { - while (wholeEnd > wholeStart) - { - ((PixelARGB*) dest)->blend (colour); - ++wholeStart; - dest += lineStride; - } - } - - if (lastAlpha > 0) - { - ((PixelARGB*) dest)->blend (colour, lastAlpha); - } - } - else if (image.getFormat() == Image::RGB) - { - ((PixelRGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (1.0 - (top - wholeStart)))); - ++wholeStart; - dest += lineStride; - - if (colour.getAlpha() == 0xff) - { - while (wholeEnd > wholeStart) - { - ((PixelRGB*) dest)->set (colour); - ++wholeStart; - dest += lineStride; - } - } - else - { - while (wholeEnd > wholeStart) - { - ((PixelRGB*) dest)->blend (colour); - ++wholeStart; - dest += lineStride; - } - } - - if (lastAlpha > 0) - { - ((PixelRGB*) dest)->blend (colour, lastAlpha); - } - } - else - { - jassertfalse - } - } - - image.releasePixelDataReadWrite (dstPixels); -} - -void LowLevelGraphicsSoftwareRenderer::drawHorizontal (const int y, - const double top, - const double bottom) -{ - int wholeStart = (int) top; - const int wholeEnd = (int) bottom; - - const int lastAlpha = roundDoubleToInt (255.0 * (bottom - wholeEnd)); - const int totalPixels = (wholeEnd - wholeStart) + (lastAlpha > 0 ? 1 : 0); - - if (totalPixels <= 0) - return; - - int lineStride, dstPixelStride; - uint8* const dstPixels = image.lockPixelDataReadWrite (wholeStart, y, totalPixels, 1, lineStride, dstPixelStride); - uint8* dest = dstPixels; - - PixelARGB colour (this->colour.getPixelARGB()); - - if (wholeEnd == wholeStart) - { - if (image.getFormat() == Image::ARGB) - ((PixelARGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (bottom - top))); - else if (image.getFormat() == Image::RGB) - ((PixelRGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (bottom - top))); - else - { - jassertfalse - } - } - else - { - if (image.getFormat() == Image::ARGB) - { - ((PixelARGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (1.0 - (top - wholeStart)))); - dest += dstPixelStride; - ++wholeStart; - - if (colour.getAlpha() == 0xff) - { - while (wholeEnd > wholeStart) - { - ((PixelARGB*) dest)->set (colour); - dest += dstPixelStride; - ++wholeStart; - } - } - else - { - while (wholeEnd > wholeStart) - { - ((PixelARGB*) dest)->blend (colour); - dest += dstPixelStride; - ++wholeStart; - } - } - - if (lastAlpha > 0) - { - ((PixelARGB*) dest)->blend (colour, lastAlpha); - } - } - else if (image.getFormat() == Image::RGB) - { - ((PixelRGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (1.0 - (top - wholeStart)))); - dest += dstPixelStride; - ++wholeStart; - - if (colour.getAlpha() == 0xff) - { - while (wholeEnd > wholeStart) - { - ((PixelRGB*) dest)->set (colour); - dest += dstPixelStride; - ++wholeStart; - } - } - else - { - while (wholeEnd > wholeStart) - { - ((PixelRGB*) dest)->blend (colour); - dest += dstPixelStride; - ++wholeStart; - } - } - - if (lastAlpha > 0) - { - ((PixelRGB*) dest)->blend (colour, lastAlpha); - } - } - else - { - jassertfalse - } - } - - image.releasePixelDataReadWrite (dstPixels); + EdgeTable et ((float) (left + currentState->xOffset), (float) (y + currentState->yOffset), + (float) (right - left), 1.0f); + currentState->fillEdgeTable (image, et); } //============================================================================== void LowLevelGraphicsSoftwareRenderer::setFont (const Font& newFont) { - font = newFont; + currentState->font = newFont; } void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, float x, float y) { - font.renderGlyphIndirectly (*this, glyphNumber, x, y); + currentState->font.renderGlyphIndirectly (*this, glyphNumber, x, y); } void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform) { - font.renderGlyphIndirectly (*this, glyphNumber, transform); + currentState->font.renderGlyphIndirectly (*this, glyphNumber, transform); } +#if JUCE_MSVC + #pragma warning (pop) +#endif END_JUCE_NAMESPACE diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h index 271a4eff1f..7aedd21424 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h @@ -27,7 +27,7 @@ #define __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ #include "juce_LowLevelGraphicsContext.h" - +class LLGCSavedState; //============================================================================== /** @@ -98,50 +98,19 @@ public: void drawGlyph (int glyphNumber, float x, float y); void drawGlyph (int glyphNumber, const AffineTransform& transform); - //============================================================================== - RectangleList* getRawClipRegion() throw() { return clip; } - //============================================================================== juce_UseDebuggingNewOperator protected: //============================================================================== Image& image; - RectangleList* clip; - int xOffset, yOffset; - Font font; - Colour colour; - ColourGradient* gradient; - Graphics::ResamplingQuality interpolationQuality; - struct SavedState - { - SavedState (RectangleList* const clip, const int xOffset, const int yOffset, - const Font& font, const Colour& colour, ColourGradient* gradient, - Graphics::ResamplingQuality interpolationQuality); - ~SavedState(); + LLGCSavedState* currentState; + OwnedArray stateStack; - RectangleList* clip; - const int xOffset, yOffset; - Font font; - Colour colour; - ColourGradient* gradient; - Graphics::ResamplingQuality interpolationQuality; - - private: - SavedState (const SavedState&); - const SavedState& operator= (const SavedState&); - }; - - OwnedArray stateStack; - - void drawVertical (const int x, const double top, const double bottom); +/* void drawVertical (const int x, const double top, const double bottom); void drawHorizontal (const int y, const double top, const double bottom); - bool getPathBounds (int clipX, int clipY, int clipW, int clipH, - const Path& path, const AffineTransform& transform, - int& x, int& y, int& w, int& h) const; - 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); @@ -164,7 +133,7 @@ protected: void clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2); void clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom); - void clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom); + void clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom);*/ LowLevelGraphicsSoftwareRenderer (const LowLevelGraphicsSoftwareRenderer& other); const LowLevelGraphicsSoftwareRenderer& operator= (const LowLevelGraphicsSoftwareRenderer&); diff --git a/src/gui/graphics/effects/juce_DropShadowEffect.cpp b/src/gui/graphics/effects/juce_DropShadowEffect.cpp index 5fb65f2c23..adfe9b7b35 100644 --- a/src/gui/graphics/effects/juce_DropShadowEffect.cpp +++ b/src/gui/graphics/effects/juce_DropShadowEffect.cpp @@ -65,12 +65,10 @@ void DropShadowEffect::applyEffect (Image& image, Graphics& g) const int w = image.getWidth(); const int h = image.getHeight(); - int lineStride, pixelStride; - const PixelARGB* srcPixels = (const PixelARGB*) image.lockPixelDataReadOnly (0, 0, image.getWidth(), image.getHeight(), lineStride, pixelStride); - Image shadowImage (Image::SingleChannel, w, h, false); - int destStride, destPixelStride; - uint8* const shadowChannel = (uint8*) shadowImage.lockPixelDataReadWrite (0, 0, w, h, destStride, destPixelStride); + + const Image::BitmapData srcData (image, 0, 0, w, h); + const Image::BitmapData destData (shadowImage, 0, 0, w, h, true); const int filter = roundFloatToInt (63.0f / radius); const int radiusMinus1 = roundFloatToInt ((radius - 1.0f) * 63.0f); @@ -79,23 +77,23 @@ void DropShadowEffect::applyEffect (Image& image, Graphics& g) { int shadowAlpha = 0; - const PixelARGB* src = srcPixels + x; - uint8* shadowPix = shadowChannel + x; + const PixelARGB* src = ((const PixelARGB*) srcData.data) + x; + uint8* shadowPix = destData.data + x; for (int y = h; --y >= 0;) { shadowAlpha = ((shadowAlpha * radiusMinus1 + (src->getAlpha() << 6)) * filter) >> 12; *shadowPix = (uint8) shadowAlpha; - src = (const PixelARGB*) (((const uint8*) src) + lineStride); - shadowPix += destStride; + src = (const PixelARGB*) (((const uint8*) src) + srcData.lineStride); + shadowPix += destData.lineStride; } } for (int y = h; --y >= 0;) { int shadowAlpha = 0; - uint8* shadowPix = shadowChannel + y * destStride; + uint8* shadowPix = destData.getLinePointer (y); for (int x = w; --x >= 0;) { @@ -104,9 +102,6 @@ void DropShadowEffect::applyEffect (Image& image, Graphics& g) } } - image.releasePixelDataReadOnly (srcPixels); - shadowImage.releasePixelDataReadWrite (shadowChannel); - g.setColour (Colours::black.withAlpha (opacity)); g.drawImageAt (&shadowImage, offsetX, offsetY, true); diff --git a/src/gui/graphics/geometry/juce_Path.cpp b/src/gui/graphics/geometry/juce_Path.cpp index df5c31dd23..cbb74264f6 100644 --- a/src/gui/graphics/geometry/juce_Path.cpp +++ b/src/gui/graphics/geometry/juce_Path.cpp @@ -1553,14 +1553,14 @@ bool Path::Iterator::next() class MaskBitmapRenderer { public: - MaskBitmapRenderer (uint8* const data_, const int stride_) throw() - : data (data_), stride (stride_) + MaskBitmapRenderer (const Image::BitmapData& destData_) throw() + : destData (destData_) { } forcedinline void setEdgeTableYPos (const int y) throw() { - lineStart = data + (stride * y); + lineStart = destData.getLinePointer (y); } forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw() @@ -1577,8 +1577,7 @@ public: } private: - uint8* const data; - const int stride; + const Image::BitmapData& destData; uint8* lineStart; MaskBitmapRenderer (const MaskBitmapRenderer&); @@ -1606,14 +1605,12 @@ Image* Path::createMaskBitmap (const AffineTransform& transform, EdgeTable edgeTable (Rectangle (0, 0, imagePosition.getWidth(), imagePosition.getHeight()), *this, transform.translated ((float) -imagePosition.getX(), (float) -imagePosition.getY())); - int stride, pixelStride; - uint8* const pixels = (uint8*) im->lockPixelDataReadWrite (0, 0, imagePosition.getWidth(), imagePosition.getHeight(), stride, pixelStride); + const Image::BitmapData destData (*im, 0, 0, imagePosition.getWidth(), imagePosition.getHeight(), true); - jassert (pixelStride == 1); - MaskBitmapRenderer renderer (pixels, stride); + jassert (destData.pixelStride == 1); + MaskBitmapRenderer renderer (destData); edgeTable.iterate (renderer); - im->releasePixelDataReadWrite (pixels); return im; } diff --git a/src/gui/graphics/imaging/image_file_formats/juce_GIFLoader.cpp b/src/gui/graphics/imaging/image_file_formats/juce_GIFLoader.cpp index d36af50d24..2bdb4f2b06 100644 --- a/src/gui/graphics/imaging/image_file_formats/juce_GIFLoader.cpp +++ b/src/gui/graphics/imaging/image_file_formats/juce_GIFLoader.cpp @@ -387,9 +387,8 @@ bool GIFLoader::readImage (const int width, const int height, int index; int xpos = 0, ypos = 0, pass = 0; - int stride, pixelStride; - uint8* const pixels = image->lockPixelDataReadWrite (0, 0, width, height, stride, pixelStride); - uint8* p = pixels; + const Image::BitmapData destData (*image, 0, 0, width, height, true); + uint8* p = destData.data; const bool hasAlpha = image->hasAlphaChannel(); while ((index = readLZWByte (false, c)) >= 0) @@ -404,8 +403,6 @@ bool GIFLoader::readImage (const int width, const int height, paletteEntry[2]); ((PixelARGB*) p)->premultiply(); - - p += pixelStride; } else { @@ -413,10 +410,9 @@ bool GIFLoader::readImage (const int width, const int height, paletteEntry[0], paletteEntry[1], paletteEntry[2]); - - p += pixelStride; } + p += destData.pixelStride; ++xpos; if (xpos == width) @@ -464,14 +460,13 @@ bool GIFLoader::readImage (const int width, const int height, ++ypos; } - p = pixels + xpos * pixelStride + ypos * stride; + p = destData.getPixelPointer (xpos, ypos); } if (ypos >= height) break; } - image->releasePixelDataReadWrite (pixels); return true; } diff --git a/src/gui/graphics/imaging/image_file_formats/juce_JPEGLoader.cpp b/src/gui/graphics/imaging/image_file_formats/juce_JPEGLoader.cpp index f3207cc8ce..bcaa9434c7 100644 --- a/src/gui/graphics/imaging/image_file_formats/juce_JPEGLoader.cpp +++ b/src/gui/graphics/imaging/image_file_formats/juce_JPEGLoader.cpp @@ -224,14 +224,14 @@ Image* juce_loadJPEGImageFromStream (InputStream& in) throw() image = Image::createNativeImage (Image::RGB, width, height, false); const bool hasAlphaChan = image->hasAlphaChannel(); + const Image::BitmapData destData (*image, 0, 0, width, height, true); + for (int y = 0; y < height; ++y) { jpeg_read_scanlines (&jpegDecompStruct, buffer, 1); - int stride, pixelStride; - uint8* pixels = image->lockPixelDataReadWrite (0, y, width, 1, stride, pixelStride); const uint8* src = *buffer; - uint8* dest = pixels; + uint8* dest = destData.getLinePointer (y); if (hasAlphaChan) { @@ -239,7 +239,7 @@ Image* juce_loadJPEGImageFromStream (InputStream& in) throw() { ((PixelARGB*) dest)->setARGB (0xff, src[0], src[1], src[2]); ((PixelARGB*) dest)->premultiply(); - dest += pixelStride; + dest += destData.pixelStride; src += 3; } } @@ -248,12 +248,10 @@ Image* juce_loadJPEGImageFromStream (InputStream& in) throw() for (int i = width; --i >= 0;) { ((PixelRGB*) dest)->setARGB (0xff, src[0], src[1], src[2]); - dest += pixelStride; + dest += destData.pixelStride; src += 3; } } - - image->releasePixelDataReadWrite (pixels); } jpeg_finish_decompress (&jpegDecompStruct); @@ -363,11 +361,11 @@ bool juce_writeJPEGImageToStream (const Image& image, JPOOL_IMAGE, strideBytes, 1); + const Image::BitmapData srcData (image, 0, 0, jpegCompStruct.image_width, jpegCompStruct.image_height); + while (jpegCompStruct.next_scanline < jpegCompStruct.image_height) { - int stride, pixelStride; - const uint8* pixels = image.lockPixelDataReadOnly (0, jpegCompStruct.next_scanline, jpegCompStruct.image_width, 1, stride, pixelStride); - const uint8* src = pixels; + const uint8* src = srcData.getLinePointer (jpegCompStruct.next_scanline); uint8* dst = *buffer; for (int i = jpegCompStruct.image_width; --i >= 0;) @@ -375,11 +373,10 @@ bool juce_writeJPEGImageToStream (const Image& image, *dst++ = ((const PixelRGB*) src)->getRed(); *dst++ = ((const PixelRGB*) src)->getGreen(); *dst++ = ((const PixelRGB*) src)->getBlue(); - src += pixelStride; + src += srcData.pixelStride; } jpeg_write_scanlines (&jpegCompStruct, buffer, 1); - image.releasePixelDataReadOnly (pixels); } jpeg_finish_compress (&jpegCompStruct); diff --git a/src/gui/graphics/imaging/image_file_formats/juce_PNGLoader.cpp b/src/gui/graphics/imaging/image_file_formats/juce_PNGLoader.cpp index 1bfae3e78b..9b85437d1e 100644 --- a/src/gui/graphics/imaging/image_file_formats/juce_PNGLoader.cpp +++ b/src/gui/graphics/imaging/image_file_formats/juce_PNGLoader.cpp @@ -197,17 +197,16 @@ Image* juce_loadPNGImageFromStream (InputStream& in) throw() hasAlphaChan = image->hasAlphaChannel(); // (the native image creator may not give back what we expect) - int stride, pixelStride; - uint8* const pixels = image->lockPixelDataReadWrite (0, 0, width, height, stride, pixelStride); + const Image::BitmapData destData (*image, 0, 0, width, height, true); uint8* srcRow = tempBuffer; - uint8* destRow = pixels; + uint8* destRow = destData.data; for (y = 0; y < (int) height; ++y) { const uint8* src = srcRow; srcRow += (width << 2); uint8* dest = destRow; - destRow += stride; + destRow += destData.lineStride; if (hasAlphaChan) { @@ -215,7 +214,7 @@ Image* juce_loadPNGImageFromStream (InputStream& in) throw() { ((PixelARGB*) dest)->setARGB (src[3], src[0], src[1], src[2]); ((PixelARGB*) dest)->premultiply(); - dest += pixelStride; + dest += destData.pixelStride; src += 4; } } @@ -224,13 +223,12 @@ Image* juce_loadPNGImageFromStream (InputStream& in) throw() for (int i = width; --i >= 0;) { ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); - dest += pixelStride; + dest += destData.pixelStride; src += 4; } } } - image->releasePixelDataReadWrite (pixels); juce_free (tempBuffer); } @@ -289,12 +287,12 @@ bool juce_writePNGImageToStream (const Image& image, OutputStream& out) throw() png_set_shift (pngWriteStruct, &sig_bit); png_set_packing (pngWriteStruct); + const Image::BitmapData srcData (image, 0, 0, width, height); + for (int y = 0; y < height; ++y) { uint8* dst = (uint8*) rowData; - int stride, pixelStride; - const uint8* pixels = image.lockPixelDataReadOnly (0, y, width, 1, stride, pixelStride); - const uint8* src = pixels; + const uint8* src = srcData.getLinePointer (y); if (image.hasAlphaChannel()) { @@ -307,7 +305,7 @@ bool juce_writePNGImageToStream (const Image& image, OutputStream& out) throw() *dst++ = p.getGreen(); *dst++ = p.getBlue(); *dst++ = p.getAlpha(); - src += pixelStride; + src += srcData.pixelStride; } } else @@ -317,12 +315,11 @@ bool juce_writePNGImageToStream (const Image& image, OutputStream& out) throw() *dst++ = ((const PixelRGB*) src)->getRed(); *dst++ = ((const PixelRGB*) src)->getGreen(); *dst++ = ((const PixelRGB*) src)->getBlue(); - src += pixelStride; + src += srcData.pixelStride; } } png_write_rows (pngWriteStruct, &rowData, 1); - image.releasePixelDataReadOnly (pixels); } juce_free (rowData); diff --git a/src/gui/graphics/imaging/juce_Image.cpp b/src/gui/graphics/imaging/juce_Image.cpp index 6afd42663b..d9ccd024ea 100644 --- a/src/gui/graphics/imaging/juce_Image.cpp +++ b/src/gui/graphics/imaging/juce_Image.cpp @@ -82,10 +82,8 @@ Image::Image (const Image& other) imageData = (uint8*) juce_malloc (dataSize); - int ls, ps; - const uint8* srcData = other.lockPixelDataReadOnly (0, 0, imageWidth, imageHeight, ls, ps); - setPixelData (0, 0, imageWidth, imageHeight, srcData, ls); - other.releasePixelDataReadOnly (srcData); + BitmapData srcData (other, 0, 0, imageWidth, imageHeight); + setPixelData (0, 0, imageWidth, imageHeight, srcData.data, srcData.lineStride); } Image::~Image() @@ -100,33 +98,27 @@ LowLevelGraphicsContext* Image::createLowLevelContext() } //============================================================================== -uint8* Image::lockPixelDataReadWrite (int x, int y, int w, int h, int& ls, int& ps) +Image::BitmapData::BitmapData (Image& image, int x, int y, int w, int h, const bool /*makeWritable*/) throw() + : data (image.imageData + image.lineStride * y + image.pixelStride * x), + lineStride (image.lineStride), + pixelStride (image.pixelStride), + width (w), + height (h) { - jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= imageWidth && y + h <= imageHeight); - w = w; - h = h; - - ls = lineStride; - ps = pixelStride; - return imageData + x * pixelStride + y * lineStride; + jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= image.getWidth() && y + h <= image.getHeight()); } -void Image::releasePixelDataReadWrite (void*) +Image::BitmapData::BitmapData (const Image& image, int x, int y, int w, int h) throw() + : data (image.imageData + image.lineStride * y + image.pixelStride * x), + lineStride (image.lineStride), + pixelStride (image.pixelStride), + width (w), + height (h) { + jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= image.getWidth() && y + h <= image.getHeight()); } -const uint8* Image::lockPixelDataReadOnly (int x, int y, int w, int h, int& ls, int& ps) const -{ - jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= imageWidth && y + h <= imageHeight); - w = w; - h = h; - - ls = lineStride; - ps = pixelStride; - return imageData + x * pixelStride + y * lineStride; -} - -void Image::releasePixelDataReadOnly (const void*) const +Image::BitmapData::~BitmapData() throw() { } @@ -137,17 +129,14 @@ void Image::setPixelData (int x, int y, int w, int h, if (Rectangle::intersectRectangles (x, y, w, h, 0, 0, imageWidth, imageHeight)) { - int ls, ps; - uint8* dest = lockPixelDataReadWrite (x, y, w, h, ls, ps); + const BitmapData dest (*this, x, y, w, h, true); for (int i = 0; i < h; ++i) { - memcpy (dest + ls * i, + memcpy (dest.getLinePointer(i), sourcePixelData + sourceLineStride * i, - w * pixelStride); + w * dest.pixelStride); } - - releasePixelDataReadWrite (dest); } } @@ -157,21 +146,20 @@ void Image::clear (int dx, int dy, int dw, int dh, { const PixelARGB col (colourToClearTo.getPixelARGB()); - int ls, ps; - uint8* dstData = lockPixelDataReadWrite (dx, dy, dw, dh, ls, ps); - uint8* dest = dstData; + const BitmapData destData (*this, dx, dy, dw, dh, true); + uint8* dest = destData.data; while (--dh >= 0) { uint8* line = dest; - dest += ls; + dest += destData.lineStride; if (isARGB()) { for (int x = dw; --x >= 0;) { ((PixelARGB*) line)->set (col); - line += ps; + line += destData.pixelStride; } } else if (isRGB()) @@ -179,7 +167,7 @@ void Image::clear (int dx, int dy, int dw, int dh, for (int x = dw; --x >= 0;) { ((PixelRGB*) line)->set (col); - line += ps; + line += destData.pixelStride; } } else @@ -187,12 +175,10 @@ void Image::clear (int dx, int dy, int dw, int dh, for (int x = dw; --x >= 0;) { *line = col.getAlpha(); - line += ps; + line += destData.pixelStride; } } } - - releasePixelDataReadWrite (dstData); } Image* Image::createCopy (int newWidth, int newHeight, @@ -229,16 +215,13 @@ Image* Image::createCopyOfAlphaChannel() const } else { - int dls, dps; - uint8* dstData = newImage->lockPixelDataReadWrite (0, 0, imageWidth, imageHeight, dls, dps); - - int sls, sps; - const uint8* srcData = lockPixelDataReadOnly (0, 0, imageWidth, imageHeight, sls, sps); + const BitmapData destData (*newImage, 0, 0, imageWidth, imageHeight, true); + const BitmapData srcData (*this, 0, 0, imageWidth, imageHeight); for (int y = 0; y < imageHeight; ++y) { - const PixelARGB* src = (const PixelARGB*) (srcData + y * sls); - uint8* dst = dstData + y * dls; + const PixelARGB* src = (const PixelARGB*) srcData.getLinePointer(y); + uint8* dst = destData.getLinePointer (y); for (int x = imageWidth; --x >= 0;) { @@ -246,9 +229,6 @@ Image* Image::createCopyOfAlphaChannel() const ++src; } } - - releasePixelDataReadOnly (srcData); - newImage->releasePixelDataReadWrite (dstData); } return newImage; @@ -262,21 +242,18 @@ const Colour Image::getPixelAt (const int x, const int y) const if (((unsigned int) x) < (unsigned int) imageWidth && ((unsigned int) y) < (unsigned int) imageHeight) { - int ls, ps; - const uint8* const pixels = lockPixelDataReadOnly (x, y, 1, 1, ls, ps); + const BitmapData srcData (*this, x, y, 1, 1); if (isARGB()) { - PixelARGB p (*(const PixelARGB*) pixels); + PixelARGB p (*(const PixelARGB*) srcData.data); p.unpremultiply(); c = Colour (p.getARGB()); } else if (isRGB()) - c = Colour (((const PixelRGB*) pixels)->getARGB()); + c = Colour (((const PixelRGB*) srcData.data)->getARGB()); else - c = Colour ((uint8) 0, (uint8) 0, (uint8) 0, *pixels); - - releasePixelDataReadOnly (pixels); + c = Colour ((uint8) 0, (uint8) 0, (uint8) 0, *(srcData.data)); } return c; @@ -288,18 +265,15 @@ void Image::setPixelAt (const int x, const int y, if (((unsigned int) x) < (unsigned int) imageWidth && ((unsigned int) y) < (unsigned int) imageHeight) { - int ls, ps; - uint8* const pixels = lockPixelDataReadWrite (x, y, 1, 1, ls, ps); + const BitmapData destData (*this, x, y, 1, 1, true); const PixelARGB col (colour.getPixelARGB()); if (isARGB()) - ((PixelARGB*) pixels)->set (col); + ((PixelARGB*) destData.data)->set (col); else if (isRGB()) - ((PixelRGB*) pixels)->set (col); + ((PixelRGB*) destData.data)->set (col); else - *pixels = col.getAlpha(); - - releasePixelDataReadWrite (pixels); + *(destData.data) = col.getAlpha(); } } @@ -310,15 +284,12 @@ void Image::multiplyAlphaAt (const int x, const int y, && ((unsigned int) y) < (unsigned int) imageHeight && hasAlphaChannel()) { - int ls, ps; - uint8* const pixels = lockPixelDataReadWrite (x, y, 1, 1, ls, ps); + const BitmapData destData (*this, x, y, 1, 1, true); if (isARGB()) - ((PixelARGB*) pixels)->multiplyAlpha (multiplier); + ((PixelARGB*) destData.data)->multiplyAlpha (multiplier); else - *pixels = (uint8) (*pixels * multiplier); - - releasePixelDataReadWrite (pixels); + *(destData.data) = (uint8) (*(destData.data) * multiplier); } } @@ -326,19 +297,18 @@ void Image::multiplyAllAlphas (const float amountToMultiplyBy) { if (hasAlphaChannel()) { - int ls, ps; - uint8* const pixels = lockPixelDataReadWrite (0, 0, getWidth(), getHeight(), ls, ps); + const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), true); if (isARGB()) { for (int y = 0; y < imageHeight; ++y) { - uint8* p = pixels + y * ls; + uint8* p = destData.getLinePointer (y); for (int x = 0; x < imageWidth; ++x) { ((PixelARGB*) p)->multiplyAlpha (amountToMultiplyBy); - p += ps; + p += destData.pixelStride; } } } @@ -346,17 +316,15 @@ void Image::multiplyAllAlphas (const float amountToMultiplyBy) { for (int y = 0; y < imageHeight; ++y) { - uint8* p = pixels + y * ls; + uint8* p = destData.getLinePointer (y); for (int x = 0; x < imageWidth; ++x) { *p = (uint8) (*p * amountToMultiplyBy); - p += ps; + p += destData.pixelStride; } } } - - releasePixelDataReadWrite (pixels); } else { @@ -368,19 +336,18 @@ void Image::desaturate() { if (isARGB() || isRGB()) { - int ls, ps; - uint8* const pixels = lockPixelDataReadWrite (0, 0, getWidth(), getHeight(), ls, ps); + const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), true); if (isARGB()) { for (int y = 0; y < imageHeight; ++y) { - uint8* p = pixels + y * ls; + uint8* p = destData.getLinePointer (y); for (int x = 0; x < imageWidth; ++x) { ((PixelARGB*) p)->desaturate(); - p += ps; + p += destData.pixelStride; } } } @@ -388,17 +355,15 @@ void Image::desaturate() { for (int y = 0; y < imageHeight; ++y) { - uint8* p = pixels + y * ls; + uint8* p = destData.getLinePointer (y); for (int x = 0; x < imageWidth; ++x) { ((PixelRGB*) p)->desaturate(); - p += ps; + p += destData.pixelStride; } } } - - releasePixelDataReadWrite (pixels); } } @@ -409,13 +374,12 @@ void Image::createSolidAreaMask (RectangleList& result, const float alphaThresho const uint8 threshold = (uint8) jlimit (0, 255, roundFloatToInt (alphaThreshold * 255.0f)); SparseSet pixelsOnRow; - int ls, ps; - const uint8* const pixels = lockPixelDataReadOnly (0, 0, imageWidth, imageHeight, ls, ps); + const BitmapData srcData (*this, 0, 0, getWidth(), getHeight()); for (int y = 0; y < imageHeight; ++y) { pixelsOnRow.clear(); - const uint8* lineData = pixels + ls * y; + const uint8* lineData = srcData.getLinePointer (y); if (isARGB()) { @@ -424,7 +388,7 @@ void Image::createSolidAreaMask (RectangleList& result, const float alphaThresho if (((const PixelARGB*) lineData)->getAlpha() >= threshold) pixelsOnRow.addRange (x, 1); - lineData += ps; + lineData += srcData.pixelStride; } } else @@ -434,7 +398,7 @@ void Image::createSolidAreaMask (RectangleList& result, const float alphaThresho if (*lineData >= threshold) pixelsOnRow.addRange (x, 1); - lineData += ps; + lineData += srcData.pixelStride; } } @@ -448,8 +412,6 @@ void Image::createSolidAreaMask (RectangleList& result, const float alphaThresho result.consolidate(); } - - releasePixelDataReadOnly (pixels); } else { @@ -500,19 +462,18 @@ void Image::moveImageSection (int dx, int dy, const int maxX = jmax (dx, sx) + w; const int maxY = jmax (dy, sy) + h; - int ls, ps; - uint8* const pixels = lockPixelDataReadWrite (minX, minY, maxX - minX, maxY - minY, ls, ps); + const BitmapData destData (*this, minX, minY, maxX - minX, maxY - minY, true); - uint8* dst = pixels + ls * (dy - minY) + ps * (dx - minX); - const uint8* src = pixels + ls * (sy - minY) + ps * (sx - minX); + uint8* dst = destData.getPixelPointer (dx - minX, dy - minY); + const uint8* src = destData.getPixelPointer (sx - minX, sy - minY); - const int lineSize = ps * w; + const int lineSize = destData.pixelStride * w; if (dy > sy) { while (--h >= 0) { - const int offset = h * ls; + const int offset = h * destData.lineStride; memmove (dst + offset, src + offset, lineSize); } } @@ -521,12 +482,10 @@ void Image::moveImageSection (int dx, int dy, while (--h >= 0) { memmove (dst, src, lineSize); - dst += ls; - src += ls; + dst += destData.lineStride; + src += destData.lineStride; } } - - releasePixelDataReadWrite (pixels); } } diff --git a/src/gui/graphics/imaging/juce_Image.h b/src/gui/graphics/imaging/juce_Image.h index 1cb1ff3538..cb78b077a4 100644 --- a/src/gui/graphics/imaging/juce_Image.h +++ b/src/gui/graphics/imaging/juce_Image.h @@ -199,55 +199,44 @@ public: virtual void desaturate(); //============================================================================== - /** Locks some of the pixels in the image so they can be read and written to. + /** Retrieves a section of an image as raw pixel data, so it can be read or written to. - This returns a pointer to some memory containing the pixels in the given - rectangle. It also returns values for the line and pixel stride used within - the data. The format of the pixel data is the same as that of this image. + You should only use this class as a last resort - messing about with the internals of + an image is only recommended for people who really know what they're doing! - When you've finished reading and changing the data, you must call - releasePixelDataReadWrite() to give the pixels back to the image. + A BitmapData object should be used as a temporary, stack-based object. Don't keep one + hanging around while the image is being used elsewhere. - For images that are stored in memory, this method may just return a direct - pointer to the image's data, but other types of image may be stored elsewhere, - e.g. in video memory, and if so, this lockPixelDataReadWrite() and - releasePixelDataReadWrite() may need to create a temporary copy in main memory. + Depending on the way the image class is implemented, this may create a temporary buffer + which is copied back to the image when the object is deleted, or it may just get a pointer + directly into the image's raw data. - If you only need read-access to the pixel data, use lockPixelDataReadOnly() - instead. - - @see releasePixelDataReadWrite, lockPixelDataReadOnly + You can use the stride and data values in this class directly, but don't alter them! + The actual format of the pixel data depends on the image's format - see Image::getFormat(), + and the PixelRGB, PixelARGB and PixelAlpha classes for more info. */ - virtual uint8* lockPixelDataReadWrite (int x, int y, int w, int h, int& lineStride, int& pixelStride); + class BitmapData + { + public: + BitmapData (Image& image, int x, int y, int w, int h, const bool needsToBeWritable) throw(); + BitmapData (const Image& image, int x, int y, int w, int h) throw(); + ~BitmapData() throw(); - /** Releases a block of memory that was locked with lockPixelDataReadWrite(). - */ - virtual void releasePixelDataReadWrite (void* sourceData); + /** Returns a pointer to the start of a line in the image. + The co-ordinate you provide here isn't checked, so it's the caller's responsibility to make + sure it's not out-of-range. + */ + inline uint8* getLinePointer (const int y) const throw() { return data + y * lineStride; } - /** Locks some of the pixels in the image so they can be read. + /** Returns a pointer to a pixel in the image. + The co-ordinates you give here are not checked, so it's the caller's responsibility to make sure they're + not out-of-range. + */ + inline uint8* getPixelPointer (const int x, const int y) const throw() { return data + y * lineStride + x * pixelStride; } - This returns a pointer to some memory containing the pixels in the given - rectangle. It also returns values for the line and pixel stride used within - the data. The format of the pixel data is the same as that of this image. - - When you've finished reading the data, you must call releasePixelDataReadOnly() - to let the image free the memory if necessary. - - For images that are stored in memory, this method may just return a direct - pointer to the image's data, but other types of image may be stored elsewhere, - e.g. in video memory, and if so, this lockPixelDataReadWrite() and - releasePixelDataReadWrite() may need to create a temporary copy in main memory. - - If you only need to read and write the pixel data, use lockPixelDataReadWrite() - instead. - - @see releasePixelDataReadOnly, lockPixelDataReadWrite - */ - virtual const uint8* lockPixelDataReadOnly (int x, int y, int w, int h, int& lineStride, int& pixelStride) const; - - /** Releases a block of memory that was locked with lockPixelDataReadOnly(). - */ - virtual void releasePixelDataReadOnly (const void* sourceData) const; + uint8* data; + int lineStride, pixelStride, width, height; + }; /** Copies some pixel values to a rectangle of the image. @@ -283,6 +272,7 @@ public: virtual LowLevelGraphicsContext* createLowLevelContext(); protected: + friend class BitmapData; const PixelFormat format; const int imageWidth, imageHeight; diff --git a/src/gui/graphics/imaging/juce_ImageConvolutionKernel.cpp b/src/gui/graphics/imaging/juce_ImageConvolutionKernel.cpp index 0e9feeec35..1550174b4f 100644 --- a/src/gui/graphics/imaging/juce_ImageConvolutionKernel.cpp +++ b/src/gui/graphics/imaging/juce_ImageConvolutionKernel.cpp @@ -152,19 +152,17 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, const int dx2 = dx + dw; const int dy2 = dy + dh; - int lineStride, pixelStride; - uint8* pixels = destImage.lockPixelDataReadWrite (dx, dy, dw, dh, lineStride, pixelStride); - uint8* line = pixels; + const Image::BitmapData destData (destImage, dx, dy, dw, dh, true); + uint8* line = destData.data; - int srcLineStride, srcPixelStride; - const uint8* srcPixels = sourceImage->lockPixelDataReadOnly (0, 0, sourceImage->getWidth(), sourceImage->getHeight(), srcLineStride, srcPixelStride); + const Image::BitmapData srcData (*sourceImage, 0, 0, sourceImage->getWidth(), sourceImage->getHeight()); - if (pixelStride == 4) + if (destData.pixelStride == 4) { for (int y = dy; y < dy2; ++y) { uint8* dest = line; - line += lineStride; + line += destData.lineStride; for (int x = dx; x < dx2; ++x) { @@ -183,7 +181,7 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, if (sy >= 0) { int sx = x - (size >> 1); - const uint8* src = srcPixels + srcLineStride * sy + srcPixelStride * sx; + const uint8* src = srcData.getPixelPointer (sx, sy); for (int xx = 0; xx < size; ++xx) { @@ -215,12 +213,12 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, } } } - else if (pixelStride == 3) + else if (destData.pixelStride == 3) { for (int y = dy; y < dy2; ++y) { uint8* dest = line; - line += lineStride; + line += destData.lineStride; for (int x = dx; x < dx2; ++x) { @@ -238,7 +236,7 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, if (sy >= 0) { int sx = x - (size >> 1); - const uint8* src = srcPixels + srcLineStride * sy + srcPixelStride * sx; + const uint8* src = srcData.getPixelPointer (sx, sy); for (int xx = 0; xx < size; ++xx) { @@ -269,11 +267,7 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, } } - sourceImage->releasePixelDataReadOnly (srcPixels); - destImage.releasePixelDataReadWrite (pixels); - - if (imageCreated != 0) - delete imageCreated; + delete imageCreated; } END_JUCE_NAMESPACE diff --git a/src/native/linux/juce_linux_Windowing.cpp b/src/native/linux/juce_linux_Windowing.cpp index 8752290f46..8ba89c2347 100644 --- a/src/native/linux/juce_linux_Windowing.cpp +++ b/src/native/linux/juce_linux_Windowing.cpp @@ -595,17 +595,16 @@ public: const uint32 bShiftL = jmax (0, getShiftNeeded (bMask)); const uint32 bShiftR = jmax (0, -getShiftNeeded (bMask)); - int ls, ps; - const uint8* const pixels = lockPixelDataReadOnly (0, 0, getWidth(), getHeight(), ls, ps); + const Image::BitmapData srcData (*this, 0, 0, getWidth(), getHeight()); for (int y = sy; y < sy + dh; ++y) { - const uint8* p = pixels + y * ls + sx * ps; + const uint8* p = srcData.getPixelPointer (sx, y); for (int x = sx; x < sx + dw; ++x) { const PixelRGB* const pixel = (const PixelRGB*) p; - p += ps; + p += srcData.pixelStride; XPutPixel (xImage, x, y, (((((uint32) pixel->getRed()) << rShiftL) >> rShiftR) & rMask) @@ -613,8 +612,6 @@ public: | (((((uint32) pixel->getBlue()) << bShiftL) >> bShiftR) & bMask)); } } - - releasePixelDataReadOnly (pixels); } // blit results to screen. diff --git a/src/native/mac/juce_mac_CameraDevice.mm b/src/native/mac/juce_mac_CameraDevice.mm index c926d6e7e6..ec9e8c38cc 100644 --- a/src/native/mac/juce_mac_CameraDevice.mm +++ b/src/native/mac/juce_mac_CameraDevice.mm @@ -148,22 +148,21 @@ public: static void drawNSBitmapIntoJuceImage (Image& dest, NSBitmapImageRep* source) { const ScopedAutoReleasePool pool; - int lineStride, pixelStride; - uint8* pixels = dest.lockPixelDataReadWrite (0, 0, dest.getWidth(), dest.getHeight(), - lineStride, pixelStride); + + const Image::BitmapData destData (dest, 0, 0, dest.getWidth(), dest.getHeight(), true); NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes: &pixels - pixelsWide: dest.getWidth() - pixelsHigh: dest.getHeight() + initWithBitmapDataPlanes: (unsigned char**) &(destData.data) + pixelsWide: destData.width + pixelsHigh: destData.height bitsPerSample: 8 - samplesPerPixel: pixelStride + samplesPerPixel: destData.pixelStride hasAlpha: dest.hasAlphaChannel() isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bitmapFormat: (NSBitmapFormat) 0 - bytesPerRow: lineStride - bitsPerPixel: pixelStride * 8]; + bytesPerRow: destData.lineStride + bitsPerPixel: destData.pixelStride * 8]; [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithBitmapImageRep: rep]]; @@ -173,11 +172,11 @@ public: [[NSGraphicsContext currentContext] flushGraphics]; [NSGraphicsContext restoreGraphicsState]; - uint8* start = pixels; + uint8* start = destData.data; for (int h = dest.getHeight(); --h >= 0;) { uint8* p = start; - start += lineStride; + start += destData.lineStride; for (int i = dest.getWidth(); --i >= 0;) { @@ -194,11 +193,9 @@ public: p[2] = oldp0; #endif - p += pixelStride; + p += destData.pixelStride; } } - - dest.releasePixelDataReadWrite (pixels); } void callListeners (NSBitmapImageRep* bitmap) diff --git a/src/native/mac/juce_mac_CoreGraphicsContext.mm b/src/native/mac/juce_mac_CoreGraphicsContext.mm index 9167dbca32..ea489b035f 100644 --- a/src/native/mac/juce_mac_CoreGraphicsContext.mm +++ b/src/native/mac/juce_mac_CoreGraphicsContext.mm @@ -601,27 +601,21 @@ private: } else { - int lineStride = 0; - int pixelStride = 0; - const uint8* imageData = juceImage.lockPixelDataReadOnly (0, 0, juceImage.getWidth(), juceImage.getHeight(), - lineStride, pixelStride); - - CGDataProviderRef provider = CGDataProviderCreateWithData (0, imageData, lineStride * pixelStride, 0); + const Image::BitmapData srcData (juceImage, 0, 0, juceImage.getWidth(), juceImage.getHeight()); + CGDataProviderRef provider = CGDataProviderCreateWithData (0, srcData.data, srcData.lineStride * srcData.pixelStride, 0); CGColorSpaceRef colourSpace = forAlpha ? greyColourSpace : rgbColourSpace; - CGImageRef imageRef = CGImageCreate (juceImage.getWidth(), juceImage.getHeight(), - 8, pixelStride * 8, lineStride, - colourSpace, - (juceImage.hasAlphaChannel() && ! forAlpha) - ? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little) - : kCGBitmapByteOrderDefault, - provider, - 0, true, kCGRenderingIntentDefault); + CGImageRef imageRef = CGImageCreate (srcData.width, srcData.height, + 8, srcData.pixelStride * 8, srcData.lineStride, + colourSpace, + (juceImage.hasAlphaChannel() && ! forAlpha) + ? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little) + : kCGBitmapByteOrderDefault, + provider, + 0, true, kCGRenderingIntentDefault); CGDataProviderRelease (provider); - - juceImage.releasePixelDataReadOnly (imageData); return imageRef; } } diff --git a/src/native/mac/juce_mac_MouseCursor.mm b/src/native/mac/juce_mac_MouseCursor.mm index 915cc87a57..ab493bbe19 100644 --- a/src/native/mac/juce_mac_MouseCursor.mm +++ b/src/native/mac/juce_mac_MouseCursor.mm @@ -33,26 +33,24 @@ static NSImage* juceImageToNSImage (const Image& image) { const ScopedAutoReleasePool pool; - int lineStride, pixelStride; - const uint8* pixels = image.lockPixelDataReadOnly (0, 0, image.getWidth(), image.getHeight(), - lineStride, pixelStride); + + const Image::BitmapData srcData (image, 0, 0, image.getWidth(), image.getHeight()); NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL - pixelsWide: image.getWidth() - pixelsHigh: image.getHeight() + pixelsWide: srcData.width + pixelsHigh: srcData.height bitsPerSample: 8 samplesPerPixel: image.hasAlphaChannel() ? 4 : 3 hasAlpha: image.hasAlphaChannel() isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bitmapFormat: (NSBitmapFormat) 0 - bytesPerRow: lineStride - bitsPerPixel: pixelStride * 8]; + bytesPerRow: srcData.lineStride + bitsPerPixel: srcData.pixelStride * 8]; unsigned char* newData = [rep bitmapData]; - memcpy (newData, pixels, lineStride * image.getHeight()); - image.releasePixelDataReadOnly (pixels); + memcpy (newData, srcData.data, srcData.lineStride * srcData.height); NSImage* im = [[NSImage alloc] init]; [im addRepresentation: rep]; diff --git a/src/native/mac/juce_mac_NSViewComponentPeer.mm b/src/native/mac/juce_mac_NSViewComponentPeer.mm index 91b13224ba..ee86088955 100644 --- a/src/native/mac/juce_mac_NSViewComponentPeer.mm +++ b/src/native/mac/juce_mac_NSViewComponentPeer.mm @@ -561,27 +561,21 @@ class JuceNSImage public: JuceNSImage (const int width, const int height, const bool hasAlpha) : juceImage (hasAlpha ? Image::ARGB : Image::RGB, - width, height, hasAlpha) + width, height, hasAlpha), + srcData (juceImage, 0, 0, width, height) { - lineStride = 0; - pixelStride = 0; - imageData = juceImage.lockPixelDataReadWrite (0, 0, width, height, - lineStride, pixelStride); - imageRep = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes: &imageData + initWithBitmapDataPlanes: (unsigned char**) &(srcData.data) pixelsWide: width pixelsHigh: height bitsPerSample: 8 - samplesPerPixel: pixelStride + samplesPerPixel: srcData.pixelStride hasAlpha: hasAlpha isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bitmapFormat: /*NSAlphaFirstBitmapFormat*/ (NSBitmapFormat) 0 - bytesPerRow: lineStride - bitsPerPixel: 8 * pixelStride ]; - - juceImage.releasePixelDataReadWrite (imageData); + bytesPerRow: srcData.lineStride + bitsPerPixel: 8 * srcData.pixelStride ]; } ~JuceNSImage() @@ -640,23 +634,22 @@ public: private: Image juceImage; NSBitmapImageRep* imageRep; - uint8* imageData; - int pixelStride, lineStride; + const Image::BitmapData srcData; void swapRGBOrder (const int x, const int y, const int w, int h) const { #if JUCE_BIG_ENDIAN - jassert (pixelStride == 4); + jassert (srcData.pixelStride == 4); #endif jassert (Rectangle (0, 0, juceImage.getWidth(), juceImage.getHeight()) .contains (Rectangle (x, y, w, h))); - uint8* start = imageData + x * pixelStride + y * lineStride; + uint8* start = srcData.getPixelPointer (x, y); while (--h >= 0) { uint8* p = start; - start += lineStride; + start += srcData.lineStride; for (int i = w; --i >= 0;) { @@ -673,7 +666,7 @@ private: p[2] = oldp0; #endif - p += pixelStride; + p += srcData.pixelStride; } } } diff --git a/src/native/windows/juce_win32_CameraDevice.cpp b/src/native/windows/juce_win32_CameraDevice.cpp index 20b272f62b..0f91722a6a 100644 --- a/src/native/windows/juce_win32_CameraDevice.cpp +++ b/src/native/windows/juce_win32_CameraDevice.cpp @@ -45,7 +45,9 @@ public: width (0), height (0), activeUsers (0), - recordNextFrameTime (false) + recordNextFrameTime (false), + activeImage (0), + loadingImage (0) { HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph, CLSCTX_INPROC); if (FAILED (hr)) @@ -147,7 +149,9 @@ public: ~DShowCameraDeviceInteral() { - mediaControl->Stop(); + if (mediaControl != 0) + mediaControl->Stop(); + removeGraphFromRot(); for (int i = viewerComps.size(); --i >= 0;) @@ -190,16 +194,14 @@ public: } imageSwapLock.enter(); - int ls, ps; const int lineStride = width * 3; - uint8* const dest = loadingImage->lockPixelDataReadWrite (0, 0, width, height, ls, ps); + const Image::BitmapData destData (*loadingImage, 0, 0, width, height, true); for (int i = 0; i < height; ++i) - memcpy (dest + ls * ((height - 1) - i), + memcpy (destData.getLinePointer ((height - 1) - i), buffer + lineStride * i, lineStride); - loadingImage->releasePixelDataReadWrite (dest); imageNeedsFlipping = true; imageSwapLock.exit(); diff --git a/src/native/windows/juce_win32_Windowing.cpp b/src/native/windows/juce_win32_Windowing.cpp index 15b653997c..4d166d4953 100644 --- a/src/native/windows/juce_win32_Windowing.cpp +++ b/src/native/windows/juce_win32_Windowing.cpp @@ -1180,12 +1180,7 @@ private: WindowsBitmapImage* const offscreenImage = offscreenImageGenerator.getImage (transparent, w, h); - LowLevelGraphicsSoftwareRenderer context (*offscreenImage); - - RectangleList* const contextClip = context.getRawClipRegion(); - contextClip->clear(); - - context.setOrigin (-x, -y); + RectangleList contextClip; bool needToPaintAll = true; @@ -1221,7 +1216,7 @@ private: if (cx + cw - x <= w && cy + ch - y <= h) { - contextClip->addWithoutMerging (Rectangle (cx - x, cy - y, cw, ch)); + contextClip.addWithoutMerging (Rectangle (cx - x, cy - y, cw, ch)); } else { @@ -1237,13 +1232,13 @@ private: if (needToPaintAll) { - contextClip->clear(); - contextClip->addWithoutMerging (Rectangle (0, 0, w, h)); + contextClip.clear(); + contextClip.addWithoutMerging (Rectangle (0, 0, w, h)); } if (transparent) { - RectangleList::Iterator i (*contextClip); + RectangleList::Iterator i (contextClip); while (i.next()) { @@ -1257,6 +1252,10 @@ private: updateCurrentModifiers(); + LowLevelGraphicsSoftwareRenderer context (*offscreenImage); + context.reduceClipRegion (contextClip); + context.setOrigin (-x, -y); + handlePaint (context); if (! dontRepaint)