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:
parent
9fc4b6d822
commit
6fdde63a63
25 changed files with 3542 additions and 4458 deletions
|
|
@ -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);
|
||||
|
|
|
|||
3796
juce_amalgamated.cpp
3796
juce_amalgamated.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -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&);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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&);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue