2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../../core/juce_StandardHeader.h"
30 #include "juce_Image.h"
31 #include "../contexts/juce_Graphics.h"
32 #include "../contexts/juce_LowLevelGraphicsSoftwareRenderer.h"
33 #include "../colour/juce_PixelFormats.h"
34 #include "../../../containers/juce_SparseSet.h"
37 //==============================================================================
38 Image::SharedImage::SharedImage (const PixelFormat format_
, const int width_
, const int height_
)
39 : format (format_
), width (width_
), height (height_
)
41 jassert (format_
== RGB
|| format_
== ARGB
|| format_
== SingleChannel
);
42 jassert (width
> 0 && height
> 0); // It's illegal to create a zero-sized image!
45 Image::SharedImage::~SharedImage()
49 //==============================================================================
50 class SoftwareSharedImage
: public Image::SharedImage
53 SoftwareSharedImage (const Image::PixelFormat format_
, const int width_
, const int height_
, const bool clearImage
)
54 : Image::SharedImage (format_
, width_
, height_
),
55 pixelStride (format_
== Image::RGB
? 3 : ((format_
== Image::ARGB
) ? 4 : 1)),
56 lineStride ((pixelStride
* jmax (1, width_
) + 3) & ~3)
58 imageData
.allocate (lineStride
* jmax (1, height_
), clearImage
);
61 Image::ImageType
getType() const
63 return Image::SoftwareImage
;
66 LowLevelGraphicsContext
* createLowLevelContext()
68 return new LowLevelGraphicsSoftwareRenderer (Image (this));
71 void initialiseBitmapData (Image::BitmapData
& bitmap
, int x
, int y
, Image::BitmapData::ReadWriteMode
/*mode*/)
73 bitmap
.data
= imageData
+ x
* pixelStride
+ y
* lineStride
;
74 bitmap
.pixelFormat
= format
;
75 bitmap
.lineStride
= lineStride
;
76 bitmap
.pixelStride
= pixelStride
;
79 Image::SharedImage
* clone()
81 SoftwareSharedImage
* s
= new SoftwareSharedImage (format
, width
, height
, false);
82 memcpy (s
->imageData
, imageData
, lineStride
* height
);
87 HeapBlock
<uint8
> imageData
;
88 const int pixelStride
, lineStride
;
90 JUCE_LEAK_DETECTOR (SoftwareSharedImage
);
93 Image::SharedImage
* Image::SharedImage::createSoftwareImage (Image::PixelFormat format
, int width
, int height
, bool clearImage
)
95 return new SoftwareSharedImage (format
, width
, height
, clearImage
);
98 //==============================================================================
99 class SubsectionSharedImage
: public Image::SharedImage
102 SubsectionSharedImage (Image::SharedImage
* const image_
, const Rectangle
<int>& area_
)
103 : Image::SharedImage (image_
->getPixelFormat(), area_
.getWidth(), area_
.getHeight()),
104 image (image_
), area (area_
)
108 Image::ImageType
getType() const
110 return Image::SoftwareImage
;
113 LowLevelGraphicsContext
* createLowLevelContext()
115 LowLevelGraphicsContext
* g
= image
->createLowLevelContext();
116 g
->clipToRectangle (area
);
117 g
->setOrigin (area
.getX(), area
.getY());
121 void initialiseBitmapData (Image::BitmapData
& bitmap
, int x
, int y
, Image::BitmapData::ReadWriteMode mode
)
123 image
->initialiseBitmapData (bitmap
, x
+ area
.getX(), y
+ area
.getY(), mode
);
126 Image::SharedImage
* clone()
128 return new SubsectionSharedImage (image
->clone(), area
);
132 const ReferenceCountedObjectPtr
<Image::SharedImage
> image
;
133 const Rectangle
<int> area
;
135 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubsectionSharedImage
);
138 Image
Image::getClippedImage (const Rectangle
<int>& area
) const
140 if (area
.contains (getBounds()))
143 const Rectangle
<int> validArea (area
.getIntersection (getBounds()));
144 if (validArea
.isEmpty())
147 return Image (new SubsectionSharedImage (image
, validArea
));
151 //==============================================================================
156 Image::Image (SharedImage
* const instance
)
161 Image::Image (const PixelFormat format
,
162 const int width
, const int height
,
163 const bool clearImage
, const ImageType type
)
164 : image (type
== Image::NativeImage
? SharedImage::createNativeImage (format
, width
, height
, clearImage
)
165 : new SoftwareSharedImage (format
, width
, height
, clearImage
))
169 Image::Image (const Image
& other
)
170 : image (other
.image
)
174 Image
& Image::operator= (const Image
& other
)
184 const Image
Image::null
;
186 LowLevelGraphicsContext
* Image::createLowLevelContext() const
188 return image
== nullptr ? nullptr : image
->createLowLevelContext();
191 void Image::duplicateIfShared()
193 if (image
!= nullptr && image
->getReferenceCount() > 1)
194 image
= image
->clone();
197 Image
Image::rescaled (const int newWidth
, const int newHeight
, const Graphics::ResamplingQuality quality
) const
199 if (image
== nullptr || (image
->width
== newWidth
&& image
->height
== newHeight
))
202 Image
newImage (image
->format
, newWidth
, newHeight
, hasAlphaChannel(), image
->getType());
204 Graphics
g (newImage
);
205 g
.setImageResamplingQuality (quality
);
206 g
.drawImage (*this, 0, 0, newWidth
, newHeight
, 0, 0, image
->width
, image
->height
, false);
211 Image
Image::convertedToFormat (PixelFormat newFormat
) const
213 if (image
== nullptr || newFormat
== image
->format
)
216 const int w
= image
->width
, h
= image
->height
;
217 Image
newImage (newFormat
, w
, h
, false, image
->getType());
219 if (newFormat
== SingleChannel
)
221 if (! hasAlphaChannel())
223 newImage
.clear (getBounds(), Colours::black
);
227 const BitmapData
destData (newImage
, 0, 0, w
, h
, BitmapData::writeOnly
);
228 const BitmapData
srcData (*this, 0, 0, w
, h
);
230 for (int y
= 0; y
< h
; ++y
)
232 const PixelARGB
* src
= (const PixelARGB
*) srcData
.getLinePointer(y
);
233 uint8
* dst
= destData
.getLinePointer (y
);
235 for (int x
= w
; --x
>= 0;)
237 *dst
++ = src
->getAlpha();
245 if (hasAlphaChannel())
246 newImage
.clear (getBounds());
248 Graphics
g (newImage
);
249 g
.drawImageAt (*this, 0, 0);
255 NamedValueSet
* Image::getProperties() const
257 return image
== nullptr ? nullptr : &(image
->userData
);
260 //==============================================================================
261 Image::BitmapData::BitmapData (Image
& image
, const int x
, const int y
, const int w
, const int h
, BitmapData::ReadWriteMode mode
)
265 // The BitmapData class must be given a valid image, and a valid rectangle within it!
266 jassert (image
.image
!= nullptr);
267 jassert (x
>= 0 && y
>= 0 && w
> 0 && h
> 0 && x
+ w
<= image
.getWidth() && y
+ h
<= image
.getHeight());
269 image
.image
->initialiseBitmapData (*this, x
, y
, mode
);
270 jassert (data
!= nullptr && pixelStride
> 0 && lineStride
!= 0);
273 Image::BitmapData::BitmapData (const Image
& image
, const int x
, const int y
, const int w
, const int h
)
277 // The BitmapData class must be given a valid image, and a valid rectangle within it!
278 jassert (image
.image
!= nullptr);
279 jassert (x
>= 0 && y
>= 0 && w
> 0 && h
> 0 && x
+ w
<= image
.getWidth() && y
+ h
<= image
.getHeight());
281 image
.image
->initialiseBitmapData (*this, x
, y
, readOnly
);
282 jassert (data
!= nullptr && pixelStride
> 0 && lineStride
!= 0);
285 Image::BitmapData::BitmapData (const Image
& image
, BitmapData::ReadWriteMode mode
)
286 : width (image
.getWidth()),
287 height (image
.getHeight())
289 // The BitmapData class must be given a valid image!
290 jassert (image
.image
!= nullptr);
292 image
.image
->initialiseBitmapData (*this, 0, 0, mode
);
293 jassert (data
!= nullptr && pixelStride
> 0 && lineStride
!= 0);
296 Image::BitmapData::~BitmapData()
300 const Colour
Image::BitmapData::getPixelColour (const int x
, const int y
) const noexcept
302 jassert (isPositiveAndBelow (x
, width
) && isPositiveAndBelow (y
, height
));
304 const uint8
* const pixel
= getPixelPointer (x
, y
);
308 case Image::ARGB
: return Colour (((const PixelARGB
*) pixel
)->getUnpremultipliedARGB());
309 case Image::RGB
: return Colour (((const PixelRGB
*) pixel
)->getUnpremultipliedARGB());
310 case Image::SingleChannel
: return Colour (((const PixelAlpha
*) pixel
)->getUnpremultipliedARGB());
311 default: jassertfalse
; break;
317 void Image::BitmapData::setPixelColour (const int x
, const int y
, const Colour
& colour
) const noexcept
319 jassert (isPositiveAndBelow (x
, width
) && isPositiveAndBelow (y
, height
));
321 uint8
* const pixel
= getPixelPointer (x
, y
);
322 const PixelARGB
col (colour
.getPixelARGB());
326 case Image::ARGB
: ((PixelARGB
*) pixel
)->set (col
); break;
327 case Image::RGB
: ((PixelRGB
*) pixel
)->set (col
); break;
328 case Image::SingleChannel
: *pixel
= col
.getAlpha(); break;
329 default: jassertfalse
; break;
333 void Image::setPixelData (int x
, int y
, int w
, int h
,
334 const uint8
* const sourcePixelData
, const int sourceLineStride
)
336 jassert (x
>= 0 && y
>= 0 && w
> 0 && h
> 0 && x
+ w
<= getWidth() && y
+ h
<= getHeight());
338 if (Rectangle
<int>::intersectRectangles (x
, y
, w
, h
, 0, 0, getWidth(), getHeight()))
340 const BitmapData
dest (*this, x
, y
, w
, h
, BitmapData::writeOnly
);
342 for (int i
= 0; i
< h
; ++i
)
344 memcpy (dest
.getLinePointer(i
),
345 sourcePixelData
+ sourceLineStride
* i
,
346 w
* dest
.pixelStride
);
351 //==============================================================================
352 void Image::clear (const Rectangle
<int>& area
, const Colour
& colourToClearTo
)
354 const Rectangle
<int> clipped (area
.getIntersection (getBounds()));
356 if (! clipped
.isEmpty())
358 const PixelARGB
col (colourToClearTo
.getPixelARGB());
360 const BitmapData
destData (*this, clipped
.getX(), clipped
.getY(), clipped
.getWidth(), clipped
.getHeight(), BitmapData::writeOnly
);
361 uint8
* dest
= destData
.data
;
362 int dh
= clipped
.getHeight();
367 dest
+= destData
.lineStride
;
371 for (int x
= clipped
.getWidth(); --x
>= 0;)
373 ((PixelARGB
*) line
)->set (col
);
374 line
+= destData
.pixelStride
;
379 for (int x
= clipped
.getWidth(); --x
>= 0;)
381 ((PixelRGB
*) line
)->set (col
);
382 line
+= destData
.pixelStride
;
387 for (int x
= clipped
.getWidth(); --x
>= 0;)
389 *line
= col
.getAlpha();
390 line
+= destData
.pixelStride
;
397 //==============================================================================
398 const Colour
Image::getPixelAt (const int x
, const int y
) const
400 if (isPositiveAndBelow (x
, getWidth()) && isPositiveAndBelow (y
, getHeight()))
402 const BitmapData
srcData (*this, x
, y
, 1, 1);
403 return srcData
.getPixelColour (0, 0);
409 void Image::setPixelAt (const int x
, const int y
, const Colour
& colour
)
411 if (isPositiveAndBelow (x
, getWidth()) && isPositiveAndBelow (y
, getHeight()))
413 const BitmapData
destData (*this, x
, y
, 1, 1, BitmapData::writeOnly
);
414 destData
.setPixelColour (0, 0, colour
);
418 void Image::multiplyAlphaAt (const int x
, const int y
, const float multiplier
)
420 if (isPositiveAndBelow (x
, getWidth()) && isPositiveAndBelow (y
, getHeight())
421 && hasAlphaChannel())
423 const BitmapData
destData (*this, x
, y
, 1, 1, BitmapData::readWrite
);
426 ((PixelARGB
*) destData
.data
)->multiplyAlpha (multiplier
);
428 *(destData
.data
) = (uint8
) (*(destData
.data
) * multiplier
);
432 void Image::multiplyAllAlphas (const float amountToMultiplyBy
)
434 if (hasAlphaChannel())
436 const BitmapData
destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite
);
440 for (int y
= 0; y
< destData
.height
; ++y
)
442 uint8
* p
= destData
.getLinePointer (y
);
444 for (int x
= 0; x
< destData
.width
; ++x
)
446 ((PixelARGB
*) p
)->multiplyAlpha (amountToMultiplyBy
);
447 p
+= destData
.pixelStride
;
453 for (int y
= 0; y
< destData
.height
; ++y
)
455 uint8
* p
= destData
.getLinePointer (y
);
457 for (int x
= 0; x
< destData
.width
; ++x
)
459 *p
= (uint8
) (*p
* amountToMultiplyBy
);
460 p
+= destData
.pixelStride
;
467 jassertfalse
; // can't do this without an alpha-channel!
471 void Image::desaturate()
473 if (isARGB() || isRGB())
475 const BitmapData
destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite
);
479 for (int y
= 0; y
< destData
.height
; ++y
)
481 uint8
* p
= destData
.getLinePointer (y
);
483 for (int x
= 0; x
< destData
.width
; ++x
)
485 ((PixelARGB
*) p
)->desaturate();
486 p
+= destData
.pixelStride
;
492 for (int y
= 0; y
< destData
.height
; ++y
)
494 uint8
* p
= destData
.getLinePointer (y
);
496 for (int x
= 0; x
< destData
.width
; ++x
)
498 ((PixelRGB
*) p
)->desaturate();
499 p
+= destData
.pixelStride
;
506 void Image::createSolidAreaMask (RectangleList
& result
, const float alphaThreshold
) const
508 if (hasAlphaChannel())
510 const uint8 threshold
= (uint8
) jlimit (0, 255, roundToInt (alphaThreshold
* 255.0f
));
511 SparseSet
<int> pixelsOnRow
;
513 const BitmapData
srcData (*this, 0, 0, getWidth(), getHeight());
515 for (int y
= 0; y
< srcData
.height
; ++y
)
518 const uint8
* lineData
= srcData
.getLinePointer (y
);
522 for (int x
= 0; x
< srcData
.width
; ++x
)
524 if (((const PixelARGB
*) lineData
)->getAlpha() >= threshold
)
525 pixelsOnRow
.addRange (Range
<int> (x
, x
+ 1));
527 lineData
+= srcData
.pixelStride
;
532 for (int x
= 0; x
< srcData
.width
; ++x
)
534 if (*lineData
>= threshold
)
535 pixelsOnRow
.addRange (Range
<int> (x
, x
+ 1));
537 lineData
+= srcData
.pixelStride
;
541 for (int i
= 0; i
< pixelsOnRow
.getNumRanges(); ++i
)
543 const Range
<int> range (pixelsOnRow
.getRange (i
));
544 result
.add (Rectangle
<int> (range
.getStart(), y
, range
.getLength(), 1));
547 result
.consolidate();
552 result
.add (0, 0, getWidth(), getHeight());
556 void Image::moveImageSection (int dx
, int dy
,
588 const int minX
= jmin (dx
, sx
);
589 const int minY
= jmin (dy
, sy
);
591 w
= jmin (w
, getWidth() - jmax (sx
, dx
));
592 h
= jmin (h
, getHeight() - jmax (sy
, dy
));
596 const int maxX
= jmax (dx
, sx
) + w
;
597 const int maxY
= jmax (dy
, sy
) + h
;
599 const BitmapData
destData (*this, minX
, minY
, maxX
- minX
, maxY
- minY
, BitmapData::readWrite
);
601 uint8
* dst
= destData
.getPixelPointer (dx
- minX
, dy
- minY
);
602 const uint8
* src
= destData
.getPixelPointer (sx
- minX
, sy
- minY
);
604 const int lineSize
= destData
.pixelStride
* w
;
610 const int offset
= h
* destData
.lineStride
;
611 memmove (dst
+ offset
, src
+ offset
, lineSize
);
618 memmove (dst
, src
, lineSize
);
619 dst
+= destData
.lineStride
;
620 src
+= destData
.lineStride
;