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