The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

645 lines
20KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. ImagePixelData::ImagePixelData (const Image::PixelFormat format, const int w, const int h)
  21. : pixelFormat (format), width (w), height (h)
  22. {
  23. jassert (format == Image::RGB || format == Image::ARGB || format == Image::SingleChannel);
  24. jassert (w > 0 && h > 0); // It's illegal to create a zero-sized image!
  25. }
  26. ImagePixelData::~ImagePixelData()
  27. {
  28. }
  29. //==============================================================================
  30. ImageType::ImageType() {}
  31. ImageType::~ImageType() {}
  32. Image ImageType::convert (const Image& source) const
  33. {
  34. if (source.isNull() || getTypeID() == (ScopedPointer<ImageType> (source.getPixelData()->createType())->getTypeID()))
  35. return source;
  36. const Image::BitmapData src (source, Image::BitmapData::readOnly);
  37. Image newImage (create (src.pixelFormat, src.width, src.height, false));
  38. Image::BitmapData dest (newImage, Image::BitmapData::writeOnly);
  39. jassert (src.pixelStride == dest.pixelStride && src.pixelFormat == dest.pixelFormat);
  40. for (int y = 0; y < dest.height; ++y)
  41. memcpy (dest.getLinePointer (y), src.getLinePointer (y), dest.lineStride);
  42. return newImage;
  43. }
  44. //==============================================================================
  45. NativeImageType::NativeImageType() {}
  46. NativeImageType::~NativeImageType() {}
  47. int NativeImageType::getTypeID() const
  48. {
  49. return 1;
  50. }
  51. //==============================================================================
  52. class SoftwarePixelData : public ImagePixelData
  53. {
  54. public:
  55. SoftwarePixelData (const Image::PixelFormat format_, const int w, const int h, const bool clearImage)
  56. : ImagePixelData (format_, w, h),
  57. pixelStride (format_ == Image::RGB ? 3 : ((format_ == Image::ARGB) ? 4 : 1)),
  58. lineStride ((pixelStride * jmax (1, w) + 3) & ~3)
  59. {
  60. imageData.allocate ((size_t) (lineStride * jmax (1, h)), clearImage);
  61. }
  62. LowLevelGraphicsContext* createLowLevelContext()
  63. {
  64. return new LowLevelGraphicsSoftwareRenderer (Image (this));
  65. }
  66. void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode)
  67. {
  68. bitmap.data = imageData + x * pixelStride + y * lineStride;
  69. bitmap.pixelFormat = pixelFormat;
  70. bitmap.lineStride = lineStride;
  71. bitmap.pixelStride = pixelStride;
  72. }
  73. ImagePixelData* clone()
  74. {
  75. SoftwarePixelData* s = new SoftwarePixelData (pixelFormat, width, height, false);
  76. memcpy (s->imageData, imageData, (size_t) (lineStride * height));
  77. return s;
  78. }
  79. ImageType* createType() const { return new SoftwareImageType(); }
  80. private:
  81. HeapBlock<uint8> imageData;
  82. const int pixelStride, lineStride;
  83. JUCE_LEAK_DETECTOR (SoftwarePixelData);
  84. };
  85. SoftwareImageType::SoftwareImageType() {}
  86. SoftwareImageType::~SoftwareImageType() {}
  87. ImagePixelData* SoftwareImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const
  88. {
  89. return new SoftwarePixelData (format, width, height, clearImage);
  90. }
  91. int SoftwareImageType::getTypeID() const
  92. {
  93. return 2;
  94. }
  95. //==============================================================================
  96. class SubsectionPixelData : public ImagePixelData
  97. {
  98. public:
  99. SubsectionPixelData (ImagePixelData* const image_, const Rectangle<int>& area_)
  100. : ImagePixelData (image_->pixelFormat, area_.getWidth(), area_.getHeight()),
  101. image (image_), area (area_)
  102. {
  103. }
  104. LowLevelGraphicsContext* createLowLevelContext()
  105. {
  106. LowLevelGraphicsContext* g = image->createLowLevelContext();
  107. g->clipToRectangle (area);
  108. g->setOrigin (area.getX(), area.getY());
  109. return g;
  110. }
  111. void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode)
  112. {
  113. image->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode);
  114. }
  115. ImagePixelData* clone()
  116. {
  117. jassert (getReferenceCount() > 0); // (This method can't be used on an unowned pointer, as it will end up self-deleting)
  118. const ScopedPointer<ImageType> type (image->createType());
  119. Image newImage (type->create (pixelFormat, area.getWidth(), area.getHeight(), pixelFormat != Image::RGB));
  120. {
  121. Graphics g (newImage);
  122. g.drawImageAt (Image (this), -area.getX(), -area.getY());
  123. }
  124. newImage.getPixelData()->incReferenceCount();
  125. return newImage.getPixelData();
  126. }
  127. ImageType* createType() const { return image->createType(); }
  128. private:
  129. const ReferenceCountedObjectPtr<ImagePixelData> image;
  130. const Rectangle<int> area;
  131. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubsectionPixelData);
  132. };
  133. Image Image::getClippedImage (const Rectangle<int>& area) const
  134. {
  135. if (area.contains (getBounds()))
  136. return *this;
  137. const Rectangle<int> validArea (area.getIntersection (getBounds()));
  138. return Image (validArea.isEmpty() ? nullptr : new SubsectionPixelData (image, validArea));
  139. }
  140. //==============================================================================
  141. Image::Image()
  142. {
  143. }
  144. Image::Image (ImagePixelData* const instance)
  145. : image (instance)
  146. {
  147. }
  148. Image::Image (const PixelFormat format, int width, int height, bool clearImage)
  149. : image (NativeImageType().create (format, width, height, clearImage))
  150. {
  151. }
  152. Image::Image (const PixelFormat format, int width, int height, bool clearImage, const ImageType& type)
  153. : image (type.create (format, width, height, clearImage))
  154. {
  155. }
  156. Image::Image (const Image& other)
  157. : image (other.image)
  158. {
  159. }
  160. Image& Image::operator= (const Image& other)
  161. {
  162. image = other.image;
  163. return *this;
  164. }
  165. #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
  166. Image::Image (Image&& other) noexcept
  167. : image (static_cast <ReferenceCountedObjectPtr<ImagePixelData>&&> (other.image))
  168. {
  169. }
  170. Image& Image::operator= (Image&& other) noexcept
  171. {
  172. image = static_cast <ReferenceCountedObjectPtr<ImagePixelData>&&> (other.image);
  173. return *this;
  174. }
  175. #endif
  176. Image::~Image()
  177. {
  178. }
  179. const Image Image::null;
  180. int Image::getReferenceCount() const noexcept { return image == nullptr ? 0 : image->getReferenceCount(); }
  181. int Image::getWidth() const noexcept { return image == nullptr ? 0 : image->width; }
  182. int Image::getHeight() const noexcept { return image == nullptr ? 0 : image->height; }
  183. Rectangle<int> Image::getBounds() const noexcept { return image == nullptr ? Rectangle<int>() : Rectangle<int> (image->width, image->height); }
  184. Image::PixelFormat Image::getFormat() const noexcept { return image == nullptr ? UnknownFormat : image->pixelFormat; }
  185. bool Image::isARGB() const noexcept { return getFormat() == ARGB; }
  186. bool Image::isRGB() const noexcept { return getFormat() == RGB; }
  187. bool Image::isSingleChannel() const noexcept { return getFormat() == SingleChannel; }
  188. bool Image::hasAlphaChannel() const noexcept { return getFormat() != RGB; }
  189. LowLevelGraphicsContext* Image::createLowLevelContext() const
  190. {
  191. return image == nullptr ? nullptr : image->createLowLevelContext();
  192. }
  193. void Image::duplicateIfShared()
  194. {
  195. if (image != nullptr && image->getReferenceCount() > 1)
  196. image = image->clone();
  197. }
  198. Image Image::createCopy() const
  199. {
  200. return Image (image != nullptr ? image->clone() : nullptr);
  201. }
  202. Image Image::rescaled (const int newWidth, const int newHeight, const Graphics::ResamplingQuality quality) const
  203. {
  204. if (image == nullptr || (image->width == newWidth && image->height == newHeight))
  205. return *this;
  206. const ScopedPointer<ImageType> type (image->createType());
  207. Image newImage (type->create (image->pixelFormat, newWidth, newHeight, hasAlphaChannel()));
  208. Graphics g (newImage);
  209. g.setImageResamplingQuality (quality);
  210. g.drawImage (*this, 0, 0, newWidth, newHeight, 0, 0, image->width, image->height, false);
  211. return newImage;
  212. }
  213. Image Image::convertedToFormat (PixelFormat newFormat) const
  214. {
  215. if (image == nullptr || newFormat == image->pixelFormat)
  216. return *this;
  217. const int w = image->width, h = image->height;
  218. const ScopedPointer<ImageType> type (image->createType());
  219. Image newImage (type->create (newFormat, w, h, false));
  220. if (newFormat == SingleChannel)
  221. {
  222. if (! hasAlphaChannel())
  223. {
  224. newImage.clear (getBounds(), Colours::black);
  225. }
  226. else
  227. {
  228. const BitmapData destData (newImage, 0, 0, w, h, BitmapData::writeOnly);
  229. const BitmapData srcData (*this, 0, 0, w, h);
  230. for (int y = 0; y < h; ++y)
  231. {
  232. const PixelARGB* const src = (const PixelARGB*) srcData.getLinePointer (y);
  233. uint8* const dst = destData.getLinePointer (y);
  234. for (int x = 0; x < w; ++x)
  235. dst[x] = src[x].getAlpha();
  236. }
  237. }
  238. }
  239. else if (image->pixelFormat == SingleChannel && newFormat == Image::ARGB)
  240. {
  241. const BitmapData destData (newImage, 0, 0, w, h, BitmapData::writeOnly);
  242. const BitmapData srcData (*this, 0, 0, w, h);
  243. for (int y = 0; y < h; ++y)
  244. {
  245. const PixelAlpha* const src = (const PixelAlpha*) srcData.getLinePointer (y);
  246. PixelARGB* const dst = (PixelARGB*) destData.getLinePointer (y);
  247. for (int x = 0; x < w; ++x)
  248. dst[x].set (src[x]);
  249. }
  250. }
  251. else
  252. {
  253. if (hasAlphaChannel())
  254. newImage.clear (getBounds());
  255. Graphics g (newImage);
  256. g.drawImageAt (*this, 0, 0);
  257. }
  258. return newImage;
  259. }
  260. NamedValueSet* Image::getProperties() const
  261. {
  262. return image == nullptr ? nullptr : &(image->userData);
  263. }
  264. //==============================================================================
  265. Image::BitmapData::BitmapData (Image& image, const int x, const int y, const int w, const int h, BitmapData::ReadWriteMode mode)
  266. : width (w),
  267. height (h)
  268. {
  269. // The BitmapData class must be given a valid image, and a valid rectangle within it!
  270. jassert (image.image != nullptr);
  271. jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= image.getWidth() && y + h <= image.getHeight());
  272. image.image->initialiseBitmapData (*this, x, y, mode);
  273. jassert (data != nullptr && pixelStride > 0 && lineStride != 0);
  274. }
  275. Image::BitmapData::BitmapData (const Image& image, const int x, const int y, const int w, const int h)
  276. : width (w),
  277. height (h)
  278. {
  279. // The BitmapData class must be given a valid image, and a valid rectangle within it!
  280. jassert (image.image != nullptr);
  281. jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= image.getWidth() && y + h <= image.getHeight());
  282. image.image->initialiseBitmapData (*this, x, y, readOnly);
  283. jassert (data != nullptr && pixelStride > 0 && lineStride != 0);
  284. }
  285. Image::BitmapData::BitmapData (const Image& image, BitmapData::ReadWriteMode mode)
  286. : width (image.getWidth()),
  287. height (image.getHeight())
  288. {
  289. // The BitmapData class must be given a valid image!
  290. jassert (image.image != nullptr);
  291. image.image->initialiseBitmapData (*this, 0, 0, mode);
  292. jassert (data != nullptr && pixelStride > 0 && lineStride != 0);
  293. }
  294. Image::BitmapData::~BitmapData()
  295. {
  296. }
  297. Colour Image::BitmapData::getPixelColour (const int x, const int y) const noexcept
  298. {
  299. jassert (isPositiveAndBelow (x, width) && isPositiveAndBelow (y, height));
  300. const uint8* const pixel = getPixelPointer (x, y);
  301. switch (pixelFormat)
  302. {
  303. case Image::ARGB: return Colour (((const PixelARGB*) pixel)->getUnpremultipliedARGB());
  304. case Image::RGB: return Colour (((const PixelRGB*) pixel)->getUnpremultipliedARGB());
  305. case Image::SingleChannel: return Colour (((const PixelAlpha*) pixel)->getUnpremultipliedARGB());
  306. default: jassertfalse; break;
  307. }
  308. return Colour();
  309. }
  310. void Image::BitmapData::setPixelColour (const int x, const int y, const Colour& colour) const noexcept
  311. {
  312. jassert (isPositiveAndBelow (x, width) && isPositiveAndBelow (y, height));
  313. uint8* const pixel = getPixelPointer (x, y);
  314. const PixelARGB col (colour.getPixelARGB());
  315. switch (pixelFormat)
  316. {
  317. case Image::ARGB: ((PixelARGB*) pixel)->set (col); break;
  318. case Image::RGB: ((PixelRGB*) pixel)->set (col); break;
  319. case Image::SingleChannel: ((PixelAlpha*) pixel)->set (col); break;
  320. default: jassertfalse; break;
  321. }
  322. }
  323. //==============================================================================
  324. void Image::clear (const Rectangle<int>& area, const Colour& colourToClearTo)
  325. {
  326. const ScopedPointer<LowLevelGraphicsContext> g (image->createLowLevelContext());
  327. g->setFill (colourToClearTo);
  328. g->fillRect (area, true);
  329. }
  330. //==============================================================================
  331. Colour Image::getPixelAt (const int x, const int y) const
  332. {
  333. if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight()))
  334. {
  335. const BitmapData srcData (*this, x, y, 1, 1);
  336. return srcData.getPixelColour (0, 0);
  337. }
  338. return Colour();
  339. }
  340. void Image::setPixelAt (const int x, const int y, const Colour& colour)
  341. {
  342. if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight()))
  343. {
  344. const BitmapData destData (*this, x, y, 1, 1, BitmapData::writeOnly);
  345. destData.setPixelColour (0, 0, colour);
  346. }
  347. }
  348. void Image::multiplyAlphaAt (const int x, const int y, const float multiplier)
  349. {
  350. if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight())
  351. && hasAlphaChannel())
  352. {
  353. const BitmapData destData (*this, x, y, 1, 1, BitmapData::readWrite);
  354. if (isARGB())
  355. ((PixelARGB*) destData.data)->multiplyAlpha (multiplier);
  356. else
  357. *(destData.data) = (uint8) (*(destData.data) * multiplier);
  358. }
  359. }
  360. template <class PixelType>
  361. struct PixelIterator
  362. {
  363. template <class PixelOperation>
  364. static void iterate (const Image::BitmapData& data, const PixelOperation& pixelOp)
  365. {
  366. for (int y = 0; y < data.height; ++y)
  367. {
  368. uint8* p = data.getLinePointer (y);
  369. for (int x = 0; x < data.width; ++x)
  370. {
  371. pixelOp (*(PixelType*) p);
  372. p += data.pixelStride;
  373. }
  374. }
  375. }
  376. };
  377. template <class PixelOperation>
  378. static void performPixelOp (const Image::BitmapData& data, const PixelOperation& pixelOp)
  379. {
  380. switch (data.pixelFormat)
  381. {
  382. case Image::ARGB: PixelIterator<PixelARGB> ::iterate (data, pixelOp); break;
  383. case Image::RGB: PixelIterator<PixelRGB> ::iterate (data, pixelOp); break;
  384. case Image::SingleChannel: PixelIterator<PixelAlpha>::iterate (data, pixelOp); break;
  385. default: jassertfalse; break;
  386. }
  387. }
  388. struct AlphaMultiplyOp
  389. {
  390. AlphaMultiplyOp (float alpha_) noexcept : alpha (alpha_) {}
  391. const float alpha;
  392. template <class PixelType>
  393. void operator() (PixelType& pixel) const
  394. {
  395. pixel.multiplyAlpha (alpha);
  396. }
  397. JUCE_DECLARE_NON_COPYABLE (AlphaMultiplyOp);
  398. };
  399. void Image::multiplyAllAlphas (const float amountToMultiplyBy)
  400. {
  401. jassert (hasAlphaChannel());
  402. const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite);
  403. performPixelOp (destData, AlphaMultiplyOp (amountToMultiplyBy));
  404. }
  405. struct DesaturateOp
  406. {
  407. template <class PixelType>
  408. void operator() (PixelType& pixel) const
  409. {
  410. pixel.desaturate();
  411. }
  412. };
  413. void Image::desaturate()
  414. {
  415. if (isARGB() || isRGB())
  416. {
  417. const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite);
  418. performPixelOp (destData, DesaturateOp());
  419. }
  420. }
  421. void Image::createSolidAreaMask (RectangleList& result, const float alphaThreshold) const
  422. {
  423. if (hasAlphaChannel())
  424. {
  425. const uint8 threshold = (uint8) jlimit (0, 255, roundToInt (alphaThreshold * 255.0f));
  426. SparseSet<int> pixelsOnRow;
  427. const BitmapData srcData (*this, 0, 0, getWidth(), getHeight());
  428. for (int y = 0; y < srcData.height; ++y)
  429. {
  430. pixelsOnRow.clear();
  431. const uint8* lineData = srcData.getLinePointer (y);
  432. if (isARGB())
  433. {
  434. for (int x = 0; x < srcData.width; ++x)
  435. {
  436. if (((const PixelARGB*) lineData)->getAlpha() >= threshold)
  437. pixelsOnRow.addRange (Range<int> (x, x + 1));
  438. lineData += srcData.pixelStride;
  439. }
  440. }
  441. else
  442. {
  443. for (int x = 0; x < srcData.width; ++x)
  444. {
  445. if (*lineData >= threshold)
  446. pixelsOnRow.addRange (Range<int> (x, x + 1));
  447. lineData += srcData.pixelStride;
  448. }
  449. }
  450. for (int i = 0; i < pixelsOnRow.getNumRanges(); ++i)
  451. {
  452. const Range<int> range (pixelsOnRow.getRange (i));
  453. result.add (Rectangle<int> (range.getStart(), y, range.getLength(), 1));
  454. }
  455. result.consolidate();
  456. }
  457. }
  458. else
  459. {
  460. result.add (0, 0, getWidth(), getHeight());
  461. }
  462. }
  463. void Image::moveImageSection (int dx, int dy,
  464. int sx, int sy,
  465. int w, int h)
  466. {
  467. if (dx < 0)
  468. {
  469. w += dx;
  470. sx -= dx;
  471. dx = 0;
  472. }
  473. if (dy < 0)
  474. {
  475. h += dy;
  476. sy -= dy;
  477. dy = 0;
  478. }
  479. if (sx < 0)
  480. {
  481. w += sx;
  482. dx -= sx;
  483. sx = 0;
  484. }
  485. if (sy < 0)
  486. {
  487. h += sy;
  488. dy -= sy;
  489. sy = 0;
  490. }
  491. const int minX = jmin (dx, sx);
  492. const int minY = jmin (dy, sy);
  493. w = jmin (w, getWidth() - jmax (sx, dx));
  494. h = jmin (h, getHeight() - jmax (sy, dy));
  495. if (w > 0 && h > 0)
  496. {
  497. const int maxX = jmax (dx, sx) + w;
  498. const int maxY = jmax (dy, sy) + h;
  499. const BitmapData destData (*this, minX, minY, maxX - minX, maxY - minY, BitmapData::readWrite);
  500. uint8* dst = destData.getPixelPointer (dx - minX, dy - minY);
  501. const uint8* src = destData.getPixelPointer (sx - minX, sy - minY);
  502. const size_t lineSize = (size_t) (destData.pixelStride * w);
  503. if (dy > sy)
  504. {
  505. while (--h >= 0)
  506. {
  507. const int offset = h * destData.lineStride;
  508. memmove (dst + offset, src + offset, lineSize);
  509. }
  510. }
  511. else if (dst != src)
  512. {
  513. while (--h >= 0)
  514. {
  515. memmove (dst, src, lineSize);
  516. dst += destData.lineStride;
  517. src += destData.lineStride;
  518. }
  519. }
  520. }
  521. }
  522. END_JUCE_NAMESPACE