1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/html/m_image.cpp
3 // Purpose: wxHtml module for displaying images
4 // Author: Vaclav Slavik
6 // Copyright: (c) 1999 Vaclav Slavik, Joel Lucsy
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 #include "wx/wxprec.h"
16 #if wxUSE_HTML && wxUSE_STREAMS
19 #include "wx/dynarray.h"
21 #include "wx/scrolwin.h"
23 #include "wx/dcmemory.h"
27 #include "wx/wxcrtvararg.h"
30 #include "wx/html/forcelnk.h"
31 #include "wx/html/m_templ.h"
32 #include "wx/html/htmlwin.h"
34 #include "wx/gifdecod.h"
35 #include "wx/artprov.h"
39 FORCE_LINK_ME(m_image
)
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
)
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 // ---------------------------------------------------------------------------
55 class wxHtmlImageMapAreaCell
: public wxHtmlCell
58 enum celltype
{ CIRCLE
, RECT
, POLY
};
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
)) {}
72 wxDECLARE_NO_COPY_CLASS(wxHtmlImageMapAreaCell
);
79 wxHtmlImageMapAreaCell
::wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell
::celltype t
, wxString
&incoords
, double pixel_scale
)
82 wxString x
= incoords
, y
;
85 while ((i
= x
.Find( ',' )) != wxNOT_FOUND
)
87 coords
.Add( (int)(pixel_scale
* (double)wxAtoi( x
.Left( i
).c_str())) );
90 coords
.Add( (int)(pixel_scale
* (double)wxAtoi( x
.c_str())) );
93 wxHtmlLinkInfo
*wxHtmlImageMapAreaCell
::GetLink( int x
, int y
) const
105 if (x
>= l
&& x
<= r
&& y
>= t
&& y
<= b
)
119 d
= sqrt( (double) (((x
- l
) * (x
- l
)) + ((y
- t
) * (y
- t
))) );
127 if (coords
.GetCount() >= 6)
132 int totalv
= coords
.GetCount() / 2;
133 int totalc
= totalv
* 2;
134 int xval
= coords
[totalc
- 2];
135 int yval
= coords
[totalc
- 1];
139 if ((yval
>= wherey
) != (coords
[pointer
] >= wherey
))
141 if ((xval
>= wherex
) == (coords
[0] >= wherex
))
143 intersects
+= (xval
>= wherex
) ?
1 : 0;
147 intersects
+= ((xval
- (yval
- wherey
) *
149 (coords
[pointer
] - yval
)) >= wherex
) ?
1 : 0;
153 while (pointer
< end
)
155 yval
= coords
[pointer
];
159 while ((pointer
< end
) && (coords
[pointer
] >= wherey
))
167 if ((coords
[pointer
- 3] >= wherex
) ==
168 (coords
[pointer
- 1] >= wherex
)) {
169 intersects
+= (coords
[pointer
- 3] >= wherex
) ?
1 : 0;
174 ((coords
[pointer
- 3] - (coords
[pointer
- 2] - wherey
) *
175 (coords
[pointer
- 1] - coords
[pointer
- 3]) /
176 (coords
[pointer
] - coords
[pointer
- 2])) >= wherex
) ?
1 : 0;
181 while ((pointer
< end
) && (coords
[pointer
] < wherey
))
189 if ((coords
[pointer
- 3] >= wherex
) ==
190 (coords
[pointer
- 1] >= wherex
))
192 intersects
+= (coords
[pointer
- 3] >= wherex
) ?
1 : 0;
197 ((coords
[pointer
- 3] - (coords
[pointer
- 2] - wherey
) *
198 (coords
[pointer
- 1] - coords
[pointer
- 3]) /
199 (coords
[pointer
] - coords
[pointer
- 2])) >= wherex
) ?
1 : 0;
203 if ((intersects
& 1) != 0)
213 wxHtmlImageMapAreaCell
*a
= (wxHtmlImageMapAreaCell
*)m_Next
;
214 return a
->GetLink( x
, y
);
226 //--------------------------------------------------------------------------------
227 // wxHtmlImageMapCell
228 // 0-width, 0-height cell that represents map from imagemaps
229 // it is always placed before wxHtmlImageMapAreaCells
230 // It responds to Find(wxHTML_COND_ISIMAGEMAP)
231 //--------------------------------------------------------------------------------
234 class wxHtmlImageMapCell
: public wxHtmlCell
237 wxHtmlImageMapCell( wxString
&name
);
241 virtual wxHtmlLinkInfo
*GetLink( int x
= 0, int y
= 0 ) const;
242 virtual const wxHtmlCell
*Find( int cond
, const void *param
) const;
243 void Draw(wxDC
& WXUNUSED(dc
),
244 int WXUNUSED(x
), int WXUNUSED(y
),
245 int WXUNUSED(view_y1
), int WXUNUSED(view_y2
),
246 wxHtmlRenderingInfo
& WXUNUSED(info
)) {}
248 wxDECLARE_NO_COPY_CLASS(wxHtmlImageMapCell
);
252 wxHtmlImageMapCell
::wxHtmlImageMapCell( wxString
&name
)
257 wxHtmlLinkInfo
*wxHtmlImageMapCell
::GetLink( int x
, int y
) const
259 wxHtmlImageMapAreaCell
*a
= (wxHtmlImageMapAreaCell
*)m_Next
;
261 return a
->GetLink( x
, y
);
262 return wxHtmlCell
::GetLink( x
, y
);
265 const wxHtmlCell
*wxHtmlImageMapCell
::Find( int cond
, const void *param
) const
267 if (cond
== wxHTML_COND_ISIMAGEMAP
)
269 if (m_Name
== *((wxString
*)(param
)))
272 return wxHtmlCell
::Find(cond
, param
);
279 //--------------------------------------------------------------------------------
282 //--------------------------------------------------------------------------------
284 class wxHtmlImageCell
: public wxHtmlCell
287 wxHtmlImageCell(wxHtmlWindowInterface
*windowIface
,
289 int w
= wxDefaultCoord
, bool wpercent
= false,
290 int h
= wxDefaultCoord
, bool hpresent
= false,
291 double scale
= 1.0, int align
= wxHTML_ALIGN_BOTTOM
,
292 const wxString
& mapname
= wxEmptyString
);
293 virtual ~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;
298 void SetImage(const wxImage
& img
);
300 // If "alt" text is set, it will be used when converting this cell to text.
301 void SetAlt(const wxString
& alt
);
302 virtual wxString
ConvertToText(wxHtmlSelection
*sel
) const;
304 #if wxUSE_GIF && wxUSE_TIMER
305 void AdvanceAnimation(wxTimer
*timer
);
306 virtual void Layout(int w
);
313 bool m_bmpWpercent
:1;
314 bool m_bmpHpresent
:1;
316 wxHtmlWindowInterface
*m_windowIface
;
317 #if wxUSE_GIF && wxUSE_TIMER
318 wxGIFDecoder
*m_gifDecoder
;
320 int m_physX
, m_physY
;
324 wxHtmlImageMapCell
*m_imageMap
;
328 wxDECLARE_NO_COPY_CLASS(wxHtmlImageCell
);
331 #if wxUSE_GIF && wxUSE_TIMER
332 class wxGIFTimer
: public wxTimer
335 wxGIFTimer(wxHtmlImageCell
*cell
) : m_cell(cell
) {}
336 virtual void Notify()
338 m_cell
->AdvanceAnimation(this);
342 wxHtmlImageCell
*m_cell
;
344 wxDECLARE_NO_COPY_CLASS(wxGIFTimer
);
349 //----------------------------------------------------------------------------
351 //----------------------------------------------------------------------------
354 wxHtmlImageCell
::wxHtmlImageCell(wxHtmlWindowInterface
*windowIface
,
356 int w
, bool wpercent
, int h
, bool hpresent
, double scale
, int align
,
357 const wxString
& mapname
) : wxHtmlCell()
359 m_windowIface
= windowIface
;
366 m_bmpWpercent
= wpercent
;
367 m_bmpHpresent
= hpresent
;
370 SetCanLiveOnPagebreak(false);
371 #if wxUSE_GIF && wxUSE_TIMER
374 m_physX
= m_physY
= wxDefaultCoord
;
378 if ( m_bmpW
&& m_bmpH
)
382 wxInputStream
*s
= input
->GetStream();
386 #if wxUSE_GIF && wxUSE_TIMER
388 if ( m_windowIface
&&
389 (input
->GetLocation().Matches(wxT("*.gif")) ||
390 input
->GetLocation().Matches(wxT("*.GIF"))) )
392 m_gifDecoder
= new wxGIFDecoder();
393 if ( m_gifDecoder
->LoadGIF(*s
) == wxGIF_OK
)
396 if ( m_gifDecoder
->ConvertToImage(0, &img
) )
401 if ( m_gifDecoder
->IsAnimation() )
403 m_gifTimer
= new wxGIFTimer(this);
404 long delay
= m_gifDecoder
->GetDelay(0);
407 m_gifTimer
->Start(delay
, true);
411 wxDELETE(m_gifDecoder
);
416 wxDELETE(m_gifDecoder
);
421 #endif // wxUSE_GIF && wxUSE_TIMER
423 wxImage
image(*s
, wxBITMAP_TYPE_ANY
);
429 else // input==NULL, use "broken image" bitmap
431 if ( m_bmpW
== wxDefaultCoord
&& m_bmpH
== wxDefaultCoord
)
439 if ( m_bmpW
== wxDefaultCoord
) m_bmpW
= 31;
440 if ( m_bmpH
== wxDefaultCoord
) m_bmpH
= 33;
443 new wxBitmap(wxArtProvider
::GetBitmap(wxART_MISSING_IMAGE
));
446 //else: ignore the 0-sized images used sometimes on the Web pages
450 void wxHtmlImageCell
::SetImage(const wxImage
& img
)
452 #if !defined(__WXMSW__) || wxUSE_WXDIB
459 hh
= img
.GetHeight();
461 if ( m_bmpW
== wxDefaultCoord
)
463 if ( m_bmpH
== wxDefaultCoord
)
466 // Only scale the bitmap at the rendering stage,
467 // so we don't lose quality twice
469 if ((m_bmpW != ww) || (m_bmpH != hh))
471 wxImage img2 = img.Scale(m_bmpW, m_bmpH);
472 m_bitmap = new wxBitmap(img2);
476 m_bitmap
= new wxBitmap(img
);
481 void wxHtmlImageCell
::SetAlt(const wxString
& alt
)
486 wxString wxHtmlImageCell
::ConvertToText(wxHtmlSelection
* WXUNUSED(sel
)) const
491 #if wxUSE_GIF && wxUSE_TIMER
492 void wxHtmlImageCell
::AdvanceAnimation(wxTimer
*timer
)
496 // advance current frame
498 if (m_nCurrFrame
== m_gifDecoder
->GetFrameCount())
501 if ( m_physX
== wxDefaultCoord
)
503 m_physX
= m_physY
= 0;
504 for (wxHtmlCell
*cell
= this; cell
; cell
= cell
->GetParent())
506 m_physX
+= cell
->GetPosX();
507 m_physY
+= cell
->GetPosY();
511 wxWindow
*win
= m_windowIface
->GetHTMLWindow();
513 m_windowIface
->HTMLCoordsToWindow(this, wxPoint(m_physX
, m_physY
));
514 wxRect
rect(pos
, wxSize(m_Width
, m_Height
));
516 if ( win
->GetClientRect().Intersects(rect
) &&
517 m_gifDecoder
->ConvertToImage(m_nCurrFrame
, &img
) )
519 #if !defined(__WXMSW__) || wxUSE_WXDIB
520 if ( m_gifDecoder
->GetFrameSize(m_nCurrFrame
) != wxSize(m_Width
, m_Height
) ||
521 m_gifDecoder
->GetFramePosition(m_nCurrFrame
) != wxPoint(0, 0) )
525 dc
.SelectObject(*m_bitmap
);
526 dc
.DrawBitmap(bmp
, m_gifDecoder
->GetFramePosition(m_nCurrFrame
),
527 true /* use mask */);
532 win
->Refresh(img
.HasMask(), &rect
);
535 long delay
= m_gifDecoder
->GetDelay(m_nCurrFrame
);
538 timer
->Start(delay
, true);
541 void wxHtmlImageCell
::Layout(int w
)
546 m_Width
= w
*m_bmpW
/100;
548 if (!m_bmpHpresent
&& m_bitmap
!= NULL
)
549 m_Height
= m_bitmap
->GetHeight()*m_Width
/m_bitmap
->GetWidth();
551 m_Height
= static_cast<int>(m_scale
*m_bmpH
);
554 m_Width
= static_cast<int>(m_scale
*m_bmpW
);
555 m_Height
= static_cast<int>(m_scale
*m_bmpH
);
560 case wxHTML_ALIGN_TOP
:
561 m_Descent
= m_Height
;
563 case wxHTML_ALIGN_CENTER
:
564 m_Descent
= m_Height
/ 2;
566 case wxHTML_ALIGN_BOTTOM
:
572 wxHtmlCell
::Layout(w
);
573 m_physX
= m_physY
= wxDefaultCoord
;
578 wxHtmlImageCell
::~wxHtmlImageCell()
581 #if wxUSE_GIF && wxUSE_TIMER
588 void wxHtmlImageCell
::Draw(wxDC
& dc
, int x
, int y
,
589 int WXUNUSED(view_y1
), int WXUNUSED(view_y2
),
590 wxHtmlRenderingInfo
& WXUNUSED(info
))
594 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
595 dc
.SetPen(*wxBLACK_PEN
);
596 dc
.DrawRectangle(x
+ m_PosX
, y
+ m_PosY
, m_Width
, m_Height
);
601 // We add in the scaling from the desired bitmap width
602 // and height, so we only do the scaling once.
603 double imageScaleX
= 1.0;
604 double imageScaleY
= 1.0;
605 if (m_Width
!= m_bitmap
->GetWidth())
606 imageScaleX
= (double) m_Width
/ (double) m_bitmap
->GetWidth();
607 if (m_Height
!= m_bitmap
->GetHeight())
608 imageScaleY
= (double) m_Height
/ (double) m_bitmap
->GetHeight();
611 dc
.GetUserScale(&us_x
, &us_y
);
612 dc
.SetUserScale(us_x
* imageScaleX
, us_y
* imageScaleY
);
614 dc
.DrawBitmap(*m_bitmap
, (int) ((x
+ m_PosX
) / (imageScaleX
)),
615 (int) ((y
+ m_PosY
) / (imageScaleY
)), true);
616 dc
.SetUserScale(us_x
, us_y
);
620 wxHtmlLinkInfo
*wxHtmlImageCell
::GetLink( int x
, int y
) const
622 if (m_mapName
.empty())
623 return wxHtmlCell
::GetLink( x
, y
);
626 wxHtmlContainerCell
*p
, *op
;
627 op
= p
= GetParent();
634 wxHtmlCell
*cell
= (wxHtmlCell
*)p
->Find(wxHTML_COND_ISIMAGEMAP
,
635 (const void*)(&m_mapName
));
638 ((wxString
&)m_mapName
).Clear();
639 return wxHtmlCell
::GetLink( x
, y
);
641 { // dirty hack, ask Joel why he fills m_ImageMap in this place
642 // THE problem is that we're in const method and we can't modify m_ImageMap
643 wxHtmlImageMapCell
**cx
= (wxHtmlImageMapCell
**)(&m_imageMap
);
644 *cx
= (wxHtmlImageMapCell
*)cell
;
647 return m_imageMap
->GetLink(x
, y
);
652 //--------------------------------------------------------------------------------
654 //--------------------------------------------------------------------------------
656 TAG_HANDLER_BEGIN(IMG
, "IMG,MAP,AREA")
657 TAG_HANDLER_CONSTR(IMG
) { }
659 TAG_HANDLER_PROC(tag
)
661 if (tag
.GetName() == wxT("IMG"))
663 if (tag
.HasParam(wxT("SRC")))
665 int w
= wxDefaultCoord
, h
= wxDefaultCoord
;
666 bool wpercent
= false;
667 bool hpresent
= false;
670 wxString tmp
= tag
.GetParam(wxT("SRC"));
671 wxString mn
= wxEmptyString
;
673 str
= m_WParser
->OpenURL(wxHTML_URL_IMAGE
, tmp
);
675 if (tag
.HasParam(wxT("WIDTH")))
677 if (tag
.GetParamAsIntOrPercent(wxT("WIDTH"), &w
, wpercent
))
689 if (tag
.HasParam(wxT("HEIGHT")))
691 tag
.GetParamAsInt(wxT("HEIGHT"), &h
);
695 al
= wxHTML_ALIGN_BOTTOM
;
696 if (tag
.HasParam(wxT("ALIGN")))
698 wxString alstr
= tag
.GetParam(wxT("ALIGN"));
699 alstr
.MakeUpper(); // for the case alignment was in ".."
700 if (alstr
== wxT("TEXTTOP"))
701 al
= wxHTML_ALIGN_TOP
;
702 else if ((alstr
== wxT("CENTER")) || (alstr
== wxT("ABSCENTER")))
703 al
= wxHTML_ALIGN_CENTER
;
705 if (tag
.HasParam(wxT("USEMAP")))
707 mn
= tag
.GetParam( wxT("USEMAP") );
708 if ( !mn
.empty() && *mn
.begin() == '#' )
713 wxHtmlImageCell
*cel
= new wxHtmlImageCell(
714 m_WParser
->GetWindowInterface(),
715 str
, w
, wpercent
, h
, hpresent
,
716 m_WParser
->GetPixelScale(),
718 m_WParser
->ApplyStateToCell(cel
);
719 m_WParser
->StopCollapsingSpaces();
720 cel
->SetId(tag
.GetParam(wxT("id"))); // may be empty
721 cel
->SetAlt(tag
.GetParam(wxT("alt")));
722 m_WParser
->GetContainer()->InsertCell(cel
);
727 if (tag
.GetName() == wxT("MAP"))
729 m_WParser
->CloseContainer();
730 m_WParser
->OpenContainer();
731 if (tag
.HasParam(wxT("NAME")))
733 wxString tmp
= tag
.GetParam(wxT("NAME"));
734 wxHtmlImageMapCell
*cel
= new wxHtmlImageMapCell( tmp
);
735 m_WParser
->GetContainer()->InsertCell( cel
);
738 m_WParser
->CloseContainer();
739 m_WParser
->OpenContainer();
741 if (tag
.GetName() == wxT("AREA"))
743 if (tag
.HasParam(wxT("SHAPE")))
745 wxString tmp
= tag
.GetParam(wxT("SHAPE"));
746 wxString coords
= wxEmptyString
;
748 wxHtmlImageMapAreaCell
*cel
= NULL
;
749 if (tag
.HasParam(wxT("COORDS")))
751 coords
= tag
.GetParam(wxT("COORDS"));
753 if (tmp
== wxT("POLY"))
755 cel
= new wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell
::POLY
, coords
, m_WParser
->GetPixelScale() );
757 else if (tmp
== wxT("CIRCLE"))
759 cel
= new wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell
::CIRCLE
, coords
, m_WParser
->GetPixelScale() );
761 else if (tmp
== wxT("RECT"))
763 cel
= new wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell
::RECT
, coords
, m_WParser
->GetPixelScale() );
765 if (cel
!= NULL
&& tag
.HasParam(wxT("HREF")))
768 if (tag
.HasParam(wxT("TARGET")))
769 target
= tag
.GetParam(wxT("TARGET"));
770 cel
->SetLink(wxHtmlLinkInfo(tag
.GetParam(wxT("HREF")), target
));
773 m_WParser
->GetContainer()->InsertCell( cel
);
784 TAGS_MODULE_BEGIN(Image
)
788 TAGS_MODULE_END(Image
)