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.

623 lines
19KB

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