Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

392 lines
12 KiB

  1. /* DITHER.C
  2. Frosting: Master Theme Selector for Windows '95
  3. Copyright (c) 1994-1998 Microsoft Corporation. All rights reserved.
  4. */
  5. #include <windows.h>
  6. #include <windowsx.h>
  7. #ifdef DBG
  8. #define _DEBUG
  9. #endif
  10. #ifdef DEBUG
  11. #define _DEBUG
  12. #endif
  13. #ifdef _DEBUG
  14. #include <mmsystem.h>
  15. #define TIMESTART(sz) { TCHAR szTime[80]; DWORD time = timeGetTime();
  16. #define TIMESTOP(sz) time = timeGetTime() - time; wsprintf(szTime, TEXT("%s took %d.%03d sec\r\n"), sz, time/1000, time%1000); OutputDebugString(szTime); }
  17. #else
  18. #define TIMESTART(sz)
  19. #define TIMESTOP(sz)
  20. #endif
  21. //-----------------------------------------------------------------------------
  22. // helpers
  23. //-----------------------------------------------------------------------------
  24. __inline UINT Clamp8(int z)
  25. {
  26. return (UINT)(((z) < 0) ? 0 : (((z) > 255) ? 255 : (z)));
  27. }
  28. __inline WORD rgb555(r, g, b)
  29. {
  30. return (((WORD)(r) << 10) | ((WORD)(g) << 5) | (WORD)(b));
  31. }
  32. //-----------------------------------------------------------------------------
  33. // Rounding stuff
  34. //-----------------------------------------------------------------------------
  35. //
  36. // round an 8bit value to a 5bit value with good distribution
  37. //
  38. #pragma data_seg(".text", "CODE")
  39. BYTE aRound8to5[] = {
  40. 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
  41. 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4,
  42. 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6,
  43. 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8,
  44. 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10,
  45. 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12,
  46. 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13,
  47. 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
  48. 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17,
  49. 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19,
  50. 19, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21,
  51. 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23,
  52. 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25,
  53. 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27,
  54. 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29,
  55. 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31,
  56. };
  57. #pragma data_seg()
  58. //
  59. // complement of table above
  60. //
  61. #pragma data_seg(".text", "CODE")
  62. BYTE aRound5to8[] = {
  63. 0, 8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99,107,115,123,
  64. 132,140,148,156,165,173,181,189,197,206,214,222,230,239,247,255,
  65. };
  66. #pragma data_seg()
  67. //-----------------------------------------------------------------------------
  68. // RoundPixel555
  69. //-----------------------------------------------------------------------------
  70. __inline WORD RoundPixel555(int r, int g, int b)
  71. {
  72. return rgb555(aRound8to5[r], aRound8to5[g], aRound8to5[b]);
  73. }
  74. //-----------------------------------------------------------------------------
  75. // Round24from555
  76. //-----------------------------------------------------------------------------
  77. __inline void Round24from555(RGBQUAD *out, WORD in)
  78. {
  79. out->rgbBlue = aRound5to8[ (in) & 0x1f];
  80. out->rgbGreen = aRound5to8[ (in >> 5) & 0x1f];
  81. out->rgbRed = aRound5to8[(in >> 10) & 0x1f];
  82. }
  83. /******************************Public*Routine******************************\
  84. * 16bpp dither stuff
  85. *
  86. * pimped from dibengine
  87. *
  88. \**************************************************************************/
  89. //
  90. // map a 8bit value (0-255) to a 5bit value (0-31) *evenly*
  91. //
  92. // if (i < 8) return 0;
  93. // if (i == 255) return 31;
  94. // return (i-8)/8;
  95. //
  96. #pragma data_seg(".text", "CODE")
  97. BYTE aMap8to5[] = {
  98. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  99. 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
  100. 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
  101. 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
  102. 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8,
  103. 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
  104. 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12,
  105. 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14,
  106. 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
  107. 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18,
  108. 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20,
  109. 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
  110. 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24,
  111. 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
  112. 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28,
  113. 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 31,
  114. };
  115. #pragma data_seg()
  116. //
  117. // map a 5bit value back to a 8bit value
  118. //
  119. // if (i==0) return 0;
  120. // if (i==31) return 255;
  121. // return i*8+8;
  122. //
  123. #pragma data_seg(".text", "CODE")
  124. BYTE aMap5to8[] = {
  125. 0, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96,104,112,120,128,
  126. 136,144,152,160,168,176,184,192,200,208,216,224,232,240,248,255,
  127. };
  128. #pragma data_seg()
  129. //
  130. // halftone table for 16bpp dithers
  131. //
  132. #pragma data_seg(".text", "CODE")
  133. BYTE aHalftone16[4][4] = {
  134. 0, 4, 1, 5,
  135. 6, 2, 7, 3,
  136. 1, 5, 0, 4,
  137. 7, 3, 6, 2,
  138. };
  139. #pragma data_seg()
  140. /******************************Public*Routine******************************\
  141. * Dither555
  142. *
  143. \**************************************************************************/
  144. __inline WORD
  145. Dither555(BYTE r, BYTE re, BYTE g, BYTE ge, BYTE b, BYTE be, BYTE e)
  146. {
  147. return rgb555(r + (re > e), g + (ge > e), b + (be > e));
  148. }
  149. /******************************Public*Routine******************************\
  150. * DitherPixel555
  151. *
  152. * Takes RGB and x/y coords
  153. * and produces an appropriate 555 pixel.
  154. *
  155. \**************************************************************************/
  156. __inline WORD DitherPixel555(int rs, int gs, int bs, int x, int y)
  157. {
  158. BYTE r = aMap8to5[rs];
  159. BYTE re = rs - aMap5to8[r];
  160. BYTE g = aMap8to5[gs];
  161. BYTE ge = gs - aMap5to8[g];
  162. BYTE b = aMap8to5[bs];
  163. BYTE be = bs - aMap5to8[b];
  164. return Dither555(r, re, g, ge, b, be, aHalftone16[x % 4][y % 4]);
  165. }
  166. ///////////////////////////////////////////////////////////////////////////////
  167. typedef struct {int r, g, b;} ERRBUF;
  168. ///////////////////////////////////////////////////////////////////////////////
  169. void DitherScan(LPBYTE dst, LPBYTE src, RGBQUAD *colors, LPBYTE map,
  170. ERRBUF *cur_err, ERRBUF *nxt_err, int dx, int y, BOOL f8bpp, BOOL fOrder16)
  171. {
  172. RGBQUAD rgbChosen, *pChosen = &rgbChosen;
  173. WORD wColor16;
  174. int er,eg,eb;
  175. int r, g, b;
  176. int x;
  177. for (x=0; x<dx; x++)
  178. {
  179. r = Clamp8((int)src[2] + cur_err[x].r / 16);
  180. g = Clamp8((int)src[1] + cur_err[x].g / 16);
  181. b = Clamp8((int)src[0] + cur_err[x].b / 16);
  182. wColor16 = (fOrder16? DitherPixel555(r,g,b,x,y) : RoundPixel555(r,g,b));
  183. if (f8bpp)
  184. pChosen = colors + (*dst++ = map[wColor16]);
  185. else
  186. Round24from555(pChosen, (*((WORD *)dst)++ = wColor16));
  187. er = r - (int)pChosen->rgbRed;
  188. eg = g - (int)pChosen->rgbGreen;
  189. eb = b - (int)pChosen->rgbBlue;
  190. cur_err[x+1].r += er * 7;
  191. cur_err[x+1].g += eg * 7;
  192. cur_err[x+1].b += eb * 7;
  193. nxt_err[x-1].r += er * 3;
  194. nxt_err[x-1].g += eg * 3;
  195. nxt_err[x-1].b += eb * 3;
  196. nxt_err[x+0].r += er * 5;
  197. nxt_err[x+0].g += eg * 5;
  198. nxt_err[x+0].b += eb * 5;
  199. nxt_err[x+1].r += er * 1;
  200. nxt_err[x+1].g += eg * 1;
  201. nxt_err[x+1].b += eb * 1;
  202. src+=3;
  203. }
  204. }
  205. ///////////////////////////////////////////////////////////////////////////////
  206. void DitherEngine(LPBYTE dst, LPBYTE src, RGBQUAD *colors, LPBYTE map,
  207. int dx, int dy, int dx_bytes, BOOL f8bpp, BOOL fOrder16)
  208. {
  209. ERRBUF *buf;
  210. ERRBUF *err0;
  211. ERRBUF *err1;
  212. ERRBUF *cur_err;
  213. ERRBUF *nxt_err;
  214. int src_next_scan;
  215. int y, i;
  216. src_next_scan = (dx*3+3)&~3;
  217. buf = (ERRBUF *)LocalAlloc(LPTR, sizeof(ERRBUF) * (dx+2) * 2);
  218. if (buf)
  219. {
  220. err0 = cur_err = buf+1;
  221. err1 = nxt_err = buf+1 + dx + 2;
  222. /* read line by line, quantize, and transfer */
  223. for (y=0; y<dy; y++)
  224. {
  225. DitherScan(dst, src, colors, map, cur_err, nxt_err, dx, y,
  226. f8bpp, fOrder16);
  227. src += src_next_scan;
  228. dst += dx_bytes;
  229. cur_err = nxt_err;
  230. nxt_err = cur_err == err0 ? err1 : err0;
  231. for (i=-1; i<=dx; i++)
  232. nxt_err[i].r = nxt_err[i].g = nxt_err[i].b = 0;
  233. }
  234. LocalFree((HLOCAL)buf);
  235. }
  236. }
  237. ///////////////////////////////////////////////////////////////////////////////
  238. // note that this layout has scanlines DWORD aligned
  239. #define INVMAP_IMAGE_PELS (0x8000)
  240. #define INVMAP_IMAGE_X (0x0100)
  241. #define INVMAP_IMAGE_Y (INVMAP_IMAGE_PELS / INVMAP_IMAGE_X)
  242. HBITMAP CreateInverseMapping(BYTE **ppMap, HDC hdcColors)
  243. {
  244. BOOL fResult = FALSE;
  245. HBITMAP hbmDst, hbmOldColors;
  246. HDC hdcSrc;
  247. *ppMap = NULL;
  248. if ((hbmDst = CreateCompatibleBitmap(hdcColors,
  249. INVMAP_IMAGE_X, INVMAP_IMAGE_Y)) == NULL)
  250. {
  251. return NULL;
  252. }
  253. hbmOldColors = SelectBitmap(hdcColors, hbmDst);
  254. if ((hdcSrc = CreateCompatibleDC(NULL)) != NULL)
  255. {
  256. WORD *pSrc;
  257. BITMAPINFO bmiSrc = {sizeof(BITMAPINFOHEADER), INVMAP_IMAGE_X,
  258. INVMAP_IMAGE_Y, 1, 16, BI_RGB, 0, 0, 0, 0, 0};
  259. HBITMAP hbmSrc = CreateDIBSection(hdcColors, &bmiSrc, DIB_RGB_COLORS,
  260. &pSrc, NULL, 0);
  261. if (hbmSrc)
  262. {
  263. BITMAP bmDst;
  264. if (GetObject(hbmDst, sizeof(bmDst), &bmDst))
  265. {
  266. HBITMAP hbmOldSrc = SelectBitmap(hdcSrc, hbmSrc);
  267. UINT u = 0;
  268. while (u < INVMAP_IMAGE_PELS)
  269. *pSrc++ = (WORD)u++;
  270. BitBlt(hdcColors, 0, 0, INVMAP_IMAGE_X, INVMAP_IMAGE_Y, hdcSrc,
  271. 0, 0, SRCCOPY);
  272. *ppMap = (BYTE *)bmDst.bmBits;
  273. SelectBitmap(hdcSrc, hbmOldSrc);
  274. }
  275. DeleteBitmap(hbmSrc);
  276. }
  277. DeleteDC(hdcSrc);
  278. }
  279. SelectBitmap(hdcColors, hbmOldColors);
  280. if (*ppMap)
  281. return hbmDst;
  282. DeleteBitmap(hbmDst);
  283. return NULL;
  284. }
  285. ///////////////////////////////////////////////////////////////////////////////
  286. BOOL DitherImage(HDC hdcDst, HBITMAP hbmDst, LPBITMAPINFOHEADER lpbiSrc,
  287. LPBYTE lpbSrc, BOOL fOrder16)
  288. {
  289. BOOL fResult = FALSE;
  290. BITMAP bmDst;
  291. if (lpbiSrc->biBitCount != 24)
  292. return FALSE;
  293. if (GetObject(hbmDst, sizeof(bmDst), &bmDst))
  294. {
  295. if ((lpbiSrc->biWidth == bmDst.bmWidth) &&
  296. (lpbiSrc->biHeight == bmDst.bmHeight) &&
  297. ((bmDst.bmBitsPixel == 16) || (bmDst.bmBitsPixel == 8)))
  298. {
  299. RGBQUAD rgbColors[256];
  300. HBITMAP hbmMap = NULL;
  301. BYTE *pMap;
  302. BOOL f8bpp = (bmDst.bmBitsPixel == 8);
  303. if (f8bpp)
  304. {
  305. TIMESTART(TEXT("CreateInverseMap"));
  306. if ((hbmMap = CreateInverseMapping(&pMap, hdcDst)) == NULL)
  307. goto error;
  308. GetDIBColorTable(hdcDst, 0, 256, rgbColors);
  309. TIMESTOP(TEXT("CreateInverseMap"));
  310. }
  311. DitherEngine((LPBYTE)bmDst.bmBits, lpbSrc, rgbColors, pMap,
  312. bmDst.bmWidth, bmDst.bmHeight, bmDst.bmWidthBytes,
  313. f8bpp, fOrder16);
  314. if (hbmMap)
  315. DeleteBitmap(hbmMap);
  316. fResult = TRUE;
  317. error:
  318. ; // make the compiler happy it wants a statement here
  319. }
  320. }
  321. return fResult;
  322. }