Compile with wxUSE_DYNLIB_CLASS and wxUSE_WXDIB set to 0
[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 #include "wx/wxprec.h"
11
12 #include "wx/defs.h"
13 #if wxUSE_HTML && wxUSE_STREAMS
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #ifndef WXPRECOMP
20 #include "wx/dc.h"
21 #include "wx/scrolwin.h"
22 #include "wx/timer.h"
23 #include "wx/dcmemory.h"
24 #endif
25
26 #include "wx/html/forcelnk.h"
27 #include "wx/html/m_templ.h"
28 #include "wx/html/htmlwin.h"
29
30 #include "wx/image.h"
31 #include "wx/gifdecod.h"
32 #include "wx/dynarray.h"
33 #include "wx/log.h"
34 #include "wx/artprov.h"
35 #include "wx/math.h"
36
37 #include <float.h>
38
39 FORCE_LINK_ME(m_image)
40
41
42
43
44 WX_DECLARE_OBJARRAY(int, CoordArray);
45 #include "wx/arrimpl.cpp" // this is a magic incantation which must be done!
46 WX_DEFINE_OBJARRAY(CoordArray)
47
48
49 // ---------------------------------------------------------------------------
50 // wxHtmlImageMapAreaCell
51 // 0-width, 0-height cell that represents single area in
52 // imagemap (it's GetLink is called from wxHtmlImageCell's)
53 // ---------------------------------------------------------------------------
54
55 class wxHtmlImageMapAreaCell : public wxHtmlCell
56 {
57 public:
58 enum celltype { CIRCLE, RECT, POLY };
59 protected:
60 CoordArray coords;
61 celltype type;
62 int radius;
63 public:
64 wxHtmlImageMapAreaCell( celltype t, wxString &coords, double pixel_scale = 1.0);
65 virtual wxHtmlLinkInfo *GetLink( int x = 0, int y = 0 ) const;
66 void Draw(wxDC& WXUNUSED(dc),
67 int WXUNUSED(x), int WXUNUSED(y),
68 int WXUNUSED(view_y1), int WXUNUSED(view_y2),
69 wxHtmlRenderingInfo& WXUNUSED(info)) {}
70
71
72 DECLARE_NO_COPY_CLASS(wxHtmlImageMapAreaCell)
73 };
74
75
76
77
78
79 wxHtmlImageMapAreaCell::wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::celltype t, wxString &incoords, double pixel_scale )
80 {
81 int i;
82 wxString x = incoords, y;
83
84 type = t;
85 while ((i = x.Find( ',' )) != wxNOT_FOUND)
86 {
87 coords.Add( (int)(pixel_scale * (double)wxAtoi( x.Left( i ).c_str())) );
88 x = x.Mid( i + 1 );
89 }
90 coords.Add( (int)(pixel_scale * (double)wxAtoi( x.c_str())) );
91 }
92
93 wxHtmlLinkInfo *wxHtmlImageMapAreaCell::GetLink( int x, int y ) const
94 {
95 switch (type)
96 {
97 case RECT:
98 {
99 int l, t, r, b;
100
101 l = coords[ 0 ];
102 t = coords[ 1 ];
103 r = coords[ 2 ];
104 b = coords[ 3 ];
105 if (x >= l && x <= r && y >= t && y <= b)
106 {
107 return m_Link;
108 }
109 break;
110 }
111 case CIRCLE:
112 {
113 int l, t, r;
114 double d;
115
116 l = coords[ 0 ];
117 t = coords[ 1 ];
118 r = coords[ 2 ];
119 d = sqrt( (double) (((x - l) * (x - l)) + ((y - t) * (y - t))) );
120 if (d < (double)r)
121 {
122 return m_Link;
123 }
124 }
125 break;
126 case POLY:
127 {
128 if (coords.GetCount() >= 6)
129 {
130 int intersects = 0;
131 int wherex = x;
132 int wherey = y;
133 int totalv = coords.GetCount() / 2;
134 int totalc = totalv * 2;
135 int xval = coords[totalc - 2];
136 int yval = coords[totalc - 1];
137 int end = totalc;
138 int pointer = 1;
139
140 if ((yval >= wherey) != (coords[pointer] >= wherey))
141 {
142 if ((xval >= wherex) == (coords[0] >= wherex))
143 {
144 intersects += (xval >= wherex) ? 1 : 0;
145 }
146 else
147 {
148 intersects += ((xval - (yval - wherey) *
149 (coords[0] - xval) /
150 (coords[pointer] - yval)) >= wherex) ? 1 : 0;
151 }
152 }
153
154 while (pointer < end)
155 {
156 yval = coords[pointer];
157 pointer += 2;
158 if (yval >= wherey)
159 {
160 while ((pointer < end) && (coords[pointer] >= wherey))
161 {
162 pointer += 2;
163 }
164 if (pointer >= end)
165 {
166 break;
167 }
168 if ((coords[pointer - 3] >= wherex) ==
169 (coords[pointer - 1] >= wherex)) {
170 intersects += (coords[pointer - 3] >= wherex) ? 1 : 0;
171 }
172 else
173 {
174 intersects +=
175 ((coords[pointer - 3] - (coords[pointer - 2] - wherey) *
176 (coords[pointer - 1] - coords[pointer - 3]) /
177 (coords[pointer] - coords[pointer - 2])) >= wherex) ? 1 : 0;
178 }
179 }
180 else
181 {
182 while ((pointer < end) && (coords[pointer] < wherey))
183 {
184 pointer += 2;
185 }
186 if (pointer >= end)
187 {
188 break;
189 }
190 if ((coords[pointer - 3] >= wherex) ==
191 (coords[pointer - 1] >= wherex))
192 {
193 intersects += (coords[pointer - 3] >= wherex) ? 1 : 0;
194 }
195 else
196 {
197 intersects +=
198 ((coords[pointer - 3] - (coords[pointer - 2] - wherey) *
199 (coords[pointer - 1] - coords[pointer - 3]) /
200 (coords[pointer] - coords[pointer - 2])) >= wherex) ? 1 : 0;
201 }
202 }
203 }
204 if ((intersects & 1) != 0)
205 {
206 return m_Link;
207 }
208 }
209 }
210 break;
211 }
212
213 if (m_Next)
214 {
215 wxHtmlImageMapAreaCell *a = (wxHtmlImageMapAreaCell*)m_Next;
216 return a->GetLink( x, y );
217 }
218 return NULL;
219 }
220
221
222
223
224
225
226
227
228 //--------------------------------------------------------------------------------
229 // wxHtmlImageMapCell
230 // 0-width, 0-height cell that represents map from imagemaps
231 // it is always placed before wxHtmlImageMapAreaCells
232 // It responds to Find(wxHTML_COND_ISIMAGEMAP)
233 //--------------------------------------------------------------------------------
234
235
236 class wxHtmlImageMapCell : public wxHtmlCell
237 {
238 public:
239 wxHtmlImageMapCell( wxString &name );
240 protected:
241 wxString m_Name;
242 public:
243 virtual wxHtmlLinkInfo *GetLink( int x = 0, int y = 0 ) const;
244 virtual const wxHtmlCell *Find( int cond, const void *param ) const;
245 void Draw(wxDC& WXUNUSED(dc),
246 int WXUNUSED(x), int WXUNUSED(y),
247 int WXUNUSED(view_y1), int WXUNUSED(view_y2),
248 wxHtmlRenderingInfo& WXUNUSED(info)) {}
249
250 DECLARE_NO_COPY_CLASS(wxHtmlImageMapCell)
251 };
252
253
254 wxHtmlImageMapCell::wxHtmlImageMapCell( wxString &name )
255 {
256 m_Name = name ;
257 }
258
259 wxHtmlLinkInfo *wxHtmlImageMapCell::GetLink( int x, int y ) const
260 {
261 wxHtmlImageMapAreaCell *a = (wxHtmlImageMapAreaCell*)m_Next;
262 if (a)
263 return a->GetLink( x, y );
264 return wxHtmlCell::GetLink( x, y );
265 }
266
267 const wxHtmlCell *wxHtmlImageMapCell::Find( int cond, const void *param ) const
268 {
269 if (cond == wxHTML_COND_ISIMAGEMAP)
270 {
271 if (m_Name == *((wxString*)(param)))
272 return this;
273 }
274 return wxHtmlCell::Find(cond, param);
275 }
276
277
278
279
280
281 //--------------------------------------------------------------------------------
282 // wxHtmlImageCell
283 // Image/bitmap
284 //--------------------------------------------------------------------------------
285
286 class wxHtmlImageCell : public wxHtmlCell
287 {
288 public:
289 wxHtmlImageCell(wxWindow *window,
290 wxFSFile *input, int w = wxDefaultCoord, int h = wxDefaultCoord,
291 double scale = 1.0, int align = wxHTML_ALIGN_BOTTOM,
292 const wxString& mapname = wxEmptyString);
293 ~wxHtmlImageCell();
294 void Draw(wxDC& dc, int x, int y, int view_y1, int view_y2,
295 wxHtmlRenderingInfo& info);
296 virtual wxHtmlLinkInfo *GetLink(int x = 0, int y = 0) const;
297
298 void SetImage(const wxImage& img);
299 #if wxUSE_GIF && wxUSE_TIMER
300 void AdvanceAnimation(wxTimer *timer);
301 virtual void Layout(int w);
302 #endif
303
304 private:
305 wxBitmap *m_bitmap;
306 int m_bmpW, m_bmpH;
307 bool m_showFrame:1;
308 wxScrolledWindow *m_window;
309 #if wxUSE_GIF && wxUSE_TIMER
310 wxGIFDecoder *m_gifDecoder;
311 wxTimer *m_gifTimer;
312 int m_physX, m_physY;
313 #endif
314 double m_scale;
315 wxHtmlImageMapCell *m_imageMap;
316 wxString m_mapName;
317
318 DECLARE_NO_COPY_CLASS(wxHtmlImageCell)
319 };
320
321 #if wxUSE_GIF && wxUSE_TIMER
322 class wxGIFTimer : public wxTimer
323 {
324 public:
325 wxGIFTimer(wxHtmlImageCell *cell) : m_cell(cell) {}
326 virtual void Notify()
327 {
328 m_cell->AdvanceAnimation(this);
329 }
330
331 private:
332 wxHtmlImageCell *m_cell;
333
334 DECLARE_NO_COPY_CLASS(wxGIFTimer)
335 };
336 #endif
337
338
339 //----------------------------------------------------------------------------
340 // wxHtmlImageCell
341 //----------------------------------------------------------------------------
342
343
344 wxHtmlImageCell::wxHtmlImageCell(wxWindow *window, wxFSFile *input,
345 int w, int h, double scale, int align,
346 const wxString& mapname) : wxHtmlCell()
347 {
348 m_window = window ? wxStaticCast(window, wxScrolledWindow) : NULL;
349 m_scale = scale;
350 m_showFrame = false;
351 m_bitmap = NULL;
352 m_bmpW = w;
353 m_bmpH = h;
354 m_imageMap = NULL;
355 m_mapName = mapname;
356 SetCanLiveOnPagebreak(false);
357 #if wxUSE_GIF && wxUSE_TIMER
358 m_gifDecoder = NULL;
359 m_gifTimer = NULL;
360 m_physX = m_physY = wxDefaultCoord;
361 #endif
362
363 if ( m_bmpW && m_bmpH )
364 {
365 if ( input )
366 {
367 wxInputStream *s = input->GetStream();
368
369 if ( s )
370 {
371 #if wxUSE_GIF && wxUSE_TIMER
372 bool readImg = true;
373 if ( (input->GetLocation().Matches(wxT("*.gif")) ||
374 input->GetLocation().Matches(wxT("*.GIF"))) && m_window )
375 {
376 m_gifDecoder = new wxGIFDecoder(s, true);
377 if ( m_gifDecoder->ReadGIF() == wxGIF_OK )
378 {
379 wxImage img;
380 if ( m_gifDecoder->ConvertToImage(&img) )
381 SetImage(img);
382
383 readImg = false;
384
385 if ( m_gifDecoder->IsAnimation() )
386 {
387 m_gifTimer = new wxGIFTimer(this);
388 m_gifTimer->Start(m_gifDecoder->GetDelay(), true);
389 }
390 else
391 {
392 wxDELETE(m_gifDecoder);
393 }
394 }
395 else
396 {
397 wxDELETE(m_gifDecoder);
398 }
399 }
400
401 if ( readImg )
402 #endif // wxUSE_GIF && wxUSE_TIMER
403 {
404 wxImage image(*s, wxBITMAP_TYPE_ANY);
405 if ( image.Ok() )
406 SetImage(image);
407 }
408 }
409 }
410 else // input==NULL, use "broken image" bitmap
411 {
412 if ( m_bmpW == wxDefaultCoord && m_bmpH == wxDefaultCoord )
413 {
414 m_bmpW = 29;
415 m_bmpH = 31;
416 }
417 else
418 {
419 m_showFrame = true;
420 if ( m_bmpW == wxDefaultCoord ) m_bmpW = 31;
421 if ( m_bmpH == wxDefaultCoord ) m_bmpH = 33;
422 }
423 m_bitmap =
424 new wxBitmap(wxArtProvider::GetBitmap(wxART_MISSING_IMAGE));
425 }
426 }
427 //else: ignore the 0-sized images used sometimes on the Web pages
428
429 m_Width = (int)(scale * (double)m_bmpW);
430 m_Height = (int)(scale * (double)m_bmpH);
431
432 switch (align)
433 {
434 case wxHTML_ALIGN_TOP :
435 m_Descent = m_Height;
436 break;
437 case wxHTML_ALIGN_CENTER :
438 m_Descent = m_Height / 2;
439 break;
440 case wxHTML_ALIGN_BOTTOM :
441 default :
442 m_Descent = 0;
443 break;
444 }
445 }
446
447 void wxHtmlImageCell::SetImage(const wxImage& img)
448 {
449 #if !defined(__WXMSW__) || wxUSE_WXDIB
450 if ( img.Ok() )
451 {
452 delete m_bitmap;
453
454 int ww, hh;
455 ww = img.GetWidth();
456 hh = img.GetHeight();
457
458 if ( m_bmpW == wxDefaultCoord )
459 m_bmpW = ww;
460 if ( m_bmpH == wxDefaultCoord )
461 m_bmpH = hh;
462
463 // Only scale the bitmap at the rendering stage,
464 // so we don't lose quality twice
465 /*
466 if ((m_bmpW != ww) || (m_bmpH != hh))
467 {
468 wxImage img2 = img.Scale(m_bmpW, m_bmpH);
469 m_bitmap = new wxBitmap(img2);
470 }
471 else
472 */
473 m_bitmap = new wxBitmap(img);
474 }
475 #endif
476 }
477
478 #if wxUSE_GIF && wxUSE_TIMER
479 void wxHtmlImageCell::AdvanceAnimation(wxTimer *timer)
480 {
481 wxImage img;
482
483 m_gifDecoder->GoNextFrame(true);
484
485 if ( m_physX == wxDefaultCoord )
486 {
487 m_physX = m_physY = 0;
488 for (wxHtmlCell *cell = this; cell; cell = cell->GetParent())
489 {
490 m_physX += cell->GetPosX();
491 m_physY += cell->GetPosY();
492 }
493 }
494
495 int x, y;
496 m_window->CalcScrolledPosition(m_physX, m_physY, &x, &y);
497 wxRect rect(x, y, m_Width, m_Height);
498
499 if ( m_window->GetClientRect().Intersects(rect) &&
500 m_gifDecoder->ConvertToImage(&img) )
501 {
502 #if !defined(__WXMSW__) || wxUSE_WXDIB
503 if ( (int)m_gifDecoder->GetWidth() != m_Width ||
504 (int)m_gifDecoder->GetHeight() != m_Height ||
505 m_gifDecoder->GetLeft() != 0 || m_gifDecoder->GetTop() != 0 )
506 {
507 wxBitmap bmp(img);
508 wxMemoryDC dc;
509 dc.SelectObject(*m_bitmap);
510 dc.DrawBitmap(bmp, m_gifDecoder->GetLeft(), m_gifDecoder->GetTop(),
511 true /* use mask */);
512 }
513 else
514 #endif
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 m_WParser->ApplyStateToCell(cel);
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 target;
700 if (tag.HasParam(wxT("TARGET")))
701 target = tag.GetParam(wxT("TARGET"));
702 cel->SetLink(wxHtmlLinkInfo(tag.GetParam(wxT("HREF")), target));
703 }
704 if (cel != NULL)
705 m_WParser->GetContainer()->InsertCell( cel );
706 }
707 }
708
709 return false;
710 }
711
712 TAG_HANDLER_END(IMG)
713
714
715
716 TAGS_MODULE_BEGIN(Image)
717
718 TAGS_MODULE_ADD(IMG)
719
720 TAGS_MODULE_END(Image)
721
722
723 #endif