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.

614 lines
18KB

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