1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-23 01:44:22 +00:00

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.

This commit is contained in:
Julian Storer 2009-11-26 21:36:45 +00:00
parent 9fc4b6d822
commit 6fdde63a63
25 changed files with 3542 additions and 4458 deletions

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -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 <class Pixel>
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 <class Pixel>
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 <class Pixel>
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 <class Pixel>
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 <class Pixel>
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 <LLGCSavedState> 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 <SavedState> 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&);

View file

@ -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);

View file

@ -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 <class Pixel>
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 <class Pixel>
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 <class Pixel>
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 <class Pixel>
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 <class Pixel>
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)

View file

@ -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

View file

@ -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();
};

View file

@ -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";
}

View file

@ -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 <LLGCSavedState> 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 <SavedState> 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&);

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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 <int> 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);
}
}

View file

@ -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;

View file

@ -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

View file

@ -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.

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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];

View file

@ -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;
}
}
}

View file

@ -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();

View file

@ -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)