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