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.

634 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 LowLevelGraphicsSoftwareRenderer (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* const src = (const PixelARGB*) srcData.getLinePointer (y);
  196. uint8* const dst = destData.getLinePointer (y);
  197. for (int x = 0; x < w; ++x)
  198. dst[x] = src[x].getAlpha();
  199. }
  200. }
  201. }
  202. else if (image->format == SingleChannel && newFormat == Image::ARGB)
  203. {
  204. const BitmapData destData (newImage, 0, 0, w, h, BitmapData::writeOnly);
  205. const BitmapData srcData (*this, 0, 0, w, h);
  206. for (int y = 0; y < h; ++y)
  207. {
  208. const PixelAlpha* const src = (const PixelAlpha*) srcData.getLinePointer (y);
  209. PixelARGB* const dst = (PixelARGB*) destData.getLinePointer (y);
  210. for (int x = 0; x < w; ++x)
  211. dst[x].set (src[x]);
  212. }
  213. }
  214. else
  215. {
  216. if (hasAlphaChannel())
  217. newImage.clear (getBounds());
  218. Graphics g (newImage);
  219. g.drawImageAt (*this, 0, 0);
  220. }
  221. return newImage;
  222. }
  223. NamedValueSet* Image::getProperties() const
  224. {
  225. return image == nullptr ? nullptr : &(image->userData);
  226. }
  227. //==============================================================================
  228. Image::BitmapData::BitmapData (Image& image, const int x, const int y, const int w, const int h, BitmapData::ReadWriteMode mode)
  229. : width (w),
  230. height (h)
  231. {
  232. // The BitmapData class must be given a valid image, and a valid rectangle within it!
  233. jassert (image.image != nullptr);
  234. jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= image.getWidth() && y + h <= image.getHeight());
  235. image.image->initialiseBitmapData (*this, x, y, mode);
  236. jassert (data != nullptr && pixelStride > 0 && lineStride != 0);
  237. }
  238. Image::BitmapData::BitmapData (const Image& image, const int x, const int y, const int w, const int h)
  239. : width (w),
  240. height (h)
  241. {
  242. // The BitmapData class must be given a valid image, and a valid rectangle within it!
  243. jassert (image.image != nullptr);
  244. jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= image.getWidth() && y + h <= image.getHeight());
  245. image.image->initialiseBitmapData (*this, x, y, readOnly);
  246. jassert (data != nullptr && pixelStride > 0 && lineStride != 0);
  247. }
  248. Image::BitmapData::BitmapData (const Image& image, BitmapData::ReadWriteMode mode)
  249. : width (image.getWidth()),
  250. height (image.getHeight())
  251. {
  252. // The BitmapData class must be given a valid image!
  253. jassert (image.image != nullptr);
  254. image.image->initialiseBitmapData (*this, 0, 0, mode);
  255. jassert (data != nullptr && pixelStride > 0 && lineStride != 0);
  256. }
  257. Image::BitmapData::~BitmapData()
  258. {
  259. }
  260. const Colour Image::BitmapData::getPixelColour (const int x, const int y) const noexcept
  261. {
  262. jassert (isPositiveAndBelow (x, width) && isPositiveAndBelow (y, height));
  263. const uint8* const pixel = getPixelPointer (x, y);
  264. switch (pixelFormat)
  265. {
  266. case Image::ARGB: return Colour (((const PixelARGB*) pixel)->getUnpremultipliedARGB());
  267. case Image::RGB: return Colour (((const PixelRGB*) pixel)->getUnpremultipliedARGB());
  268. case Image::SingleChannel: return Colour (((const PixelAlpha*) pixel)->getUnpremultipliedARGB());
  269. default: jassertfalse; break;
  270. }
  271. return Colour();
  272. }
  273. void Image::BitmapData::setPixelColour (const int x, const int y, const Colour& colour) const noexcept
  274. {
  275. jassert (isPositiveAndBelow (x, width) && isPositiveAndBelow (y, height));
  276. uint8* const pixel = getPixelPointer (x, y);
  277. const PixelARGB col (colour.getPixelARGB());
  278. switch (pixelFormat)
  279. {
  280. case Image::ARGB: ((PixelARGB*) pixel)->set (col); break;
  281. case Image::RGB: ((PixelRGB*) pixel)->set (col); break;
  282. case Image::SingleChannel: *pixel = col.getAlpha(); break;
  283. default: jassertfalse; break;
  284. }
  285. }
  286. //==============================================================================
  287. void Image::clear (const Rectangle<int>& area, const Colour& colourToClearTo)
  288. {
  289. const Rectangle<int> clipped (area.getIntersection (getBounds()));
  290. if (! clipped.isEmpty())
  291. {
  292. const PixelARGB col (colourToClearTo.getPixelARGB());
  293. const BitmapData destData (*this, clipped.getX(), clipped.getY(), clipped.getWidth(), clipped.getHeight(), BitmapData::writeOnly);
  294. uint8* dest = destData.data;
  295. int dh = clipped.getHeight();
  296. while (--dh >= 0)
  297. {
  298. uint8* line = dest;
  299. dest += destData.lineStride;
  300. if (isARGB())
  301. {
  302. for (int x = clipped.getWidth(); --x >= 0;)
  303. {
  304. ((PixelARGB*) line)->set (col);
  305. line += destData.pixelStride;
  306. }
  307. }
  308. else if (isRGB())
  309. {
  310. for (int x = clipped.getWidth(); --x >= 0;)
  311. {
  312. ((PixelRGB*) line)->set (col);
  313. line += destData.pixelStride;
  314. }
  315. }
  316. else
  317. {
  318. for (int x = clipped.getWidth(); --x >= 0;)
  319. {
  320. *line = col.getAlpha();
  321. line += destData.pixelStride;
  322. }
  323. }
  324. }
  325. }
  326. }
  327. //==============================================================================
  328. const Colour Image::getPixelAt (const int x, const int y) const
  329. {
  330. if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight()))
  331. {
  332. const BitmapData srcData (*this, x, y, 1, 1);
  333. return srcData.getPixelColour (0, 0);
  334. }
  335. return Colour();
  336. }
  337. void Image::setPixelAt (const int x, const int y, const Colour& colour)
  338. {
  339. if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight()))
  340. {
  341. const BitmapData destData (*this, x, y, 1, 1, BitmapData::writeOnly);
  342. destData.setPixelColour (0, 0, colour);
  343. }
  344. }
  345. void Image::multiplyAlphaAt (const int x, const int y, const float multiplier)
  346. {
  347. if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight())
  348. && hasAlphaChannel())
  349. {
  350. const BitmapData destData (*this, x, y, 1, 1, BitmapData::readWrite);
  351. if (isARGB())
  352. ((PixelARGB*) destData.data)->multiplyAlpha (multiplier);
  353. else
  354. *(destData.data) = (uint8) (*(destData.data) * multiplier);
  355. }
  356. }
  357. void Image::multiplyAllAlphas (const float amountToMultiplyBy)
  358. {
  359. if (hasAlphaChannel())
  360. {
  361. const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite);
  362. if (isARGB())
  363. {
  364. for (int y = 0; y < destData.height; ++y)
  365. {
  366. uint8* p = destData.getLinePointer (y);
  367. for (int x = 0; x < destData.width; ++x)
  368. {
  369. ((PixelARGB*) p)->multiplyAlpha (amountToMultiplyBy);
  370. p += destData.pixelStride;
  371. }
  372. }
  373. }
  374. else
  375. {
  376. for (int y = 0; y < destData.height; ++y)
  377. {
  378. uint8* p = destData.getLinePointer (y);
  379. for (int x = 0; x < destData.width; ++x)
  380. {
  381. *p = (uint8) (*p * amountToMultiplyBy);
  382. p += destData.pixelStride;
  383. }
  384. }
  385. }
  386. }
  387. else
  388. {
  389. jassertfalse; // can't do this without an alpha-channel!
  390. }
  391. }
  392. void Image::desaturate()
  393. {
  394. if (isARGB() || isRGB())
  395. {
  396. const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite);
  397. if (isARGB())
  398. {
  399. for (int y = 0; y < destData.height; ++y)
  400. {
  401. uint8* p = destData.getLinePointer (y);
  402. for (int x = 0; x < destData.width; ++x)
  403. {
  404. ((PixelARGB*) p)->desaturate();
  405. p += destData.pixelStride;
  406. }
  407. }
  408. }
  409. else
  410. {
  411. for (int y = 0; y < destData.height; ++y)
  412. {
  413. uint8* p = destData.getLinePointer (y);
  414. for (int x = 0; x < destData.width; ++x)
  415. {
  416. ((PixelRGB*) p)->desaturate();
  417. p += destData.pixelStride;
  418. }
  419. }
  420. }
  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. }
  524. END_JUCE_NAMESPACE