cgv
Loading...
Searching...
No Matches
gl_cursor.cxx
1#include "gl_cursor.h"
2#include <iostream>
3#include <cgv/media/riff.h>
4#include "gl.h"
5
6#ifdef WIN32
7#include <windows.h>
8
9PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp)
10{
11 BITMAP bmp;
12 PBITMAPINFO pbmi;
13 WORD cClrBits;
14
15 // Retrieve the bitmap color format, width, and height.
16 if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
17 return 0;
18
19 // Convert the color format to a count of bits.
20 cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
21 if (cClrBits == 1)
22 cClrBits = 1;
23 else if (cClrBits <= 4)
24 cClrBits = 4;
25 else if (cClrBits <= 8)
26 cClrBits = 8;
27 else if (cClrBits <= 16)
28 cClrBits = 16;
29 else if (cClrBits <= 24)
30 cClrBits = 24;
31 else cClrBits = 32;
32
33 // Allocate memory for the BITMAPINFO structure. (This structure
34 // contains a BITMAPINFOHEADER structure and an array of RGBQUAD
35 // data structures.)
36
37 if (cClrBits < 24)
38 pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
39 sizeof(BITMAPINFOHEADER) +
40 sizeof(RGBQUAD) * (size_t(1) << cClrBits));
41
42 // There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel
43
44 else
45 pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
46 sizeof(BITMAPINFOHEADER));
47
48 // Initialize the fields in the BITMAPINFO structure.
49
50 pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
51 pbmi->bmiHeader.biWidth = bmp.bmWidth;
52 pbmi->bmiHeader.biHeight = bmp.bmHeight;
53 pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
54 pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
55 if (cClrBits < 24)
56 pbmi->bmiHeader.biClrUsed = (1<<cClrBits);
57
58 // If the bitmap is not compressed, set the BI_RGB flag.
59 pbmi->bmiHeader.biCompression = BI_RGB;
60
61 // Compute the number of bytes in the array of color
62 // indices and store the result in biSizeImage.
63 // The width must be DWORD aligned unless the bitmap is RLE
64 // compressed.
65 pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
66 * pbmi->bmiHeader.biHeight;
67 // Set biClrImportant to 0, indicating that all of the
68 // device colors are important.
69 pbmi->bmiHeader.biClrImportant = 0;
70 return pbmi;
71}
72
73unsigned char* get_bitmap_data(HBITMAP hbmp, unsigned& tex_w, unsigned& tex_h)
74{
75 BITMAP bmp;
76 if (!GetObject(hbmp, sizeof(BITMAP), (LPSTR)&bmp))
77 return 0;
78 PBITMAPINFO pbi = CreateBitmapInfoStruct(hbmp);
79 PBITMAPINFOHEADER pbih = (PBITMAPINFOHEADER) pbi;
80 unsigned my_size = 4 * pbih->biWidth * pbih->biHeight;
81 if (pbih->biSizeImage >= my_size)
82 my_size = pbih->biSizeImage;
83 unsigned char* lpBits = new unsigned char[my_size];
84 if (!lpBits)
85 return 0;
86 if (!GetDIBits(GetDC(NULL), hbmp, 0, (WORD) pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS))
87 return 0;
88 tex_w = bmp.bmWidth;
89 tex_h = bmp.bmHeight;
90 return lpBits;
91}
92
93unsigned char* get_cursor_bitmap(HCURSOR hc, unsigned& w, unsigned& h, int anim_step = 0)
94{
95 ICONINFO ii;
96 GetIconInfo(hc, &ii);
97 BITMAP bmp;
98 if (!GetObject(ii.hbmColor ? ii.hbmColor : ii.hbmMask, sizeof(BITMAP), (LPSTR)&bmp))
99 return 0;
100 w = bmp.bmWidth;
101 h = bmp.bmHeight;
102 if (h == 2*w)
103 h = w;
104 HDC dc = GetDC(NULL);
105 HDC mem_dc = CreateCompatibleDC(dc);
106 HBITMAP hbm = CreateCompatibleBitmap (dc, w, h);
107 HBITMAP old_bm = (HBITMAP)SelectObject(mem_dc, hbm);
108 RECT r;
109 r.left = 0;
110 r.bottom = 0;
111 r.right = w;
112 r.top = h;
113 HBRUSH black = CreateSolidBrush(RGB(0,0,0));
114
115 FillRect(mem_dc,&r, black);
116 if (DrawIconEx(mem_dc,0,0,hc,w,h,anim_step,NULL,DI_NORMAL | DI_COMPAT) == FALSE) {
117 SelectObject(mem_dc, old_bm);
118 DeleteObject(black);
119 DeleteObject(hbm);
120 DeleteDC(mem_dc);
121 ReleaseDC(NULL,dc);
122 return 0;
123 }
124
125 SelectObject(mem_dc, old_bm);
126 unsigned char* data_b = get_bitmap_data(hbm,w,h);
127
128 old_bm = (HBITMAP)SelectObject(mem_dc, hbm);
129 HBRUSH white = CreateSolidBrush(RGB(255,255,255));
130 FillRect(mem_dc,&r, white);
131 DrawIconEx(mem_dc,0,0,hc,w,h,anim_step,NULL,DI_NORMAL | DI_COMPAT);
132 SelectObject(mem_dc, old_bm);
133 unsigned char* data_w = get_bitmap_data(hbm,w,h);
134
135 unsigned n=w*h;
136 for (unsigned i=0; i<n; ++i) {
137 int sum_alpha = 0;
138 for (unsigned j=0; j<3; ++j)
139 sum_alpha += 255+(int)data_b[4*i+j]-(int)data_w[4*i+j];
140 if (sum_alpha > 3*255)
141 sum_alpha = 3*255;
142 unsigned char alpha = (unsigned char)(sum_alpha/3);
143 data_b[4*i+3] = alpha;
144 if (alpha > 0) {
145 for (unsigned j=0; j<3; ++j) {
146 float v = data_b[4*i+j]*255.0f/alpha;
147 if (v > 255)
148 v = 255;
149 data_b[4*i+j] = (unsigned char) v;
150 }
151 }
152 }
153
154 delete [] data_w;
155
156 DeleteObject(white);
157 DeleteObject(black);
158 DeleteObject(hbm);
159 DeleteDC(mem_dc);
160 ReleaseDC(NULL,dc);
161 return data_b;
162}
163
164unsigned create_texture_from_cursor(HCURSOR hc, unsigned& tex_w, unsigned& tex_h, int& hot_x, int& hot_y, int anim_step = 0)
165{
166 unsigned tex_id;
167 ICONINFO ii;
168 GetIconInfo(hc, &ii);
169/* ICONINFOEX iie;
170 iie.cbSize = sizeof(ICONINFOEX);
171 GetIconInfoEx(hc, &iie);*/
172 unsigned char* bgra_data = get_cursor_bitmap(hc, tex_w, tex_h, anim_step);
173 if (!bgra_data)
174 return -1;
175 if (anim_step == 1) {
176 unsigned w,h;
177 unsigned char* first_bgra_data = get_cursor_bitmap(hc, w, h, 0);
178 int m=4*tex_w*tex_h;
179 bool found_diff = false;
180 for (int j=0; !found_diff && (j<m); ++j)
181 if (first_bgra_data[j] != bgra_data[j])
182 found_diff = true;
183 delete [] first_bgra_data;
184 if (!found_diff) {
185 delete [] bgra_data;
186 return -1;
187 }
188 }
189 hot_x = ii.xHotspot;
190 hot_y = ii.yHotspot;
191 glGenTextures(1, &tex_id);
192 glBindTexture(GL_TEXTURE_2D, tex_id);
193 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, bgra_data);
194 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
195 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST);
196 delete [] bgra_data;
197 return tex_id;
198}
199#endif
200
201namespace cgv {
202 namespace render {
203 namespace gl {
204
206{
207 w=h=hot_x=hot_y=0;
208 duration = 1;
209}
210
212{
213 return tex_ids.size() > 0;
214}
215
216void gl_cursor::clear()
217{
218 while (tex_ids.size() > 0) {
219 glDeleteTextures(1, &tex_ids.back());
220 tex_ids.pop_back();
221 }
222 periods.clear();
223 frames.clear();
224}
225
226void gl_cursor::add_frame(unsigned tex_id)
227{
228 append_step((unsigned)tex_ids.size(),3);
229 tex_ids.push_back(tex_id);
230}
231
232#ifdef WIN32
233
234#include <windows.h>
235#include <Winnls.h>
236
237std::wstring str2wstr(const std::string& s)
238{
239 std::wstring ws;
240 ws.resize(s.size());
241#ifdef WIN32
242 int n = MultiByteToWideChar(CP_ACP,0,s.c_str(),(int)s.size(),&ws[0],(int)ws.size());
243 ws.resize(n);
244#else
245 std::cerr << "str2wstr(const std::string& s) not implemented" << std::endl;
246#endif
247 return ws;
248}
249
250
251bool create_from_cursor(HCURSOR hc, gl_cursor* c)
252{
253 if (!hc)
254 return false;
255 c->clear();
256 unsigned tex_id, i = 0;
257 do {
258 tex_id = create_texture_from_cursor(hc, c->w, c->h, c->hot_x, c->hot_y, i);
259 if (tex_id != -1)
260 c->add_frame(tex_id);
261 ++i;
262 } while (tex_id != -1);
263 return c->is_created();
264}
265
266bool gl_cursor::create(const std::string& id)
267{
268 static struct { const char* id;
269#ifdef UNICODE
270 LPCWSTR cid;
271#else
272 LPCSTR cid;
273#endif
274 } lookup[] =
275 {
276 { "startup", IDC_APPSTARTING },
277 { "arrow", IDC_ARROW },
278 { "cross", IDC_CROSS },
279 { "wait", IDC_WAIT },
280 { "hand", IDC_HAND },
281 { "help", IDC_HELP },
282 { "no", IDC_NO },
283 { "ns", IDC_SIZENS },
284 { "we", IDC_SIZEWE },
285 { "move", IDC_SIZEALL },
286 { "nesw", IDC_SIZENESW },
287 { "nwse", IDC_SIZENWSE }
288 };
289#ifdef UNICODE
290 LPCWSTR cid = 0;
291#else
292 LPCSTR cid = 0;
293#endif
294 for (unsigned i=0; i<10; ++i)
295 if (id == lookup[i].id) {
296 cid = lookup[i].cid;
297 break;
298 }
299 if (!cid)
300 return false;
301 return create_from_cursor(LoadCursor(NULL, cid), this);
302}
303
304#include <cgv/type/standard_types.h>
305
306using namespace cgv::type;
307
308struct anih_structure
309{
310 uint32_type size;
311 uint32_type nr_frames;
312 uint32_type nr_steps;
313 uint32_type width;
314 uint32_type height;
315 uint32_type nr_bits_per_pixel;
316 uint32_type nr_planes;
317 uint32_type display_rate;
318 uint32_type flags;
319};
320
321struct ani_riff_handler : public media::riff_handler
322{
323 unsigned nr_frames;
324 unsigned nr_steps;
325 unsigned period;
326 gl_cursor* c;
327 ani_riff_handler(gl_cursor* _c) : c(_c)
328 {
329 nr_frames = 0;
330 nr_steps = 0;
331 period = 3;
332 }
333 void process_chunk_data(media::fourcc id, unsigned size, void* data_ptr)
334 {
335 if (id == "anih") {
336 anih_structure& as = *((anih_structure*)data_ptr);
337 nr_frames = as.nr_frames;
338 nr_steps = as.nr_steps;
339 period = as.display_rate == 0 ? 3 : as.display_rate;
340 c->tex_ids.resize(nr_frames);
341 c->frames.resize(nr_steps);
342 c->periods.resize(nr_steps);
343 unsigned i;
344 for (i=0; i<nr_steps; ++i) {
345 c->frames[i] = i % nr_frames;
346 c->periods[i] = period;
347 }
348 for (i=0; i<nr_frames; ++i)
349 c->tex_ids[i] = -1;
350 c->update_duration();
351 }
352 else if (id == "seq") {
353 unsigned count = size/4;
354 unsigned *values = (unsigned*)data_ptr;
355 c->frames.resize(count);
356 c->periods.resize(count);
357 if (count != c->periods.size())
358 std::cerr << "found wrong number of seq entries" << std::endl;
359 else
360 for (unsigned i=0; i<count; ++i)
361 c->frames[i] = values[i];
362 }
363 else if (id == "rate") {
364 unsigned count = size/4;
365 unsigned *values = (unsigned*)data_ptr;
366 if (count != c->periods.size())
367 std::cerr << "found wrong number of rate entries" << std::endl;
368 else
369 for (unsigned i=0; i<count; ++i)
370 c->periods[i] = values[i];
371 c->update_duration();
372 }
373 }
374};
375
376bool gl_cursor::create_from_file(const std::string& file_name)
377{
378 // extract upper case extension
379 unsigned dot = (unsigned)file_name.find_last_of('.');
380 if (dot == std::string::npos)
381 return false;
382 std::string ext = file_name.substr(dot+1);
383 for (unsigned i=0; i<ext.size(); ++i)
384 if (ext[i] >= 'a' && ext[i] <= 'z') {
385 ext[i] -= 'a';
386 ext[i] += 'A';
387 }
388
389 // load static cursors
390 if (ext == "CUR") {
391 return create_from_cursor(LoadCursorFromFile(
392#ifdef UNICODE
393 str2wstr(file_name).c_str()
394#else
395 file_name.c_str()
396#endif
397 ), this);
398
399 }
400 else if (ext == "ANI") {
401 // open file and get seq and rate chunks
402 clear();
403 ani_riff_handler arh(this);
404 media::riff_reader rr(&arh);
405 if (!rr.read(file_name))
406 return false;
407 //
408 HCURSOR hc = LoadCursorFromFile(
409#ifdef UNICODE
410 str2wstr(file_name).c_str()
411#else
412 file_name.c_str()
413#endif
414 );
415 if (!hc)
416 return false;
417 for (unsigned i = 0; i<get_nr_steps(); ++i) {
418 if (tex_ids[frames[i]] == -1) {
419 tex_ids[frames[i]] = create_texture_from_cursor(hc, w, h, hot_x, hot_y, i);
420 if (tex_ids[frames[i]] == -1) {
421 clear();
422 return false;
423 }
424 }
425 }
426 return true;
427 }
428 return false;
429}
430
431#else
432bool gl_cursor::create(const std::string& id)
433{
434 std::cerr << "gl_cursor::create only implemented under WIN32" << std::endl;
435 return false;
436}
437bool gl_cursor::create_from_file(const std::string& file_name)
438{
439 std::cerr << "gl_cursor::create_from_file only implemented under WIN32" << std::endl;
440 return false;
441}
442#endif
443
444
445void gl_cursor::update_duration()
446{
447 duration = 0;
448 for (unsigned i=0; i<periods.size(); ++i)
449 duration += periods[i];
450}
451
454{
455 return (unsigned)frames.size();
456}
457
460{
461 return (unsigned)tex_ids.size();
462}
463
465unsigned gl_cursor::get_texture_id(unsigned frame_idx) const
466{
467 return tex_ids[frame_idx];
468}
469
471unsigned gl_cursor::get_step_frame(unsigned step_idx) const
472{
473 return frames[step_idx];
474}
475
477void gl_cursor::set_step_frame(unsigned step_idx, unsigned frame_idx)
478{
479 frames[step_idx] = frame_idx;
480}
481
483unsigned gl_cursor::get_step_period(unsigned step_idx) const
484{
485 return periods[step_idx];
486}
487
489void gl_cursor::set_step_period(unsigned step_idx, unsigned period)
490{
491 periods[step_idx] = period;
492 update_duration();
493}
494
496void gl_cursor::append_step(unsigned frame_idx, unsigned period)
497{
498 frames.push_back(frame_idx);
499 periods.push_back(period);
500 update_duration();
501}
502
504unsigned gl_cursor::find_step_index(double elapsed_seconds) const
505{
506 unsigned i, period = ((int)(elapsed_seconds * 60))%duration;
507 for (i=0; i<periods.size(); ++i) {
508 if (periods[i] > period)
509 return i;
510 period -= periods[i];
511 }
512 return i % periods.size();
513}
514
515void gl_cursor::draw(int x, int y, bool use_color, unsigned frame_idx)
516{
517 if (frame_idx >= tex_ids.size())
518 return;
519 if (tex_ids[frame_idx] == -1)
520 return;
521
522 glPushAttrib(GL_COLOR_BUFFER_BIT|GL_TEXTURE_BIT);
523 glEnable(GL_ALPHA_TEST);
524 glEnable(GL_BLEND);
525 glAlphaFunc(GL_GREATER, 0.0f);
526 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
527 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, use_color ? GL_MODULATE : GL_REPLACE);
528 glBindTexture(GL_TEXTURE_2D, tex_ids[frame_idx]);
529 x -= hot_x;
530 y -= hot_y;
531 glEnable(GL_TEXTURE_2D);
532 glBegin(GL_QUADS);
533 glTexCoord2f(1,1);
534 glVertex2i(x+w,y);
535 glTexCoord2f(0,1);
536 glVertex2i(x,y);
537 glTexCoord2f(0,0);
538 glVertex2i(x,y+h);
539 glTexCoord2f(1,0);
540 glVertex2i(x+w,y+h);
541 glEnd();
542 glPopAttrib();
543}
544
545 }
546 }
547}
complete implementation of method actions that only call one method when entering a node
Definition action.h:113
reader class for riff files such as .ani or .avi
Definition riff.h:51
unsigned get_nr_steps() const
return the number of animation steps
gl_cursor()
construct empty gl_cursor instance
void append_step(unsigned frame_idx, unsigned period)
append an animation step given by frame index and step period
bool is_created() const
check whether cursor has been created
void set_step_period(unsigned step_idx, unsigned period)
return the period of given step in 1/60th of a second
unsigned get_texture_id(unsigned frame_idx=0) const
return the gl texture id of the given frame
unsigned get_step_period(unsigned step_idx) const
return the period of given step in 1/60th of a second
bool create(const std::string &id="arrow")
Create cursor from textual name of default cursor.
unsigned get_step_frame(unsigned step_idx) const
return the frame index of given step
void set_step_frame(unsigned step_idx, unsigned frame_idx)
set the frame index of given step
unsigned get_nr_frames() const
return the number of animation frames
void draw(int x, int y, bool use_color=false, unsigned frame_idx=0)
draw the cursor at the given location
bool create_from_file(const std::string &file_name)
Create cursor from a file of type .cur or .ani.
unsigned find_step_index(double elapsed_seconds) const
find the step index of a given elapsed time in seconds
namespace for compile time type information
the cgv namespace
Definition print.h:11
represents fourcc ids as used in the riff format to identify chunks
Definition riff.h:14
callback handler passed to riff reader
Definition riff.h:35