Graphics_image.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. /* Graphics_image.cpp
  2. *
  3. * Copyright (C) 1992-2012,2013,2014,2015,2016,2017 Paul Boersma
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. * See the GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "GraphicsP.h"
  19. #include "../fon/Photo.h"
  20. #if gdi
  21. #include <GdiPlus.h>
  22. #elif quartz
  23. #include <time.h>
  24. #include "macport_on.h"
  25. static void _mac_releaseDataCallback (void *info, const void *data, size_t size) {
  26. (void) info;
  27. (void) size;
  28. Melder_free (data);
  29. }
  30. #endif
  31. #define wdx(x) ((x) * my scaleX + my deltaX)
  32. #define wdy(y) ((y) * my scaleY + my deltaY)
  33. static void _GraphicsScreen_cellArrayOrImage (GraphicsScreen me, double **z_float, double_rgbt **z_rgbt, unsigned char **z_byte,
  34. integer ix1, integer ix2, integer x1DC, integer x2DC,
  35. integer iy1, integer iy2, integer y1DC, integer y2DC,
  36. double minimum, double maximum,
  37. integer clipx1, integer clipx2, integer clipy1, integer clipy2, int interpolate)
  38. {
  39. /*integer t=clock();*/
  40. integer nx = ix2 - ix1 + 1; /* The number of cells along the horizontal axis. */
  41. integer ny = iy2 - iy1 + 1; /* The number of cells along the vertical axis. */
  42. double dx = (double) (x2DC - x1DC) / (double) nx; /* Horizontal pixels per cell. Positive. */
  43. double dy = (double) (y2DC - y1DC) / (double) ny; /* Vertical pixels per cell. Negative. */
  44. double scale = 255.0 / (maximum - minimum), offset = 255.0 + minimum * scale;
  45. if (x2DC <= x1DC || y1DC <= y2DC) return;
  46. trace (U"scale ", scale);
  47. /* Clip by the intersection of the world window and the outline of the cells. */
  48. //Melder_casual (U"clipy1 ", clipy1, U" clipy2 ", clipy2);
  49. if (clipx1 < x1DC) clipx1 = x1DC;
  50. if (clipx2 > x2DC) clipx2 = x2DC;
  51. if (clipy1 > y1DC) clipy1 = y1DC;
  52. if (clipy2 < y2DC) clipy2 = y2DC;
  53. /*
  54. * The first decision is whether we are going to use the standard rectangle drawing
  55. * (cellArray only), or whether we are going to write into a bitmap.
  56. * The standard drawing is best for small numbers of cells,
  57. * provided that some cells are larger than a pixel.
  58. */
  59. if (! interpolate && nx * ny < 3000 && (dx > 1.0 || dy < -1.0)) {
  60. try {
  61. /*unsigned int cellWidth = (unsigned int) dx + 1;*/
  62. unsigned int cellHeight = (unsigned int) (- (int) dy) + 1;
  63. integer ix, iy;
  64. #if cairo
  65. cairo_pattern_t *grey [256];
  66. for (int igrey = 0; igrey < sizeof (grey) / sizeof (*grey); igrey ++) {
  67. double v = igrey / ((double) (sizeof (grey) / sizeof (*grey)) - 1.0);
  68. grey [igrey] = cairo_pattern_create_rgb (v, v, v);
  69. }
  70. #elif gdi
  71. static HBRUSH greyBrush [256];
  72. RECT rect;
  73. if (! greyBrush [0])
  74. for (int igrey = 0; igrey <= 255; igrey ++)
  75. greyBrush [igrey] = CreateSolidBrush (RGB (igrey, igrey, igrey)); // once
  76. #elif quartz
  77. GraphicsQuartz_initDraw (me);
  78. CGContextSetAlpha (my d_macGraphicsContext, 1.0);
  79. CGContextSetBlendMode (my d_macGraphicsContext, kCGBlendModeNormal);
  80. #endif
  81. autoNUMvector <integer> lefts (ix1, ix2 + 1);
  82. for (ix = ix1; ix <= ix2 + 1; ix ++)
  83. lefts [ix] = x1DC + (integer) ((ix - ix1) * dx);
  84. for (iy = iy1; iy <= iy2; iy ++) {
  85. integer bottom = y1DC + (integer) ((iy - iy1) * dy), top = bottom - cellHeight;
  86. if (top > clipy1 || bottom < clipy2) continue;
  87. if (top < clipy2) top = clipy2;
  88. if (bottom > clipy1) bottom = clipy1;
  89. #if gdi
  90. rect. bottom = bottom; rect. top = top;
  91. #endif
  92. for (ix = ix1; ix <= ix2; ix ++) {
  93. integer left = lefts [ix], right = lefts [ix + 1];
  94. if (right < clipx1 || left > clipx2) continue;
  95. if (left < clipx1) left = clipx1;
  96. if (right > clipx2) right = clipx2;
  97. if (z_rgbt) {
  98. #if cairo
  99. // NYI
  100. #elif gdi
  101. // NYI
  102. #elif quartz
  103. double red = z_rgbt [iy] [ix]. red;
  104. double green = z_rgbt [iy] [ix]. green;
  105. double blue = z_rgbt [iy] [ix]. blue;
  106. double transparency = z_rgbt [iy] [ix]. transparency;
  107. red = ( red <= 0.0 ? 0.0 : red >= 1.0 ? 1.0 : red );
  108. green = ( green <= 0.0 ? 0.0 : green >= 1.0 ? 1.0 : green );
  109. blue = ( blue <= 0.0 ? 0.0 : blue >= 1.0 ? 1.0 : blue );
  110. CGContextSetRGBFillColor (my d_macGraphicsContext, red, green, blue, 1.0 - transparency);
  111. CGContextFillRect (my d_macGraphicsContext, CGRectMake (left, top, right - left, bottom - top));
  112. #endif
  113. } else {
  114. #if cairo
  115. integer value = offset - scale * ( z_float ? z_float [iy] [ix] : z_byte [iy] [ix] );
  116. cairo_set_source (my d_cairoGraphicsContext, grey [value <= 0 ? 0 : value >= sizeof (grey) / sizeof (*grey) ? sizeof (grey) / sizeof (*grey) : value]);
  117. cairo_rectangle (my d_cairoGraphicsContext, left, top, right - left, bottom - top);
  118. cairo_fill (my d_cairoGraphicsContext);
  119. #elif gdi
  120. integer value = offset - scale * ( z_float ? z_float [iy] [ix] : z_byte [iy] [ix] );
  121. rect. left = left; rect. right = right;
  122. FillRect (my d_gdiGraphicsContext, & rect, greyBrush [value <= 0 ? 0 : value >= 255 ? 255 : value]);
  123. #elif quartz
  124. double value = offset - scale * ( z_float ? z_float [iy] [ix] : z_byte [iy] [ix] );
  125. double igrey = ( value <= 0 ? 0 : value >= 255 ? 255 : value ) / 255.0;
  126. CGContextSetRGBFillColor (my d_macGraphicsContext, igrey, igrey, igrey, 1.0);
  127. CGContextFillRect (my d_macGraphicsContext, CGRectMake (left, top, right - left, bottom - top));
  128. #endif
  129. }
  130. }
  131. }
  132. #if cairo
  133. for (int igrey = 0; igrey < sizeof (grey) / sizeof (*grey); igrey ++)
  134. cairo_pattern_destroy (grey [igrey]);
  135. #elif quartz
  136. CGContextSetRGBFillColor (my d_macGraphicsContext, 0.0, 0.0, 0.0, 1.0);
  137. GraphicsQuartz_exitDraw (me);
  138. #endif
  139. } catch (MelderError) { }
  140. } else {
  141. integer xDC, yDC;
  142. integer undersampling = 1;
  143. /*
  144. * Prepare for off-screen bitmap drawing.
  145. */
  146. #if cairo
  147. integer arrayWidth = clipx2 - clipx1;
  148. integer arrayHeight = clipy1 - clipy2;
  149. trace (U"arrayWidth ", arrayWidth, U", arrayHeight ", arrayHeight);
  150. cairo_surface_t *sfc = cairo_image_surface_create (CAIRO_FORMAT_RGB24, arrayWidth, arrayHeight);
  151. unsigned char *bits = cairo_image_surface_get_data (sfc);
  152. int scanLineLength = cairo_image_surface_get_stride (sfc);
  153. unsigned char grey [256];
  154. trace (
  155. U"image surface address ", Melder_pointer (sfc),
  156. U", bits address ", Melder_pointer (bits),
  157. U", scanLineLength ", scanLineLength,
  158. U", numberOfGreys ", sizeof (grey) / sizeof (*grey)
  159. );
  160. for (int igrey = 0; igrey < sizeof (grey) / sizeof (*grey); igrey++)
  161. grey [igrey] = 255 - (unsigned char) (igrey * 255.0 / (sizeof (grey) / sizeof (*grey) - 1));
  162. #elif gdi
  163. integer bitmapWidth = clipx2 - clipx1, bitmapHeight = clipy1 - clipy2;
  164. int igrey;
  165. /*
  166. * Create a device-independent bitmap, 32 bits deep.
  167. */
  168. struct { BITMAPINFOHEADER header; } bitmapInfo;
  169. integer scanLineLength = bitmapWidth * 4; // for 24 bits: (bitmapWidth * 3 + 3) & ~3L;
  170. HBITMAP bitmap;
  171. unsigned char *bits; // a pointer to memory allocated by VirtualAlloc or by CreateDIBSection ()
  172. bitmapInfo. header.biSize = sizeof (BITMAPINFOHEADER);
  173. bitmapInfo. header.biWidth = bitmapWidth; // scanLineLength;
  174. bitmapInfo. header.biHeight = bitmapHeight;
  175. bitmapInfo. header.biPlanes = 1;
  176. bitmapInfo. header.biBitCount = 32;
  177. bitmapInfo. header.biCompression = 0;
  178. bitmapInfo. header.biSizeImage = 0;
  179. bitmapInfo. header.biXPelsPerMeter = 0;
  180. bitmapInfo. header.biYPelsPerMeter = 0;
  181. bitmapInfo. header.biClrUsed = 0;
  182. bitmapInfo. header.biClrImportant = 0;
  183. bitmap = CreateDIBSection (my d_gdiGraphicsContext /* ignored */, (CONST BITMAPINFO *) & bitmapInfo,
  184. DIB_RGB_COLORS, (VOID **) & bits, nullptr, 0);
  185. #elif quartz
  186. integer bytesPerRow = (clipx2 - clipx1) * 4;
  187. Melder_assert (bytesPerRow > 0);
  188. integer numberOfRows = clipy1 - clipy2;
  189. Melder_assert (numberOfRows > 0);
  190. unsigned char *imageData = Melder_malloc_f (unsigned char, bytesPerRow * numberOfRows);
  191. #endif
  192. /*
  193. * Draw into the bitmap.
  194. */
  195. #if cairo
  196. #define ROW_START_ADDRESS (bits + (clipy1 - 1 - yDC) * scanLineLength)
  197. #define PUT_PIXEL \
  198. if (1) { \
  199. unsigned char kar = value <= 0 ? 0 : value >= 255 ? 255 : (int) value; \
  200. *pixelAddress ++ = kar; \
  201. *pixelAddress ++ = kar; \
  202. *pixelAddress ++ = kar; \
  203. *pixelAddress ++ = 0; \
  204. }
  205. #elif gdi
  206. #define ROW_START_ADDRESS (bits + (clipy1 - 1 - yDC) * scanLineLength)
  207. #define PUT_PIXEL \
  208. if (1) { \
  209. unsigned char kar = value <= 0 ? 0 : value >= 255 ? 255 : (int) value; \
  210. *pixelAddress ++ = kar; \
  211. *pixelAddress ++ = kar; \
  212. *pixelAddress ++ = kar; \
  213. *pixelAddress ++ = 0; \
  214. }
  215. #elif quartz
  216. #define ROW_START_ADDRESS (imageData + (clipy1 - 1 - yDC) * bytesPerRow)
  217. #define PUT_PIXEL \
  218. if (my colourScale == kGraphics_colourScale::GREY) { \
  219. unsigned char kar = value <= 0 ? 0 : value >= 255 ? 255 : (int) value; \
  220. *pixelAddress ++ = kar; \
  221. *pixelAddress ++ = kar; \
  222. *pixelAddress ++ = kar; \
  223. *pixelAddress ++ = 255; \
  224. } else if (my colourScale == kGraphics_colourScale::BLUE_TO_RED) { \
  225. if (value < 0.0) { \
  226. *pixelAddress ++ = 0; \
  227. *pixelAddress ++ = 0; \
  228. *pixelAddress ++ = 63; \
  229. *pixelAddress ++ = 255; \
  230. } else if (value < 64.0) { \
  231. *pixelAddress ++ = 0; \
  232. *pixelAddress ++ = 0; \
  233. *pixelAddress ++ = (int) (value * 3 + 63.999); \
  234. *pixelAddress ++ = 255; \
  235. } else if (value < 128.0) { \
  236. *pixelAddress ++ = (int) (value * 4 - 256.0); \
  237. *pixelAddress ++ = (int) (value * 4 - 256.0); \
  238. *pixelAddress ++ = 255; \
  239. *pixelAddress ++ = 255; \
  240. } else if (value < 192.0) { \
  241. *pixelAddress ++ = 255; \
  242. *pixelAddress ++ = (int) ((256.0 - value) * 4 - 256.0); \
  243. *pixelAddress ++ = (int) ((256.0 - value) * 4 - 256.0); \
  244. *pixelAddress ++ = 255; \
  245. } else if (value < 256.0) { \
  246. *pixelAddress ++ = (int) ((256.0 - value) * 3 + 63.999); \
  247. *pixelAddress ++ = 0; \
  248. *pixelAddress ++ = 0; \
  249. *pixelAddress ++ = 255; \
  250. } else { \
  251. *pixelAddress ++ = 63; \
  252. *pixelAddress ++ = 0; \
  253. *pixelAddress ++ = 0; \
  254. *pixelAddress ++ = 255; \
  255. } \
  256. }
  257. #else
  258. #define ROW_START_ADDRESS nullptr
  259. #define PUT_PIXEL
  260. #endif
  261. if (interpolate) {
  262. try {
  263. autoNUMvector <integer> ileft (clipx1, clipx2);
  264. autoNUMvector <integer> iright (clipx1, clipx2);
  265. autoNUMvector <double> rightWeight (clipx1, clipx2);
  266. autoNUMvector <double> leftWeight (clipx1, clipx2);
  267. for (xDC = clipx1; xDC < clipx2; xDC += undersampling) {
  268. double ix_real = ix1 - 0.5 + ((double) nx * (xDC - x1DC)) / (x2DC - x1DC);
  269. ileft [xDC] = (integer) floor (ix_real), iright [xDC] = ileft [xDC] + 1;
  270. rightWeight [xDC] = ix_real - ileft [xDC], leftWeight [xDC] = 1.0 - rightWeight [xDC];
  271. if (ileft [xDC] < ix1) ileft [xDC] = ix1;
  272. if (iright [xDC] > ix2) iright [xDC] = ix2;
  273. }
  274. for (yDC = clipy2; yDC < clipy1; yDC += undersampling) {
  275. double iy_real = iy2 + 0.5 - ((double) ny * (yDC - y2DC)) / (y1DC - y2DC);
  276. integer itop = Melder_iceiling (iy_real), ibottom = itop - 1;
  277. double bottomWeight = itop - iy_real, topWeight = 1.0 - bottomWeight;
  278. unsigned char *pixelAddress = ROW_START_ADDRESS;
  279. if (itop > iy2) itop = iy2;
  280. if (ibottom < iy1) ibottom = iy1;
  281. if (z_float) {
  282. double *ztop = z_float [itop], *zbottom = z_float [ibottom];
  283. for (xDC = clipx1; xDC < clipx2; xDC += undersampling) {
  284. double interpol =
  285. rightWeight [xDC] *
  286. (topWeight * ztop [iright [xDC]] + bottomWeight * zbottom [iright [xDC]]) +
  287. leftWeight [xDC] *
  288. (topWeight * ztop [ileft [xDC]] + bottomWeight * zbottom [ileft [xDC]]);
  289. double value = offset - scale * interpol;
  290. PUT_PIXEL
  291. }
  292. } else if (z_rgbt) {
  293. double_rgbt *ztop = z_rgbt [itop], *zbottom = z_rgbt [ibottom];
  294. for (xDC = clipx1; xDC < clipx2; xDC += undersampling) {
  295. double red =
  296. rightWeight [xDC] * (topWeight * ztop [iright [xDC]]. red + bottomWeight * zbottom [iright [xDC]]. red) +
  297. leftWeight [xDC] * (topWeight * ztop [ileft [xDC]]. red + bottomWeight * zbottom [ileft [xDC]]. red);
  298. double green =
  299. rightWeight [xDC] * (topWeight * ztop [iright [xDC]]. green + bottomWeight * zbottom [iright [xDC]]. green) +
  300. leftWeight [xDC] * (topWeight * ztop [ileft [xDC]]. green + bottomWeight * zbottom [ileft [xDC]]. green);
  301. double blue =
  302. rightWeight [xDC] * (topWeight * ztop [iright [xDC]]. blue + bottomWeight * zbottom [iright [xDC]]. blue) +
  303. leftWeight [xDC] * (topWeight * ztop [ileft [xDC]]. blue + bottomWeight * zbottom [ileft [xDC]]. blue);
  304. double transparency =
  305. rightWeight [xDC] * (topWeight * ztop [iright [xDC]]. transparency + bottomWeight * zbottom [iright [xDC]]. transparency) +
  306. leftWeight [xDC] * (topWeight * ztop [ileft [xDC]]. transparency + bottomWeight * zbottom [ileft [xDC]]. transparency);
  307. if (red < 0.0) red = 0.0; else if (red > 1.0) red = 1.0;
  308. if (green < 0.0) green = 0.0; else if (green > 1.0) green = 1.0;
  309. if (blue < 0.0) blue = 0.0; else if (blue > 1.0) blue = 1.0;
  310. if (transparency < 0.0) transparency = 0.0; else if (transparency > 1.0) transparency = 1.0;
  311. #if cairo
  312. *pixelAddress ++ = blue * 255.0;
  313. *pixelAddress ++ = green * 255.0;
  314. *pixelAddress ++ = red * 255.0;
  315. *pixelAddress ++ = transparency * 255.0;
  316. #elif gdi
  317. *pixelAddress ++ = blue * 255.0;
  318. *pixelAddress ++ = green * 255.0;
  319. *pixelAddress ++ = red * 255.0;
  320. *pixelAddress ++ = 0;
  321. #elif quartz
  322. *pixelAddress ++ = red * 255.0;
  323. *pixelAddress ++ = green * 255.0;
  324. *pixelAddress ++ = blue * 255.0;
  325. *pixelAddress ++ = (1.0 - transparency) * 255.0;
  326. #endif
  327. }
  328. } else {
  329. unsigned char *ztop = z_byte [itop], *zbottom = z_byte [ibottom];
  330. for (xDC = clipx1; xDC < clipx2; xDC += undersampling) {
  331. double interpol =
  332. rightWeight [xDC] *
  333. (topWeight * ztop [iright [xDC]] + bottomWeight * zbottom [iright [xDC]]) +
  334. leftWeight [xDC] *
  335. (topWeight * ztop [ileft [xDC]] + bottomWeight * zbottom [ileft [xDC]]);
  336. double value = offset - scale * interpol;
  337. PUT_PIXEL
  338. }
  339. }
  340. }
  341. } catch (MelderError) { Melder_clearError (); }
  342. } else {
  343. try {
  344. autoNUMvector <integer> ix (clipx1, clipx2);
  345. for (xDC = clipx1; xDC < clipx2; xDC += undersampling)
  346. ix [xDC] = Melder_ifloor (ix1 + (nx * (xDC - x1DC)) / (x2DC - x1DC));
  347. for (yDC = clipy2; yDC < clipy1; yDC += undersampling) {
  348. integer iy = Melder_iceiling (iy2 - (ny * (yDC - y2DC)) / (y1DC - y2DC));
  349. unsigned char *pixelAddress = ROW_START_ADDRESS;
  350. Melder_assert (iy >= iy1 && iy <= iy2);
  351. if (z_float) {
  352. double *ziy = z_float [iy];
  353. for (xDC = clipx1; xDC < clipx2; xDC += undersampling) {
  354. double value = offset - scale * ziy [ix [xDC]];
  355. PUT_PIXEL
  356. }
  357. } else {
  358. unsigned char *ziy = z_byte [iy];
  359. for (xDC = clipx1; xDC < clipx2; xDC += undersampling) {
  360. double value = offset - scale * ziy [ix [xDC]];
  361. PUT_PIXEL
  362. }
  363. }
  364. }
  365. } catch (MelderError) { Melder_clearError (); }
  366. }
  367. /*
  368. * Copy the bitmap to the screen.
  369. */
  370. #if cairo
  371. cairo_matrix_t clip_trans;
  372. cairo_matrix_init_identity (& clip_trans);
  373. cairo_matrix_scale (& clip_trans, 1, -1); // we painted in the reverse y-direction
  374. cairo_matrix_translate (& clip_trans, - clipx1, - clipy1);
  375. cairo_pattern_t *bitmap_pattern = cairo_pattern_create_for_surface (sfc);
  376. trace (U"bitmap pattern ", Melder_pointer (bitmap_pattern));
  377. if (cairo_status_t status = cairo_pattern_status (bitmap_pattern)) {
  378. Melder_casual (U"bitmap pattern status: ", Melder_peek8to32 (cairo_status_to_string (status)));
  379. } else {
  380. cairo_pattern_set_matrix (bitmap_pattern, & clip_trans);
  381. cairo_save (my d_cairoGraphicsContext);
  382. cairo_set_source (my d_cairoGraphicsContext, bitmap_pattern);
  383. cairo_paint (my d_cairoGraphicsContext);
  384. cairo_restore (my d_cairoGraphicsContext);
  385. }
  386. cairo_pattern_destroy (bitmap_pattern);
  387. #elif gdi
  388. SetDIBitsToDevice (my d_gdiGraphicsContext, clipx1, clipy2, bitmapWidth, bitmapHeight, 0, 0, 0, bitmapHeight,
  389. bits, (CONST BITMAPINFO *) & bitmapInfo, DIB_RGB_COLORS);
  390. //StretchDIBits (my d_gdiGraphicsContext, clipx1, clipy2, bitmapWidth, bitmapHeight, 0, 0, 0, bitmapHeight,
  391. // bits, (CONST BITMAPINFO *) & bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
  392. #elif quartz
  393. CGImageRef image;
  394. static CGColorSpaceRef colourSpace = nullptr;
  395. if (! colourSpace) {
  396. colourSpace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB); // used to be kCGColorSpaceUserRGB
  397. Melder_assert (colourSpace != nullptr);
  398. }
  399. if (1) {
  400. CGDataProviderRef dataProvider = CGDataProviderCreateWithData (nullptr,
  401. imageData,
  402. bytesPerRow * numberOfRows,
  403. _mac_releaseDataCallback // we need this because we cannot release the image data immediately after drawing,
  404. // because in PDF files the imageData has to stay available through EndPage
  405. );
  406. Melder_assert (dataProvider != nullptr);
  407. image = CGImageCreate (clipx2 - clipx1, numberOfRows,
  408. 8, 32, bytesPerRow, colourSpace, kCGImageAlphaLast, dataProvider, nullptr, false, kCGRenderingIntentDefault);
  409. CGDataProviderRelease (dataProvider);
  410. } else if (0) {
  411. Melder_assert (CGBitmapContextCreate != nullptr);
  412. CGContextRef bitmaptest = CGBitmapContextCreate (imageData, 100, 100,
  413. 8, 800, colourSpace, 0);
  414. Melder_assert (bitmaptest != nullptr);
  415. CGContextRef bitmap = CGBitmapContextCreate (nullptr /* imageData */, clipx2 - clipx1, numberOfRows,
  416. 8, bytesPerRow, colourSpace, kCGImageAlphaLast);
  417. Melder_assert (bitmap != nullptr);
  418. image = CGBitmapContextCreateImage (bitmap);
  419. // release bitmap?
  420. }
  421. Melder_assert (image != nullptr);
  422. GraphicsQuartz_initDraw (me);
  423. CGContextDrawImage (my d_macGraphicsContext, CGRectMake (clipx1, clipy2, clipx2 - clipx1, clipy1 - clipy2), image);
  424. GraphicsQuartz_exitDraw (me);
  425. //CGColorSpaceRelease (colourSpace);
  426. CGImageRelease (image);
  427. #endif
  428. /*
  429. * Clean up.
  430. */
  431. #if cairo
  432. cairo_surface_destroy (sfc);
  433. #elif gdi
  434. DeleteBitmap (bitmap);
  435. #endif
  436. }
  437. #if gdi
  438. end:
  439. return;
  440. #endif
  441. }
  442. static void _GraphicsPostscript_cellArrayOrImage (GraphicsPostscript me, double **z_float, double_rgbt **z_rgbt, unsigned char **z_byte,
  443. integer ix1, integer ix2, integer x1DC, integer x2DC,
  444. integer iy1, integer iy2, integer y1DC, integer y2DC,
  445. double minimum, double maximum,
  446. integer clipx1, integer clipx2, integer clipy1, integer clipy2, int interpolate)
  447. {
  448. integer interpolateX = 1, interpolateY = 1;
  449. integer nx = ix2 - ix1 + 1, ny = iy2 - iy1 + 1, filling = 0;
  450. double scale = ( my photocopyable ? 200.1f : 255.1f ) / (maximum - minimum);
  451. double offset = 255.1f + minimum * scale;
  452. int minimalGrey = my photocopyable ? 55 : 0;
  453. my d_printf (my d_file, "gsave N %s %s M %s %s L %s %s L %s %s L closepath clip\n",
  454. Melder8_integer (clipx1), Melder8_integer (clipy1), Melder8_integer (clipx2 - clipx1), Melder8_integer (0),
  455. Melder8_integer (0), Melder8_integer (clipy2 - clipy1), Melder8_integer (clipx1 - clipx2), Melder8_integer (0));
  456. my d_printf (my d_file, "%s %s translate %s %s scale\n",
  457. Melder8_integer (x1DC), Melder8_integer (y1DC), Melder8_integer (x2DC - x1DC), Melder8_integer (y2DC - y1DC));
  458. if (interpolate) {
  459. /* The smallest image resolution is 300 dpi. If a sample takes up more than 25.4/300 mm, the 300 dpi resolution is achieved by interpolation. */
  460. const double smallestImageResolution = 300.0;
  461. double colSize_pixels = (double) (x2DC - x1DC) / nx;
  462. double rowSize_pixels = (double) (y2DC - y1DC) / ny;
  463. double colSize_inches = colSize_pixels / my resolution;
  464. double rowSize_inches = rowSize_pixels / my resolution;
  465. interpolateX = Melder_iceiling (colSize_inches * smallestImageResolution); // number of interpolation points per horizontal sample
  466. interpolateY = Melder_iceiling (rowSize_inches * smallestImageResolution); // number of interpolation points per vertical sample
  467. }
  468. if (interpolateX <= 1 && interpolateY <= 1) {
  469. /* Do not interpolate. */
  470. my d_printf (my d_file, "/picstr %s string def %s %s 8 [%s 0 0 %s 0 0]\n"
  471. "{ currentfile picstr readhexstring pop } image\n",
  472. Melder8_integer (nx), Melder8_integer (nx), Melder8_integer (ny), Melder8_integer (nx), Melder8_integer (ny));
  473. } else if (interpolateX > 1 && interpolateY > 1) {
  474. /* Interpolate both horizontally and vertically. */
  475. integer nx_new = nx * interpolateX;
  476. integer ny_new = ny * interpolateY;
  477. /* Interpolation between rows requires us to remember two original rows: */
  478. my d_printf (my d_file, "/lorow %s string def /hirow %s string def\n",
  479. Melder8_integer (nx), Melder8_integer (nx));
  480. /* New rows (scanlines) are longer: */
  481. my d_printf (my d_file, "/scanline %s string def\n",
  482. Melder8_integer (nx_new));
  483. /* The first four arguments to the 'image' command, */
  484. /* namely the new number of columns, the new number of rows, the bit depth, and the matrix: */
  485. my d_printf (my d_file, "%s %s 8 [%s 0 0 %s 0 0]\n",
  486. Melder8_integer (nx_new), Melder8_integer (ny_new), Melder8_integer (nx_new), Melder8_integer (ny_new));
  487. /* Since our imageproc is going to output only one scanline at a time, */
  488. /* the outer loop variable (scanline number) has to be initialized outside the imageproc: */
  489. my d_printf (my d_file, "/irow 0 def\n");
  490. /* The imageproc starts here. First, we fill one or two original rows if necessary; */
  491. /* they are read as hexadecimal strings from the current file, i.e. just after the image command. */
  492. my d_printf (my d_file, "{\n"
  493. /* First test: are we in the first scanline? If so, read two original rows: */
  494. "irow 0 eq { currentfile lorow readhexstring pop pop lorow hirow copy pop } if\n"
  495. /* Second test: did we just pass an original data row? */
  496. /* If so, */
  497. /* (1) move that row backwards; */
  498. /* (2) read a new one unless we just passed the last original row: */
  499. "irow %s mod %s eq { hirow lorow copy pop\n"
  500. "irow %s ne { currentfile hirow readhexstring pop pop } if } if\n",
  501. Melder8_integer (interpolateY), Melder8_integer (interpolateY / 2),
  502. Melder8_integer (ny_new - interpolateY + interpolateY / 2));
  503. /* Where are we between those two rows? */
  504. my d_printf (my d_file, "/rowphase irow %s add %s mod %s div def\n",
  505. Melder8_integer (interpolateY - interpolateY / 2), Melder8_integer (interpolateY), Melder8_integer (interpolateY));
  506. /* Inner loop starts here. It cycles through all new columns: */
  507. my d_printf (my d_file, "0 1 %s {\n", Melder8_integer (nx_new - 1));
  508. /* Get the inner loop variable: */
  509. my d_printf (my d_file, " /icol exch def\n");
  510. /* Where are the two original columns? */
  511. my d_printf (my d_file, " /locol icol %s sub %s idiv def\n",
  512. Melder8_integer (interpolateX / 2), Melder8_integer (interpolateX));
  513. my d_printf (my d_file, " /hicol icol %s ge { %s } { icol %s add %s idiv } ifelse def\n",
  514. Melder8_integer (nx_new - interpolateX / 2), Melder8_integer (nx - 1),
  515. Melder8_integer (interpolateX / 2), Melder8_integer (interpolateX));
  516. /* Where are we between those two columns? */
  517. my d_printf (my d_file, " /colphase icol %s add %s mod %s div def\n",
  518. Melder8_integer (interpolateX - interpolateX / 2), Melder8_integer (interpolateX), Melder8_integer (interpolateX));
  519. /* Four-point interpolation: */
  520. my d_printf (my d_file,
  521. " /plow lorow locol get def\n"
  522. " /phigh lorow hicol get def\n"
  523. " /qlow hirow locol get def\n"
  524. " /qhigh hirow hicol get def\n"
  525. " /value\n"
  526. " plow phigh plow sub colphase mul add 1 rowphase sub mul\n"
  527. " qlow qhigh qlow sub colphase mul add rowphase mul\n"
  528. " add def\n"
  529. " scanline icol value 0 le { 0 } { value 255 ge { 255 } { value } ifelse } ifelse cvi put\n"
  530. "} for\n"
  531. "/irow irow 1 add def scanline } image\n");
  532. } else if (interpolateX > 1) {
  533. /* Interpolate horizontally only. */
  534. integer nx_new = nx * interpolateX;
  535. /* Remember one original row: */
  536. my d_printf (my d_file, "/row %s string def\n", Melder8_integer (nx));
  537. /* New rows (scanlines) are longer: */
  538. my d_printf (my d_file, "/scanline %s string def\n", Melder8_integer (nx_new));
  539. /* The first four arguments to the 'image' command, */
  540. /* namely the new number of columns, the number of rows, the bit depth, and the matrix: */
  541. my d_printf (my d_file, "%s %s 8 [%s 0 0 %s 0 0]\n",
  542. Melder8_integer (nx_new), Melder8_integer (ny), Melder8_integer (nx_new), Melder8_integer (ny));
  543. /* The imageproc starts here. We fill one original row. */
  544. my d_printf (my d_file, "{\n"
  545. "currentfile row readhexstring pop pop\n");
  546. /* Loop starts here. It cycles through all new columns: */
  547. my d_printf (my d_file, "0 1 %s {\n", Melder8_integer (nx_new - 1));
  548. /* Get the loop variable: */
  549. my d_printf (my d_file, " /icol exch def\n");
  550. /* Where are the two original columns? */
  551. my d_printf (my d_file, " /locol icol %s sub %s idiv def\n",
  552. Melder8_integer (interpolateX / 2), Melder8_integer (interpolateX));
  553. my d_printf (my d_file, " /hicol icol %s ge { %s } { icol %s add %s idiv } ifelse def\n",
  554. Melder8_integer (nx_new - interpolateX / 2), Melder8_integer (nx - 1),
  555. Melder8_integer (interpolateX / 2), Melder8_integer (interpolateX));
  556. /* Where are we between those two columns? */
  557. my d_printf (my d_file, " /colphase icol %s add %s mod %s div def\n",
  558. Melder8_integer (interpolateX - interpolateX / 2), Melder8_integer (interpolateX), Melder8_integer (interpolateX));
  559. /* Two-point interpolation: */
  560. my d_printf (my d_file,
  561. " /plow row locol get def\n"
  562. " /phigh row hicol get def\n"
  563. " /value plow phigh plow sub colphase mul add def\n"
  564. " scanline icol value 0 le { 0 } { value 255 ge { 255 } { value } ifelse } ifelse cvi put\n"
  565. "} for\n"
  566. "scanline } image\n");
  567. } else {
  568. /* Interpolate vertically only. */
  569. integer ny_new = ny * interpolateY;
  570. /* Interpolation between rows requires us to remember two original rows: */
  571. my d_printf (my d_file, "/lorow %s string def /hirow %s string def\n", Melder8_integer (nx), Melder8_integer (nx));
  572. /* New rows (scanlines) are equally long: */
  573. my d_printf (my d_file, "/scanline %s string def\n", Melder8_integer (nx));
  574. /* The first four arguments to the 'image' command, */
  575. /* namely the number of columns, the new number of rows, the bit depth, and the matrix: */
  576. my d_printf (my d_file, "%s %s 8 [%s 0 0 %s 0 0]\n",
  577. Melder8_integer (nx), Melder8_integer (ny_new), Melder8_integer (nx), Melder8_integer (ny_new));
  578. /* Since our imageproc is going to output only one scanline at a time, */
  579. /* the outer loop variable (scanline number) has to be initialized outside the imageproc: */
  580. my d_printf (my d_file, "/irow 0 def\n");
  581. /* The imageproc starts here. First, we fill one or two original rows if necessary; */
  582. /* they are read as hexadecimal strings from the current file, i.e. just after the image command. */
  583. my d_printf (my d_file, "{\n"
  584. /* First test: are we in the first scanline? If so, read two original rows: */
  585. "irow 0 eq { currentfile lorow readhexstring pop pop lorow hirow copy pop } if\n"
  586. /* Second test: did we just pass an original data row? */
  587. /* If so, */
  588. /* (1) move that row backwards; */
  589. /* (2) read a new one unless we just passed the last original row: */
  590. "irow %s mod %s eq { hirow lorow copy pop\n"
  591. "irow %s ne { currentfile hirow readhexstring pop pop } if } if\n",
  592. Melder8_integer (interpolateY), Melder8_integer (interpolateY / 2),
  593. Melder8_integer (ny_new - interpolateY + interpolateY / 2));
  594. /* Where are we between those two rows? */
  595. my d_printf (my d_file, "/rowphase irow %s add %s mod %s div def\n",
  596. Melder8_integer (interpolateY - interpolateY / 2),
  597. Melder8_integer (interpolateY), Melder8_integer (interpolateY));
  598. /* Inner loop starts here. It cycles through all columns: */
  599. my d_printf (my d_file, "0 1 %s {\n", Melder8_integer (nx - 1));
  600. /* Get the inner loop variable: */
  601. my d_printf (my d_file, " /icol exch def\n");
  602. /* Two-point interpolation: */
  603. my d_printf (my d_file,
  604. " /p lorow icol get def\n"
  605. " /q hirow icol get def\n"
  606. " /value\n"
  607. " p 1 rowphase sub mul\n"
  608. " q rowphase mul\n"
  609. " add def\n"
  610. " scanline icol value 0 le { 0 } { value 255 ge { 255 } { value } ifelse } ifelse cvi put\n"
  611. "} for\n"
  612. "/irow irow 1 add def scanline } image\n");
  613. }
  614. for (integer iy = iy1; iy <= iy2; iy ++) for (integer ix = ix1; ix <= ix2; ix ++) {
  615. int value = (int) (offset - scale * ( z_float ? z_float [iy] [ix] : z_byte [iy] [ix] ));
  616. my d_printf (my d_file, "%.2x", value <= minimalGrey ? minimalGrey : value >= 255 ? 255 : value);
  617. if (++ filling == 39) { my d_printf (my d_file, "\n"); filling = 0; }
  618. }
  619. if (filling) my d_printf (my d_file, "\n");
  620. my d_printf (my d_file, "grestore\n");
  621. }
  622. static void _cellArrayOrImage (Graphics me, double **z_float, double_rgbt **z_rgbt, unsigned char **z_byte,
  623. integer ix1, integer ix2, integer x1DC, integer x2DC,
  624. integer iy1, integer iy2, integer y1DC, integer y2DC, double minimum, double maximum,
  625. integer clipx1, integer clipx2, integer clipy1, integer clipy2, int interpolate)
  626. {
  627. if (my screen) {
  628. _GraphicsScreen_cellArrayOrImage (static_cast <GraphicsScreen> (me), z_float, z_rgbt, z_byte, ix1, ix2, x1DC, x2DC, iy1, iy2, y1DC, y2DC,
  629. minimum, maximum, clipx1, clipx2, clipy1, clipy2, interpolate);
  630. } else if (my postScript) {
  631. _GraphicsPostscript_cellArrayOrImage (static_cast <GraphicsPostscript> (me), z_float, z_rgbt, z_byte, ix1, ix2, x1DC, x2DC, iy1, iy2, y1DC, y2DC,
  632. minimum, maximum, clipx1, clipx2, clipy1, clipy2, interpolate);
  633. }
  634. _Graphics_setColour (me, my colour);
  635. }
  636. static void cellArrayOrImage (Graphics me, double **z_float, double_rgbt **z_rgbt, unsigned char **z_byte,
  637. integer ix1, integer ix2, double x1WC, double x2WC,
  638. integer iy1, integer iy2, double y1WC, double y2WC,
  639. double minimum, double maximum, int interpolate)
  640. {
  641. if (ix2 < ix1 || iy2 < iy1 || minimum == maximum) return;
  642. _cellArrayOrImage (me, z_float, z_rgbt, z_byte,
  643. ix1, ix2, wdx (x1WC), wdx (x2WC),
  644. iy1, iy2, wdy (y1WC), wdy (y2WC), minimum, maximum,
  645. wdx (my d_x1WC), wdx (my d_x2WC), wdy (my d_y1WC), wdy (my d_y2WC), interpolate);
  646. if (my recording) {
  647. integer nrow = iy2 - iy1 + 1, ncol = ix2 - ix1 + 1;
  648. op (interpolate ? ( z_float ? IMAGE : z_rgbt ? IMAGE_COLOUR : IMAGE8 ) :
  649. ( z_float ? CELL_ARRAY : z_rgbt ? CELL_ARRAY_COLOUR : CELL_ARRAY8 ),
  650. 8 + nrow * ncol * ( z_rgbt ? 4 : 1 ));
  651. put (x1WC); put (x2WC); put (y1WC); put (y2WC);
  652. put (minimum); put (maximum);
  653. put (nrow); put (ncol);
  654. for (integer iy = iy1; iy <= iy2; iy ++) {
  655. if (z_float) {
  656. double *row = z_float [iy];
  657. for (integer ix = ix1; ix <= ix2; ix ++) {
  658. put (row [ix]);
  659. }
  660. } else if (z_rgbt) {
  661. double_rgbt *row = z_rgbt [iy];
  662. for (integer ix = ix1; ix <= ix2; ix ++) {
  663. put (row [ix]. red);
  664. put (row [ix]. green);
  665. put (row [ix]. blue);
  666. put (row [ix]. transparency);
  667. }
  668. } else {
  669. unsigned char *row = z_byte [iy];
  670. for (integer ix = ix1; ix <= ix2; ix ++) {
  671. put (row [ix]);
  672. }
  673. }
  674. }
  675. }
  676. }
  677. void Graphics_cellArray (Graphics me, double **z, integer ix1, integer ix2, double x1WC, double x2WC,
  678. integer iy1, integer iy2, double y1WC, double y2WC, double minimum, double maximum)
  679. { cellArrayOrImage (me, z, nullptr, nullptr, ix1, ix2, x1WC, x2WC, iy1, iy2, y1WC, y2WC, minimum, maximum, false); }
  680. void Graphics_cellArray_colour (Graphics me, double_rgbt **z, integer ix1, integer ix2, double x1WC, double x2WC,
  681. integer iy1, integer iy2, double y1WC, double y2WC, double minimum, double maximum)
  682. { cellArrayOrImage (me, nullptr, z, nullptr, ix1, ix2, x1WC, x2WC, iy1, iy2, y1WC, y2WC, minimum, maximum, false); }
  683. void Graphics_cellArray8 (Graphics me, unsigned char **z, integer ix1, integer ix2, double x1WC, double x2WC,
  684. integer iy1, integer iy2, double y1WC, double y2WC, unsigned char minimum, unsigned char maximum)
  685. { cellArrayOrImage (me, nullptr, nullptr, z, ix1, ix2, x1WC, x2WC, iy1, iy2, y1WC, y2WC, minimum, maximum, false); }
  686. void Graphics_image (Graphics me, double **z, integer ix1, integer ix2, double x1WC, double x2WC,
  687. integer iy1, integer iy2, double y1WC, double y2WC, double minimum, double maximum)
  688. { cellArrayOrImage (me, z, nullptr, nullptr, ix1, ix2, x1WC, x2WC, iy1, iy2, y1WC, y2WC, minimum, maximum, true); }
  689. void Graphics_image_colour (Graphics me, double_rgbt **z, integer ix1, integer ix2, double x1WC, double x2WC,
  690. integer iy1, integer iy2, double y1WC, double y2WC, double minimum, double maximum)
  691. { cellArrayOrImage (me, nullptr, z, nullptr, ix1, ix2, x1WC, x2WC, iy1, iy2, y1WC, y2WC, minimum, maximum, true); }
  692. void Graphics_image8 (Graphics me, unsigned char **z, integer ix1, integer ix2, double x1WC, double x2WC,
  693. integer iy1, integer iy2, double y1WC, double y2WC, uint8 minimum, uint8 maximum)
  694. { cellArrayOrImage (me, nullptr, nullptr, z, ix1, ix2, x1WC, x2WC, iy1, iy2, y1WC, y2WC, minimum, maximum, true); }
  695. static void _GraphicsScreen_imageFromFile (GraphicsScreen me, conststring32 relativeFileName, double x1, double x2, double y1, double y2) {
  696. integer x1DC = wdx (x1), x2DC = wdx (x2), y1DC = wdy (y1), y2DC = wdy (y2);
  697. integer width = x2DC - x1DC, height = my yIsZeroAtTheTop ? y1DC - y2DC : y2DC - y1DC;
  698. #if 0
  699. structMelderFile file { };
  700. Melder_relativePathToFile (relativeFileName, & file);
  701. try {
  702. autoPhoto photo = Photo_readFromImageFile (& file);
  703. if (x1 == x2 && y1 == y2) {
  704. width = photo -> nx, x1DC -= width / 2, x2DC = x1DC + width;
  705. height = photo -> ny, y2DC -= height / 2, y1DC = y2DC + height;
  706. } else if (x1 == x2) {
  707. width = height * (double) photo -> nx / (double) photo -> ny;
  708. x1DC -= width / 2, x2DC = x1DC + width;
  709. } else if (y1 == y2) {
  710. height = width * (double) photo -> ny / (double) photo -> nx;
  711. y2DC -= height / 2, y1DC = y2DC + height;
  712. }
  713. autoNUMmatrix <double_rgbt> z (1, photo -> ny, 1, photo -> nx);
  714. for (integer iy = 1; iy <= photo -> ny; iy ++) {
  715. for (integer ix = 1; ix <= photo -> nx; ix ++) {
  716. z [iy] [ix]. red = photo -> d_red -> z [iy] [ix];
  717. z [iy] [ix]. green = photo -> d_green -> z [iy] [ix];
  718. z [iy] [ix]. blue = photo -> d_blue -> z [iy] [ix];
  719. z [iy] [ix]. transparency = photo -> d_transparency -> z [iy] [ix];
  720. }
  721. }
  722. _cellArrayOrImage (me, nullptr, z.peek(), nullptr,
  723. 1, photo -> nx, x1DC, x2DC, 1, photo -> ny, y1DC, y2DC,
  724. 0.0, 1.0,
  725. //wdx (my d_x1WC), wdx (my d_x2WC), wdy (my d_y1WC), wdy (my d_y2WC), // in case of clipping
  726. LONG_MIN, LONG_MAX, LONG_MAX, LONG_MIN, // in case of no clipping
  727. true);
  728. } catch (MelderError) {
  729. Melder_clearError ();
  730. }
  731. #elif gdi
  732. if (my d_useGdiplus) {
  733. structMelderFile file { };
  734. Melder_relativePathToFile (relativeFileName, & file);
  735. Gdiplus::Bitmap image (Melder_peek32toW (file. path));
  736. if (x1 == x2 && y1 == y2) {
  737. width = image. GetWidth (), x1DC -= width / 2, x2DC = x1DC + width;
  738. height = image. GetHeight (), y2DC -= height / 2, y1DC = y2DC + height;
  739. } else if (x1 == x2) {
  740. width = height * (double) image. GetWidth () / (double) image. GetHeight ();
  741. x1DC -= width / 2, x2DC = x1DC + width;
  742. } else if (y1 == y2) {
  743. height = width * (double) image. GetHeight () / (double) image. GetWidth ();
  744. y2DC -= height / 2, y1DC = y2DC + height;
  745. }
  746. Gdiplus::Graphics dcplus (my d_gdiGraphicsContext);
  747. Gdiplus::Rect rect (x1DC, y2DC, width, height);
  748. dcplus. DrawImage (& image, rect);
  749. } else {
  750. }
  751. #elif quartz
  752. structMelderFile file { };
  753. Melder_relativePathToFile (relativeFileName, & file);
  754. char utf8 [500];
  755. Melder_str32To8bitFileRepresentation_inplace (file. path, utf8);
  756. CFStringRef path = CFStringCreateWithCString (nullptr, utf8, kCFStringEncodingUTF8);
  757. CFURLRef url = CFURLCreateWithFileSystemPath (nullptr, path, kCFURLPOSIXPathStyle, false);
  758. CFRelease (path);
  759. CGImageSourceRef imageSource = CGImageSourceCreateWithURL (url, nullptr);
  760. CFRelease (url);
  761. if (imageSource) {
  762. CGImageRef image = CGImageSourceCreateImageAtIndex (imageSource, 0, nullptr);
  763. CFRelease (imageSource);
  764. if (image) {
  765. if (x1 == x2 && y1 == y2) {
  766. width = CGImageGetWidth (image), x1DC -= width / 2, x2DC = x1DC + width;
  767. height = CGImageGetHeight (image), y2DC -= height / 2, y1DC = y2DC + height;
  768. } else if (x1 == x2) {
  769. width = height * (double) CGImageGetWidth (image) / (double) CGImageGetHeight (image);
  770. x1DC -= width / 2, x2DC = x1DC + width;
  771. } else if (y1 == y2) {
  772. height = width * (double) CGImageGetHeight (image) / (double) CGImageGetWidth (image);
  773. y2DC -= height / 2, y1DC = y2DC + height;
  774. }
  775. GraphicsQuartz_initDraw (me);
  776. CGContextSaveGState (my d_macGraphicsContext);
  777. NSCAssert(my d_macGraphicsContext, @"nil context");
  778. CGContextTranslateCTM (my d_macGraphicsContext, 0, y1DC);
  779. CGContextScaleCTM (my d_macGraphicsContext, 1.0, -1.0);
  780. CGContextDrawImage (my d_macGraphicsContext, CGRectMake (x1DC, 0, width, height), image);
  781. CGContextRestoreGState (my d_macGraphicsContext);
  782. GraphicsQuartz_exitDraw (me);
  783. CGImageRelease (image);
  784. }
  785. }
  786. #endif
  787. }
  788. void Graphics_imageFromFile (Graphics me, conststring32 relativeFileName, double x1, double x2, double y1, double y2) {
  789. if (my screen) {
  790. _GraphicsScreen_imageFromFile (static_cast <GraphicsScreen> (me), relativeFileName, x1, x2, y1, y2);
  791. }
  792. if (my recording) {
  793. conststring8 txt_utf8 = Melder_peek32to8 (relativeFileName);
  794. int length = strlen (txt_utf8) / sizeof (double) + 1;
  795. op (IMAGE_FROM_FILE, 5 + length); put (x1); put (x2); put (y1); put (y2); sput (txt_utf8, length)
  796. }
  797. }
  798. /* End of file Graphics_image.cpp */