special_input_device 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/ext/special_input_device/color.c +25 -6
  3. data/ext/special_input_device/color.h +5 -1
  4. data/ext/special_input_device/extconf.rb +2 -2
  5. data/ext/special_input_device/image.c +728 -0
  6. data/ext/special_input_device/image.h +8 -0
  7. data/ext/special_input_device/{interception_connector.c → interception.c} +7 -3
  8. data/ext/special_input_device/{interception_connector.h → interception.h} +0 -0
  9. data/ext/special_input_device/keyboard.c +4 -4
  10. data/ext/special_input_device/mouse.c +26 -28
  11. data/ext/special_input_device/ruby_macro.h +11 -3
  12. data/ext/special_input_device/screen.c +54 -164
  13. data/ext/special_input_device/special_input_device.c +2 -2
  14. data/ext/special_input_device/special_input_device.h +2 -1
  15. data/ext/special_input_device/win32error.c +0 -2
  16. data/ext/special_input_device/window.c +82 -105
  17. data/lib/special_input_device/color.rb +2 -3
  18. data/lib/special_input_device/image.rb +2 -61
  19. data/lib/special_input_device/point.rb +0 -1
  20. data/lib/special_input_device/special_input_device.rb +0 -1
  21. metadata +7 -15
  22. data/lib/special_input_device/image/bmp.rb +0 -89
  23. data/lib/special_input_device/image/error/damaged_image_error.rb +0 -4
  24. data/lib/special_input_device/image/error/image_error.rb +0 -3
  25. data/lib/special_input_device/image/error/unsupported_image_format_error.rb +0 -4
  26. data/lib/special_input_device/table_2d.rb +0 -157
  27. data/stab/keyboard.rb +0 -35
  28. data/stab/mouse.rb +0 -189
  29. data/stab/screen.rb +0 -56
  30. data/stab/win32_error.rb +0 -20
  31. data/stab/window.rb +0 -398
