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