fixed assert failure in wxStaticCast()
[wxWidgets.git] / src / html / m_image.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: m_image.cpp
3 // Purpose: wxHtml module for displaying images
4 // Author: Vaclav Slavik
5 // RCS-ID: $Id$
6 // Copyright: (c) 1999 Vaclav Slavik, Joel Lucsy
7 // Licence: wxWindows Licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation
12 #endif
13
14 #include "wx/wxprec.h"
15
16 #include "wx/defs.h"
17 #if wxUSE_HTML && wxUSE_STREAMS
18
19 #ifdef __BORDLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WXPRECOMP
24 #include "wx/dc.h"
25 #include "wx/scrolwin.h"
26 #include "wx/timer.h"
27 #include "wx/dcmemory.h"
28 #endif
29
30 #include "wx/html/forcelnk.h"
31 #include "wx/html/m_templ.h"
32
33 #include "wx/image.h"
34 #include "wx/gifdecod.h"
35 #include "wx/dynarray.h"
36
37 #include <math.h>
38 #include <float.h>
39
40 FORCE_LINK_ME(m_image)
41
42
43
44
45 WX_DECLARE_OBJARRAY(int, CoordArray);
46 #include "wx/arrimpl.cpp" // this is a magic incantation which must be done!
47 WX_DEFINE_OBJARRAY(CoordArray);
48
49
50 //--------------------------------------------------------------------------------
51 // wxHtmlImageMapAreaCell
52 // 0-width, 0-height cell that represents single area in imagemap
53 // (it's GetLink is called from wxHtmlImageCell's)
54 //--------------------------------------------------------------------------------
55
56 class wxHtmlImageMapAreaCell : public wxHtmlCell
57 {
58 public:
59 enum celltype { CIRCLE, RECT, POLY };
60 protected:
61 CoordArray coords;
62 celltype type;
63 int radius;
64 public:
65 wxHtmlImageMapAreaCell( celltype t, wxString &coords, double pixel_scale = 1.0);
66 virtual wxHtmlLinkInfo *GetLink( int x = 0, int y = 0 ) const;
67 };
68
69
70
71
72
73 wxHtmlImageMapAreaCell::wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::celltype t, wxString &incoords, double pixel_scale )
74 {
75 int i;
76 wxString x = incoords, y;
77
78 type = t;
79 while ((i = x.Find( ',' )) != -1)
80 {
81 coords.Add( (int)(pixel_scale * (double)wxAtoi( x.Left( i ).c_str())) );
82 x = x.Mid( i + 1 );
83 }
84 coords.Add( (int)(pixel_scale * (double)wxAtoi( x.c_str())) );
85 }
86
87 wxHtmlLinkInfo *wxHtmlImageMapAreaCell::GetLink( int x, int y ) const
88 {
89 switch (type)
90 {
91 case RECT:
92 {
93 int l, t, r, b;
94
95 l = coords[ 0 ];
96 t = coords[ 1 ];
97 r = coords[ 2 ];
98 b = coords[ 3 ];
99 if (x >= l && x <= r && y >= t && y <= b)
100 {
101 return m_Link;
102 }
103 break;
104 }
105 case CIRCLE:
106 {
107 int l, t, r;
108 double d;
109
110 l = coords[ 0 ];
111 t = coords[ 1 ];
112 r = coords[ 2 ];
113 d = sqrt( (double) (((x - l) * (x - l)) + ((y - t) * (y - t))) );
114 if (d < (double)r)
115 {
116 return m_Link;
117 }
118 }
119 break;
120 case POLY:
121 {
122 if (coords.GetCount() >= 6)
123 {
124 int intersects = 0;
125 int wherex = x;
126 int wherey = y;
127 int totalv = coords.GetCount() / 2;
128 int totalc = totalv * 2;
129 int xval = coords[totalc - 2];
130 int yval = coords[totalc - 1];
131 int end = totalc;
132 int pointer = 1;
133
134 if ((yval >= wherey) != (coords[pointer] >= wherey))
135 {
136 if ((xval >= wherex) == (coords[0] >= wherex))
137 {
138 intersects += (xval >= wherex) ? 1 : 0;
139 }
140 else
141 {
142 intersects += ((xval - (yval - wherey) *
143 (coords[0] - xval) /
144 (coords[pointer] - yval)) >= wherex) ? 1 : 0;
145 }
146 }
147
148 while (pointer < end)
149 {
150 yval = coords[pointer];
151 pointer += 2;
152 if (yval >= wherey)
153 {
154 while ((pointer < end) && (coords[pointer] >= wherey))
155 {
156 pointer += 2;
157 }
158 if (pointer >= end)
159 {
160 break;
161 }
162 if ((coords[pointer - 3] >= wherex) ==
163 (coords[pointer - 1] >= wherex)) {
164 intersects += (coords[pointer - 3] >= wherex) ? 1 : 0;
165 }
166 else
167 {
168 intersects +=
169 ((coords[pointer - 3] - (coords[pointer - 2] - wherey) *
170 (coords[pointer - 1] - coords[pointer - 3]) /
171 (coords[pointer] - coords[pointer - 2])) >= wherex) ? 1 : 0;
172 }
173 }
174 else
175 {
176 while ((pointer < end) && (coords[pointer] < wherey))
177 {
178 pointer += 2;
179 }
180 if (pointer >= end)
181 {
182 break;
183 }
184 if ((coords[pointer - 3] >= wherex) ==
185 (coords[pointer - 1] >= wherex))
186 {
187 intersects += (coords[pointer - 3] >= wherex) ? 1 : 0;
188 }
189 else
190 {
191 intersects +=
192 ((coords[pointer - 3] - (coords[pointer - 2] - wherey) *
193 (coords[pointer - 1] - coords[pointer - 3]) /
194 (coords[pointer] - coords[pointer - 2])) >= wherex) ? 1 : 0;
195 }
196 }
197 }
198 if ((intersects & 1) != 0)
199 {
200 return m_Link;
201 }
202 }
203 }
204 break;
205 }
206
207 if (m_Next)
208 {
209 wxHtmlImageMapAreaCell *a = (wxHtmlImageMapAreaCell*)m_Next;
210 return a->GetLink( x, y );
211 }
212 return NULL;
213 }
214
215
216
217
218
219
220
221
222 //--------------------------------------------------------------------------------
223 // wxHtmlImageMapCell
224 // 0-width, 0-height cell that represents map from imagemaps
225 // it is always placed before wxHtmlImageMapAreaCells
226 // It responds to Find(wxHTML_COND_ISIMAGEMAP)
227 //--------------------------------------------------------------------------------
228
229
230 class wxHtmlImageMapCell : public wxHtmlCell
231 {
232 public:
233 wxHtmlImageMapCell( wxString &name );
234 protected:
235 wxString m_Name;
236 public:
237 virtual wxHtmlLinkInfo *GetLink( int x = 0, int y = 0 ) const;
238 virtual const wxHtmlCell *Find( int cond, const void *param ) const;
239 };
240
241
242 wxHtmlImageMapCell::wxHtmlImageMapCell( wxString &name )
243 {
244 m_Name = name ;
245 }
246
247 wxHtmlLinkInfo *wxHtmlImageMapCell::GetLink( int x, int y ) const
248 {
249 wxHtmlImageMapAreaCell *a = (wxHtmlImageMapAreaCell*)m_Next;
250 if (a)
251 return a->GetLink( x, y );
252 return wxHtmlCell::GetLink( x, y );
253 }
254
255 const wxHtmlCell *wxHtmlImageMapCell::Find( int cond, const void *param ) const
256 {
257 if (cond == wxHTML_COND_ISIMAGEMAP)
258 {
259 if (m_Name == *((wxString*)(param)))
260 return this;
261 }
262 return wxHtmlCell::Find(cond, param);
263 }
264
265
266
267
268
269 //--------------------------------------------------------------------------------
270 // wxHtmlImageCell
271 // Image/bitmap
272 //--------------------------------------------------------------------------------
273
274 class wxHtmlImageCell : public wxHtmlCell
275 {
276 public:
277 wxHtmlImageCell(wxWindow *window,
278 wxFSFile *input, int w = -1, int h = -1,
279 double scale = 1.0, int align = wxHTML_ALIGN_BOTTOM,
280 const wxString& mapname = wxEmptyString);
281 ~wxHtmlImageCell();
282 void Draw(wxDC& dc, int x, int y, int view_y1, int view_y2);
283 virtual wxHtmlLinkInfo *GetLink(int x = 0, int y = 0) const;
284
285 void SetImage(const wxImage& img);
286 #if wxUSE_GIF && wxUSE_TIMER
287 void AdvanceAnimation(wxTimer *timer);
288 virtual void Layout(int w);
289 #endif
290
291 private:
292 wxBitmap *m_bitmap;
293 int m_bmpW, m_bmpH;
294 wxScrolledWindow *m_window;
295 #if wxUSE_GIF && wxUSE_TIMER
296 wxGIFDecoder *m_gifDecoder;
297 wxTimer *m_gifTimer;
298 int m_physX, m_physY;
299 #endif
300 double m_scale;
301 wxHtmlImageMapCell *m_imageMap;
302 wxString m_mapName;
303 };
304
305 #if wxUSE_GIF && wxUSE_TIMER
306 class wxGIFTimer : public wxTimer
307 {
308 public:
309 wxGIFTimer(wxHtmlImageCell *cell) : m_cell(cell) {}
310 virtual void Notify()
311 {
312 m_cell->AdvanceAnimation(this);
313 }
314
315 private:
316 wxHtmlImageCell *m_cell;
317 };
318 #endif
319
320
321 //--------------------------------------------------------------------------------
322 // wxHtmlImageCell
323 //--------------------------------------------------------------------------------
324
325 wxHtmlImageCell::wxHtmlImageCell(wxWindow *window, wxFSFile *input,
326 int w, int h, double scale, int align,
327 const wxString& mapname) : wxHtmlCell()
328 {
329 m_window = window ? wxStaticCast(window, wxScrolledWindow) : NULL;
330 m_scale = scale;
331 m_bitmap = NULL;
332 m_bmpW = w;
333 m_bmpH = h;
334 m_imageMap = NULL;
335 m_mapName = mapname;
336 SetCanLiveOnPagebreak(FALSE);
337 #if wxUSE_GIF && wxUSE_TIMER
338 m_gifDecoder = NULL;
339 m_gifTimer = NULL;
340 m_physX = m_physY = -1;
341 #endif
342
343 wxInputStream *s = input->GetStream();
344
345 if ( s )
346 {
347 bool readImg = TRUE;
348
349 #if wxUSE_GIF && wxUSE_TIMER
350 if ( (input->GetLocation().Matches(wxT("*.gif")) ||
351 input->GetLocation().Matches(wxT("*.GIF"))) && m_window )
352 {
353 m_gifDecoder = new wxGIFDecoder(s, TRUE);
354 if ( m_gifDecoder->ReadGIF() == wxGIF_OK )
355 {
356 wxImage img;
357 if ( m_gifDecoder->ConvertToImage(&img) )
358 SetImage(img);
359
360 readImg = FALSE;
361
362 if ( m_gifDecoder->IsAnimation() )
363 {
364 m_gifTimer = new wxGIFTimer(this);
365 m_gifTimer->Start(m_gifDecoder->GetDelay(), TRUE);
366 }
367 else
368 {
369 wxDELETE(m_gifDecoder);
370 }
371 }
372 else
373 {
374 wxDELETE(m_gifDecoder);
375 }
376 }
377
378 if ( readImg )
379 #endif
380 {
381 SetImage(wxImage(*s, wxBITMAP_TYPE_ANY));
382 }
383 }
384
385 m_Width = (int)(scale * (double)m_bmpW);
386 m_Height = (int)(scale * (double)m_bmpH);
387
388 switch (align)
389 {
390 case wxHTML_ALIGN_TOP :
391 m_Descent = m_Height;
392 break;
393 case wxHTML_ALIGN_CENTER :
394 m_Descent = m_Height / 2;
395 break;
396 case wxHTML_ALIGN_BOTTOM :
397 default :
398 m_Descent = 0;
399 break;
400 }
401 }
402
403 void wxHtmlImageCell::SetImage(const wxImage& img)
404 {
405 if ( img.Ok() )
406 {
407 delete m_bitmap;
408
409 int ww, hh;
410 ww = img.GetWidth();
411 hh = img.GetHeight();
412
413 if ( m_bmpW == -1 )
414 m_bmpW = ww;
415 if ( m_bmpH == -1 )
416 m_bmpH = hh;
417
418 if ((m_bmpW != ww) || (m_bmpH != hh))
419 {
420 wxImage img2 = img.Scale(m_bmpW, m_bmpH);
421 m_bitmap = new wxBitmap(img2);
422 }
423 else
424 m_bitmap = new wxBitmap(img);
425 }
426 }
427
428 #if wxUSE_GIF && wxUSE_TIMER
429 void wxHtmlImageCell::AdvanceAnimation(wxTimer *timer)
430 {
431 wxImage img;
432
433 m_gifDecoder->GoNextFrame(TRUE);
434
435 if ( m_physX == -1 )
436 {
437 m_physX = m_physY = 0;
438 for (wxHtmlCell *cell = this; cell; cell = cell->GetParent())
439 {
440 m_physX += cell->GetPosX();
441 m_physY += cell->GetPosY();
442 }
443 }
444
445 int x, y;
446 m_window->CalcScrolledPosition(m_physX, m_physY, &x, &y);
447 wxRect rect(x, y, m_Width, m_Height);
448
449 if ( m_window->GetClientRect().Intersects(rect) &&
450 m_gifDecoder->ConvertToImage(&img) )
451 {
452 if ( (int)m_gifDecoder->GetWidth() != m_Width ||
453 (int)m_gifDecoder->GetHeight() != m_Height ||
454 m_gifDecoder->GetLeft() != 0 || m_gifDecoder->GetTop() != 0 )
455 {
456 wxBitmap bmp(img);
457 wxMemoryDC dc;
458 dc.SelectObject(*m_bitmap);
459 dc.DrawBitmap(bmp, m_gifDecoder->GetLeft(), m_gifDecoder->GetTop());
460 }
461 else
462 SetImage(img);
463 m_window->Refresh(img.HasMask(), &rect);
464 }
465
466 timer->Start(m_gifDecoder->GetDelay(), TRUE);
467 }
468
469 void wxHtmlImageCell::Layout(int w)
470 {
471 wxHtmlCell::Layout(w);
472 m_physX = m_physY = -1;
473 }
474
475 #endif
476
477 wxHtmlImageCell::~wxHtmlImageCell()
478 {
479 delete m_bitmap;
480 #if wxUSE_GIF && wxUSE_TIMER
481 delete m_gifTimer;
482 delete m_gifDecoder;
483 #endif
484 }
485
486
487 void wxHtmlImageCell::Draw(wxDC& dc, int x, int y, int WXUNUSED(view_y1), int WXUNUSED(view_y2))
488 {
489 if (m_bitmap)
490 {
491 double us_x, us_y;
492 dc.GetUserScale(&us_x, &us_y);
493 dc.SetUserScale(us_x * m_scale, us_y * m_scale);
494
495 dc.DrawBitmap(*m_bitmap, (int) ((x + m_PosX) / m_scale),
496 (int) ((y + m_PosY) / m_scale), TRUE);
497 dc.SetUserScale(us_x, us_y);
498 }
499 }
500
501 wxHtmlLinkInfo *wxHtmlImageCell::GetLink( int x, int y ) const
502 {
503 if (m_mapName.IsEmpty())
504 return wxHtmlCell::GetLink( x, y );
505 if (!m_imageMap)
506 {
507 wxHtmlContainerCell *p, *op;
508 op = p = GetParent();
509 while (p)
510 {
511 op = p;
512 p = p->GetParent();
513 }
514 p = op;
515 wxHtmlCell *cell = (wxHtmlCell*)p->Find(wxHTML_COND_ISIMAGEMAP,
516 (const void*)(&m_mapName));
517 if (!cell)
518 {
519 ((wxString&)m_mapName).Clear();
520 return wxHtmlCell::GetLink( x, y );
521 }
522 { // dirty hack, ask Joel why he fills m_ImageMap in this place
523 // THE problem is that we're in const method and we can't modify m_ImageMap
524 wxHtmlImageMapCell **cx = (wxHtmlImageMapCell**)(&m_imageMap);
525 *cx = (wxHtmlImageMapCell*)cell;
526 }
527 }
528 return m_imageMap->GetLink(x, y);
529 }
530
531
532
533 //--------------------------------------------------------------------------------
534 // tag handler
535 //--------------------------------------------------------------------------------
536
537 TAG_HANDLER_BEGIN(IMG, "IMG,MAP,AREA")
538
539 TAG_HANDLER_PROC(tag)
540 {
541 if (tag.GetName() == wxT("IMG"))
542 {
543 if (tag.HasParam(wxT("SRC")))
544 {
545 int w = -1, h = -1;
546 int al;
547 wxFSFile *str;
548 wxString tmp = tag.GetParam(wxT("SRC"));
549 wxString mn = wxEmptyString;
550
551 str = m_WParser->GetFS()->OpenFile(tmp);
552 if (tag.HasParam(wxT("WIDTH")))
553 tag.GetParamAsInt(wxT("WIDTH"), &w);
554 if (tag.HasParam(wxT("HEIGHT")))
555 tag.GetParamAsInt(wxT("HEIGHT"), &h);
556 al = wxHTML_ALIGN_BOTTOM;
557 if (tag.HasParam(wxT("ALIGN")))
558 {
559 wxString alstr = tag.GetParam(wxT("ALIGN"));
560 alstr.MakeUpper(); // for the case alignment was in ".."
561 if (alstr == wxT("TEXTTOP"))
562 al = wxHTML_ALIGN_TOP;
563 else if ((alstr == wxT("CENTER")) || (alstr == wxT("ABSCENTER")))
564 al = wxHTML_ALIGN_CENTER;
565 }
566 if (tag.HasParam(wxT("USEMAP")))
567 {
568 mn = tag.GetParam( wxT("USEMAP") );
569 if (mn.GetChar(0) == wxT('#'))
570 {
571 mn = mn.Mid( 1 );
572 }
573 }
574 wxHtmlImageCell *cel = NULL;
575 if (str)
576 {
577 cel = new wxHtmlImageCell(m_WParser->GetWindow(),
578 str, w, h,
579 m_WParser->GetPixelScale(),
580 al, mn);
581 cel->SetLink(m_WParser->GetLink());
582 cel->SetId(tag.GetParam(wxT("id"))); // may be empty
583 m_WParser->GetContainer()->InsertCell(cel);
584 delete str;
585 }
586 }
587 }
588 if (tag.GetName() == wxT("MAP"))
589 {
590 m_WParser->CloseContainer();
591 m_WParser->OpenContainer();
592 if (tag.HasParam(wxT("NAME")))
593 {
594 wxString tmp = tag.GetParam(wxT("NAME"));
595 wxHtmlImageMapCell *cel = new wxHtmlImageMapCell( tmp );
596 m_WParser->GetContainer()->InsertCell( cel );
597 }
598 ParseInner( tag );
599 m_WParser->CloseContainer();
600 m_WParser->OpenContainer();
601 }
602 if (tag.GetName() == wxT("AREA"))
603 {
604 if (tag.HasParam(wxT("SHAPE")))
605 {
606 wxString tmp = tag.GetParam(wxT("SHAPE"));
607 wxString coords = wxEmptyString;
608 tmp.MakeUpper();
609 wxHtmlImageMapAreaCell *cel = NULL;
610 if (tag.HasParam(wxT("COORDS")))
611 {
612 coords = tag.GetParam(wxT("COORDS"));
613 }
614 if (tmp == wxT("POLY"))
615 {
616 cel = new wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::POLY, coords, m_WParser->GetPixelScale() );
617 }
618 else if (tmp == wxT("CIRCLE"))
619 {
620 cel = new wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::CIRCLE, coords, m_WParser->GetPixelScale() );
621 }
622 else if (tmp == wxT("RECT"))
623 {
624 cel = new wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::RECT, coords, m_WParser->GetPixelScale() );
625 }
626 if (cel != NULL && tag.HasParam(wxT("HREF")))
627 {
628 wxString tmp = tag.GetParam(wxT("HREF"));
629 wxString target = wxEmptyString;
630 if (tag.HasParam(wxT("TARGET"))) target = tag.GetParam(wxT("TARGET"));
631 cel->SetLink( wxHtmlLinkInfo(tmp, target));
632 }
633 if (cel != NULL) m_WParser->GetContainer()->InsertCell( cel );
634 }
635 }
636
637 return FALSE;
638 }
639
640 TAG_HANDLER_END(IMG)
641
642
643
644 TAGS_MODULE_BEGIN(Image)
645
646 TAGS_MODULE_ADD(IMG)
647
648 TAGS_MODULE_END(Image)
649
650
651 #endif