Audio plugin host https://kx.studio/carla
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.

juce_Image.cpp 21KB

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