@@ -0,0 +1,728 @@
1
+ #include "special_input_device.h"
2
+ #include "color.h"
3
+
4
+ #define BI_RGB 0L
5
+ #define BI_BITFIELDS 3L
6
+ #define BI_ALPHABITFIELDS 6L
7
+
8
+ #define PixelAt(x, y) (cData->pixelTable[cData->height + ~y][x])
9
+
10
+ static VALUE ruby__alloc(VALUE self);
11
+
12
+ typedef struct {
13
+ UINT height;
14
+ RGBQUAD **pixelTable;
15
+ } SID_IMAGE_DATA;
16
+
17
+ VALUE ruby___image_new(UINT cWidth, UINT cHeight) {
18
+ VALUE instance = ruby__alloc(specialInputDevice_image);
19
+ SID_IMAGE_DATA *cData = GET_DATA_OBJECT(instance);
20
+ UINT cT;
21
+ cData->height = cHeight;
22
+ cData->pixelTable = ALLOC_N(LPRGBQUAD, cHeight);
23
+ rb_iv_set(instance, "@width", UINT2NUM(cWidth));
24
+ rb_iv_set(instance, "@height", UINT2NUM(cHeight));
25
+ for (cT = 0; cT < cHeight; cT++)
26
+ cData->pixelTable[cT] = ALLOC_N(RGBQUAD, cWidth);
27
+ return instance;
28
+ }
29
+
30
+ VALUE ruby___image_new_from_bitmap(HBITMAP cHBitmap) {
31
+ VALUE instance;
32
+ SID_IMAGE_DATA *cData;
33
+ HDC cScreenDC = GetDC(NULL);
34
+ BITMAP cBitmap;
35
+ UINT cWidth;
36
+ UINT cHeight;
37
+ BITMAPINFO cBitmapInfo;
38
+ UINT cT0;
39
+ UINT cT1;
40
+ GetObject(cHBitmap, sizeof(BITMAP), &cBitmap);
41
+ cWidth = (UINT) abs(cBitmap.bmWidth);
42
+ cHeight = (UINT) abs(cBitmap.bmHeight);
43
+ instance = ruby___image_new(cWidth, cHeight);
44
+ cData = GET_DATA_OBJECT(instance);
45
+ cBitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
46
+ cBitmapInfo.bmiHeader.biWidth = (LONG) cWidth;
47
+ cBitmapInfo.bmiHeader.biHeight = (LONG) cHeight;
48
+ cBitmapInfo.bmiHeader.biPlanes = 1;
49
+ cBitmapInfo.bmiHeader.biBitCount = 32;
50
+ cBitmapInfo.bmiHeader.biCompression = BI_RGB;
51
+ cBitmapInfo.bmiHeader.biSizeImage = 0;
52
+ cBitmapInfo.bmiHeader.biXPelsPerMeter = 0;
53
+ cBitmapInfo.bmiHeader.biYPelsPerMeter = 0;
54
+ cBitmapInfo.bmiHeader.biClrUsed = 0;
55
+ cBitmapInfo.bmiHeader.biClrImportant = 0;
56
+ for (cT0 = 0; cT0 < cHeight; cT0++) {
57
+ GetDIBits(cScreenDC, cHBitmap, cT0, 1, cData->pixelTable[cT0], &cBitmapInfo, DIB_RGB_COLORS);
58
+ for (cT1 = 0; cT1 < cWidth; cT1++)
59
+ cData->pixelTable[cT0][cT1].rgbReserved = 0xFF;
60
+ }
61
+ return instance;
62
+ }
63
+
64
+ typedef union {
65
+ BITMAPINFOHEADER v1;
66
+ BITMAPV4HEADER v4;
67
+ BITMAPV5HEADER v5;
68
+ } DIB_HEADER;
69
+
70
+ /*
71
+ * Load an image from a file.
72
+ * @overload load(path)
73
+ * @param [String] path a path of file
74
+ * @return [SpecialInputDevice::Image] a new image
75
+ */
76
+ static VALUE ruby__load(VALUE self, VALUE path) {
77
+ LPCTSTR cPath = RUBY_VALUE_TO_LPCTSTR(path);
78
+ VALUE image;
79
+ SID_IMAGE_DATA *cData;
80
+ FILE *cFile;
81
+ BITMAPFILEHEADER cFileHeader;
82
+ DIB_HEADER cDIBHeader;
83
+ RGBQUAD *cColorTable;
84
+ BYTE *cPixelArray;
85
+ LARGE_INTEGER cSystemFileSize;
86
+ UINT cWidth;
87
+ UINT cHeight;
88
+ UINT cRowSize;
89
+ DWORD cByteRead;
90
+ UINT cT0;
91
+ UINT cT1;
92
+ UINT cT2;
93
+ UINT cT3;
94
+ cFile = CreateFile(cPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
95
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
96
+ if (cFile == INVALID_HANDLE_VALUE)
97
+ RAISE_WIN32_ERROR();
98
+ if (!ReadFile(cFile, &cFileHeader, sizeof(cFileHeader), &cByteRead, NULL))
99
+ RAISE_WIN32_ERROR();
100
+ switch (cFileHeader.bfType) {
101
+ case 0x4D42: // BM
102
+ case 0x4142: // BA
103
+ case 0x4943: // CI
104
+ case 0x5043: // CP
105
+ case 0x4349: // IC
106
+ case 0x5450: // PT
107
+ break;
108
+ default:
109
+ rb_raise(rb_eArgError, "The path does not point to a bitmap file.");
110
+ }
111
+ if (!GetFileSizeEx(cFile, &cSystemFileSize))
112
+ RAISE_WIN32_ERROR();
113
+ if (cSystemFileSize.QuadPart != cFileHeader.bfSize)
114
+ rb_raise(rb_eArgError, "The size of the file should be %lu bytes, but the image only has %lli bytes.",
115
+ cFileHeader.bfSize, cSystemFileSize.QuadPart);
116
+ if (!ReadFile(cFile, &cDIBHeader.v1.biSize, sizeof(DWORD), &cByteRead, NULL))
117
+ RAISE_WIN32_ERROR();
118
+ switch (cDIBHeader.v1.biSize) {
119
+ case sizeof(BITMAPINFOHEADER):
120
+ case sizeof(BITMAPV4HEADER):
121
+ case sizeof(BITMAPV5HEADER):
122
+ break;
123
+ default:
124
+ rb_raise(rb_eArgError, "The bitmap file is using an unsupported DIB header.");
125
+ }
126
+ if (!ReadFile(cFile, &cDIBHeader.v1.biSize + 1, cDIBHeader.v1.biSize - sizeof(DWORD), &cByteRead, NULL))
127
+ RAISE_WIN32_ERROR();
128
+ cWidth = (UINT) abs(cDIBHeader.v1.biWidth);
129
+ cHeight = (UINT) abs(cDIBHeader.v1.biHeight);
130
+ cRowSize =
131
+ (cDIBHeader.v1.biBitCount * cWidth + (8 * sizeof(RGBQUAD) - 1)) / (8 * sizeof(RGBQUAD)) * sizeof(RGBQUAD);
132
+ if (cDIBHeader.v1.biWidth < 0)
133
+ rb_raise(rb_eArgError, "The bitmap file has been damaged. (width < 0)");
134
+ if (cDIBHeader.v1.biPlanes != 1)
135
+ rb_raise(rb_eArgError, "The bitmap file has been damaged. (planes != 1)");
136
+ switch (cDIBHeader.v1.biCompression) {
137
+ case BI_RGB:
138
+ break;
139
+ case BI_BITFIELDS:
140
+ case BI_ALPHABITFIELDS:
141
+ switch (cDIBHeader.v1.biBitCount) {
142
+ case 16:
143
+ case 24:
144
+ case 32:
145
+ break;
146
+ default:
147
+ rb_raise(rb_eArgError, "The bitmap file is using an unsupported compression method.");
148
+ }
149
+ break;
150
+ default:
151
+ rb_raise(rb_eArgError, "The bitmap file is using an unsupported compression method.");
152
+ }
153
+ if (cDIBHeader.v1.biSizeImage != cRowSize * cHeight)
154
+ rb_raise(rb_eArgError, "The bitmap file has been damaged. (incorrect sizeImage)");
155
+ switch (cDIBHeader.v1.biSize) {
156
+ case sizeof(BITMAPINFOHEADER):
157
+ switch (cDIBHeader.v1.biCompression) {
158
+ case BI_BITFIELDS:
159
+ if (!ReadFile(cFile, &cDIBHeader.v5.bV5RedMask, sizeof(DWORD) * 3, &cByteRead, NULL))
160
+ RAISE_WIN32_ERROR();
161
+ cDIBHeader.v5.bV5AlphaMask = 0x00000000;
162
+ break;
163
+ case BI_ALPHABITFIELDS:
164
+ if (!ReadFile(cFile, &cDIBHeader.v5.bV5RedMask, sizeof(DWORD) * 4, &cByteRead, NULL))
165
+ RAISE_WIN32_ERROR();
166
+ break;
167
+ default:
168
+ switch (cDIBHeader.v1.biBitCount) {
169
+ case 1:
170
+ case 4:
171
+ case 8:
172
+ cDIBHeader.v5.bV5RedMask = 0;
173
+ cDIBHeader.v5.bV5GreenMask = 0;
174
+ cDIBHeader.v5.bV5BlueMask = 0;
175
+ cDIBHeader.v5.bV5AlphaMask = 0;
176
+ case 16:
177
+ cDIBHeader.v5.bV5RedMask = 0x7C00;
178
+ cDIBHeader.v5.bV5GreenMask = 0x03E0;
179
+ cDIBHeader.v5.bV5BlueMask = 0x001F;
180
+ cDIBHeader.v5.bV5AlphaMask = 0x0000;
181
+ case 24:
182
+ cDIBHeader.v5.bV5RedMask = 0xFF0000;
183
+ cDIBHeader.v5.bV5GreenMask = 0x00FF00;
184
+ cDIBHeader.v5.bV5BlueMask = 0x0000FF;
185
+ cDIBHeader.v5.bV5AlphaMask = 0x000000;
186
+ break;
187
+ case 32:
188
+ cDIBHeader.v5.bV5RedMask = 0x00FF0000;
189
+ cDIBHeader.v5.bV5GreenMask = 0x0000FF00;
190
+ cDIBHeader.v5.bV5BlueMask = 0x000000FF;
191
+ cDIBHeader.v5.bV5AlphaMask = 0x00000000;
192
+ break;
193
+ default:
194
+ break;
195
+ }
196
+ break;
197
+ }
198
+ cDIBHeader.v5.bV5CSType = LCS_WINDOWS_COLOR_SPACE;
199
+ cDIBHeader.v5.bV5Endpoints.ciexyzRed.ciexyzX = 0;
200
+ cDIBHeader.v5.bV5Endpoints.ciexyzRed.ciexyzY = 0;
201
+ cDIBHeader.v5.bV5Endpoints.ciexyzRed.ciexyzZ = 0;
202
+ cDIBHeader.v5.bV5Endpoints.ciexyzGreen.ciexyzX = 0;
203
+ cDIBHeader.v5.bV5Endpoints.ciexyzGreen.ciexyzY = 0;
204
+ cDIBHeader.v5.bV5Endpoints.ciexyzGreen.ciexyzZ = 0;
205
+ cDIBHeader.v5.bV5Endpoints.ciexyzBlue.ciexyzX = 0;
206
+ cDIBHeader.v5.bV5Endpoints.ciexyzBlue.ciexyzY = 0;
207
+ cDIBHeader.v5.bV5Endpoints.ciexyzBlue.ciexyzZ = 0;
208
+ cDIBHeader.v5.bV5GammaRed = 0;
209
+ cDIBHeader.v5.bV5GammaGreen = 0;
210
+ cDIBHeader.v5.bV5GammaBlue = 0;
211
+ case sizeof(BITMAPV4HEADER):
212
+ cDIBHeader.v5.bV5Intent = LCS_GM_ABS_COLORIMETRIC;
213
+ cDIBHeader.v5.bV5ProfileData = 0;
214
+ cDIBHeader.v5.bV5ProfileSize = 0;
215
+ cDIBHeader.v5.bV5Reserved = 0;
216
+ case sizeof(BITMAPV5HEADER):
217
+ break;
218
+ default:
219
+ break;
220
+ }
221
+
222
+ cColorTable = NULL;
223
+ switch (cDIBHeader.v1.biBitCount) {
224
+ case 1:
225
+ case 4:
226
+ case 8: {
227
+ UINT cColorTableSize = cDIBHeader.v1.biClrUsed;
228
+ UINT cMaxColorTableSize = (UINT) (1 << cDIBHeader.v1.biBitCount);
229
+ if (cColorTableSize > cMaxColorTableSize)
230
+ rb_raise(rb_eArgError, "The bitmap file has been damaged. (clrUsed too large)");
231
+ if (cColorTableSize == 0)
232
+ cColorTableSize = cMaxColorTableSize;
233
+ cColorTable = ALLOC_N(RGBQUAD, cColorTableSize);
234
+ ReadFile(cFile, cColorTable, sizeof(RGBQUAD) * cColorTableSize, &cByteRead, NULL);
235
+ break;
236
+ }
237
+ case 16:
238
+ case 24:
239
+ case 32:
240
+ break;
241
+ default:
242
+ rb_raise(rb_eArgError, "The bitmap file is using unsupported color depth.");
243
+ }
244
+
245
+ if (!SetFilePointer(cFile, cFileHeader.bfOffBits, NULL, FILE_BEGIN))
246
+ RAISE_WIN32_ERROR();
247
+
248
+ image = ruby___image_new(cWidth, cHeight);
249
+ cData = GET_DATA_OBJECT(image);
250
+
251
+ cPixelArray = ALLOC_N(BYTE, cRowSize);
252
+ for (cT0 = 0; cT0 < cHeight; cT0++) {
253
+ UINT cPixelY = cDIBHeader.v1.biHeight >= 0 ? cT0 : cHeight + ~cT0;
254
+ if (!ReadFile(cFile, cPixelArray, cRowSize, &cByteRead, NULL))
255
+ RAISE_WIN32_ERROR();
256
+ switch (cDIBHeader.v1.biBitCount) {
257
+ case 1:
258
+ case 4:
259
+ case 8: {
260
+ INT cPixelPerByte = 8 / cDIBHeader.v1.biBitCount;
261
+ for (cT1 = 0; cT1 < cWidth; cT1++) {
262
+ INT cByteIndex = cT1 / cPixelPerByte;
263
+ INT cBitsIndex = (cPixelPerByte + ~(cT1 % cPixelPerByte)) * cDIBHeader.v1.biBitCount;
264
+ INT cBitsFilter = (1 << cDIBHeader.v1.biBitCount) - 1;
265
+ INT cColorIndex = (cPixelArray[cByteIndex] >> cBitsIndex) & cBitsFilter;
266
+ cData->pixelTable[cPixelY][cT1] = cColorTable[cColorIndex];
267
+ cData->pixelTable[cPixelY][cT1].rgbReserved = 0xFF;
268
+ }
269
+ break;
270
+ }
271
+ case 16:
272
+ case 24:
273
+ case 32: {
274
+ BYTE *cPixel;
275
+ INT cBytePrePixel = cDIBHeader.v1.biBitCount / 8;
276
+ LONG cValue;
277
+ LONG cMask;
278
+ LONG cBuffer;
279
+ LONG cBitIndex;
280
+ LONG cBit;
281
+
282
+ for (cT1 = 0; cT1 < cWidth; cT1++) {
283
+
284
+ cPixel = (BYTE *) &cData->pixelTable[cPixelY][cT1];
285
+
286
+ cValue = 0;
287
+ for (cT2 = 0; cT2 < cBytePrePixel; cT2++)
288
+ cValue += ((LONG) cPixelArray[cT1 * cBytePrePixel + cT2]) << (cT2 * 8);
289
+
290
+ for (cT2 = 0; cT2 < 4; cT2++) {
291
+ switch (cT2) {
292
+ case 0:
293
+ cMask = cDIBHeader.v5.bV5BlueMask;
294
+ break;
295
+ case 1:
296
+ cMask = cDIBHeader.v5.bV5GreenMask;
297
+ break;
298
+ case 2:
299
+ cMask = cDIBHeader.v5.bV5RedMask;
300
+ break;
301
+ case 3:
302
+ cMask = cDIBHeader.v5.bV5AlphaMask;
303
+ break;
304
+ default:
305
+ cMask = 0;
306
+ break;
307
+ }
308
+ if (cMask) {
309
+ cBuffer = 0;
310
+ cBitIndex = 0;
311
+ for (cT3 = 0; cT3 < cDIBHeader.v1.biBitCount; cT3++) {
312
+ cBit = 1 << cT3;
313
+ if (cMask & cBit)
314
+ cBuffer += (cValue & cBit) >> (cT3 - cBitIndex++);
315
+ }
316
+ cPixel[cT2] = (BYTE) (cBuffer * 0xFF / ((1 << cBitIndex) - 1));
317
+ } else
318
+ cPixel[cT2] = 0xFF;
319
+ }
320
+ }
321
+ break;
322
+ }
323
+ default:
324
+ break;
325
+ }
326
+ }
327
+
328
+ xfree(cPixelArray);
329
+ if (cColorTable)
330
+ xfree(cColorTable);
331
+
332
+ return image;
333
+ }
334
+
335
+ static VOID callback_image_free(SID_IMAGE_DATA *cData) {
336
+ UINT cT;
337
+ for (cT = 0; cT < cData->height; cT++)
338
+ xfree(cData->pixelTable[cT]);
339
+ xfree(cData->pixelTable);
340
+ xfree(cData);
341
+ }
342
+
343
+ static VALUE ruby__alloc(VALUE self) {
344
+ return NEW_DATA_OBJECT(specialInputDevice_image, SID_IMAGE_DATA, NULL, callback_image_free);
345
+ }
346
+
347
+ /*
348
+ * @!scope class
349
+ * @overload new(width, height)
350
+ * @param [Integer] width the width of table
351
+ * @param [Integer] height the height of table
352
+ */
353
+ static VALUE ruby_initialize(VALUE self, VALUE width, VALUE height) {
354
+ UINT cWidth = NUM2UINT(width);
355
+ UINT cHeight = NUM2UINT(height);
356
+ SID_IMAGE_DATA *cData = GET_SELF_DATA_OBJECT();
357
+ INT cT0;
358
+ INT cT1;
359
+ cData->height = cHeight;
360
+ cData->pixelTable = ALLOC_N(LPRGBQUAD, cHeight);
361
+ for (cT0 = 0; cT0 < cHeight; cT0++) {
362
+ cData->pixelTable[cT0] = ALLOC_N(RGBQUAD, cWidth);
363
+ for (cT1 = 0; cT1 < cWidth; cT1++) {
364
+ cData->pixelTable[cT0][cT1].rgbBlue = 0;
365
+ cData->pixelTable[cT0][cT1].rgbGreen = 0;
366
+ cData->pixelTable[cT0][cT1].rgbRed = 0;
367
+ cData->pixelTable[cT0][cT1].rgbReserved = 0;
368
+ }
369
+ }
370
+ rb_iv_set(self, "@width", width);
371
+ rb_iv_set(self, "@height", height);
372
+ return Qnil;
373
+ }
374
+
375
+ /*
376
+ * Save this image to a file.
377
+ * @overload save(path)
378
+ * @param [String] path a path to target file
379
+ * @return [SpecialInputDevice::Image] self
380
+ */
381
+ static VALUE ruby_save(VALUE self, VALUE path) {
382
+ VALUE width = rb_iv_get(self, "@width");
383
+ UINT cWidth = NUM2UINT(width);
384
+ LPCTSTR cPath = RUBY_VALUE_TO_LPCTSTR(path);
385
+ SID_IMAGE_DATA *cData = GET_SELF_DATA_OBJECT();
386
+ size_t cHeaderSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV5HEADER);
387
+ INT cDataSize = sizeof(RGBQUAD) * cWidth * cData->height;
388
+ FILE *cFile;
389
+ BITMAPFILEHEADER cFileHeader;
390
+ BITMAPV5HEADER cDIBHeader;
391
+ DWORD cByteWritten;
392
+ INT cT;
393
+ cFile = CreateFile(cPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
394
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
395
+ if (cFile == INVALID_HANDLE_VALUE)
396
+ RAISE_WIN32_ERROR();
397
+ cFileHeader.bfType = 0x4D42;
398
+ cFileHeader.bfSize = cHeaderSize + cDataSize;
399
+ cFileHeader.bfReserved1 = 0;
400
+ cFileHeader.bfReserved2 = 0;
401
+ cFileHeader.bfOffBits = cHeaderSize;
402
+ cDIBHeader.bV5Size = sizeof(BITMAPV5HEADER);
403
+ cDIBHeader.bV5Width = (LONG) cWidth;
404
+ cDIBHeader.bV5Height = (LONG) cData->height;
405
+ cDIBHeader.bV5Planes = 1;
406
+ cDIBHeader.bV5BitCount = 8 * sizeof(RGBQUAD);
407
+ cDIBHeader.bV5Compression = BI_BITFIELDS;
408
+ cDIBHeader.bV5SizeImage = (DWORD) cDataSize;
409
+ cDIBHeader.bV5XPelsPerMeter = 0;
410
+ cDIBHeader.bV5YPelsPerMeter = 0;
411
+ cDIBHeader.bV5ClrUsed = 0;
412
+ cDIBHeader.bV5ClrImportant = 0;
413
+ cDIBHeader.bV5RedMask = 0x00FF0000;
414
+ cDIBHeader.bV5GreenMask = 0x0000FF00;
415
+ cDIBHeader.bV5BlueMask = 0x000000FF;
416
+ cDIBHeader.bV5AlphaMask = 0xFF000000;
417
+ cDIBHeader.bV5CSType = LCS_WINDOWS_COLOR_SPACE;
418
+ cDIBHeader.bV5Endpoints.ciexyzRed.ciexyzX = 0;
419
+ cDIBHeader.bV5Endpoints.ciexyzRed.ciexyzY = 0;
420
+ cDIBHeader.bV5Endpoints.ciexyzRed.ciexyzZ = 0;
421
+ cDIBHeader.bV5Endpoints.ciexyzGreen.ciexyzX = 0;
422
+ cDIBHeader.bV5Endpoints.ciexyzGreen.ciexyzY = 0;
423
+ cDIBHeader.bV5Endpoints.ciexyzGreen.ciexyzZ = 0;
424
+ cDIBHeader.bV5Endpoints.ciexyzBlue.ciexyzX = 0;
425
+ cDIBHeader.bV5Endpoints.ciexyzBlue.ciexyzY = 0;
426
+ cDIBHeader.bV5Endpoints.ciexyzBlue.ciexyzZ = 0;
427
+ cDIBHeader.bV5GammaRed = 0;
428
+ cDIBHeader.bV5GammaGreen = 0;
429
+ cDIBHeader.bV5GammaBlue = 0;
430
+ cDIBHeader.bV5Intent = LCS_GM_ABS_COLORIMETRIC;
431
+ cDIBHeader.bV5ProfileData = 0;
432
+ cDIBHeader.bV5ProfileSize = 0;
433
+ cDIBHeader.bV5Reserved = 0;
434
+ WriteFile(cFile, &cFileHeader, sizeof(cFileHeader), &cByteWritten, NULL);
435
+ WriteFile(cFile, &cDIBHeader, sizeof(cDIBHeader), &cByteWritten, NULL);
436
+ for (cT = 0; cT < cData->height; cT++)
437
+ WriteFile(cFile, cData->pixelTable[cT], (DWORD) (sizeof(RGBQUAD) * cWidth), &cByteWritten, NULL);
438
+ return self;
439
+ }
440
+
441
+ /*
442
+ * @overload [](x, y)
443
+ * @param [Integer] x the x coordinate, can be negative
444
+ * @param [Integer] y the y coordinate, can be negative
445
+ * @return [SpecialInputDevice::Color] the color of pixel at the specific coordinate
446
+ */
447
+ static VALUE ruby_OS_CS(VALUE self, VALUE x, VALUE y) {
448
+ INT cWidth = NUM2INT(rb_iv_get(self, "@width"));
449
+ INT cX = NUM2INT(x);
450
+ INT cY = NUM2INT(y);
451
+ SID_IMAGE_DATA *cData = GET_SELF_DATA_OBJECT();
452
+ if (cX < -cWidth || cX >= cWidth)
453
+ rb_raise(rb_eRangeError, "The image only has %d columns. x = %d is out of range.", cWidth, cX);
454
+ if (cX < 0)
455
+ cX += cWidth;
456
+ if (cY < (INT) -cData->height || cY >= cData->height)
457
+ rb_raise(rb_eRangeError, "The image only has %d rows. y = %d is out of range.", cData->height, cY);
458
+ if (cY < 0)
459
+ cY += cData->height;
460
+ return ruby___color_new_from_rgb_quad(&PixelAt(cX, cY));
461
+ }
462
+
463
+ /*
464
+ * @overload []=(x, y, value)
465
+ * @param [Integer] x the x coordinate, can be negative
466
+ * @param [Integer] y the y coordinate, can be negative
467
+ * @param [SpecialInputDevice::Color] value the new color
468
+ * @return [SpecialInputDevice::Color] the new color
469
+ */
470
+ static VALUE ruby_OS_CS_EQ(VALUE self, VALUE x, VALUE y, VALUE value) {
471
+ INT cWidth = NUM2INT(rb_iv_get(self, "@width"));
472
+ INT cX = NUM2INT(x);
473
+ INT cY = NUM2INT(y);
474
+ RGBQUAD cVALUE = ruby___color_to_rgb_quad(value);
475
+ SID_IMAGE_DATA *cData = GET_SELF_DATA_OBJECT();
476
+ if (cX < -cWidth || cX >= cWidth)
477
+ rb_raise(rb_eRangeError, "The image only has %d columns. x = %d is out of range.", cWidth, cX);
478
+ if (cX < 0)
479
+ cX += cWidth;
480
+ if (cY < (INT) -cData->height || cY >= cData->height)
481
+ rb_raise(rb_eRangeError, "The image only has %d rows. y = %d is out of range.", cData->height, cY);
482
+ if (cY < 0)
483
+ cY += cData->height;
484
+ PixelAt(cX, cY) = cVALUE;
485
+ return value;
486
+ }
487
+
488
+ /*
489
+ * @overload try_get(x, y)
490
+ * @param [Integer] x the x coordinate, can be negative
491
+ * @param [Integer] y the y coordinate, can be negative
492
+ * @return [NilClass, SpecialInputDevice::Color] the color of pixel at the specific coordinate or nil if out of range
493
+ */
494
+ static VALUE ruby_try_get(VALUE self, VALUE x, VALUE y) {
495
+ INT cWidth = NUM2INT(rb_iv_get(self, "@width"));
496
+ INT cX = NUM2INT(x);
497
+ INT cY = NUM2INT(y);
498
+ SID_IMAGE_DATA *cData = GET_SELF_DATA_OBJECT();
499
+ if (cX < -cWidth || cX >= cWidth || cY < (INT) -cData->height || cY >= cData->height)
500
+ return Qnil;
501
+ if (cX < 0)
502
+ cX += cWidth;
503
+ if (cY < 0)
504
+ cY += cData->height;
505
+ return ruby___color_new_from_rgb_quad(&PixelAt(cX, cY));
506
+ }
507
+
508
+ /*
509
+ * @overload try_set(x, y, value)
510
+ * @param [Integer] x the x coordinate, can be negative
511
+ * @param [Integer] y the y coordinate, can be negative
512
+ * @param [SpecialInputDevice::Color] value the new color
513
+ * @return [SpecialInputDevice::Color] the new color
514
+ */
515
+ static VALUE ruby_try_set(VALUE self, VALUE x, VALUE y, VALUE value) {
516
+ INT cWidth = NUM2INT(rb_iv_get(self, "@width"));
517
+ INT cX = NUM2INT(x);
518
+ INT cY = NUM2INT(y);
519
+ RGBQUAD cVALUE = ruby___color_to_rgb_quad(value);
520
+ SID_IMAGE_DATA *cData = GET_SELF_DATA_OBJECT();
521
+ if (cX >= -cWidth && cX < cWidth && cY >= (INT) -cData->height && cY < cData->height) {
522
+ if (cX < 0)
523
+ cX += cWidth;
524
+ if (cY < 0)
525
+ cY += cData->height;
526
+ PixelAt(cX, cY) = cVALUE;
527
+ }
528
+ return value;
529
+ }
530
+
531
+ /*
532
+ * @overload each(&block)
533
+ * @param [Proc] block the action
534
+ * @yield [value] the action
535
+ * @yieldparam [SpecialInputDevice::Color] value the value
536
+ * @yieldreturn [Object] value will be ignored
537
+ * @return [Enumerator, SpecialInputDevice::Image] a Enumerator if no block gavin, self otherwise.
538
+ */
539
+ static VALUE ruby_each(VARIABLE_ARGUMENTS_C) {
540
+ VALUE block;
541
+ SID_IMAGE_DATA *cData = GET_SELF_DATA_OBJECT();
542
+ UINT cWidth = NUM2UINT(rb_iv_get(self, "@width"));
543
+ SCAN_ARGUMENTS("&", &block);
544
+ if (RTEST(block)) {
545
+ UINT cT0;
546
+ UINT cT1;
547
+ for (cT0 = 0; cT0 < cData->height; cT0++) {
548
+ for (cT1 = 0; cT1 < cWidth; cT1++) {
549
+ RGBQUAD *cColor = &PixelAt(cT1, cT0);
550
+ VALUE color = ruby___color_new_from_rgb_quad(cColor);
551
+ rb_yield(color);
552
+ }
553
+ }
554
+ return self;
555
+ } else {
556
+ VALUE newEnumeratorArguments[1];
557
+ newEnumeratorArguments[0] = self;
558
+ return rb_class_new_instance(1, newEnumeratorArguments, rb_cEnumerator);
559
+ }
560
+ }
561
+
562
+ /*
563
+ * @overload each_row(&block)
564
+ * @param [Proc] block the action
565
+ * @yield [row] the action
566
+ * @yieldparam [Array<SpecialInputDevice::Color>] row the row
567
+ * @yieldreturn [Object] value will be ignored
568
+ * @return [Enumerator, SpecialInputDevice::Image] a Enumerator if no block gavin, self otherwise.
569
+ */
570
+ static VALUE ruby_each_row(VARIABLE_ARGUMENTS_C) {
571
+ VALUE block;
572
+ SID_IMAGE_DATA *cData = GET_SELF_DATA_OBJECT();
573
+ LONG cWidth = NUM2LONG(rb_iv_get(self, "@width"));
574
+ SCAN_ARGUMENTS("&", &block);
575
+ if (RTEST(block)) {
576
+ UINT cT0;
577
+ UINT cT1;
578
+ VALUE *cRow = ALLOC_N(VALUE, cWidth);
579
+ for (cT0 = 0; cT0 < cData->height; cT0++) {
580
+ for (cT1 = 0; cT1 < cWidth; cT1++) {
581
+ cRow[cT1] = ruby___color_new_from_rgb_quad(&PixelAt(cT1, cT0));
582
+ rb_yield(rb_ary_new_from_values(cWidth, cRow));
583
+ }
584
+ }
585
+ return self;
586
+ } else {
587
+ VALUE newEnumeratorArguments[2];
588
+ newEnumeratorArguments[0] = self;
589
+ newEnumeratorArguments[1] = NEW_SYMBOL("each_row");
590
+ return rb_class_new_instance(2, newEnumeratorArguments, rb_cEnumerator);
591
+ }
592
+ }
593
+
594
+ /*
595
+ * @overload each_column(&block)
596
+ * @param [Proc] block the action
597
+ * @yield [row] the action
598
+ * @yieldparam [Array<SpecialInputDevice::Color>] column the column
599
+ * @yieldreturn [Object] value will be ignored
600
+ * @return [Enumerator, SpecialInputDevice::Image] a Enumerator if no block gavin, self otherwise.
601
+ */
602
+ static VALUE ruby_each_column(VARIABLE_ARGUMENTS_C) {
603
+ VALUE block;
604
+ SID_IMAGE_DATA *cData = GET_SELF_DATA_OBJECT();
605
+ LONG cWidth = NUM2LONG(rb_iv_get(self, "@width"));
606
+ SCAN_ARGUMENTS("&", &block);
607
+ if (RTEST(block)) {
608
+ UINT cT0;
609
+ UINT cT1;
610
+ VALUE *cColumn = ALLOC_N(VALUE, cData->height);
611
+ for (cT0 = 0; cT0 < cWidth; cT0++) {
612
+ for (cT1 = 0; cT1 < cData->height; cT1++) {
613
+ cColumn[cT1] = ruby___color_new_from_rgb_quad(&PixelAt(cT0, cT1));
614
+ rb_yield(rb_ary_new_from_values((LONG) cData->height, cColumn));
615
+ }
616
+ }
617
+ return self;
618
+ } else {
619
+ VALUE newEnumeratorArguments[2];
620
+ newEnumeratorArguments[0] = self;
621
+ newEnumeratorArguments[1] = NEW_SYMBOL("each_column");
622
+ return rb_class_new_instance(2, newEnumeratorArguments, rb_cEnumerator);
623
+ }
624
+ }
625
+
626
+ /*
627
+ * @overload each_in_row(y, &block)
628
+ * @param [Integer] y specific which row, can be negative
629
+ * @param [Proc] block the action
630
+ * @yield [pixel] the action
631
+ * @yieldparam [SpecialInputDevice::Color] pixel the pixel
632
+ * @yieldreturn [Object] value will be ignored
633
+ * @return [Enumerator, SpecialInputDevice::Image] a Enumerator if no block gavin, self otherwise.
634
+ */
635
+ static VALUE ruby_each_in_row(VARIABLE_ARGUMENTS_C) {
636
+ VALUE y;
637
+ VALUE block;
638
+ SID_IMAGE_DATA *cData = GET_SELF_DATA_OBJECT();
639
+ INT cY;
640
+ LONG cWidth = NUM2LONG(rb_iv_get(self, "@width"));
641
+ SCAN_ARGUMENTS("1&", &y, &block);
642
+ cY = NUM2INT(y);
643
+ if (cY < (INT) -cData->height || cY >= cData->height)
644
+ rb_raise(rb_eRangeError, "The image only has %d rows. y = %d is out of range.", cData->height, cY);
645
+ if (cY < 0)
646
+ cY += cWidth;
647
+ if (RTEST(block)) {
648
+ UINT cT;
649
+ VALUE cPixel;
650
+ for (cT = 0; cT < cWidth; cT++) {
651
+ cPixel = ruby___color_new_from_rgb_quad(&PixelAt(cT, cY));
652
+ rb_yield(cPixel);
653
+ }
654
+ return self;
655
+ } else {
656
+ VALUE newEnumeratorArguments[3];
657
+ newEnumeratorArguments[0] = self;
658
+ newEnumeratorArguments[1] = NEW_SYMBOL("each_in_row");
659
+ newEnumeratorArguments[2] = y;
660
+ return rb_class_new_instance(3, newEnumeratorArguments, rb_cEnumerator);
661
+ }
662
+ }
663
+
664
+ /*
665
+ * @overload each_in_column(x, &block)
666
+ * @param [Integer] x specific which column, can be negative
667
+ * @param [Proc] block the action
668
+ * @yield [pixel] the action
669
+ * @yieldparam [SpecialInputDevice::Color] pixel the pixel
670
+ * @yieldreturn [Object] value will be ignored
671
+ * @return [Enumerator, SpecialInputDevice::Image] a Enumerator if no block gavin, self otherwise.
672
+ */
673
+ static VALUE ruby_each_in_column(VARIABLE_ARGUMENTS_C) {
674
+ VALUE x;
675
+ VALUE block;
676
+ SID_IMAGE_DATA *cData = GET_SELF_DATA_OBJECT();
677
+ INT cX;
678
+ LONG cWidth = NUM2LONG(rb_iv_get(self, "@width"));
679
+ SCAN_ARGUMENTS("1&", &x, &block);
680
+ cX = NUM2INT(x);
681
+ if (cX < -cWidth || cX >= cWidth)
682
+ rb_raise(rb_eRangeError, "The image only has %d columns. x = %d is out of range.", (INT) cWidth, cX);
683
+ if (cX < 0)
684
+ cX += cWidth;
685
+ if (RTEST(block)) {
686
+ UINT cT;
687
+ VALUE cPixel;
688
+ for (cT = 0; cT < cData->height; cT++) {
689
+ cPixel = ruby___color_new_from_rgb_quad(&PixelAt(cX, cT));
690
+ rb_yield(cPixel);
691
+ }
692
+ return self;
693
+ } else {
694
+ VALUE newEnumeratorArguments[3];
695
+ newEnumeratorArguments[0] = self;
696
+ newEnumeratorArguments[1] = NEW_SYMBOL("each_column");
697
+ newEnumeratorArguments[2] = x;
698
+ return rb_class_new_instance(3, newEnumeratorArguments, rb_cEnumerator);
699
+ }
700
+ }
701
+
702
+ VOID Init_image() {
703
+ #ifdef YARD
704
+ specialInputDevice = rb_define_module("SpecialInputDevice");
705
+ specialInputDevice_image = rb_define_class_under(specialInputDevice, "Image", rb_cData);
706
+ #endif
707
+ rb_define_singleton_method(specialInputDevice_image, "load", ruby__load, 1);
708
+ rb_define_alloc_func(specialInputDevice_image, (rb_alloc_func_t) ruby__alloc);
709
+ /*
710
+ * @return [Integer] the width of the bitmap
711
+ */
712
+ rb_define_attr(specialInputDevice_image, "width", TRUE, FALSE);
713
+ /*
714
+ * @return [Integer] the height of the bitmap
715
+ */
716
+ rb_define_attr(specialInputDevice_image, "height", TRUE, FALSE);
717
+ rb_define_method(specialInputDevice_image, "initialize", ruby_initialize, 2);
718
+ rb_define_method(specialInputDevice_image, "save", ruby_save, 1);
719
+ rb_define_method(specialInputDevice_image, "[]", ruby_OS_CS, 2);
720
+ rb_define_method(specialInputDevice_image, "[]=", ruby_OS_CS_EQ, 3);
721
+ rb_define_method(specialInputDevice_image, "try_get", ruby_try_get, 2);
722
+ rb_define_method(specialInputDevice_image, "try_set", ruby_try_set, 3);
723
+ rb_define_method(specialInputDevice_image, "each", ruby_each, -1);
724
+ rb_define_method(specialInputDevice_image, "each_row", ruby_each_row, -1);
725
+ rb_define_method(specialInputDevice_image, "each_column", ruby_each_column, -1);
726
+ rb_define_method(specialInputDevice_image, "each_in_row", ruby_each_in_row, -1);
727
+ rb_define_method(specialInputDevice_image, "each_in_column", ruby_each_in_column, -1);
728
+ }