image.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. /* SLiM - Simple Login Manager
  2. Copyright (C) 2004-06 Simone Rota <sip@varlock.com>
  3. Copyright (C) 2004-06 Johannes Winkelmann <jw@tks6.net>
  4. Copyright (C) 2012 Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
  5. This program 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
  8. (at your option) any later version.
  9. The following code has been adapted and extended from
  10. xplanet 1.0.1, Copyright (C) 2002-04 Hari Nair <hari@alumni.caltech.edu>
  11. */
  12. #include <cctype>
  13. #include <cmath>
  14. #include <cstdio>
  15. #include <cstdlib>
  16. #include <cstring>
  17. #include <iostream>
  18. using namespace std;
  19. #include "image.h"
  20. extern "C" {
  21. int
  22. read_png(const char *filename, int *width, int *height, unsigned char **rgb,
  23. unsigned char **alpha);
  24. int
  25. read_jpeg(const char *filename, int *width, int *height, unsigned char **rgb);
  26. }
  27. Image::Image() : width(0), height(0), area(0),
  28. rgb_data(NULL), png_alpha(NULL), quality_(80) {}
  29. Image::Image(const int w, const int h, const unsigned char *rgb, const unsigned char *alpha) :
  30. width(w), height(h), area(w*h), quality_(80) {
  31. width = w;
  32. height = h;
  33. area = w * h;
  34. rgb_data = (unsigned char *) malloc(3 * area);
  35. memcpy(rgb_data, rgb, 3 * area);
  36. if (alpha == NULL) {
  37. png_alpha = NULL;
  38. } else {
  39. png_alpha = (unsigned char *) malloc(area);
  40. memcpy(png_alpha, alpha, area);
  41. }
  42. }
  43. Image::~Image() {
  44. free(rgb_data);
  45. free(png_alpha);
  46. }
  47. bool
  48. Image::Read(const char *filename) {
  49. char buf[4];
  50. unsigned char *ubuf = (unsigned char *) buf;
  51. int success = 0;
  52. FILE *file;
  53. file = fopen(filename, "rb");
  54. if (file == NULL)
  55. return(false);
  56. /* see what kind of file we have */
  57. fread(buf, 1, 4, file);
  58. fclose(file);
  59. if ((ubuf[0] == 0x89) && !strncmp("PNG", buf+1, 3)) {
  60. success = read_png(filename, &width, &height, &rgb_data, &png_alpha);
  61. }
  62. else if ((ubuf[0] == 0xff) && (ubuf[1] == 0xd8)){
  63. success = read_jpeg(filename, &width, &height, &rgb_data);
  64. } else {
  65. fprintf(stderr, "Unknown image format\n");
  66. success = 0;
  67. }
  68. return(success == 1);
  69. }
  70. void
  71. Image::Reduce(const int factor) {
  72. if (factor < 1)
  73. return;
  74. int scale = 1;
  75. for (int i = 0; i < factor; i++)
  76. scale *= 2;
  77. double scale2 = scale*scale;
  78. int w = width / scale;
  79. int h = height / scale;
  80. int new_area = w * h;
  81. unsigned char *new_rgb = (unsigned char *) malloc(3 * new_area);
  82. memset(new_rgb, 0, 3 * new_area);
  83. unsigned char *new_alpha = NULL;
  84. if (png_alpha != NULL) {
  85. new_alpha = (unsigned char *) malloc(new_area);
  86. memset(new_alpha, 0, new_area);
  87. }
  88. int ipos = 0;
  89. for (int j = 0; j < height; j++) {
  90. int js = j / scale;
  91. for (int i = 0; i < width; i++) {
  92. int is = i/scale;
  93. for (int k = 0; k < 3; k++)
  94. new_rgb[3*(js * w + is) + k] += static_cast<unsigned char> ((rgb_data[3*ipos + k] + 0.5) / scale2);
  95. if (png_alpha != NULL)
  96. new_alpha[js * w + is] += static_cast<unsigned char> (png_alpha[ipos]/scale2);
  97. ipos++;
  98. }
  99. }
  100. free(rgb_data);
  101. free(png_alpha);
  102. rgb_data = new_rgb;
  103. png_alpha = new_alpha;
  104. width = w;
  105. height = h;
  106. area = w * h;
  107. }
  108. void
  109. Image::Resize(const int w, const int h) {
  110. if (width==w && height==h){
  111. return;
  112. }
  113. int new_area = w * h;
  114. unsigned char *new_rgb = (unsigned char *) malloc(3 * new_area);
  115. unsigned char *new_alpha = NULL;
  116. if (png_alpha != NULL)
  117. new_alpha = (unsigned char *) malloc(new_area);
  118. const double scale_x = ((double) w) / width;
  119. const double scale_y = ((double) h) / height;
  120. int ipos = 0;
  121. for (int j = 0; j < h; j++) {
  122. const double y = j / scale_y;
  123. for (int i = 0; i < w; i++) {
  124. const double x = i / scale_x;
  125. if (new_alpha == NULL)
  126. getPixel(x, y, new_rgb + 3*ipos);
  127. else
  128. getPixel(x, y, new_rgb + 3*ipos, new_alpha + ipos);
  129. ipos++;
  130. }
  131. }
  132. free(rgb_data);
  133. free(png_alpha);
  134. rgb_data = new_rgb;
  135. png_alpha = new_alpha;
  136. width = w;
  137. height = h;
  138. area = w * h;
  139. }
  140. // Find the color of the desired point using bilinear interpolation.
  141. // Assume the array indices refer to the denter of the pixel, so each
  142. // pixel has corners at (i - 0.5, j - 0.5) and (i + 0.5, j + 0.5)
  143. void
  144. Image::getPixel(double x, double y, unsigned char *pixel) {
  145. getPixel(x, y, pixel, NULL);
  146. }
  147. void
  148. Image::getPixel(double x, double y, unsigned char *pixel, unsigned char *alpha) {
  149. if (x < -0.5)
  150. x = -0.5;
  151. if (x >= width - 0.5)
  152. x = width - 0.5;
  153. if (y < -0.5)
  154. y = -0.5;
  155. if (y >= height - 0.5)
  156. y = height - 0.5;
  157. int ix0 = (int) (floor(x));
  158. int ix1 = ix0 + 1;
  159. if (ix0 < 0)
  160. ix0 = width - 1;
  161. if (ix1 >= width)
  162. ix1 = 0;
  163. int iy0 = (int) (floor(y));
  164. int iy1 = iy0 + 1;
  165. if (iy0 < 0)
  166. iy0 = 0;
  167. if (iy1 >= height)
  168. iy1 = height - 1;
  169. const double t = x - floor(x);
  170. const double u = 1 - (y - floor(y));
  171. double weight[4];
  172. weight[1] = t * u;
  173. weight[0] = u - weight[1];
  174. weight[2] = 1 - t - u + weight[1];
  175. weight[3] = t - weight[1];
  176. unsigned char *pixels[4];
  177. pixels[0] = rgb_data + 3 * (iy0 * width + ix0);
  178. pixels[1] = rgb_data + 3 * (iy0 * width + ix1);
  179. pixels[2] = rgb_data + 3 * (iy1 * width + ix0);
  180. pixels[3] = rgb_data + 3 * (iy1 * width + ix1);
  181. memset(pixel, 0, 3);
  182. for (int i = 0; i < 4; i++) {
  183. for (int j = 0; j < 3; j++)
  184. pixel[j] += (unsigned char) (weight[i] * pixels[i][j]);
  185. }
  186. if (alpha != NULL) {
  187. unsigned char pixels[4];
  188. pixels[0] = png_alpha[iy0 * width + ix0];
  189. pixels[1] = png_alpha[iy0 * width + ix1];
  190. pixels[2] = png_alpha[iy0 * width + ix0];
  191. pixels[3] = png_alpha[iy1 * width + ix1];
  192. for (int i = 0; i < 4; i++)
  193. *alpha = (unsigned char) (weight[i] * pixels[i]);
  194. }
  195. }
  196. /* Merge the image with a background, taking care of the
  197. * image Alpha transparency. (background alpha is ignored).
  198. * The images is merged on position (x, y) on the
  199. * background, the background must contain the image.
  200. */
  201. void Image::Merge(Image* background, const int x, const int y) {
  202. if (x + width > background->Width()|| y + height > background->Height()) {
  203. return;
  204. }
  205. if (background->Width()*background->Height() != width*height)
  206. background->Crop(x, y, width, height);
  207. double tmp;
  208. unsigned char *new_rgb = (unsigned char *) malloc(3 * width * height);
  209. memset(new_rgb, 0, 3 * width * height);
  210. const unsigned char *bg_rgb = background->getRGBData();
  211. int ipos = 0;
  212. if (png_alpha != NULL){
  213. for (int j = 0; j < height; j++) {
  214. for (int i = 0; i < width; i++) {
  215. for (int k = 0; k < 3; k++) {
  216. tmp = rgb_data[3*ipos + k]*png_alpha[ipos]/255.0
  217. + bg_rgb[3*ipos + k]*(1-png_alpha[ipos]/255.0);
  218. new_rgb[3*ipos + k] = static_cast<unsigned char> (tmp);
  219. }
  220. ipos++;
  221. }
  222. }
  223. } else {
  224. for (int j = 0; j < height; j++) {
  225. for (int i = 0; i < width; i++) {
  226. for (int k = 0; k < 3; k++) {
  227. tmp = rgb_data[3*ipos + k];
  228. new_rgb[3*ipos + k] = static_cast<unsigned char> (tmp);
  229. }
  230. ipos++;
  231. }
  232. }
  233. }
  234. free(rgb_data);
  235. free(png_alpha);
  236. rgb_data = new_rgb;
  237. png_alpha = NULL;
  238. }
  239. /* Tile the image growing its size to the minimum entire
  240. * multiple of w * h.
  241. * The new dimensions should be > of the current ones.
  242. * Note that this flattens image (alpha removed)
  243. */
  244. void Image::Tile(const int w, const int h) {
  245. if (w < width || h < height)
  246. return;
  247. int nx = w / width;
  248. if (w % width > 0)
  249. nx++;
  250. int ny = h / height;
  251. if (h % height > 0)
  252. ny++;
  253. int newwidth = nx*width;
  254. int newheight=ny*height;
  255. unsigned char *new_rgb = (unsigned char *) malloc(3 * newwidth * newheight);
  256. memset(new_rgb, 0, 3 * width * height * nx * ny);
  257. int ipos = 0;
  258. int opos = 0;
  259. for (int r = 0; r < ny; r++) {
  260. for (int c = 0; c < nx; c++) {
  261. for (int j = 0; j < height; j++) {
  262. for (int i = 0; i < width; i++) {
  263. opos = j*width + i;
  264. ipos = r*width*height*nx + j*newwidth + c*width +i;
  265. for (int k = 0; k < 3; k++) {
  266. new_rgb[3*ipos + k] = static_cast<unsigned char> (rgb_data[3*opos + k]);
  267. }
  268. }
  269. }
  270. }
  271. }
  272. free(rgb_data);
  273. free(png_alpha);
  274. rgb_data = new_rgb;
  275. png_alpha = NULL;
  276. width = newwidth;
  277. height = newheight;
  278. area = width * height;
  279. Crop(0,0,w,h);
  280. }
  281. /* Crop the image
  282. */
  283. void Image::Crop(const int x, const int y, const int w, const int h) {
  284. if (x+w > width || y+h > height) {
  285. return;
  286. }
  287. int x2 = x + w;
  288. int y2 = y + h;
  289. unsigned char *new_rgb = (unsigned char *) malloc(3 * w * h);
  290. memset(new_rgb, 0, 3 * w * h);
  291. unsigned char *new_alpha = NULL;
  292. if (png_alpha != NULL) {
  293. new_alpha = (unsigned char *) malloc(w * h);
  294. memset(new_alpha, 0, w * h);
  295. }
  296. int ipos = 0;
  297. int opos = 0;
  298. for (int j = 0; j < height; j++) {
  299. for (int i = 0; i < width; i++) {
  300. if (j>=y && i>=x && j<y2 && i<x2) {
  301. for (int k = 0; k < 3; k++) {
  302. new_rgb[3*ipos + k] = static_cast<unsigned char> (rgb_data[3*opos + k]);
  303. }
  304. if (png_alpha != NULL)
  305. new_alpha[ipos] = static_cast<unsigned char> (png_alpha[opos]);
  306. ipos++;
  307. }
  308. opos++;
  309. }
  310. }
  311. free(rgb_data);
  312. free(png_alpha);
  313. rgb_data = new_rgb;
  314. if (png_alpha != NULL)
  315. png_alpha = new_alpha;
  316. width = w;
  317. height = h;
  318. area = w * h;
  319. }
  320. /* Center the image in a rectangle of given width and height.
  321. * Fills the remaining space (if any) with the hex color
  322. */
  323. void Image::Center(const int w, const int h, const char *hex) {
  324. unsigned long packed_rgb;
  325. sscanf(hex, "%lx", &packed_rgb);
  326. unsigned long r = packed_rgb>>16;
  327. unsigned long g = packed_rgb>>8 & 0xff;
  328. unsigned long b = packed_rgb & 0xff;
  329. unsigned char *new_rgb = (unsigned char *) malloc(3 * w * h);
  330. memset(new_rgb, 0, 3 * w * h);
  331. int x = (w - width) / 2;
  332. int y = (h - height) / 2;
  333. if (x<0) {
  334. Crop((width - w)/2,0,w,height);
  335. x = 0;
  336. }
  337. if (y<0) {
  338. Crop(0,(height - h)/2,width,h);
  339. y = 0;
  340. }
  341. int x2 = x + width;
  342. int y2 = y + height;
  343. int ipos = 0;
  344. int opos = 0;
  345. double tmp;
  346. area = w * h;
  347. for (int i = 0; i < area; i++) {
  348. new_rgb[3*i] = r;
  349. new_rgb[3*i+1] = g;
  350. new_rgb[3*i+2] = b;
  351. }
  352. if (png_alpha != NULL) {
  353. for (int j = 0; j < h; j++) {
  354. for (int i = 0; i < w; i++) {
  355. if (j>=y && i>=x && j<y2 && i<x2) {
  356. ipos = j*w + i;
  357. for (int k = 0; k < 3; k++) {
  358. tmp = rgb_data[3*opos + k]*png_alpha[opos]/255.0
  359. + new_rgb[k]*(1-png_alpha[opos]/255.0);
  360. new_rgb[3*ipos + k] = static_cast<unsigned char> (tmp);
  361. }
  362. opos++;
  363. }
  364. }
  365. }
  366. } else {
  367. for (int j = 0; j < h; j++) {
  368. for (int i = 0; i < w; i++) {
  369. if (j>=y && i>=x && j<y2 && i<x2) {
  370. ipos = j*w + i;
  371. for (int k = 0; k < 3; k++) {
  372. tmp = rgb_data[3*opos + k];
  373. new_rgb[3*ipos + k] = static_cast<unsigned char> (tmp);
  374. }
  375. opos++;
  376. }
  377. }
  378. }
  379. }
  380. free(rgb_data);
  381. free(png_alpha);
  382. rgb_data = new_rgb;
  383. png_alpha = NULL;
  384. width = w;
  385. height = h;
  386. }
  387. /* Fill the image with the given color and adjust its dimensions
  388. * to passed values.
  389. */
  390. void Image::Plain(const int w, const int h, const char *hex) {
  391. unsigned long packed_rgb;
  392. sscanf(hex, "%lx", &packed_rgb);
  393. unsigned long r = packed_rgb>>16;
  394. unsigned long g = packed_rgb>>8 & 0xff;
  395. unsigned long b = packed_rgb & 0xff;
  396. unsigned char *new_rgb = (unsigned char *) malloc(3 * w * h);
  397. memset(new_rgb, 0, 3 * w * h);
  398. area = w * h;
  399. for (int i = 0; i < area; i++) {
  400. new_rgb[3*i] = r;
  401. new_rgb[3*i+1] = g;
  402. new_rgb[3*i+2] = b;
  403. }
  404. free(rgb_data);
  405. free(png_alpha);
  406. rgb_data = new_rgb;
  407. png_alpha = NULL;
  408. width = w;
  409. height = h;
  410. }
  411. void
  412. Image::computeShift(unsigned long mask,
  413. unsigned char &left_shift,
  414. unsigned char &right_shift) {
  415. left_shift = 0;
  416. right_shift = 8;
  417. if (mask != 0) {
  418. while ((mask & 0x01) == 0) {
  419. left_shift++;
  420. mask >>= 1;
  421. }
  422. while ((mask & 0x01) == 1) {
  423. right_shift--;
  424. mask >>= 1;
  425. }
  426. }
  427. }
  428. Pixmap
  429. Image::createPixmap(Display* dpy, int scr, Window win) {
  430. int i, j; // loop variables
  431. const int depth = DefaultDepth(dpy, scr);
  432. Visual *visual = DefaultVisual(dpy, scr);
  433. Colormap colormap = DefaultColormap(dpy, scr);
  434. Pixmap tmp = XCreatePixmap(dpy, win, width, height,
  435. depth);
  436. char *pixmap_data = NULL;
  437. switch (depth) {
  438. case 32:
  439. case 24:
  440. pixmap_data = new char[4 * width * height];
  441. break;
  442. case 16:
  443. case 15:
  444. pixmap_data = new char[2 * width * height];
  445. break;
  446. case 8:
  447. pixmap_data = new char[width * height];
  448. break;
  449. default:
  450. break;
  451. }
  452. XImage *ximage = XCreateImage(dpy, visual, depth, ZPixmap, 0,
  453. pixmap_data, width, height,
  454. 8, 0);
  455. int entries;
  456. XVisualInfo v_template;
  457. v_template.visualid = XVisualIDFromVisual(visual);
  458. XVisualInfo *visual_info = XGetVisualInfo(dpy, VisualIDMask,
  459. &v_template, &entries);
  460. unsigned long ipos = 0;
  461. switch (visual_info->c_class) {
  462. case PseudoColor: {
  463. XColor xc;
  464. xc.flags = DoRed | DoGreen | DoBlue;
  465. int num_colors = 256;
  466. XColor *colors = new XColor[num_colors];
  467. for (i = 0; i < num_colors; i++)
  468. colors[i].pixel = (unsigned long) i;
  469. XQueryColors(dpy, colormap, colors, num_colors);
  470. int *closest_color = new int[num_colors];
  471. for (i = 0; i < num_colors; i++) {
  472. xc.red = (i & 0xe0) << 8; // highest 3 bits
  473. xc.green = (i & 0x1c) << 11; // middle 3 bits
  474. xc.blue = (i & 0x03) << 14; // lowest 2 bits
  475. // find the closest color in the colormap
  476. double distance, distance_squared, min_distance = 0;
  477. for (int ii = 0; ii < num_colors; ii++) {
  478. distance = colors[ii].red - xc.red;
  479. distance_squared = distance * distance;
  480. distance = colors[ii].green - xc.green;
  481. distance_squared += distance * distance;
  482. distance = colors[ii].blue - xc.blue;
  483. distance_squared += distance * distance;
  484. if ((ii == 0) || (distance_squared <= min_distance)) {
  485. min_distance = distance_squared;
  486. closest_color[i] = ii;
  487. }
  488. }
  489. }
  490. for (j = 0; j < height; j++) {
  491. for (i = 0; i < width; i++) {
  492. xc.red = (unsigned short) (rgb_data[ipos++] & 0xe0);
  493. xc.green = (unsigned short) (rgb_data[ipos++] & 0xe0);
  494. xc.blue = (unsigned short) (rgb_data[ipos++] & 0xc0);
  495. xc.pixel = xc.red | (xc.green >> 3) | (xc.blue >> 6);
  496. XPutPixel(ximage, i, j,
  497. colors[closest_color[xc.pixel]].pixel);
  498. }
  499. }
  500. delete [] colors;
  501. delete [] closest_color;
  502. }
  503. break;
  504. case TrueColor: {
  505. unsigned char red_left_shift;
  506. unsigned char red_right_shift;
  507. unsigned char green_left_shift;
  508. unsigned char green_right_shift;
  509. unsigned char blue_left_shift;
  510. unsigned char blue_right_shift;
  511. computeShift(visual_info->red_mask, red_left_shift,
  512. red_right_shift);
  513. computeShift(visual_info->green_mask, green_left_shift,
  514. green_right_shift);
  515. computeShift(visual_info->blue_mask, blue_left_shift,
  516. blue_right_shift);
  517. unsigned long pixel;
  518. unsigned long red, green, blue;
  519. for (j = 0; j < height; j++) {
  520. for (i = 0; i < width; i++) {
  521. red = (unsigned long)
  522. rgb_data[ipos++] >> red_right_shift;
  523. green = (unsigned long)
  524. rgb_data[ipos++] >> green_right_shift;
  525. blue = (unsigned long)
  526. rgb_data[ipos++] >> blue_right_shift;
  527. pixel = (((red << red_left_shift) & visual_info->red_mask)
  528. | ((green << green_left_shift)
  529. & visual_info->green_mask)
  530. | ((blue << blue_left_shift)
  531. & visual_info->blue_mask));
  532. XPutPixel(ximage, i, j, pixel);
  533. }
  534. }
  535. }
  536. break;
  537. default: {
  538. logStream << "Login.app: could not load image" << endl;
  539. return(tmp);
  540. }
  541. }
  542. GC gc = XCreateGC(dpy, win, 0, NULL);
  543. XPutImage(dpy, tmp, gc, ximage, 0, 0, 0, 0, width, height);
  544. XFreeGC(dpy, gc);
  545. XFree(visual_info);
  546. delete [] pixmap_data;
  547. // Set ximage data to NULL since pixmap data was deallocated above
  548. ximage->data = NULL;
  549. XDestroyImage(ximage);
  550. return(tmp);
  551. }