drawAPI.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
  1. 
  2. // zlib open source license
  3. //
  4. // Copyright (c) 2017 to 2025 David Forsgren Piuva
  5. //
  6. // This software is provided 'as-is', without any express or implied
  7. // warranty. In no event will the authors be held liable for any damages
  8. // arising from the use of this software.
  9. //
  10. // Permission is granted to anyone to use this software for any purpose,
  11. // including commercial applications, and to alter it and redistribute it
  12. // freely, subject to the following restrictions:
  13. //
  14. // 1. The origin of this software must not be misrepresented; you must not
  15. // claim that you wrote the original software. If you use this software
  16. // in a product, an acknowledgment in the product documentation would be
  17. // appreciated but is not required.
  18. //
  19. // 2. Altered source versions must be plainly marked as such, and must not be
  20. // misrepresented as being the original software.
  21. //
  22. // 3. This notice may not be removed or altered from any source
  23. // distribution.
  24. #include "imageAPI.h"
  25. #include "../math/scalar.h"
  26. #include "../image/PackOrder.h"
  27. #include <limits>
  28. namespace dsr {
  29. // Preconditions:
  30. // 0 <= a <= 255
  31. // 0 <= b <= 255
  32. // Postconditions:
  33. // Returns the normalized multiplication of a and b, where the 0..255 range represents decimal values from 0.0 to 1.0.
  34. // The result may not be less than zero or larger than any of the inputs.
  35. // Examples:
  36. // normalizedByteMultiplication(0, 0) = 0
  37. // normalizedByteMultiplication(x, 0) = 0
  38. // normalizedByteMultiplication(0, x) = 0
  39. // normalizedByteMultiplication(x, 255) = x
  40. // normalizedByteMultiplication(255, x) = x
  41. // normalizedByteMultiplication(255, 255) = 255
  42. inline uint32_t normalizedByteMultiplication(uint32_t a, uint32_t b) {
  43. // Approximate the reciprocal of an unsigned byte's maximum value 255 for normalization
  44. // 256³ / 255 ≈ 65793
  45. // Truncation goes down, so add half a unit before rounding to get the closest value
  46. // 2^24 / 2 = 8388608
  47. // No overflow for unsigned 32-bit integers
  48. // 255² * 65793 + 8388608 = 4286578433 < 2^32
  49. return (a * b * 65793 + 8388608) >> 24;
  50. }
  51. inline bool isUniformByte(uint16_t value) {
  52. return (value & 0xFF) == ((value & 0xFF00) >> 8);
  53. }
  54. inline bool isUniformByte(uint32_t value) {
  55. uint32_t least = value & 0x000000FF;
  56. return least == ((value & 0x0000FF00) >> 8)
  57. && least == ((value & 0x00FF0000) >> 16)
  58. && least == ((value & 0xFF000000) >> 24);
  59. }
  60. // -------------------------------- Drawing shapes --------------------------------
  61. // TODO: Use the longest available SIMD vector to assign a color and overwrite padding when the image is not a sub-image.
  62. // Create a safe and reusable 32-bit memset function in SafePointer.h.
  63. template <typename IMAGE_TYPE, typename COLOR_TYPE>
  64. void drawSolidRectangleAssign(const IMAGE_TYPE &target, int32_t left, int32_t top, int32_t right, int32_t bottom, COLOR_TYPE color) {
  65. int32_t leftBound = max(0, left);
  66. int32_t topBound = max(0, top);
  67. int32_t rightBound = min(right, image_getWidth(target));
  68. int32_t bottomBound = min(bottom, image_getHeight(target));
  69. int32_t stride = image_getStride(target);
  70. SafePointer<COLOR_TYPE> rowData = image_getSafePointer<COLOR_TYPE>(target, topBound);
  71. rowData += leftBound;
  72. for (int32_t y = topBound; y < bottomBound; y++) {
  73. SafePointer<COLOR_TYPE> pixelData = rowData;
  74. for (int32_t x = leftBound; x < rightBound; x++) {
  75. pixelData.get() = color;
  76. pixelData += 1;
  77. }
  78. rowData.increaseBytes(stride);
  79. }
  80. }
  81. template <typename IMAGE_TYPE, typename COLOR_TYPE>
  82. void drawSolidRectangleMemset(const IMAGE_TYPE &target, int32_t left, int32_t top, int32_t right, int32_t bottom, uint8_t uniformByte) {
  83. int32_t leftBound = max(0, left);
  84. int32_t topBound = max(0, top);
  85. int32_t rightBound = min(right, image_getWidth(target));
  86. int32_t bottomBound = min(bottom, image_getHeight(target));
  87. if (rightBound > leftBound && bottomBound > topBound) {
  88. int32_t stride = image_getStride(target);
  89. SafePointer<COLOR_TYPE> rowData = image_getSafePointer<COLOR_TYPE>(target, topBound);
  90. rowData += leftBound;
  91. int32_t filledWidth = rightBound - leftBound;
  92. int32_t rowSize = filledWidth * sizeof(COLOR_TYPE);
  93. int32_t rowCount = bottomBound - topBound;
  94. if ((!target.impl_dimensions.isSubImage()) && filledWidth == image_getWidth(target)) {
  95. // Write over any padding for parent images owning the whole buffer.
  96. // Including parent images with sub-images using the same data
  97. // because no child image may display the parent-image's padding bytes.
  98. safeMemorySet(rowData, uniformByte, (stride * (rowCount - 1)) + rowSize);
  99. } else if (rowSize == stride) {
  100. // When the filled row stretches all the way from left to right in the main allocation
  101. // there's no unseen pixels being overwritten in other images sharing the buffer.
  102. // This case handles sub-images that uses the full width of
  103. // the parent image which doesn't have any padding.
  104. safeMemorySet(rowData, uniformByte, rowSize * rowCount);
  105. } else {
  106. // Fall back on using one memset operation per row.
  107. // This case is for sub-images that must preserve interleaved pixel rows belonging
  108. // to other images that aren't visible and therefore not owned by this image.
  109. for (int32_t y = topBound; y < bottomBound; y++) {
  110. safeMemorySet(rowData, uniformByte, rowSize);
  111. rowData.increaseBytes(stride);
  112. }
  113. }
  114. }
  115. }
  116. void draw_rectangle(const ImageU8& image, const IRect& bound, int32_t color) {
  117. if (image_exists(image)) {
  118. if (color < 0) { color = 0; }
  119. if (color > 255) { color = 255; }
  120. drawSolidRectangleMemset<ImageU8, uint8_t>(image, bound.left(), bound.top(), bound.right(), bound.bottom(), color);
  121. }
  122. }
  123. void draw_rectangle(const ImageU16& image, const IRect& bound, int32_t color) {
  124. if (image_exists(image)) {
  125. if (color < 0) { color = 0; }
  126. if (color > 65535) { color = 65535; }
  127. uint16_t uColor = color;
  128. if (isUniformByte(uColor)) {
  129. drawSolidRectangleMemset<ImageU16, uint16_t>(image, bound.left(), bound.top(), bound.right(), bound.bottom(), 0);
  130. } else {
  131. drawSolidRectangleAssign<ImageU16, uint16_t>(image, bound.left(), bound.top(), bound.right(), bound.bottom(), uColor);
  132. }
  133. }
  134. }
  135. void draw_rectangle(const ImageF32& image, const IRect& bound, float color) {
  136. if (image_exists(image)) {
  137. // Floating-point zero is a special value where all bits are assigned zeroes to allow fast initialization.
  138. if (color == 0.0f) {
  139. drawSolidRectangleMemset<ImageF32, float>(image, bound.left(), bound.top(), bound.right(), bound.bottom(), 0);
  140. } else {
  141. drawSolidRectangleAssign<ImageF32, float>(image, bound.left(), bound.top(), bound.right(), bound.bottom(), color);
  142. }
  143. }
  144. }
  145. void draw_rectangle(const ImageRgbaU8& image, const IRect& bound, uint32_t packedColor) {
  146. if (image_exists(image)) {
  147. if (isUniformByte(packedColor)) {
  148. drawSolidRectangleMemset<ImageRgbaU8, uint32_t>(image, bound.left(), bound.top(), bound.right(), bound.bottom(), packedColor & 0xFF);
  149. } else {
  150. drawSolidRectangleAssign<ImageRgbaU8, uint32_t>(image, bound.left(), bound.top(), bound.right(), bound.bottom(), packedColor);
  151. }
  152. }
  153. }
  154. void draw_rectangle(const ImageRgbaU8& image, const IRect& bound, const ColorRgbaI32& color) {
  155. if (image_exists(image)) {
  156. uint32_t packedColor = image_saturateAndPack(image, color);
  157. if (isUniformByte(packedColor)) {
  158. drawSolidRectangleMemset<ImageRgbaU8, uint32_t>(image, bound.left(), bound.top(), bound.right(), bound.bottom(), packedColor & 0xFF);
  159. } else {
  160. drawSolidRectangleAssign<ImageRgbaU8, uint32_t>(image, bound.left(), bound.top(), bound.right(), bound.bottom(), packedColor);
  161. }
  162. }
  163. }
  164. template <typename IMAGE_TYPE, typename COLOR_TYPE>
  165. inline void drawLineSuper(const IMAGE_TYPE &target, int32_t x1, int32_t y1, int32_t x2, int32_t y2, COLOR_TYPE color) {
  166. // Culling test to reduce wasted pixels outside of the image.
  167. int32_t width = image_getWidth(target);
  168. int32_t height = image_getHeight(target);
  169. if ((x1 < 0 && x1 < 0) || (y1 < 0 && y1 < 0) || (x1 >= width && x1 >= width) || (y1 >= height && y1 >= height)) {
  170. // Skip drawing because both points are outside of the same edge.
  171. return;
  172. }
  173. if (y1 == y2) {
  174. // Sideways
  175. int32_t left = min(x1, x2);
  176. int32_t right = max(x1, x2);
  177. for (int32_t x = left; x <= right; x++) {
  178. image_writePixel(target, x, y1, color);
  179. }
  180. } else if (x1 == x2) {
  181. // Down
  182. int32_t top = min(y1, y2);
  183. int32_t bottom = max(y1, y2);
  184. for (int32_t y = top; y <= bottom; y++) {
  185. image_writePixel(target, x1, y, color);
  186. }
  187. } else {
  188. if (std::abs(y2 - y1) >= std::abs(x2 - x1)) {
  189. if (y2 < y1) {
  190. swap(x1, x2);
  191. swap(y1, y2);
  192. }
  193. assert(y2 > y1);
  194. if (x2 > x1) {
  195. // Down right
  196. int32_t x = x1;
  197. int32_t y = y1;
  198. int32_t tilt = (x2 - x1) * 2;
  199. int32_t maxError = y2 - y1;
  200. int32_t error = 0;
  201. while (y <= y2) {
  202. image_writePixel(target, x, y, color);
  203. error += tilt;
  204. if (error >= maxError) {
  205. x++;
  206. error -= maxError * 2;
  207. }
  208. y++;
  209. }
  210. } else {
  211. // Down left
  212. int32_t x = x1;
  213. int32_t y = y1;
  214. int32_t tilt = (x1 - x2) * 2;
  215. int32_t maxError = y2 - y1;
  216. int32_t error = 0;
  217. while (y <= y2) {
  218. image_writePixel(target, x, y, color);
  219. error += tilt;
  220. if (error >= maxError) {
  221. x--;
  222. error -= maxError * 2;
  223. }
  224. y++;
  225. }
  226. }
  227. } else {
  228. if (x2 < x1) {
  229. swap(x1, x2);
  230. swap(y1, y2);
  231. }
  232. assert(x2 > x1);
  233. if (y2 > y1) {
  234. // Down right
  235. int32_t x = x1;
  236. int32_t y = y1;
  237. int32_t tilt = (y2 - y1) * 2;
  238. int32_t maxError = x2 - x1;
  239. int32_t error = 0;
  240. while (x <= x2) {
  241. image_writePixel(target, x, y, color);
  242. error += tilt;
  243. if (error >= maxError) {
  244. y++;
  245. error -= maxError * 2;
  246. }
  247. x++;
  248. }
  249. } else {
  250. // Up right
  251. int32_t x = x1;
  252. int32_t y = y1;
  253. int32_t tilt = (y1 - y2) * 2;
  254. int32_t maxError = x2 - x1;
  255. int32_t error = 0;
  256. while (x <= x2) {
  257. image_writePixel(target, x, y, color);
  258. error += tilt;
  259. if (error >= maxError) {
  260. y--;
  261. error -= maxError * 2;
  262. }
  263. x++;
  264. }
  265. }
  266. }
  267. }
  268. }
  269. void draw_line(const ImageU8& image, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t color) {
  270. if (image_exists(image)) {
  271. if (color < 0) { color = 0; }
  272. if (color > 255) { color = 255; }
  273. drawLineSuper<ImageU8, uint8_t>(image, x1, y1, x2, y2, color);
  274. }
  275. }
  276. void draw_line(const ImageU16& image, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t color) {
  277. if (image_exists(image)) {
  278. if (color < 0) { color = 0; }
  279. if (color > 65535) { color = 65535; }
  280. drawLineSuper<ImageU16, uint16_t>(image, x1, y1, x2, y2, color);
  281. }
  282. }
  283. void draw_line(const ImageF32& image, int32_t x1, int32_t y1, int32_t x2, int32_t y2, float color) {
  284. if (image_exists(image)) {
  285. drawLineSuper<ImageF32, float>(image, x1, y1, x2, y2, color);
  286. }
  287. }
  288. void draw_line(const ImageRgbaU8& image, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t packedColor) {
  289. if (image_exists(image)) {
  290. drawLineSuper<ImageRgbaU8, uint32_t>(image, x1, y1, x2, y2, packedColor);
  291. }
  292. }
  293. void draw_line(const ImageRgbaU8& image, int32_t x1, int32_t y1, int32_t x2, int32_t y2, const ColorRgbaI32& color) {
  294. uint32_t packedColor = image_saturateAndPack(image, color);
  295. draw_line(image, x1, y1, x2, y2, packedColor);
  296. }
  297. // -------------------------------- Drawing images --------------------------------
  298. // Unpacked image dimensions.
  299. struct UnpackedDimensions {
  300. // width is the number of used pixels on each row.
  301. // height is the number of rows.
  302. // stride is the byte offset from one row to another including any padding.
  303. // pixelSize is the byte offset from one pixel to another from left to right.
  304. int32_t width, height, stride, pixelSize;
  305. UnpackedDimensions() : width(0), height(0), stride(0), pixelSize(0) {}
  306. UnpackedDimensions(const Image& image) :
  307. width(image_getWidth(image)), height(image_getHeight(image)), stride(image_getStride(image)), pixelSize(image_getPixelSize(image)) {}
  308. };
  309. struct ImageWriter : public UnpackedDimensions {
  310. uint8_t *data;
  311. ImageWriter(const UnpackedDimensions &dimensions, uint8_t *data) :
  312. UnpackedDimensions(dimensions), data(data) {}
  313. };
  314. struct ImageReader : public UnpackedDimensions {
  315. const uint8_t *data;
  316. ImageReader(const UnpackedDimensions &dimensions, const uint8_t *data) :
  317. UnpackedDimensions(dimensions), data(data) {}
  318. };
  319. static ImageWriter getWriter(const Image &image) {
  320. return ImageWriter(UnpackedDimensions(image), buffer_dangerous_getUnsafeData(image.impl_buffer) + image.impl_dimensions.getByteStartOffset());
  321. }
  322. static ImageReader getReader(const Image &image) {
  323. return ImageReader(UnpackedDimensions(image), buffer_dangerous_getUnsafeData(image.impl_buffer) + image.impl_dimensions.getByteStartOffset());
  324. }
  325. static Image getGenericSubImage(const Image &image, int32_t left, int32_t top, int32_t width, int32_t height) {
  326. return Image(image, IRect(left, top, width, height));
  327. }
  328. struct ImageIntersection {
  329. ImageWriter subTarget;
  330. ImageReader subSource;
  331. ImageIntersection(const ImageWriter &subTarget, const ImageReader &subSource) :
  332. subTarget(subTarget), subSource(subSource) {}
  333. static bool canCreate(const Image &target, const Image &source, int32_t left, int32_t top) {
  334. int32_t targetRegionRight = left + image_getWidth(source);
  335. int32_t targetRegionBottom = top + image_getHeight(source);
  336. return left < image_getWidth(target) && top < image_getHeight(target) && targetRegionRight > 0 && targetRegionBottom > 0;
  337. }
  338. // Only call if canCreate passed with the same arguments
  339. static ImageIntersection create(const Image &target, const Image &source, int32_t left, int32_t top) {
  340. int32_t targetRegionRight = left + image_getWidth(source);
  341. int32_t targetRegionBottom = top + image_getHeight(source);
  342. assert(ImageIntersection::canCreate(target, source, left, top));
  343. // Check if the source has to be clipped
  344. if (left < 0 || top < 0 || targetRegionRight > image_getWidth(target) || targetRegionBottom > image_getHeight(target)) {
  345. int32_t clipLeft = max(0, -left);
  346. int32_t clipTop = max(0, -top);
  347. int32_t clipRight = max(0, targetRegionRight - image_getWidth(target));
  348. int32_t clipBottom = max(0, targetRegionBottom - image_getHeight(target));
  349. int32_t newWidth = image_getWidth(source) - (clipLeft + clipRight);
  350. int32_t newHeight = image_getHeight(source) - (clipTop + clipBottom);
  351. assert(newWidth > 0 && newHeight > 0);
  352. // Partial drawing
  353. Image subTarget = getGenericSubImage(target, left + clipLeft, top + clipTop, newWidth, newHeight);
  354. Image subSource = getGenericSubImage(source, clipLeft, clipTop, newWidth, newHeight);
  355. return ImageIntersection(getWriter(subTarget), getReader(subSource));
  356. } else {
  357. // Full drawing
  358. Image subTarget = getGenericSubImage(target, left, top, image_getWidth(source), image_getHeight(source));
  359. return ImageIntersection(getWriter(subTarget), getReader(source));
  360. }
  361. }
  362. };
  363. #define ITERATE_ROWS(WRITER, READER, OPERATION) \
  364. { \
  365. uint8_t *targetRow = WRITER.data; \
  366. const uint8_t *sourceRow = READER.data; \
  367. for (int32_t y = 0; y < READER.height; y++) { \
  368. OPERATION; \
  369. targetRow += WRITER.stride; \
  370. sourceRow += READER.stride; \
  371. } \
  372. }
  373. #define ITERATE_PIXELS(WRITER, READER, OPERATION) \
  374. { \
  375. uint8_t *targetRow = WRITER.data; \
  376. const uint8_t *sourceRow = READER.data; \
  377. for (int32_t y = 0; y < READER.height; y++) { \
  378. uint8_t *targetPixel = targetRow; \
  379. const uint8_t *sourcePixel = sourceRow; \
  380. for (int32_t x = 0; x < READER.width; x++) { \
  381. {OPERATION;} \
  382. targetPixel += WRITER.pixelSize; \
  383. sourcePixel += READER.pixelSize; \
  384. } \
  385. targetRow += WRITER.stride; \
  386. sourceRow += READER.stride; \
  387. } \
  388. }
  389. #define ITERATE_PIXELS_2(WRITER1, READER1, WRITER2, READER2, OPERATION) \
  390. { \
  391. uint8_t *targetRow1 = WRITER1.data; \
  392. uint8_t *targetRow2 = WRITER2.data; \
  393. const uint8_t *sourceRow1 = READER1.data; \
  394. const uint8_t *sourceRow2 = READER2.data; \
  395. int32_t minWidth = min(READER1.width, READER2.width); \
  396. int32_t minHeight = min(READER1.height, READER2.height); \
  397. for (int32_t y = 0; y < minHeight; y++) { \
  398. uint8_t *targetPixel1 = targetRow1; \
  399. uint8_t *targetPixel2 = targetRow2; \
  400. const uint8_t *sourcePixel1 = sourceRow1; \
  401. const uint8_t *sourcePixel2 = sourceRow2; \
  402. for (int32_t x = 0; x < minWidth; x++) { \
  403. {OPERATION;} \
  404. targetPixel1 += WRITER1.pixelSize; \
  405. targetPixel2 += WRITER2.pixelSize; \
  406. sourcePixel1 += READER1.pixelSize; \
  407. sourcePixel2 += READER2.pixelSize; \
  408. } \
  409. targetRow1 += WRITER1.stride; \
  410. targetRow2 += WRITER2.stride; \
  411. sourceRow1 += READER1.stride; \
  412. sourceRow2 += READER2.stride; \
  413. } \
  414. }
  415. #define ITERATE_PIXELS_3(WRITER1, READER1, WRITER2, READER2, WRITER3, READER3, OPERATION) \
  416. { \
  417. uint8_t *targetRow1 = WRITER1.data; \
  418. uint8_t *targetRow2 = WRITER2.data; \
  419. uint8_t *targetRow3 = WRITER3.data; \
  420. const uint8_t *sourceRow1 = READER1.data; \
  421. const uint8_t *sourceRow2 = READER2.data; \
  422. const uint8_t *sourceRow3 = READER3.data; \
  423. int32_t minWidth = min(min(READER1.width, READER2.width), READER3.width); \
  424. int32_t minHeight = min(min(READER1.height, READER2.height), READER3.height); \
  425. for (int32_t y = 0; y < minHeight; y++) { \
  426. uint8_t *targetPixel1 = targetRow1; \
  427. uint8_t *targetPixel2 = targetRow2; \
  428. uint8_t *targetPixel3 = targetRow3; \
  429. const uint8_t *sourcePixel1 = sourceRow1; \
  430. const uint8_t *sourcePixel2 = sourceRow2; \
  431. const uint8_t *sourcePixel3 = sourceRow3; \
  432. for (int32_t x = 0; x < minWidth; x++) { \
  433. {OPERATION;} \
  434. targetPixel1 += WRITER1.pixelSize; \
  435. targetPixel2 += WRITER2.pixelSize; \
  436. targetPixel3 += WRITER3.pixelSize; \
  437. sourcePixel1 += READER1.pixelSize; \
  438. sourcePixel2 += READER2.pixelSize; \
  439. sourcePixel3 += READER3.pixelSize; \
  440. } \
  441. targetRow1 += WRITER1.stride; \
  442. targetRow2 += WRITER2.stride; \
  443. targetRow3 += WRITER3.stride; \
  444. sourceRow1 += READER1.stride; \
  445. sourceRow2 += READER2.stride; \
  446. sourceRow3 += READER3.stride; \
  447. } \
  448. }
  449. inline int32_t saturateFloat(float value) {
  450. if (!(value >= 0.5f)) {
  451. // NaN or negative
  452. return 0;
  453. } else if (value > 254.5f) {
  454. // Too large
  455. return 255;
  456. } else {
  457. // Round to closest
  458. return (uint8_t)(value + 0.5f);
  459. }
  460. }
  461. // Copy data from one image region to another of the same size.
  462. // Packing order is reinterpreted without conversion.
  463. static void copyImageData(ImageWriter writer, ImageReader reader) {
  464. assert(writer.width == reader.width && writer.height == reader.height && writer.pixelSize == reader.pixelSize);
  465. ITERATE_ROWS(writer, reader, std::memcpy(targetRow, sourceRow, reader.width * reader.pixelSize));
  466. }
  467. // TODO: Can SIMD be used for specific platforms where vector extract accepts a variable offset?
  468. static void imageImpl_drawCopy(const ImageRgbaU8& target, const ImageRgbaU8& source, int32_t left, int32_t top) {
  469. PackOrderIndex targetPackOrderIndex = image_getPackOrderIndex(target);
  470. PackOrderIndex sourcePackOrderIndex = image_getPackOrderIndex(source);
  471. if (ImageIntersection::canCreate(target, source, left, top)) {
  472. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  473. if (targetPackOrderIndex == sourcePackOrderIndex) {
  474. // No conversion needed
  475. copyImageData(intersection.subTarget, intersection.subSource);
  476. } else {
  477. PackOrder targetPackOrder = PackOrder::getPackOrder(targetPackOrderIndex);
  478. PackOrder sourcePackOrder = PackOrder::getPackOrder(sourcePackOrderIndex);
  479. // Read and repack to convert between different color formats
  480. // TODO: Pre-compute conversions for each combination of source and target pack order.
  481. // We do not need to store the data in RGBA order, just pack from one format to another.
  482. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  483. targetPixel[targetPackOrder.redIndex] = sourcePixel[sourcePackOrder.redIndex];
  484. targetPixel[targetPackOrder.greenIndex] = sourcePixel[sourcePackOrder.greenIndex];
  485. targetPixel[targetPackOrder.blueIndex] = sourcePixel[sourcePackOrder.blueIndex];
  486. targetPixel[targetPackOrder.alphaIndex] = sourcePixel[sourcePackOrder.alphaIndex];
  487. );
  488. }
  489. }
  490. }
  491. static void imageImpl_drawCopy(const ImageU8& target, const ImageU8& source, int32_t left, int32_t top) {
  492. if (ImageIntersection::canCreate(target, source, left, top)) {
  493. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  494. copyImageData(intersection.subTarget, intersection.subSource);
  495. }
  496. }
  497. static void imageImpl_drawCopy(const ImageU16& target, const ImageU16& source, int32_t left, int32_t top) {
  498. if (ImageIntersection::canCreate(target, source, left, top)) {
  499. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  500. copyImageData(intersection.subTarget, intersection.subSource);
  501. }
  502. }
  503. static void imageImpl_drawCopy(const ImageF32& target, const ImageF32& source, int32_t left, int32_t top) {
  504. if (ImageIntersection::canCreate(target, source, left, top)) {
  505. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  506. copyImageData(intersection.subTarget, intersection.subSource);
  507. }
  508. }
  509. static void imageImpl_drawCopy(const ImageRgbaU8& target, const ImageU8& source, int32_t left, int32_t top) {
  510. if (ImageIntersection::canCreate(target, source, left, top)) {
  511. PackOrder targetPackOrder = image_getPackOrder(target);
  512. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  513. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  514. uint8_t luma = *sourcePixel;
  515. targetPixel[targetPackOrder.redIndex] = luma;
  516. targetPixel[targetPackOrder.greenIndex] = luma;
  517. targetPixel[targetPackOrder.blueIndex] = luma;
  518. targetPixel[targetPackOrder.alphaIndex] = 255;
  519. );
  520. }
  521. }
  522. static void imageImpl_drawCopy(const ImageRgbaU8& target, const ImageU16& source, int32_t left, int32_t top) {
  523. if (ImageIntersection::canCreate(target, source, left, top)) {
  524. PackOrder targetPackOrder = image_getPackOrder(target);
  525. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  526. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  527. int32_t luma = *((const uint16_t*)sourcePixel);
  528. if (luma > 255) { luma = 255; }
  529. targetPixel[targetPackOrder.redIndex] = luma;
  530. targetPixel[targetPackOrder.greenIndex] = luma;
  531. targetPixel[targetPackOrder.blueIndex] = luma;
  532. targetPixel[targetPackOrder.alphaIndex] = 255;
  533. );
  534. }
  535. }
  536. static void imageImpl_drawCopy(const ImageRgbaU8& target, const ImageF32& source, int32_t left, int32_t top) {
  537. if (ImageIntersection::canCreate(target, source, left, top)) {
  538. PackOrder targetPackOrder = image_getPackOrder(target);
  539. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  540. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  541. int32_t luma = saturateFloat(*((const float*)sourcePixel));
  542. targetPixel[targetPackOrder.redIndex] = luma;
  543. targetPixel[targetPackOrder.greenIndex] = luma;
  544. targetPixel[targetPackOrder.blueIndex] = luma;
  545. targetPixel[targetPackOrder.alphaIndex] = 255;
  546. );
  547. }
  548. }
  549. static void imageImpl_drawCopy(const ImageU8& target, const ImageF32& source, int32_t left, int32_t top) {
  550. if (ImageIntersection::canCreate(target, source, left, top)) {
  551. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  552. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  553. *targetPixel = saturateFloat(*((const float*)sourcePixel));
  554. );
  555. }
  556. }
  557. static void imageImpl_drawCopy(const ImageU8& target, const ImageU16& source, int32_t left, int32_t top) {
  558. if (ImageIntersection::canCreate(target, source, left, top)) {
  559. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  560. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  561. int32_t luma = *((const uint16_t*)sourcePixel);
  562. if (luma > 255) { luma = 255; }
  563. *targetPixel = luma;
  564. );
  565. }
  566. }
  567. static void imageImpl_drawCopy(const ImageU16& target, const ImageU8& source, int32_t left, int32_t top) {
  568. if (ImageIntersection::canCreate(target, source, left, top)) {
  569. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  570. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  571. *((uint16_t*)targetPixel) = *sourcePixel;
  572. );
  573. }
  574. }
  575. static void imageImpl_drawCopy(const ImageU16& target, const ImageF32& source, int32_t left, int32_t top) {
  576. if (ImageIntersection::canCreate(target, source, left, top)) {
  577. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  578. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  579. int32_t luma = *((const float*)sourcePixel);
  580. if (luma < 0) { luma = 0; }
  581. if (luma > 65535) { luma = 65535; }
  582. *((uint16_t*)targetPixel) = *sourcePixel;
  583. );
  584. }
  585. }
  586. static void imageImpl_drawCopy(const ImageF32& target, const ImageU8& source, int32_t left, int32_t top) {
  587. if (ImageIntersection::canCreate(target, source, left, top)) {
  588. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  589. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  590. *((float*)targetPixel) = (float)(*sourcePixel);
  591. );
  592. }
  593. }
  594. static void imageImpl_drawCopy(const ImageF32& target, const ImageU16& source, int32_t left, int32_t top) {
  595. if (ImageIntersection::canCreate(target, source, left, top)) {
  596. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  597. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  598. int32_t luma = *((const uint16_t*)sourcePixel);
  599. if (luma > 255) { luma = 255; }
  600. *((float*)targetPixel) = (float)luma;
  601. );
  602. }
  603. }
  604. static void imageImpl_drawAlphaFilter(const ImageRgbaU8& target, const ImageRgbaU8& source, int32_t left, int32_t top) {
  605. if (ImageIntersection::canCreate(target, source, left, top)) {
  606. PackOrder targetPackOrder = image_getPackOrder(target);
  607. PackOrder sourcePackOrder = image_getPackOrder(source);
  608. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  609. // Read and repack to convert between different color formats
  610. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  611. // Optimized for anti-aliasing, where most alpha values are 0 or 255
  612. uint32_t sourceRatio = sourcePixel[sourcePackOrder.alphaIndex];
  613. if (sourceRatio > 0) {
  614. if (sourceRatio == 255) {
  615. targetPixel[targetPackOrder.redIndex] = sourcePixel[sourcePackOrder.redIndex];
  616. targetPixel[targetPackOrder.greenIndex] = sourcePixel[sourcePackOrder.greenIndex];
  617. targetPixel[targetPackOrder.blueIndex] = sourcePixel[sourcePackOrder.blueIndex];
  618. targetPixel[targetPackOrder.alphaIndex] = 255;
  619. } else {
  620. uint32_t targetRatio = 255 - sourceRatio;
  621. targetPixel[targetPackOrder.redIndex] = normalizedByteMultiplication(targetPixel[targetPackOrder.redIndex], targetRatio) + normalizedByteMultiplication(sourcePixel[sourcePackOrder.redIndex], sourceRatio);
  622. targetPixel[targetPackOrder.greenIndex] = normalizedByteMultiplication(targetPixel[targetPackOrder.greenIndex], targetRatio) + normalizedByteMultiplication(sourcePixel[sourcePackOrder.greenIndex], sourceRatio);
  623. targetPixel[targetPackOrder.blueIndex] = normalizedByteMultiplication(targetPixel[targetPackOrder.blueIndex], targetRatio) + normalizedByteMultiplication(sourcePixel[sourcePackOrder.blueIndex], sourceRatio);
  624. targetPixel[targetPackOrder.alphaIndex] = normalizedByteMultiplication(targetPixel[targetPackOrder.alphaIndex], targetRatio) + sourceRatio;
  625. }
  626. }
  627. );
  628. }
  629. }
  630. static void imageImpl_drawMaxAlpha(const ImageRgbaU8& target, const ImageRgbaU8& source, int32_t left, int32_t top, int32_t sourceAlphaOffset) {
  631. if (ImageIntersection::canCreate(target, source, left, top)) {
  632. PackOrder targetPackOrder = image_getPackOrder(target);
  633. PackOrder sourcePackOrder = image_getPackOrder(source);
  634. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  635. // Read and repack to convert between different color formats
  636. if (sourceAlphaOffset == 0) {
  637. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  638. int32_t sourceAlpha = sourcePixel[sourcePackOrder.alphaIndex];
  639. if (sourceAlpha > targetPixel[targetPackOrder.alphaIndex]) {
  640. targetPixel[targetPackOrder.redIndex] = sourcePixel[sourcePackOrder.redIndex];
  641. targetPixel[targetPackOrder.greenIndex] = sourcePixel[sourcePackOrder.greenIndex];
  642. targetPixel[targetPackOrder.blueIndex] = sourcePixel[sourcePackOrder.blueIndex];
  643. targetPixel[targetPackOrder.alphaIndex] = sourceAlpha;
  644. }
  645. );
  646. } else {
  647. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  648. int32_t sourceAlpha = sourcePixel[sourcePackOrder.alphaIndex];
  649. if (sourceAlpha > 0) {
  650. sourceAlpha += sourceAlphaOffset;
  651. if (sourceAlpha > targetPixel[targetPackOrder.alphaIndex]) {
  652. targetPixel[targetPackOrder.redIndex] = sourcePixel[sourcePackOrder.redIndex];
  653. targetPixel[targetPackOrder.greenIndex] = sourcePixel[sourcePackOrder.greenIndex];
  654. targetPixel[targetPackOrder.blueIndex] = sourcePixel[sourcePackOrder.blueIndex];
  655. if (sourceAlpha < 0) { sourceAlpha = 0; }
  656. if (sourceAlpha > 255) { sourceAlpha = 255; }
  657. targetPixel[targetPackOrder.alphaIndex] = sourceAlpha;
  658. }
  659. }
  660. );
  661. }
  662. }
  663. }
  664. static void imageImpl_drawAlphaClip(const ImageRgbaU8& target, const ImageRgbaU8& source, int32_t left, int32_t top, int32_t threshold) {
  665. if (ImageIntersection::canCreate(target, source, left, top)) {
  666. PackOrder targetPackOrder = image_getPackOrder(target);
  667. PackOrder sourcePackOrder = image_getPackOrder(source);
  668. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  669. // Read and repack to convert between different color formats
  670. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  671. if (sourcePixel[sourcePackOrder.alphaIndex] > threshold) {
  672. targetPixel[targetPackOrder.redIndex] = sourcePixel[sourcePackOrder.redIndex];
  673. targetPixel[targetPackOrder.greenIndex] = sourcePixel[sourcePackOrder.greenIndex];
  674. targetPixel[targetPackOrder.blueIndex] = sourcePixel[sourcePackOrder.blueIndex];
  675. targetPixel[targetPackOrder.alphaIndex] = 255;
  676. }
  677. );
  678. }
  679. }
  680. template <bool FULL_ALPHA>
  681. void drawSilhouette_template(const ImageRgbaU8& target, const ImageU8& source, const ColorRgbaI32& color, int32_t left, int32_t top) {
  682. if (ImageIntersection::canCreate(target, source, left, top)) {
  683. PackOrder targetPackOrder = image_getPackOrder(target);
  684. ImageIntersection intersection = ImageIntersection::create(target, source, left, top);
  685. // Read and repack to convert between different color formats
  686. ITERATE_PIXELS(intersection.subTarget, intersection.subSource,
  687. uint32_t sourceRatio;
  688. if (FULL_ALPHA) {
  689. sourceRatio = *sourcePixel;
  690. } else {
  691. sourceRatio = normalizedByteMultiplication(*sourcePixel, color.alpha);
  692. }
  693. if (sourceRatio > 0) {
  694. if (sourceRatio == 255) {
  695. targetPixel[targetPackOrder.redIndex] = color.red;
  696. targetPixel[targetPackOrder.greenIndex] = color.green;
  697. targetPixel[targetPackOrder.blueIndex] = color.blue;
  698. targetPixel[targetPackOrder.alphaIndex] = 255;
  699. } else {
  700. uint32_t targetRatio = 255 - sourceRatio;
  701. targetPixel[targetPackOrder.redIndex] = normalizedByteMultiplication(targetPixel[targetPackOrder.redIndex], targetRatio) + normalizedByteMultiplication(color.red, sourceRatio);
  702. targetPixel[targetPackOrder.greenIndex] = normalizedByteMultiplication(targetPixel[targetPackOrder.greenIndex], targetRatio) + normalizedByteMultiplication(color.green, sourceRatio);
  703. targetPixel[targetPackOrder.blueIndex] = normalizedByteMultiplication(targetPixel[targetPackOrder.blueIndex], targetRatio) + normalizedByteMultiplication(color.blue, sourceRatio);
  704. targetPixel[targetPackOrder.alphaIndex] = normalizedByteMultiplication(targetPixel[targetPackOrder.alphaIndex], targetRatio) + sourceRatio;
  705. }
  706. }
  707. );
  708. }
  709. }
  710. static void imageImpl_drawSilhouette(const ImageRgbaU8& target, const ImageU8& source, const ColorRgbaI32& color, int32_t left, int32_t top) {
  711. if (color.alpha > 0) {
  712. ColorRgbaI32 saturatedColor = color.saturate();
  713. if (color.alpha < 255) {
  714. drawSilhouette_template<false>(target, source, saturatedColor, left, top);
  715. } else {
  716. drawSilhouette_template<true>(target, source, saturatedColor, left, top);
  717. }
  718. }
  719. }
  720. static void imageImpl_drawHigher(const ImageU16& targetHeight, const ImageU16& sourceHeight, int32_t left, int32_t top, int32_t sourceHeightOffset) {
  721. if (ImageIntersection::canCreate(targetHeight, sourceHeight, left, top)) {
  722. ImageIntersection intersectionH = ImageIntersection::create(targetHeight, sourceHeight, left, top);
  723. ITERATE_PIXELS(intersectionH.subTarget, intersectionH.subSource,
  724. int32_t newHeight = *((const uint16_t*)sourcePixel);
  725. if (newHeight > 0) {
  726. newHeight += sourceHeightOffset;
  727. if (newHeight < 0) { newHeight = 0; }
  728. if (newHeight > 65535) { newHeight = 65535; }
  729. if (newHeight > 0 && newHeight > *((uint16_t*)targetPixel)) {
  730. *((uint16_t*)targetPixel) = newHeight;
  731. }
  732. }
  733. );
  734. }
  735. }
  736. static void imageImpl_drawHigher(const ImageU16& targetHeight, const ImageU16& sourceHeight, ImageRgbaU8& targetA, const ImageRgbaU8& sourceA,
  737. int32_t left, int32_t top, int32_t sourceHeightOffset) {
  738. assert(image_getWidth(sourceA) == image_getWidth(sourceHeight));
  739. assert(image_getHeight(sourceA) == image_getHeight(sourceHeight));
  740. if (ImageIntersection::canCreate(targetHeight, sourceHeight, left, top)) {
  741. PackOrder targetAPackOrder = image_getPackOrder(targetA);
  742. PackOrder sourceAPackOrder = image_getPackOrder(sourceA);
  743. ImageIntersection intersectionH = ImageIntersection::create(targetHeight, sourceHeight, left, top);
  744. ImageIntersection intersectionA = ImageIntersection::create(targetA, sourceA, left, top);
  745. ITERATE_PIXELS_2(intersectionH.subTarget, intersectionH.subSource, intersectionA.subTarget, intersectionA.subSource,
  746. int32_t newHeight = *((const uint16_t*)sourcePixel1);
  747. if (newHeight > 0) {
  748. newHeight += sourceHeightOffset;
  749. if (newHeight < 0) { newHeight = 0; }
  750. if (newHeight > 65535) { newHeight = 65535; }
  751. if (newHeight > *((uint16_t*)targetPixel1)) {
  752. *((uint16_t*)targetPixel1) = newHeight;
  753. targetPixel2[targetAPackOrder.redIndex] = sourcePixel2[sourceAPackOrder.redIndex];
  754. targetPixel2[targetAPackOrder.greenIndex] = sourcePixel2[sourceAPackOrder.greenIndex];
  755. targetPixel2[targetAPackOrder.blueIndex] = sourcePixel2[sourceAPackOrder.blueIndex];
  756. targetPixel2[targetAPackOrder.alphaIndex] = sourcePixel2[sourceAPackOrder.alphaIndex];
  757. }
  758. }
  759. );
  760. }
  761. }
  762. static void imageImpl_drawHigher(const ImageU16& targetHeight, const ImageU16& sourceHeight, ImageRgbaU8& targetA, const ImageRgbaU8& sourceA,
  763. ImageRgbaU8& targetB, const ImageRgbaU8& sourceB, int32_t left, int32_t top, int32_t sourceHeightOffset) {
  764. assert(image_getWidth(sourceA) == image_getWidth(sourceHeight));
  765. assert(image_getHeight(sourceA) == image_getHeight(sourceHeight));
  766. assert(image_getWidth(sourceB) == image_getWidth(sourceHeight));
  767. assert(image_getHeight(sourceB) == image_getHeight(sourceHeight));
  768. if (ImageIntersection::canCreate(targetHeight, sourceHeight, left, top)) {
  769. PackOrder targetAPackOrder = image_getPackOrder(targetA);
  770. PackOrder targetBPackOrder = image_getPackOrder(targetB);
  771. PackOrder sourceAPackOrder = image_getPackOrder(sourceA);
  772. PackOrder sourceBPackOrder = image_getPackOrder(sourceB);
  773. ImageIntersection intersectionH = ImageIntersection::create(targetHeight, sourceHeight, left, top);
  774. ImageIntersection intersectionA = ImageIntersection::create(targetA, sourceA, left, top);
  775. ImageIntersection intersectionB = ImageIntersection::create(targetB, sourceB, left, top);
  776. ITERATE_PIXELS_3(intersectionH.subTarget, intersectionH.subSource, intersectionA.subTarget, intersectionA.subSource, intersectionB.subTarget, intersectionB.subSource,
  777. int32_t newHeight = *((const uint16_t*)sourcePixel1);
  778. if (newHeight > 0) {
  779. newHeight += sourceHeightOffset;
  780. if (newHeight < 0) { newHeight = 0; }
  781. if (newHeight > 65535) { newHeight = 65535; }
  782. if (newHeight > *((uint16_t*)targetPixel1)) {
  783. *((uint16_t*)targetPixel1) = newHeight;
  784. targetPixel2[targetAPackOrder.redIndex] = sourcePixel2[sourceAPackOrder.redIndex];
  785. targetPixel2[targetAPackOrder.greenIndex] = sourcePixel2[sourceAPackOrder.greenIndex];
  786. targetPixel2[targetAPackOrder.blueIndex] = sourcePixel2[sourceAPackOrder.blueIndex];
  787. targetPixel2[targetAPackOrder.alphaIndex] = sourcePixel2[sourceAPackOrder.alphaIndex];
  788. targetPixel3[targetBPackOrder.redIndex] = sourcePixel3[sourceBPackOrder.redIndex];
  789. targetPixel3[targetBPackOrder.greenIndex] = sourcePixel3[sourceBPackOrder.greenIndex];
  790. targetPixel3[targetBPackOrder.blueIndex] = sourcePixel3[sourceBPackOrder.blueIndex];
  791. targetPixel3[targetBPackOrder.alphaIndex] = sourcePixel3[sourceBPackOrder.alphaIndex];
  792. }
  793. }
  794. );
  795. }
  796. }
  797. static void imageImpl_drawHigher(const ImageF32& targetHeight, const ImageF32& sourceHeight, int32_t left, int32_t top, float sourceHeightOffset) {
  798. if (ImageIntersection::canCreate(targetHeight, sourceHeight, left, top)) {
  799. ImageIntersection intersectionH = ImageIntersection::create(targetHeight, sourceHeight, left, top);
  800. ITERATE_PIXELS(intersectionH.subTarget, intersectionH.subSource,
  801. float newHeight = *((const float*)sourcePixel);
  802. if (newHeight > -std::numeric_limits<float>::infinity()) {
  803. newHeight += sourceHeightOffset;
  804. if (newHeight > *((float*)targetPixel)) {
  805. *((float*)targetPixel) = newHeight;
  806. }
  807. }
  808. );
  809. }
  810. }
  811. static void imageImpl_drawHigher(const ImageF32& targetHeight, const ImageF32& sourceHeight, ImageRgbaU8& targetA, const ImageRgbaU8& sourceA,
  812. int32_t left, int32_t top, float sourceHeightOffset) {
  813. assert(image_getWidth(sourceA) == image_getWidth(sourceHeight));
  814. assert(image_getHeight(sourceA) == image_getHeight(sourceHeight));
  815. if (ImageIntersection::canCreate(targetHeight, sourceHeight, left, top)) {
  816. PackOrder targetAPackOrder = image_getPackOrder(targetA);
  817. PackOrder sourceAPackOrder = image_getPackOrder(sourceA);
  818. ImageIntersection intersectionH = ImageIntersection::create(targetHeight, sourceHeight, left, top);
  819. ImageIntersection intersectionA = ImageIntersection::create(targetA, sourceA, left, top);
  820. ITERATE_PIXELS_2(intersectionH.subTarget, intersectionH.subSource, intersectionA.subTarget, intersectionA.subSource,
  821. float newHeight = *((const float*)sourcePixel1);
  822. if (newHeight > -std::numeric_limits<float>::infinity()) {
  823. newHeight += sourceHeightOffset;
  824. if (newHeight > *((float*)targetPixel1)) {
  825. *((float*)targetPixel1) = newHeight;
  826. targetPixel2[targetAPackOrder.redIndex] = sourcePixel2[sourceAPackOrder.redIndex];
  827. targetPixel2[targetAPackOrder.greenIndex] = sourcePixel2[sourceAPackOrder.greenIndex];
  828. targetPixel2[targetAPackOrder.blueIndex] = sourcePixel2[sourceAPackOrder.blueIndex];
  829. targetPixel2[targetAPackOrder.alphaIndex] = sourcePixel2[sourceAPackOrder.alphaIndex];
  830. }
  831. }
  832. );
  833. }
  834. }
  835. static void imageImpl_drawHigher(const ImageF32& targetHeight, const ImageF32& sourceHeight, ImageRgbaU8& targetA, const ImageRgbaU8& sourceA,
  836. ImageRgbaU8& targetB, const ImageRgbaU8& sourceB, int32_t left, int32_t top, float sourceHeightOffset) {
  837. assert(image_getWidth(sourceA) == image_getWidth(sourceHeight));
  838. assert(image_getHeight(sourceA) == image_getHeight(sourceHeight));
  839. assert(image_getWidth(sourceB) == image_getWidth(sourceHeight));
  840. assert(image_getHeight(sourceB) == image_getHeight(sourceHeight));
  841. if (ImageIntersection::canCreate(targetHeight, sourceHeight, left, top)) {
  842. PackOrder targetAPackOrder = image_getPackOrder(targetA);
  843. PackOrder targetBPackOrder = image_getPackOrder(targetB);
  844. PackOrder sourceAPackOrder = image_getPackOrder(sourceA);
  845. PackOrder sourceBPackOrder = image_getPackOrder(sourceB);
  846. ImageIntersection intersectionH = ImageIntersection::create(targetHeight, sourceHeight, left, top);
  847. ImageIntersection intersectionA = ImageIntersection::create(targetA, sourceA, left, top);
  848. ImageIntersection intersectionB = ImageIntersection::create(targetB, sourceB, left, top);
  849. ITERATE_PIXELS_3(intersectionH.subTarget, intersectionH.subSource, intersectionA.subTarget, intersectionA.subSource, intersectionB.subTarget, intersectionB.subSource,
  850. float newHeight = *((const float*)sourcePixel1);
  851. if (newHeight > -std::numeric_limits<float>::infinity()) {
  852. newHeight += sourceHeightOffset;
  853. if (newHeight > *((float*)targetPixel1)) {
  854. *((float*)targetPixel1) = newHeight;
  855. targetPixel2[targetAPackOrder.redIndex] = sourcePixel2[sourceAPackOrder.redIndex];
  856. targetPixel2[targetAPackOrder.greenIndex] = sourcePixel2[sourceAPackOrder.greenIndex];
  857. targetPixel2[targetAPackOrder.blueIndex] = sourcePixel2[sourceAPackOrder.blueIndex];
  858. targetPixel2[targetAPackOrder.alphaIndex] = sourcePixel2[sourceAPackOrder.alphaIndex];
  859. targetPixel3[targetBPackOrder.redIndex] = sourcePixel3[sourceBPackOrder.redIndex];
  860. targetPixel3[targetBPackOrder.greenIndex] = sourcePixel3[sourceBPackOrder.greenIndex];
  861. targetPixel3[targetBPackOrder.blueIndex] = sourcePixel3[sourceBPackOrder.blueIndex];
  862. targetPixel3[targetBPackOrder.alphaIndex] = sourcePixel3[sourceBPackOrder.alphaIndex];
  863. }
  864. }
  865. );
  866. }
  867. }
  868. #define DRAW_COPY_WRAPPER(TARGET_TYPE, SOURCE_TYPE) \
  869. void draw_copy(const TARGET_TYPE& target, const SOURCE_TYPE& source, int32_t left, int32_t top) { \
  870. if (image_exists(target) && image_exists(source)) { \
  871. imageImpl_drawCopy(target, source, left, top); \
  872. } \
  873. }
  874. DRAW_COPY_WRAPPER(ImageU8, ImageU8);
  875. DRAW_COPY_WRAPPER(ImageU8, ImageU16);
  876. DRAW_COPY_WRAPPER(ImageU8, ImageF32);
  877. DRAW_COPY_WRAPPER(ImageU16, ImageU8);
  878. DRAW_COPY_WRAPPER(ImageU16, ImageU16);
  879. DRAW_COPY_WRAPPER(ImageU16, ImageF32);
  880. DRAW_COPY_WRAPPER(ImageF32, ImageU8);
  881. DRAW_COPY_WRAPPER(ImageF32, ImageU16);
  882. DRAW_COPY_WRAPPER(ImageF32, ImageF32);
  883. DRAW_COPY_WRAPPER(ImageRgbaU8, ImageU8);
  884. DRAW_COPY_WRAPPER(ImageRgbaU8, ImageU16);
  885. DRAW_COPY_WRAPPER(ImageRgbaU8, ImageF32);
  886. DRAW_COPY_WRAPPER(ImageRgbaU8, ImageRgbaU8);
  887. void draw_alphaFilter(const ImageRgbaU8& target, const ImageRgbaU8& source, int32_t left, int32_t top) {
  888. if (image_exists(target) && image_exists(source)) {
  889. imageImpl_drawAlphaFilter(target, source, left, top);
  890. }
  891. }
  892. void draw_maxAlpha(const ImageRgbaU8& target, const ImageRgbaU8& source, int32_t left, int32_t top, int32_t sourceAlphaOffset) {
  893. if (image_exists(target) && image_exists(source)) {
  894. imageImpl_drawMaxAlpha(target, source, left, top, sourceAlphaOffset);
  895. }
  896. }
  897. void draw_alphaClip(const ImageRgbaU8& target, const ImageRgbaU8& source, int32_t left, int32_t top, int32_t threshold) {
  898. if (image_exists(target) && image_exists(source)) {
  899. imageImpl_drawAlphaClip(target, source, left, top, threshold);
  900. }
  901. }
  902. void draw_silhouette(const ImageRgbaU8& target, const ImageU8& source, const ColorRgbaI32& color, int32_t left, int32_t top) {
  903. if (image_exists(target) && image_exists(source)) {
  904. imageImpl_drawSilhouette(target, source, color, left, top);
  905. }
  906. }
  907. void draw_higher(const ImageU16& targetHeight, const ImageU16& sourceHeight, int32_t left, int32_t top, int32_t sourceHeightOffset) {
  908. if (image_exists(targetHeight) && image_exists(sourceHeight)) {
  909. imageImpl_drawHigher(targetHeight, sourceHeight, left, top, sourceHeightOffset);
  910. }
  911. }
  912. void draw_higher(const ImageU16& targetHeight, const ImageU16& sourceHeight, ImageRgbaU8& targetA, const ImageRgbaU8& sourceA,
  913. int32_t left, int32_t top, int32_t sourceHeightOffset) {
  914. if (image_exists(targetHeight) && image_exists(sourceHeight) && image_exists(targetA) && image_exists(sourceA)) {
  915. imageImpl_drawHigher(targetHeight, sourceHeight, targetA, sourceA, left, top, sourceHeightOffset);
  916. }
  917. }
  918. void draw_higher(const ImageU16& targetHeight, const ImageU16& sourceHeight, ImageRgbaU8& targetA, const ImageRgbaU8& sourceA,
  919. ImageRgbaU8& targetB, const ImageRgbaU8& sourceB, int32_t left, int32_t top, int32_t sourceHeightOffset) {
  920. if (image_exists(targetHeight) && image_exists(sourceHeight) && image_exists(targetA) && image_exists(sourceA) && image_exists(targetB) && image_exists(sourceB)) {
  921. imageImpl_drawHigher(targetHeight, sourceHeight, targetA, sourceA, targetB, sourceB, left, top, sourceHeightOffset);
  922. }
  923. }
  924. void draw_higher(const ImageF32& targetHeight, const ImageF32& sourceHeight, int32_t left, int32_t top, float sourceHeightOffset) {
  925. if (image_exists(targetHeight) && image_exists(sourceHeight)) {
  926. imageImpl_drawHigher(targetHeight, sourceHeight, left, top, sourceHeightOffset);
  927. }
  928. }
  929. void draw_higher(const ImageF32& targetHeight, const ImageF32& sourceHeight, ImageRgbaU8& targetA, const ImageRgbaU8& sourceA,
  930. int32_t left, int32_t top, float sourceHeightOffset) {
  931. if (image_exists(targetHeight) && image_exists(sourceHeight) && image_exists(targetA) && image_exists(sourceA)) {
  932. imageImpl_drawHigher(targetHeight, sourceHeight, targetA, sourceA, left, top, sourceHeightOffset);
  933. }
  934. }
  935. void draw_higher(const ImageF32& targetHeight, const ImageF32& sourceHeight, ImageRgbaU8& targetA, const ImageRgbaU8& sourceA,
  936. ImageRgbaU8& targetB, const ImageRgbaU8& sourceB, int32_t left, int32_t top, float sourceHeightOffset) {
  937. if (image_exists(targetHeight) && image_exists(sourceHeight) && image_exists(targetA) && image_exists(sourceA) && image_exists(targetB) && image_exists(sourceB)) {
  938. imageImpl_drawHigher(targetHeight, sourceHeight, targetA, sourceA, targetB, sourceB, left, top, sourceHeightOffset);
  939. }
  940. }
  941. }