--- /dev/null
+\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\8c\8a\8c\8c\8a\8c\8c\8a\8ctvt¼º¼üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\8c\8a\8cÌÎÌ\8c\8a\8c\8c\8a\8cÌÎÌ\8c\8a\8cÌÎÌüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\8c\8a\8cüþüDFDìîìDFDüþü\8c\8a\8cüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\8c\8a\8cüþü$"$DFD¼º¼üþü\8c\8a\8cüþüüþüìòüÄÎäÄÎäÄÎäÄÎäìòüüþüüþüüþüüþüüþüüþüÜæôÄÎäÄÎäÄÎäÄÎäüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü¼Æä\94ªÔ\94ªÔ\94ªÔ¤¶ÜÄÎäÄÎäÄÎäÄÎäÄÎäÄÎäÄÎäÄÎäÄÎäÄÎäÄÎäìòüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÌÎÌüþüüþütvtüþüDFDÌÎÌDFDÜÞÜ\8c\8a\8cüþüüþüÜæô\fF\9c\fF\9c\fF\9c\fF\9c¤¶ÜüþüüþüüþüüþüüþüüþüÔÚì\fF\9c\fF\9c\fF\9c\fF\9cÄÎäüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\14N¤\fF\9c\fF\9c\14N¤\fF\9c\fF\9c\fF\9c\fF\9c\14N¤\fF\9c\fF\9c\fF\9c\14N¤\fF\9c\fF\9c\fF\9cÄÎäüþüüþüüþüüþüüþüÌÎÌLNL\ 4\ 2\ 4\f\ e\f\^\ÜÞÜüþüüþüüþüìîì,.,üþüüþü¼º¼\8c\8a\8cüþüüþüìîìtvtìîìüþüüþüüþü$Z¬\fF\9c\fF\9c\fF\9cd\8aÄüþüüþüüþüüþüüþüüþüüþü\14N¤\fF\9c\fF\9c\14N¤t\96ÄüþüüþüüþüüþüüþüüþüüþüüþüüþüÄÎä\fB\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\14N¤\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9cÄÎäüþüüþüüþüüþü\9c\9e\9c\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\9c\9e\9cüþüüþü|~|\ 4\ 2\ 4üþüüþüüþüÌÎÌ\8c\8a\8c\8c\8a\8c\8c\8a\8cìîìüþüüþüüþüüþüt\96Ä\14N¤\fF\9c\fF\9c\14N¤üþüüþüüþüüþüüþüüþüüþüd\8aÄ\fF\9c\fF\9c\fF\9c$Z¬üþüüþüüþüüþüüþüüþüüþüüþüüþüt\96Ä\fB\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\14N¤\fF\9c\fF\9c\14N¤Dr´üþüüþüüþüüþüÌÎÌ\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4¼º¼ìîì\f\ e\f\ 4\ 2\ 4¼¾¼üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÄÎä\fF\9c\fF\9c\fF\9c\14N¤ÔÚìüþüüþüüþüüþüüþüüþü¤¶Ü\fF\9c\fF\9c\fF\9c\fF\9cÜæôüþüüþüüþüüþüüþüüþüüþüüþü4f¬\fB\9c\14N¤\fF\9c\fF\9c\14N¤\fF\9c\fF\9c\14N¤\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9cÄÎäüþüüþüüþüüþü\1c\1e\1c\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\1c\1e\1clnl\ 4\ 2\ 4\ 4\ 2\ 4\9c\9e\9cüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\14N¤\fF\9c\fF\9c\fF\9ct\96Äüþüüþüüþüüþüüþüüþüìòü\fF\9c\fF\9c\fF\9c\fF\9c\94ªÔüþüüþüüþüüþüüþüüþüüþüÜæô\fF\9c\fF\9c\fF\9c\fF\9cl\8aÄüþüüþüüþüüþüüþü´Âä\fF\9c\fF\9c\fF\9c\fF\9cd\8aÄüþüüþüüþüüþü\9c\9e\9c\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4,.,|~|lnl$"$\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\f\ e\fìîìüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüT~¼\fF\9c\fF\9c\fF\9c4f¬üþüüþüüþüüþüüþüüþüüþüDr´\fF\9c\14N¤\fF\9cT~¼üþüüþüüþüüþüüþüüþüüþü\94ªÔ\fF\9c\fF\9c\fF\9c\fF\9cìòüüþüüþüüþüüþüüþü$Z¬\fF\9c\fF\9c\fF\9c\14N¤ìòüüþüüþüüþüüþü<><\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\9c\9e\9cüþüüþüüþüìîì\^\\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4|~|üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü¤¶Ü\fF\9c\fF\9c\14N¤\fF\9cÜæôüþüüþüüþüüþüüþüüþüd\8aÄ\fF\9c\fF\9c\fF\9c\fF\9cìòüüþüüþüüþüüþüüþüüþüT~¼\fF\9c\fB\9c\fF\9c<j´üþüüþüüþüüþüüþü\84\9eÌ\fF\9c\14N¤\fF\9c\fF\9c\84\9eÌüþüüþüüþüüþüìîì\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\9c\9e\9cüþüüþüüþüüþüüþüüþü\^\\ 4\ 2\ 4\f\ e\fìîìüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüìòü\fF\9c\fF\9c\fF\9c\fF\9c¤¶Üüþüüþüüþüüþüüþüüþü\14N¤\fF\9c\fF\9c\fF\9c\fF\9c¼Æäüþüüþüüþüüþüüþüüþü\fF\9c\fF\9c\14N¤\fF\9c\94ªÔüþüüþüüþüüþüìòü\14N¤\fF\9c\fF\9c\fF\9c$Z¬üþüüþüüþüüþüüþü¬®¬\ 4\ 2\ 4\ 4\ 2\ 4\^\üþüüþüüþüüþüüþüüþüüþüüþü,.,|~|üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü<j´\fF\9c\fF\9c\fF\9cT~¼üþüüþüüþüüþüüþüÔÚì\14N¤\fF\9c\fF\9c\fF\9c\fF\9cd\8aÄüþüüþüüþüüþüüþü¼Æä\fF\9c\fF\9c\fF\9c\fF\9cÔÚìüþüüþüüþüüþüd\8aÄ\fF\9c\fF\9c\fF\9c\fF\9c¼Æäüþüüþüüþüüþüüþü|~|\ 4\ 2\ 4\ 4\ 2\ 4ÜÞÜüþüüþüüþüüþüüþüüþüüþüüþüÌÎÌìîìüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\94ªÔ\fF\9c\fF\9c\fF\9c\fF\9cüþüüþüüþüüþüüþü\84\9eÌ\fF\9c\fF\9c\14N¤\fF\9c\fF\9c$Z¬üþüüþüüþüüþüüþüd\8aÄ\fF\9c\fF\9c\fF\9c$Z¬üþüüþüüþüüþüÄÎä\fF\9c\fF\9c\14N¤\fF\9cDr´üþüüþüüþüüþüüþüüþüLNL\ 4\ 2\ 4LNLüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÔÚì\fF\9c\fF\9c\14N¤\fF\9c´Âäüþüüþüüþüüþü4f¬\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9cÔÚìüþüüþüüþüüþü$Z¬\fF\9c\fF\9c\fF\9ct\96ÄüþüüþüüþüüþüDr´\fF\9c\fF\9c\fF\9c\fF\9cÜæôüþüüþüüþüüþüüþüüþü<><\ 4\ 2\ 4\9c\9e\9cüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü$Z¬\fF\9c\fF\9c\fF\9ct\96ÄüþüüþüüþüÜæô\fF\9c\14N¤\fF\9c\fF\9c\fF\9c\fF\9c\14N¤\84\9eÌüþüüþüüþüÔÚì\fF\9c\fF\9c\fF\9c\fF\9c´Âäüþüüþüüþü¤¶Ü\fF\9c\fF\9c\fF\9c\fF\9cd\8aÄüþüüþüüþüüþüüþüüþüüþü<><\ 4\ 2\ 4ìîìüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüd\8aÄ\fF\9c\fF\9c\fF\9c$Z¬üþüüþüüþü¤¶Ü\fF\9c\fF\9c\fF\9c\14N¤\fF\9c\fF\9c\fF\9c4f¬üþüüþüüþü\94ªÔ\fF\9c\fF\9c\14N¤\14N¤üþüüþüüþüìòü$Z¬\fF\9c\14N¤\fF\9c\fF\9c\fF\9c<j´ÔÚìüþüüþüüþüüþüüþü<><\1c\1e\1cüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü¼Æä\fF\9c\fF\9c\14N¤\fF\9cÔÚìüþüüþüT~¼\fF\9c\fF\9c\14N¤\84\9eÌ\fF\9c\fF\9c\fF\9c\fF\9cìòüüþüüþü<j´\fF\9c\fF\9c\fF\9cT~¼üþüüþüüþü\94ªÔ\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\14N¤\fF\9c\fF\9c¤¶Üüþüüþüüþüüþü<><<><üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\fF\9c\fF\9c\fF\9c\fF\9c\94ªÔüþüüþü\14N¤\fF\9c\fF\9c\fF\9cÜæôT~¼\fF\9c\14N¤\fF\9c¤¶Üüþüìòü\fF\9c\fF\9c\fF\9c\fF\9c¤¶Üüþüüþüüþü\84\9eÌ\14N¤\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c¤¶Üüþüüþüüþü|~|<><üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüT~¼\fF\9c\fF\9c\fF\9cDr´üþü´Âä\fF\9c\fF\9c\fF\9c4f¬üþü\94ªÔ\fF\9c\fF\9c\fF\9cT~¼üþü¤¶Ü\fF\9c\fF\9c\fF\9c\fF\9cÜæôüþüüþüüþüÄÎä\84\9eÌ\84\9eÌ\84\9eÌ4f¬\14N¤\fF\9c\fF\9c\14N¤\fF\9c\14N¤ìòüüþüüþü\9c\9e\9c<><üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü¤¶Ü\fF\9c\14N¤\fF\9c\fF\9cìòüt\96Ä\fF\9c\fF\9c\14N¤t\96ÄüþüÜæô\fF\9c\fF\9c\fF\9c\14N¤üþüd\8aÄ\fF\9c\14N¤\fF\9c<j´üþüüþüüþüüþüüþüüþüüþüüþüüþüÄÎä\14N¤\fF\9c\fF\9c\fF\9c\fF\9c\84\9eÌüþüüþüÜÞÜ<><üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÜæô\fF\9c\fF\9c\fF\9c\fF\9c¤¶Ü$Z¬\fF\9c\fF\9c\fF\9cÔÚìüþüüþü$Z¬\fF\9c\fF\9c\fF\9cÄÎä\14N¤\fF\9c\fF\9c\fF\9c\84\9eÌüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÔÚì\fF\9c\fF\9c\fF\9c\fF\9c\14N¤üþüüþüüþü|~|üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü4f¬\fF\9c\fF\9c\fF\9c<j´\fF\9c\fF\9c\fF\9c\14N¤üþüüþüüþüt\96Ä\14N¤\fF\9c\fF\9c<j´\fF\9c\fF\9c\fF\9c\fF\9cÔÚìüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüd\8aÄ\fF\9c\14N¤\fF\9c\fF\9cÔÚìüþüüþü¼¾¼üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüt\96Ä\14N¤\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9cd\8aÄüþüüþüüþüÄÎä\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c$Z¬üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü´Âä\fF\9c\fF\9c\fF\9c\14N¤\94ªÔüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÔÚì\fF\9c\fF\9c\14N¤\fF\9c\fF\9c\14N¤\fF\9c¼Æäüþüüþüüþüüþü\14N¤\fF\9c\fF\9c\fF\9c\14N¤\fF\9c\fF\9cd\8aÄüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÔÚì\fF\9c\fF\9c\fF\9c\fF\9c\84\9eÌüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\14N¤\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9cìòüüþüüþüüþüüþüd\8aÄ\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c¼Æäüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\14N¤\fF\9c\fF\9c\fF\9c\84\9eÌüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüd\8aÄ\fF\9c\fF\9c\fF\9c\fF\9c\fF\9cT~¼üþüüþüüþüüþüüþü¤¶Ü\fF\9c\14N¤\fF\9c\fF\9c\fF\9c\fF\9cìòüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÜæô\fF\9c\fF\9c\14N¤\fF\9cl\8aļ¾¼üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüìîìüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü¼Æä\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\94ªÔüþüüþüüþüüþüüþüìòü\fF\9c\fF\9c\fF\9c\14N¤\fF\9cT~¼üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÄÎä\fF\9c\fF\9c\fF\9c\fF\9c\84\9eÌLNLüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü<><ÜÞÜüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüìòü\fF\9c\fF\9c\14N¤\fF\9c\fF\9cÜæôüþüüþüüþüüþüüþüüþüDr´\fF\9c\fF\9c\fF\9c\fF\9c\94ªÔüþüüþüüþü¼ÆäT~¼4f¬üþüüþüüþüüþüüþüüþüüþüt\96Ä\fF\9c\14N¤\fF\9c\fF\9cÄÎä\8c\8e\8c|~|üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\8c\8a\8c\ 4\ 2\ 4¬®¬üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüDr´\fF\9c\fF\9c\fF\9c<j´üþüüþüüþüüþüüþüüþüüþü\94ªÔ\fF\9c\fF\9c\fF\9c\fF\9cÜæôüþüüþü4f¬\fF\9c\fF\9c\fF\9c¼Æäüþüüþüüþüüþüüþüìòü$Z¬\fF\9c\fF\9c\fF\9c\fF\9cìòüÜÞÜ\ 4\ 2\ 4\9c\9e\9cüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÌÎÌ\f\ e\f\ 4\ 2\ 4|~|üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\94ªÔ\fF\9c\fF\9c\fF\9ct\96ÄüþüüþüüþüüþüüþüüþüüþüÜæô\fF\9c\fF\9c\fF\9c<j´üþüüþüüþüd\8aÄ\fF\9c\fF\9c\fF\9c\14N¤ÔÚìüþüüþüüþüüþüt\96Ä\fF\9c\fF\9c\fF\9c\fF\9cT~¼üþüüþülnl\ 4\ 2\ 4\9c\9e\9cüþüüþüüþüüþüüþüüþüüþüüþüÌÎÌ\f\ e\f\ 4\ 2\ 4\ 4\ 2\ 4\9c\9e\9cüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÜæô\fF\9c\fF\9c\14N¤ÄÎäüþüüþüüþüüþüüþüüþüüþüüþü$Z¬\14N¤\fF\9ct\96ÄüþüüþüüþüÜæô\14N¤\fF\9c\fF\9c\fF\9c\14N¤\84\9eÌÄÎäÔÚìd\8aÄ\fF\9c\14N¤\fF\9c\fF\9c\14N¤ÄÎäüþüüþüìîì\f\ e\f\ 4\ 2\ 4\8c\8a\8cüþüüþüüþüüþüüþüüþü\8c\8a\8c\f\ e\f\ 4\ 2\ 4\ 4\ 2\ 4,.,üþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü$Z¬\fF\9c\14N¤üþüüþüüþüüþüüþüüþüüþüüþüüþüt\96Ä\fF\9c\fF\9cÄÎäüþüüþüüþüüþü\84\9eÌ\fF\9c\fF\9c\14N¤\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9cT~¼üþüüþüüþüüþü¬®¬\ 4\ 2\ 4\ 4\ 2\ 4\1c\1e\1clnl¬®¬¬®¬lnl\1c\1e\1c\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\f\ e\fÜÞÜüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüt\96Ä\fF\9cd\8aÄüþüüþüüþüüþüüþüüþüüþüüþüüþüÄÎä\fF\9c\14N¤üþüüþüüþüüþüüþüüþüDr´\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\14N¤\fF\9c\fF\9c\fF\9c\fF\9c4f¬ìòüüþüüþüüþüüþüüþü\9c\9e\9c\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\f\ e\fÌÎÌüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÄÎä\fF\9c¤¶Üüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\14N¤d\8aÄüþüüþüüþüüþüüþüüþüìòül\8aÄ\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c\fF\9c<j´ìòüüþüüþüüþüüþüüþüüþüüþü\9c\9e\9c\f\ e\f\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\1c\1e\1cÌÎÌüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\14N¤ìòüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüd\8aĤ¶ÜüþüüþüüþüüþüüþüüþüüþüüþüÄÎäT~¼\fF\9c\14N¤\fF\9c\fF\9c<j´¤¶ÜüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüÜÞÜlnl,.,\ 4\ 2\ 4\ 4\ 2\ 4<><\8c\8e\8cìîìüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4\ 4\ 2\ 4
\ No newline at end of file
SET(TC_SOURCES
utc-Dali-AddOns.cpp
+ utc-Dali-BmpLoader.cpp
utc-Dali-CommandLineOptions.cpp
utc-Dali-CompressedTextures.cpp
utc-Dali-FontClient.cpp
utc-Dali-GifLoader.cpp
utc-Dali-IcoLoader.cpp
- utc-Dali-BmpLoader.cpp
utc-Dali-ImageOperations.cpp
utc-Dali-Internal-PixelBuffer.cpp
utc-Dali-Lifecycle-Controller.cpp
+ utc-Dali-LRUCacheContainer.cpp
utc-Dali-TiltSensor.cpp
+ utc-Dali-WbmpLoader.cpp
)
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
if(*bufferPtr != *refBufferPtr)
{
tet_result(TET_FAIL);
- tet_printf("%s Failed in %s at line %d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__);
+ tet_printf("%s Failed in %s at line %d, %u'th buffer (input : %u != expect : %u)\n", __PRETTY_FUNCTION__, __FILE__, __LINE__, i, static_cast<unsigned int>(*bufferPtr), static_cast<unsigned int>(*refBufferPtr));
break;
}
}
/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
END_TEST;
}
+int UtcDaliBmpRGB8(void)
+{
+ ImageDetails image(TEST_IMAGE_DIR "/w3c_home_256.bmp", 72u, 48u);
+
+ TestImageLoading(image, BmpLoaders);
+
+ END_TEST;
+}
\ No newline at end of file
*/
int UtcDaliImageOperationsDownscaleInPlacePow2RGB888(void)
{
- unsigned outWidth = -1, outHeight = -1;
+ unsigned outWidth = -1, outHeight = -1, outStride = -1;
// Do downscaling to 1 x 1 so we can easily assert the value of the single pixel produced:
unsigned char check_4x4[16 * 3] = {
0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff};
- Dali::Internal::Platform::DownscaleInPlacePow2RGB888(check_4x4, 4, 4, 1, 1, BoxDimensionTestBoth, outWidth, outHeight);
+ Dali::Internal::Platform::DownscaleInPlacePow2RGB888(check_4x4, 4, 4, 4, 1, 1, BoxDimensionTestBoth, outWidth, outHeight, outStride);
DALI_TEST_EQUALS(outWidth, 1u, TEST_LOCATION);
DALI_TEST_EQUALS(outHeight, 1u, TEST_LOCATION);
+ DALI_TEST_EQUALS(outStride, 1u, TEST_LOCATION);
DALI_TEST_EQUALS(check_4x4[0], (unsigned char)0x7f, TEST_LOCATION);
// Scale down a 16 pixel black image with a single white pixel to a 1/16th grey single pixel:
unsigned char single_4x4[16 * 3] = {
0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
- Dali::Internal::Platform::DownscaleInPlacePow2RGB888(single_4x4, 4, 4, 1, 1, BoxDimensionTestBoth, outWidth, outHeight);
+ Dali::Internal::Platform::DownscaleInPlacePow2RGB888(single_4x4, 4, 4, 4, 1, 1, BoxDimensionTestBoth, outWidth, outHeight, outStride);
DALI_TEST_EQUALS(outWidth, 1u, TEST_LOCATION);
DALI_TEST_EQUALS(outHeight, 1u, TEST_LOCATION);
+ DALI_TEST_EQUALS(outStride, 1u, TEST_LOCATION);
DALI_TEST_EQUALS(single_4x4[0], (unsigned char)0xf, TEST_LOCATION);
// Scale down a 16 pixel black image with a single white pixel to a 1/16th grey single pixel:
// (white pixel at bottom-right of image)
unsigned char single_4x4_2[16 * 3] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff};
- Dali::Internal::Platform::DownscaleInPlacePow2RGB888(single_4x4_2, 4, 4, 1, 1, BoxDimensionTestBoth, outWidth, outHeight);
+ Dali::Internal::Platform::DownscaleInPlacePow2RGB888(single_4x4_2, 4, 4, 4, 1, 1, BoxDimensionTestBoth, outWidth, outHeight, outStride);
DALI_TEST_EQUALS(outWidth, 1u, TEST_LOCATION);
DALI_TEST_EQUALS(outHeight, 1u, TEST_LOCATION);
+ DALI_TEST_EQUALS(outStride, 1u, TEST_LOCATION);
DALI_TEST_EQUALS(single_4x4_2[0], (unsigned char)0xf, TEST_LOCATION);
// Build a larger ~600 x ~600 uniform magenta image for tests which only test output dimensions:
}
// Scaling to 0 x 0 should stop at 1 x 1:
- Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 352, 352, 0, 0, BoxDimensionTestBoth, outWidth, outHeight);
+ Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 352, 352, 352, 0, 0, BoxDimensionTestBoth, outWidth, outHeight, outStride);
DALI_TEST_EQUALS(outWidth, 1u, TEST_LOCATION);
DALI_TEST_EQUALS(outHeight, 1u, TEST_LOCATION);
+ DALI_TEST_CHECK(outStride == outWidth);
// Scaling to 1 x 1 should hit 1 x 1:
- Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 608, 608, 1, 1, BoxDimensionTestBoth, outWidth, outHeight);
+ Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 608, 608, 608, 1, 1, BoxDimensionTestBoth, outWidth, outHeight, outStride);
DALI_TEST_EQUALS(outWidth, 1u, TEST_LOCATION);
DALI_TEST_EQUALS(outHeight, 1u, TEST_LOCATION);
+ DALI_TEST_CHECK(outStride == outWidth);
// Scaling to original dimensions should NOP:
- Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 384, 384, 384, 384, BoxDimensionTestBoth, outWidth, outHeight);
+ Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 384, 384, 384, 384, 384, BoxDimensionTestBoth, outWidth, outHeight, outStride);
DALI_TEST_EQUALS(outWidth, 384u, TEST_LOCATION);
DALI_TEST_EQUALS(outHeight, 384u, TEST_LOCATION);
+ DALI_TEST_CHECK(outStride == outWidth);
// More dimension tests:
- Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 352, 352, 44, 11, BoxDimensionTestBoth, outWidth, outHeight);
+ Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 352, 352, 352, 44, 11, BoxDimensionTestBoth, outWidth, outHeight, outStride);
DALI_TEST_EQUALS(outWidth, 44u, TEST_LOCATION);
DALI_TEST_EQUALS(outHeight, 44u, TEST_LOCATION);
+ DALI_TEST_CHECK(outStride == outWidth);
- Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 384, 384, 3, 48, BoxDimensionTestBoth, outWidth, outHeight);
+ Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 384, 384, 384, 3, 48, BoxDimensionTestBoth, outWidth, outHeight, outStride);
DALI_TEST_EQUALS(outWidth, 48u, TEST_LOCATION);
DALI_TEST_EQUALS(outHeight, 48u, TEST_LOCATION);
+ DALI_TEST_CHECK(outStride == outWidth);
- Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 384, 384, 3, 3, BoxDimensionTestBoth, outWidth, outHeight);
+ Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 384, 384, 384, 3, 3, BoxDimensionTestBoth, outWidth, outHeight, outStride);
DALI_TEST_CHECK(outWidth == 3u && outHeight == 3u);
+ DALI_TEST_CHECK(outStride == outWidth);
- Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 320, 320, 5, 5, BoxDimensionTestBoth, outWidth, outHeight);
+ Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 320, 320, 320, 5, 5, BoxDimensionTestBoth, outWidth, outHeight, outStride);
DALI_TEST_CHECK(outWidth == 5u && outHeight == 5u);
+ DALI_TEST_CHECK(outStride == outWidth);
- Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 448, 448, 7, 7, BoxDimensionTestBoth, outWidth, outHeight);
+ Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 448, 448, 448, 7, 7, BoxDimensionTestBoth, outWidth, outHeight, outStride);
DALI_TEST_CHECK(outWidth == 7u && outHeight == 7u);
+ DALI_TEST_CHECK(outStride == outWidth);
- Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 352, 352, 11, 11, BoxDimensionTestBoth, outWidth, outHeight);
+ Dali::Internal::Platform::DownscaleInPlacePow2RGB888(magenta_600_x_600, 352, 352, 352, 11, 11, BoxDimensionTestBoth, outWidth, outHeight, outStride);
DALI_TEST_CHECK(outWidth == 11u && outHeight == 11u);
+ DALI_TEST_CHECK(outStride == outWidth);
// Check that no pixel values were modified by the repeated averaging of identical pixels in tests above:
unsigned int numNonMagenta = 0u;
*/
void TestDownscaleOutputsExpectedDimensionsRGBA8888(uint32_t pixels[], unsigned inputWidth, unsigned inputHeight, unsigned int desiredWidth, unsigned int desiredHeight, unsigned int expectedWidth, unsigned int expectedHeight, const char* const location)
{
- unsigned int resultingWidth = -1, resultingHeight = -1;
+ unsigned int resultingWidth = -1, resultingHeight = -1, resultingStride = -1;
Dali::Internal::Platform::DownscaleInPlacePow2RGBA8888(
reinterpret_cast<unsigned char*>(pixels),
inputWidth,
inputHeight,
+ inputWidth,
desiredWidth,
desiredHeight,
BoxDimensionTestBoth,
resultingWidth,
- resultingHeight);
+ resultingHeight,
+ resultingStride);
DALI_TEST_EQUALS(resultingWidth, expectedWidth, location);
DALI_TEST_EQUALS(resultingHeight, expectedHeight, location);
+ DALI_TEST_EQUALS(resultingStride, expectedWidth, location);
}
/**
*/
void TestDownscaleOutputsExpectedDimensionsRGB565(uint16_t pixels[], unsigned inputWidth, unsigned inputHeight, unsigned int desiredWidth, unsigned int desiredHeight, unsigned int expectedWidth, unsigned int expectedHeight, const char* const location)
{
- unsigned int resultingWidth = -1, resultingHeight = -1;
+ unsigned int resultingWidth = -1, resultingHeight = -1, resultingStride = -1;
Dali::Internal::Platform::DownscaleInPlacePow2RGB565(
reinterpret_cast<unsigned char*>(pixels),
inputWidth,
inputHeight,
+ inputWidth,
desiredWidth,
desiredHeight,
BoxDimensionTestBoth,
resultingWidth,
- resultingHeight);
+ resultingHeight,
+ resultingStride);
DALI_TEST_EQUALS(resultingWidth, expectedWidth, location);
DALI_TEST_EQUALS(resultingHeight, expectedHeight, location);
+ DALI_TEST_EQUALS(resultingStride, expectedWidth, location);
}
/**
*/
void TestDownscaleOutputsExpectedDimensions2ComponentPair(uint8_t pixels[], unsigned inputWidth, unsigned inputHeight, unsigned int desiredWidth, unsigned int desiredHeight, unsigned int expectedWidth, unsigned int expectedHeight, const char* const location)
{
- unsigned int resultingWidth = -1, resultingHeight = -1;
+ unsigned int resultingWidth = -1, resultingHeight = -1, resultingStride = -1;
Dali::Internal::Platform::DownscaleInPlacePow2ComponentPair(
pixels,
inputWidth,
inputHeight,
+ inputWidth,
desiredWidth,
desiredHeight,
BoxDimensionTestBoth,
resultingWidth,
- resultingHeight);
+ resultingHeight,
+ resultingStride);
DALI_TEST_EQUALS(resultingWidth, expectedWidth, location);
DALI_TEST_EQUALS(resultingHeight, expectedHeight, location);
+ DALI_TEST_EQUALS(resultingStride, expectedWidth, location);
}
/**
*/
void TestDownscaleOutputsExpectedDimensionsSingleComponent(uint8_t pixels[], unsigned inputWidth, unsigned inputHeight, unsigned int desiredWidth, unsigned int desiredHeight, unsigned int expectedWidth, unsigned int expectedHeight, const char* const location)
{
- unsigned int resultingWidth = -1, resultingHeight = -1;
+ unsigned int resultingWidth = -1, resultingHeight = -1, resultingStride = -1;
Dali::Internal::Platform::DownscaleInPlacePow2SingleBytePerPixel(
pixels,
inputWidth,
inputHeight,
+ inputWidth,
desiredWidth,
desiredHeight,
BoxDimensionTestBoth,
resultingWidth,
- resultingHeight);
+ resultingHeight,
+ resultingStride);
DALI_TEST_EQUALS(resultingWidth, expectedWidth, location);
DALI_TEST_EQUALS(resultingHeight, expectedHeight, location);
+ DALI_TEST_EQUALS(resultingStride, expectedWidth, location);
}
/**
image[i] = 0xffffffff;
}
unsigned char* const pixels = reinterpret_cast<unsigned char*>(image);
- unsigned int resultingWidth = -1, resultingHeight = -1;
+ unsigned int resultingWidth = -1, resultingHeight = -1, resultingStride = -1;
// Test downscaling where the input size is an exact multiple of the desired size:
// (We expect a perfect result here)
- DownscaleInPlacePow2RGBA8888(pixels, 600, 600, 75, 75, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 600, 600, 600, 75, 75, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 75u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 75u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
- DownscaleInPlacePow2RGBA8888(pixels, 512, 512, 16, 16, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 512, 512, 512, 16, 16, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 16u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 16u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
- DownscaleInPlacePow2RGBA8888(pixels, 512, 64, 16, 2, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 512, 64, 512, 16, 2, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 16u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 2u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
- DownscaleInPlacePow2RGBA8888(pixels, 64, 1024, 4, 64, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 64, 1024, 64, 4, 64, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 4u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 64u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
// Test downscaling where the input size is slightly off being an exact multiple of the desired size:
// (We expect a perfect match at the end because of rounding-down to an even width and height at each step)
- DownscaleInPlacePow2RGBA8888(pixels, 601, 603, 75, 75, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 601, 603, 601, 75, 75, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 75u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 75u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
- DownscaleInPlacePow2RGBA8888(pixels, 736 + 1, 352 + 3, 23, 11, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 736 + 1, 352 + 3, 736 + 1, 23, 11, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 23u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 11u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
- DownscaleInPlacePow2RGBA8888(pixels, 384 + 3, 896 + 1, 3, 7, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 384 + 3, 896 + 1, 384 + 3, 3, 7, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 3u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 7u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
// Test downscales with source dimensions which are under a nice power of two by one:
// The target is hit exactly due to losing spare columns or rows at each iteration:
- DownscaleInPlacePow2RGBA8888(pixels, 63, 31, 7, 3, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 63, 31, 63, 7, 3, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 7u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 3u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
// Asking to downscale a bit smaller should stop at the dimensions of the last test as one more halving would go down to 3 x 1, which is too small.
- DownscaleInPlacePow2RGBA8888(pixels, 63, 31, 4, 2, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 63, 31, 63, 4, 2, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 7u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 3u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
// Should stop at almost twice the requested dimensions:
- DownscaleInPlacePow2RGBA8888(pixels, 15, 127, 4, 32, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 15, 127, 15, 4, 32, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 7u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 63u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
// Test downscales to 1 in one or both dimensions:
// Parameters: input-x input-y, desired-x, desired-y, expected-x, expected-y
}
const uint32_t imageHash = HashPixels(image, numPixels);
unsigned char* const pixels = reinterpret_cast<unsigned char*>(image);
- unsigned int resultingWidth = -1, resultingHeight = -1;
+ unsigned int resultingWidth = -1, resultingHeight = -1, resultingStride = -1;
// Test downscales to the same size:
// The point is just to be sure the downscale is a NOP in this case:
- DownscaleInPlacePow2RGBA8888(pixels, 600, 600, 600, 600, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 600, 600, 600, 600, 600, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 600u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 600u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
- DownscaleInPlacePow2RGBA8888(pixels, 512, 128, 512, 128, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 512, 128, 512, 512, 128, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 512u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 128u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
- DownscaleInPlacePow2RGBA8888(pixels, 17, 1001, 17, 1001, BoxDimensionTestBoth, resultingWidth, resultingHeight);
+ DownscaleInPlacePow2RGBA8888(pixels, 17, 1001, 17, 17, 1001, BoxDimensionTestBoth, resultingWidth, resultingHeight, resultingStride);
DALI_TEST_EQUALS(resultingWidth, 17u, TEST_LOCATION);
DALI_TEST_EQUALS(resultingHeight, 1001u, TEST_LOCATION);
+ DALI_TEST_EQUALS(resultingStride, resultingWidth, TEST_LOCATION);
// Test downscales that request a larger size (we never upscale so these are NOPs too):
// Parameters: input-x input-y, desired-x, desired-y, expected-x, expected-y
int UtcDaliImageOperationsDownscaleInPlacePow2RGB565(void)
{
// Test that calling with null and zero parameters doesn't blow up:
- unsigned int outWidth, outHeight;
- DownscaleInPlacePow2RGB565(0, 0, 0, 0, 0, BoxDimensionTestBoth, outWidth, outHeight);
+ unsigned int outWidth, outHeight, outStride;
+ DownscaleInPlacePow2RGB565(0, 0, 0, 0, 0, 0, BoxDimensionTestBoth, outWidth, outHeight, outStride);
uint16_t image[608 * 608];
for(unsigned i = 0; i < sizeof(image) / sizeof(image[0]); ++i)
int UtcDaliImageOperationsDownscaleInPlacePow2ComponentPair(void)
{
// Simple test that a null pointer does not get dereferenced in the function:
- unsigned int outWidth, outHeight;
- DownscaleInPlacePow2ComponentPair(0, 0, 0, 0, 0, BoxDimensionTestBoth, outWidth, outHeight);
+ unsigned int outWidth, outHeight, outStride;
+ DownscaleInPlacePow2ComponentPair(0, 0, 0, 0, 0, 0, BoxDimensionTestBoth, outWidth, outHeight, outStride);
// Simple tests of dimensions output:
int UtcDaliImageOperationsDownscaleInPlacePow2SingleBytePerPixel(void)
{
// Simple test that a null pointer does not get dereferenced in the function:
- unsigned int outWidth, outHeight;
- DownscaleInPlacePow2SingleBytePerPixel(0, 0, 0, 0, 0, BoxDimensionTestBoth, outWidth, outHeight);
+ unsigned int outWidth, outHeight, outStride;
+ DownscaleInPlacePow2SingleBytePerPixel(0, 0, 0, 0, 0, 0, BoxDimensionTestBoth, outWidth, outHeight, outStride);
// Tests of output dimensions from downscaling:
uint8_t image[608 * 608];
/**
* @brief Test the function for averaging vertically-adjacent pairs of single-byte-per-pixel pixels on a scanline.
*/
+int UtcDaliImageOperationsAverageScanlines1ExceptTest(void)
+{
+ // Edge cases for averagescanlines1:
+ unsigned char shortEven1[] = {0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x02, 0x03, 0x00, 0x01};
+ unsigned char shortEven2[] = {0x00, 0xff, 0x00, 0xff, 0x01, 0x01, 0xff, 0xfe, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x03, 0x02};
+ unsigned char expectBuffer[] = {0x00, 0x7f, 0x7f, 0xff, 0x80, 0x7f, 0x80, 0x7f, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+ unsigned char outputBuffer[sizeof(shortEven1)];
+
+ AverageScanlines1(shortEven1, shortEven2, outputBuffer, sizeof(shortEven1));
+ for(unsigned i = 0; i < sizeof(shortEven1); ++i)
+ {
+ DALI_TEST_EQUALS(unsigned(outputBuffer[i]), unsigned(expectBuffer[i]), TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+/**
+ * @brief Test the function for averaging vertically-adjacent pairs of single-byte-per-pixel pixels on a scanline.
+ */
int UtcDaliImageOperationsAverageScanlines1(void)
{
// Red and cyan, averaging to grey:
}
// Longer test reusing RGBA setup/test logic:
- const size_t scanlineLength = 4096u;
- Dali::Vector<uint32_t> scanline1;
- Dali::Vector<uint32_t> scanline2;
- Dali::Vector<uint32_t> reference;
- Dali::Vector<uint32_t> output;
- SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+ {
+ const size_t scanlineLength = 4096u;
+ Dali::Vector<uint32_t> scanline1;
+ Dali::Vector<uint32_t> scanline2;
+ Dali::Vector<uint32_t> reference;
+ Dali::Vector<uint32_t> output;
+ SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+ AverageScanlines1((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4);
+
+ // Check the output matches the independently generated reference:
+ size_t numMatches = 0;
+ MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+ DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+ }
- AverageScanlines1((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4);
+ // Longer test reusing RGBA setup/test logic with none-8-divisable length
+ {
+ const size_t scanlineLength = 1003u;
+ Dali::Vector<uint32_t> scanline1;
+ Dali::Vector<uint32_t> scanline2;
+ Dali::Vector<uint32_t> reference;
+ Dali::Vector<uint32_t> output;
+ SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+ AverageScanlines1((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4);
+
+ // Check the output matches the independently generated reference:
+ size_t numMatches = 0;
+ MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+ DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+ }
- // Check the output matches the independently generated reference:
- size_t numMatches = 0;
- MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
- DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+ // Very short test reusing RGBA setup/test logic with less-than-8 length
+ {
+ const size_t scanlineLength = 1003u;
+ Dali::Vector<uint32_t> scanline1;
+ Dali::Vector<uint32_t> scanline2;
+ Dali::Vector<uint32_t> reference;
+ Dali::Vector<uint32_t> output;
+ SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+ AverageScanlines1((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4);
+
+ // Check the output matches the independently generated reference:
+ size_t numMatches = 0;
+ MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+ DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+ }
END_TEST;
}
}
// Longer test reusing RGBA setup/test logic:
- const size_t scanlineLength = 4096u;
- Dali::Vector<uint32_t> scanline1;
- Dali::Vector<uint32_t> scanline2;
- Dali::Vector<uint32_t> reference;
- Dali::Vector<uint32_t> output;
- SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+ {
+ const size_t scanlineLength = 4096u;
+ Dali::Vector<uint32_t> scanline1;
+ Dali::Vector<uint32_t> scanline2;
+ Dali::Vector<uint32_t> reference;
+ Dali::Vector<uint32_t> output;
+ SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+ AverageScanlines2((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 2);
+
+ // Check the output matches the independently generated reference:
+ size_t numMatches = 0;
+ MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+ DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+ }
- AverageScanlines2((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 2);
+ // Longer test reusing RGBA setup/test logic with none-8-divisable length
+ {
+ const size_t scanlineLength = 501u;
+ Dali::Vector<uint32_t> scanline1;
+ Dali::Vector<uint32_t> scanline2;
+ Dali::Vector<uint32_t> reference;
+ Dali::Vector<uint32_t> output;
+ SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+ AverageScanlines2((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 2);
+
+ // Check the output matches the independently generated reference:
+ size_t numMatches = 0;
+ MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+ DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+ }
- // Check the output matches the independently generated reference:
- size_t numMatches = 0;
- MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
- DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+ // Very short test reusing RGBA setup/test logic with less-than-8 length
+ {
+ const size_t scanlineLength = 3u;
+ Dali::Vector<uint32_t> scanline1;
+ Dali::Vector<uint32_t> scanline2;
+ Dali::Vector<uint32_t> reference;
+ Dali::Vector<uint32_t> output;
+ SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+ AverageScanlines2((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 2);
+
+ // Check the output matches the independently generated reference:
+ size_t numMatches = 0;
+ MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+ DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+ }
END_TEST;
}
}
// Longer test reusing RGBA setup/test logic:
- const size_t scanlineLength = 3 * 4 * 90u;
- Dali::Vector<uint32_t> scanline1;
- Dali::Vector<uint32_t> scanline2;
- Dali::Vector<uint32_t> reference;
- Dali::Vector<uint32_t> output;
- SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+ {
+ const size_t scanlineLength = 3 * 4 * 90u;
+ Dali::Vector<uint32_t> scanline1;
+ Dali::Vector<uint32_t> scanline2;
+ Dali::Vector<uint32_t> reference;
+ Dali::Vector<uint32_t> output;
+ SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+ AverageScanlines3((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4 / 3);
+
+ // Check the output matches the independently generated reference:
+ size_t numMatches = 0;
+ MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+ DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+ }
- AverageScanlines3((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4 / 3);
+ // Longer test reusing RGBA setup/test logic with none-8-divisable length
+ {
+ const size_t scanlineLength = 3 * 501u;
+ Dali::Vector<uint32_t> scanline1;
+ Dali::Vector<uint32_t> scanline2;
+ Dali::Vector<uint32_t> reference;
+ Dali::Vector<uint32_t> output;
+ SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+ AverageScanlines3((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4 / 3);
+
+ // Check the output matches the independently generated reference:
+ size_t numMatches = 0;
+ MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+ DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+ }
- // Check the output matches the independently generated reference:
- size_t numMatches = 0;
- MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
- DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+ // Very short test reusing RGBA setup/test logic with less-than-8 length
+ {
+ const size_t scanlineLength = 3u;
+ Dali::Vector<uint32_t> scanline1;
+ Dali::Vector<uint32_t> scanline2;
+ Dali::Vector<uint32_t> reference;
+ Dali::Vector<uint32_t> output;
+ SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+ AverageScanlines3((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4 / 3);
+
+ // Check the output matches the independently generated reference:
+ size_t numMatches = 0;
+ MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+ DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+ }
END_TEST;
}
uint32_t outputImage[desiredWidth * desiredHeight];
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)&image->GetVector()[0], 256, 256, (unsigned char*)outputImage, desiredWidth, desiredHeight);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)&image->GetVector()[0], 256, 256, 256, (unsigned char*)outputImage, desiredWidth, desiredHeight);
DALI_TEST_EQUALS(outputImage[0], (uint32_t)0xff0000ff, TEST_LOCATION); // < Red corner pixel
DALI_TEST_EQUALS(outputImage[7], (uint32_t)0xff00ff00, TEST_LOCATION); // < Green corner pixel
buffer.resize(outputBufferSize);
uint32_t* outputImage = &buffer[0];
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, inputWidth, inputHeight, (unsigned char*)outputImage, desiredWidth, desiredHeight);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, inputWidth, inputHeight, inputWidth, (unsigned char*)outputImage, desiredWidth, desiredHeight);
// Check that all the output pixels are the right color:
const uint32_t reference = inputImage[inputWidth * inputHeight / 2];
// Try several different starting image sizes:
// 1x1 -> 1x1:
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 1, 1, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 1, 1, 1, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
DALI_TEST_EQUALS(outputImage, inputImage[0], TEST_LOCATION);
outputImage = 0;
// Single-pixel wide tall stripe:
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 1, 1024, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 1, 1024, 1, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
DALI_TEST_EQUALS(outputImage, inputImage[0], TEST_LOCATION);
outputImage = 0;
// Single-pixel tall, wide strip:
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 1024, 1, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 1024, 1, 1024, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
DALI_TEST_EQUALS(outputImage, inputImage[0], TEST_LOCATION);
outputImage = 0;
// Square mid-size image:
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 103, 103, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 103, 103, 103, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
DALI_TEST_EQUALS(outputImage, inputImage[0], TEST_LOCATION);
outputImage = 0;
// Wide mid-size image:
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 313, 79, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 313, 79, 313, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
DALI_TEST_EQUALS(outputImage, inputImage[0], TEST_LOCATION);
outputImage = 0;
// Tall mid-size image:
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 53, 467, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 53, 467, 53, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
DALI_TEST_EQUALS(outputImage, inputImage[0], TEST_LOCATION);
outputImage = 0;
// 0 x 0 input image (make sure output not written to):
outputImage = 0xDEADBEEF;
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 0, 0, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 0, 0, 0, (unsigned char*)&outputImage, desiredWidth, desiredHeight);
DALI_TEST_EQUALS(outputImage, (uint32_t)0xDEADBEEF, TEST_LOCATION);
outputImage = 0;
// Try several different starting image sizes:
// 1x1 -> 1x1:
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 1, 1, (unsigned char*)outputImage, 0, 0);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 1, 1, 1, (unsigned char*)outputImage, 0, 0);
DALI_TEST_EQUALS(0xaaaaaaaa, outputImage[0], TEST_LOCATION);
// Single-pixel wide tall stripe:
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 1, 102, (unsigned char*)outputImage, 0, 33);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 1, 102, 1, (unsigned char*)outputImage, 0, 33);
DALI_TEST_EQUALS(0xaaaaaaaa, outputImage[0], TEST_LOCATION);
// Single-pixel tall, wide strip:
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 102, 1, (unsigned char*)outputImage, 0, 67);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 102, 1, 102, (unsigned char*)outputImage, 0, 67);
DALI_TEST_EQUALS(0xaaaaaaaa, outputImage[0], TEST_LOCATION);
// Square mid-size image:
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 103, 103, (unsigned char*)outputImage, 21, 0);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 103, 103, 103, (unsigned char*)outputImage, 21, 0);
DALI_TEST_EQUALS(0xaaaaaaaa, outputImage[0], TEST_LOCATION);
// Wide mid-size image to 0 height
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 313, 79, (unsigned char*)outputImage, 99, 0);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 313, 79, 313, (unsigned char*)outputImage, 99, 0);
DALI_TEST_EQUALS(0xaaaaaaaa, outputImage[0], TEST_LOCATION);
// Tall mid-size image to 0 height, over width
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 53, 46, (unsigned char*)outputImage, 9999, 0);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 53, 46, 53, (unsigned char*)outputImage, 9999, 0);
DALI_TEST_EQUALS(0xaaaaaaaa, outputImage[0], TEST_LOCATION);
// 0 x 0 input image:
- Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 0, 0, (unsigned char*)outputImage, 200, 99);
+ Dali::Internal::Platform::PointSample4BPP((const unsigned char*)inputImage, 0, 0, 0, (unsigned char*)outputImage, 200, 99);
DALI_TEST_EQUALS(0xaaaaaaaa, outputImage[0], TEST_LOCATION);
END_TEST;
--- /dev/null
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <iostream>
+#include <string>
+
+#include <dali-test-suite-utils.h>
+#include <dali/internal/text/text-abstraction/plugin/lru-cache-container.h>
+
+using namespace Dali::TextAbstraction::Internal;
+using TestLRUCacheIntInt = LRUCacheContainer<int, int>;
+using TestLRUCacheIntString = LRUCacheContainer<int, std::string>;
+
+namespace
+{
+template<typename K, typename E>
+void TestLRUCacheExist(LRUCacheContainer<K, E>& cache, const K& key, bool expectExist, const char* location)
+{
+ auto iter = cache.Find(key);
+ DALI_TEST_EQUALS((iter != cache.End()), expectExist, location);
+}
+
+template<typename K, typename E>
+void TestLRUCachePop(LRUCacheContainer<K, E>& cache, const E& expectElement, const char* location)
+{
+ auto popElement = cache.Pop();
+ DALI_TEST_EQUALS(popElement, expectElement, location);
+}
+} // namespace
+
+void utc_dali_internal_lru_cache_container_startup(void)
+{
+ test_return_value = TET_UNDEF;
+}
+
+void utc_dali_internal_lru_cache_container_cleanup(void)
+{
+ test_return_value = TET_PASS;
+}
+
+int UtcDaliLRUCacheContainerPushPopTest(void)
+{
+ TestLRUCacheIntInt cache(3);
+
+ tet_infoline("Test LRUCache Push and Pop");
+
+ DALI_TEST_EQUALS(cache.IsEmpty(), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(cache.IsFull(), false, TEST_LOCATION);
+
+ cache.Push(1111, 111);
+ DALI_TEST_EQUALS(cache.IsEmpty(), false, TEST_LOCATION);
+
+ cache.Push(2222, 222);
+ cache.Push(3333, 333);
+ DALI_TEST_EQUALS(cache.IsFull(), true, TEST_LOCATION);
+
+ cache.Push(4444, 444);
+ DALI_TEST_EQUALS(cache.IsFull(), true, TEST_LOCATION);
+
+ TestLRUCacheExist(cache, 1111, false, TEST_LOCATION);
+ TestLRUCacheExist(cache, 2222, true, TEST_LOCATION);
+ TestLRUCacheExist(cache, 3333, true, TEST_LOCATION);
+ TestLRUCacheExist(cache, 4444, true, TEST_LOCATION);
+
+ TestLRUCachePop(cache, 222, TEST_LOCATION);
+ DALI_TEST_EQUALS(cache.IsFull(), false, TEST_LOCATION);
+
+ TestLRUCachePop(cache, 333, TEST_LOCATION);
+ DALI_TEST_EQUALS(cache.IsEmpty(), false, TEST_LOCATION);
+ DALI_TEST_EQUALS(cache.IsFull(), false, TEST_LOCATION);
+
+ cache.Push(5555, 555);
+ cache.Push(6666, 666);
+
+ // Replace exist key
+ cache.Push(5555, 777);
+ DALI_TEST_EQUALS(cache.IsFull(), true, TEST_LOCATION);
+
+ // Change element
+ DALI_TEST_EQUALS(cache.Get(5555), 777, TEST_LOCATION);
+ cache.Get(5555) = 888;
+ DALI_TEST_EQUALS(cache.Get(5555), 888, TEST_LOCATION);
+
+ TestLRUCachePop(cache, 444, TEST_LOCATION);
+
+ TestLRUCacheExist(cache, 2222, false, TEST_LOCATION);
+ TestLRUCacheExist(cache, 3333, false, TEST_LOCATION);
+ TestLRUCacheExist(cache, 4444, false, TEST_LOCATION);
+
+ TestLRUCachePop(cache, 666, TEST_LOCATION);
+ TestLRUCachePop(cache, 888, TEST_LOCATION);
+ DALI_TEST_EQUALS(cache.IsEmpty(), true, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliLRUCacheContainerPushPopTest2(void)
+{
+ TestLRUCacheIntString cache(3);
+
+ tet_infoline("Test LRUCache Push and Pop 2");
+
+ DALI_TEST_EQUALS(cache.IsEmpty(), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(cache.IsFull(), false, TEST_LOCATION);
+
+ cache.Push(1111, "111");
+ DALI_TEST_EQUALS(cache.IsEmpty(), false, TEST_LOCATION);
+
+ cache.Push(2222, "222");
+ cache.Push(3333, "333");
+ DALI_TEST_EQUALS(cache.IsFull(), true, TEST_LOCATION);
+
+ cache.Push(4444, "444");
+ DALI_TEST_EQUALS(cache.IsFull(), true, TEST_LOCATION);
+
+ TestLRUCacheExist(cache, 1111, false, TEST_LOCATION);
+ TestLRUCacheExist(cache, 2222, true, TEST_LOCATION);
+ TestLRUCacheExist(cache, 3333, true, TEST_LOCATION);
+ TestLRUCacheExist(cache, 4444, true, TEST_LOCATION);
+
+ TestLRUCachePop(cache, std::string("222"), TEST_LOCATION);
+ DALI_TEST_EQUALS(cache.IsFull(), false, TEST_LOCATION);
+
+ TestLRUCachePop(cache, std::string("333"), TEST_LOCATION);
+ DALI_TEST_EQUALS(cache.IsEmpty(), false, TEST_LOCATION);
+ DALI_TEST_EQUALS(cache.IsFull(), false, TEST_LOCATION);
+
+ cache.Push(5555, "555");
+ cache.Push(6666, "666");
+
+ // Replace exist key
+ cache.Push(5555, "777");
+ DALI_TEST_EQUALS(cache.IsFull(), true, TEST_LOCATION);
+
+ // Change element
+ DALI_TEST_EQUALS(cache.Get(5555), "777", TEST_LOCATION);
+ cache.Get(5555) = "888";
+ DALI_TEST_EQUALS(cache.Get(5555), "888", TEST_LOCATION);
+
+ TestLRUCachePop(cache, std::string("444"), TEST_LOCATION);
+
+ TestLRUCacheExist(cache, 2222, false, TEST_LOCATION);
+ TestLRUCacheExist(cache, 3333, false, TEST_LOCATION);
+ TestLRUCacheExist(cache, 4444, false, TEST_LOCATION);
+
+ TestLRUCachePop(cache, std::string("666"), TEST_LOCATION);
+ TestLRUCachePop(cache, std::string("888"), TEST_LOCATION);
+ DALI_TEST_EQUALS(cache.IsEmpty(), true, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliLRUCacheContainerPopEmptyNegative(void)
+{
+ TestLRUCacheIntInt cache(3);
+
+ tet_infoline("Test LRUCache Pop empty");
+
+ try
+ {
+ cache.Pop();
+ DALI_TEST_CHECK(false); // Should not get here
+ }
+ catch(...)
+ {
+ DALI_TEST_CHECK(true); // Asserted
+ }
+
+ END_TEST;
+}
+
+int UtcDaliLRUCacheContainerGetInvalidNegative(void)
+{
+ TestLRUCacheIntInt cache(3);
+
+ tet_infoline("Test LRUCache Get with invalid key");
+
+ cache.Push(111, 1);
+ cache.Push(222, 2);
+ cache.Push(333, 3);
+ cache.Push(444, 4);
+
+ try
+ {
+ cache.Get(111);
+ DALI_TEST_CHECK(false); // Should not get here
+ }
+ catch(...)
+ {
+ DALI_TEST_CHECK(true); // Asserted
+ }
+
+ END_TEST;
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-test-suite-utils.h>
+#include <stdlib.h>
+#include <iostream>
+
+#include <dali/internal/imaging/common/loader-wbmp.h>
+#include "image-loaders.h"
+
+using namespace Dali;
+
+namespace
+{
+static const LoadFunctions WbmpLoaders(TizenPlatform::LoadWbmpHeader, TizenPlatform::LoadBitmapFromWbmp);
+
+} // Unnamed namespace.
+
+int UtcDaliWbmpLoader(void)
+{
+ ImageDetails image(TEST_IMAGE_DIR "/test-image.wbmp", 32u, 64u);
+
+ TestImageLoading(image, WbmpLoaders);
+
+ END_TEST;
+}
\ No newline at end of file
mCore->ProcessEvents();
}
-void TestApplication::DoUpdate(uint32_t intervalMilliseconds, const char* location)
+void TestApplication::DoUpdate(uint32_t intervalMilliseconds, const char* location, bool uploadOnly)
{
if(GetUpdateStatus() == 0 &&
mRenderStatus.NeedsUpdate() == false &&
uint32_t nextVSyncTime = mLastVSyncTime + intervalMilliseconds;
float elapsedSeconds = static_cast<float>(intervalMilliseconds) * 0.001f;
- mCore->Update(elapsedSeconds, mLastVSyncTime, nextVSyncTime, mStatus, false, false);
+ mCore->Update(elapsedSeconds, mLastVSyncTime, nextVSyncTime, mStatus, false, false, uploadOnly);
GetRenderController().Initialize();
mLastVSyncTime = nextVSyncTime;
}
-bool TestApplication::Render(uint32_t intervalMilliseconds, const char* location)
+bool TestApplication::Render(uint32_t intervalMilliseconds, const char* location, bool uploadOnly)
{
- DoUpdate(intervalMilliseconds, location);
+ DoUpdate(intervalMilliseconds, location, uploadOnly);
// Reset the status
mRenderStatus.SetNeedsUpdate(false);
mRenderStatus.SetNeedsPostRender(false);
- mCore->PreRender(mRenderStatus, false /*do not force clear*/, false /*do not skip rendering*/);
- mCore->RenderScene(mRenderStatus, mScene, true /*render the off-screen buffers*/);
- mCore->RenderScene(mRenderStatus, mScene, false /*render the surface*/);
- mCore->PostRender(false /*do not skip rendering*/);
+ mCore->PreRender(mRenderStatus, false /*do not force clear*/);
+ if(!uploadOnly)
+ {
+ mCore->RenderScene(mRenderStatus, mScene, true /*render the off-screen buffers*/);
+ mCore->RenderScene(mRenderStatus, mScene, false /*render the surface*/);
+ }
+ mCore->PostRender();
mFrame++;
{
DoUpdate(intervalMilliseconds, location);
- mCore->PreRender(mRenderStatus, false /*do not force clear*/, false /*do not skip rendering*/);
+ mCore->PreRender(mRenderStatus, false /*do not force clear*/);
mCore->PreRender(mScene, damagedRects);
return mStatus.KeepUpdating() || mRenderStatus.NeedsUpdate();
{
mCore->RenderScene(mRenderStatus, mScene, true /*render the off-screen buffers*/);
mCore->RenderScene(mRenderStatus, mScene, false /*render the surface*/, clippingRect);
- mCore->PostRender(false /*do not skip rendering*/);
+ mCore->PostRender();
mFrame++;
bool TestApplication::RenderOnly()
{
// Update Time values
- mCore->PreRender(mRenderStatus, false /*do not force clear*/, false /*do not skip rendering*/);
+ mCore->PreRender(mRenderStatus, false /*do not force clear*/);
mCore->RenderScene(mRenderStatus, mScene, true /*render the off-screen buffers*/);
mCore->RenderScene(mRenderStatus, mScene, false /*render the surface*/);
- mCore->PostRender(false /*do not skip rendering*/);
+ mCore->PostRender();
mFrame++;
void ProcessEvent(const Integration::Event& event);
void SendNotification();
- bool Render(uint32_t intervalMilliseconds = DEFAULT_RENDER_INTERVAL, const char* location = NULL);
+ bool Render(uint32_t intervalMilliseconds = DEFAULT_RENDER_INTERVAL, const char* location = NULL, bool uploadOnly = false);
bool PreRenderWithPartialUpdate(uint32_t intervalMilliseconds, const char* location, std::vector<Rect<int>>& damagedRects);
bool RenderWithPartialUpdate(std::vector<Rect<int>>& damagedRects, Rect<int>& clippingRect);
uint32_t GetUpdateStatus();
}
private:
- void DoUpdate(uint32_t intervalMilliseconds, const char* location = NULL);
+ void DoUpdate(uint32_t intervalMilliseconds, const char* location = NULL, bool uploadOnly = false);
protected:
TestPlatformAbstraction mPlatformAbstraction;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{"uViewMatrix", GL_FLOAT_MAT4, 1},
{"uLightCameraProjectionMatrix", GL_FLOAT_MAT4, 1},
{"uLightCameraViewMatrix", GL_FLOAT_MAT4, 1}};
+
+ // WARNING: IF YOU CHANGE THIS LIST, ALSO CHANGE UNIFORMS IN test-graphics-reflection.cpp
}
void TestGlAbstraction::PreRender()
#define TEST_GL_ABSTRACTION_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
if(it2 == uniformIDs.end())
{
// Uniform not found, so add it...
- uniformIDs[name] = mLastUniformIdUsed++;
- return mLastUniformIdUsed;
+ uniformIDs[name] = ++mLastUniformIdUsed;
+ return uniformIDs[name];
}
return it2->second;
for(const auto& uniform : mCustomUniformData)
{
- GetUniformLocation(program, uniform.name.c_str());
+ auto iter = uniform.name.find("[");
+ auto name = uniform.name;
+ if(iter != std::string::npos)
+ {
+ name = uniform.name.substr(0, iter);
+ auto arrayCount = std::stoi(uniform.name.substr(iter + 1));
+ iter = uniform.name.find("]");
+ std::string suffix;
+ if(iter != std::string::npos && iter + 1 != uniform.name.length())
+ {
+ suffix = uniform.name.substr(iter + 1); // If there is a suffix, it means its an element of an array of struct
+ }
+
+ for(int i = 0; i < arrayCount; ++i)
+ {
+ std::stringstream nss;
+ nss << name << "[" << i << "]" << suffix;
+ GetUniformLocation(program, nss.str().c_str()); // Generate a GL loc per element
+ }
+ }
+ else
+ {
+ GetUniformLocation(program, name.c_str());
+ }
}
}
uint32_t nextVSyncTime = mLastVSyncTime + intervalMilliseconds;
float elapsedSeconds = static_cast<float>(intervalMilliseconds) * 0.001f;
- mCore->Update(elapsedSeconds, mLastVSyncTime, nextVSyncTime, mStatus, false, false);
+ mCore->Update(elapsedSeconds, mLastVSyncTime, nextVSyncTime, mStatus, false, false, false);
GetRenderController().Initialize();
mRenderStatus.SetNeedsUpdate(false);
mRenderStatus.SetNeedsPostRender(false);
- mCore->PreRender(mRenderStatus, false /*do not force clear*/, false /*do not skip rendering*/);
+ mCore->PreRender(mRenderStatus, false /*do not force clear*/);
mCore->RenderScene(mRenderStatus, mScene, true /*render the off-screen buffers*/);
mCore->RenderScene(mRenderStatus, mScene, false /*render the surface*/);
- mCore->PostRender(false /*do not skip rendering*/);
+ mCore->PostRender();
mFrame++;
{
DoUpdate(intervalMilliseconds, location);
- mCore->PreRender(mRenderStatus, false /*do not force clear*/, false /*do not skip rendering*/);
+ mCore->PreRender(mRenderStatus, false /*do not force clear*/);
mCore->PreRender(mScene, damagedRects);
return mStatus.KeepUpdating() || mRenderStatus.NeedsUpdate();
{
mCore->RenderScene(mRenderStatus, mScene, true /*render the off-screen buffers*/, clippingRect);
mCore->RenderScene(mRenderStatus, mScene, false /*render the surface*/, clippingRect);
- mCore->PostRender(false /*do not skip rendering*/);
+ mCore->PostRender();
mFrame++;
bool TestGraphicsApplication::RenderOnly()
{
// Update Time values
- mCore->PreRender(mRenderStatus, false /*do not force clear*/, false /*do not skip rendering*/);
+ mCore->PreRender(mRenderStatus, false /*do not force clear*/);
mCore->RenderScene(mRenderStatus, mScene, true /*render the off-screen buffers*/);
mCore->RenderScene(mRenderStatus, mScene, false /*render the surface*/);
- mCore->PostRender(false /*do not skip rendering*/);
+ mCore->PostRender();
mFrame++;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
mUsage(usage)
{
memory.resize(size);
- mGl.GetBufferTrace().EnableLogging(true);
+ mGl.GetBufferTrace().EnableLogging(false);
}
void TestGraphicsBuffer::Bind()
void TestGraphicsBuffer::BindAsUniformBuffer(const TestGraphicsProgram* program, const Dali::UniformBufferBindingDescriptor& uboBinding) const
{
- auto* reflection = static_cast<const TestGraphicsReflection*>(&program->GetReflection());
-
- Graphics::UniformBlockInfo uboInfo{};
- reflection->GetUniformBlock(0, uboInfo);
+ auto* reflection = static_cast<const TestGraphicsReflection*>(&program->GetReflection());
+ const auto& uboInfo = reflection->GetTestUniformBlock(0u);
auto offset = uboBinding.offset;
auto* data = memory.data() + offset;
for(const auto& member : uboInfo.members)
{
- auto type = reflection->GetMemberType(0, member.location);
- switch(type)
+ uint32_t numElements = member.numElements > 0 ? member.numElements : 1;
+
+ for(uint32_t i = 0; i < numElements; ++i)
{
- case Property::VECTOR4:
- {
- auto value = *reinterpret_cast<const Dali::Vector4*>(data + member.offset);
- mGl.Uniform4f(member.location, value.x, value.y, value.z, value.w);
- break;
- }
- case Property::VECTOR3:
- {
- auto value = *reinterpret_cast<const Dali::Vector3*>(data + member.offset);
- mGl.Uniform3f(member.location, value.x, value.y, value.z);
- break;
- }
- case Property::VECTOR2:
- {
- auto value = *reinterpret_cast<const Dali::Vector2*>(data + member.offset);
- mGl.Uniform2f(member.location, value.x, value.y);
- break;
- }
- case Property::FLOAT:
- {
- auto value = *reinterpret_cast<const float*>(data + member.offset);
- mGl.Uniform1f(member.location, value);
- break;
- }
- case Property::INTEGER:
- {
- auto ptr = reinterpret_cast<const GLint*>(data + member.offset);
- auto value = *ptr;
- mGl.Uniform1i(member.location, value);
- break;
- }
- case Property::MATRIX:
- {
- auto value = reinterpret_cast<const float*>(data + member.offset);
- mGl.UniformMatrix4fv(member.location, 1, GL_FALSE, value);
- break;
- }
- case Property::MATRIX3:
- {
- auto value = reinterpret_cast<const float*>(data + member.offset);
- mGl.UniformMatrix3fv(member.location, 1, GL_FALSE, value);
- break;
- }
- default:
+ switch(member.type)
{
- fprintf(stderr, "\n%s type not found\n", member.name.c_str());
+ case Property::VECTOR4:
+ {
+ auto value = *reinterpret_cast<const Dali::Vector4*>(data + member.offsets[i]);
+ mGl.Uniform4f(member.locations[i], value.x, value.y, value.z, value.w);
+ break;
+ }
+ case Property::VECTOR3:
+ {
+ auto value = *reinterpret_cast<const Dali::Vector3*>(data + member.offsets[i]);
+ mGl.Uniform3f(member.locations[i], value.x, value.y, value.z);
+ break;
+ }
+ case Property::VECTOR2:
+ {
+ auto value = *reinterpret_cast<const Dali::Vector2*>(data + member.offsets[i]);
+ mGl.Uniform2f(member.locations[i], value.x, value.y);
+ break;
+ }
+ case Property::FLOAT:
+ {
+ auto value = *reinterpret_cast<const float*>(data + member.offsets[i]);
+ mGl.Uniform1f(member.locations[i], value);
+ break;
+ }
+ case Property::INTEGER:
+ {
+ auto ptr = reinterpret_cast<const GLint*>(data + member.offsets[i]);
+ auto value = *ptr;
+ mGl.Uniform1i(member.locations[i], value);
+ break;
+ }
+ case Property::MATRIX:
+ {
+ auto value = reinterpret_cast<const float*>(data + member.offsets[i]);
+ mGl.UniformMatrix4fv(member.locations[i], 1, GL_FALSE, value);
+ break;
+ }
+ case Property::MATRIX3:
+ {
+ auto value = reinterpret_cast<const float*>(data + member.offsets[i]);
+ mGl.UniformMatrix3fv(member.locations[i], 1, GL_FALSE, value);
+ break;
+ }
+ default:
+ {
+ fprintf(stderr, "\n%s type not found\n", member.name.c_str());
+ }
}
}
}
#define DALI_TEST_GRAPHICS_COMMAND_BUFFER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
SET_DEPTH_COMPARE_OP = 1 << 24,
SET_DEPTH_TEST_ENABLE = 1 << 25,
SET_DEPTH_WRITE_ENABLE = 1 << 26,
+ DRAW_NATIVE = 1 << 27,
};
std::ostream& operator<<(std::ostream& os, Graphics::StencilOp op);
{
DRAW,
DRAW_INDEXED,
- DRAW_INDEXED_INDIRECT
+ DRAW_INDEXED_INDIRECT,
+ DRAW_NATIVE
};
Type type{}; ///< Type of the draw call
uint32_t drawCount;
uint32_t stride;
} drawIndexedIndirect;
+
+ struct
+ {
+ Graphics::DrawNativeInfo drawNativeInfo;
+ } drawNative;
};
};
data.bindUniformBuffers = rhs.data.bindUniformBuffers;
break;
}
+ case CommandType::DRAW_NATIVE:
+ {
+ data.draw.type = rhs.data.draw.type;
+ data.draw.drawNative = rhs.data.draw.drawNative;
+ break;
+ }
case CommandType::DRAW:
{
data.draw.type = rhs.data.draw.type;
data.bindPipeline = rhs.data.bindPipeline;
break;
}
+ case CommandType::DRAW_NATIVE:
+ {
+ data.draw.type = rhs.data.draw.type;
+ data.draw.drawNative = rhs.data.draw.drawNative;
+ break;
+ }
case CommandType::DRAW:
{
data.draw.type = rhs.data.draw.type;
{
}
- void BindVertexBuffers(uint32_t firstBinding,
- std::vector<const Graphics::Buffer*> buffers,
- std::vector<uint32_t> offsets) override
+ void BindVertexBuffers(uint32_t firstBinding,
+ const std::vector<const Graphics::Buffer*>& buffers,
+ const std::vector<uint32_t>& offsets) override
{
mCommands.emplace_back();
mCommands.back().type = CommandType::BIND_VERTEX_BUFFERS;
mCallStack.PushCall("BindPipeline", "");
}
- void BindTextures(std::vector<Graphics::TextureBinding>& textureBindings) override
+ void BindTextures(const std::vector<Graphics::TextureBinding>& textureBindings) override
{
mCommands.emplace_back();
mCommands.back().type = CommandType::BIND_TEXTURES;
mCallStack.PushCall("BindTextures", "");
}
- void BindSamplers(std::vector<Graphics::SamplerBinding>& samplerBindings) override
+ void BindSamplers(const std::vector<Graphics::SamplerBinding>& samplerBindings) override
{
mCommands.emplace_back();
mCommands.back().data.bindSamplers.samplerBindings = std::move(samplerBindings);
}
void BeginRenderPass(
- Graphics::RenderPass* renderPass,
- Graphics::RenderTarget* renderTarget,
- Graphics::Rect2D renderArea,
- std::vector<Graphics::ClearValue> clearValues) override
+ Graphics::RenderPass* renderPass,
+ Graphics::RenderTarget* renderTarget,
+ Graphics::Rect2D renderArea,
+ const std::vector<Graphics::ClearValue>& clearValues) override
{
mCommands.emplace_back(CommandType::BEGIN_RENDER_PASS);
auto& cmd = mCommands.back();
mCallStack.PushCall("ExecuteCommandBuffers", "");
}
+ void DrawNative(const Graphics::DrawNativeInfo* drawInfo)
+ {
+ mCommands.emplace_back();
+ mCommands.back().type = CommandType::DRAW_NATIVE;
+ auto& cmd = mCommands.back().data.draw;
+ cmd.type = DrawCallDescriptor::Type::DRAW_NATIVE;
+ cmd.drawNative.drawNativeInfo = *drawInfo;
+ mCallStack.PushCall("DrawNative", "");
+ }
+
void Draw(
uint32_t vertexCount,
uint32_t instanceCount,
};
TestGraphicsController::TestGraphicsController()
-: mCallStack(true, "TestGraphicsController."),
- mCommandBufferCallStack(true, "TestCommandBuffer."),
- mFrameBufferCallStack(true, "TestFrameBuffer.")
+: mCallStack(false, "TestGraphicsController."),
+ mCommandBufferCallStack(false, "TestCommandBuffer."),
+ mFrameBufferCallStack(false, "TestFrameBuffer.")
{
mCallStack.Enable(true);
mCommandBufferCallStack.Enable(true);
auto& trace = mGl.GetTextureTrace();
trace.Enable(true);
- trace.EnableLogging(true);
+ trace.EnableLogging(false);
}
void TestGraphicsController::SubmitCommandBuffers(const Graphics::SubmitInfo& submitInfo)
BindPipeline(currentPipeline);
break;
}
+ case CommandType::DRAW_NATIVE:
+ {
+ auto info = &cmd.data.draw.drawNative.drawNativeInfo;
+ CallbackBase::ExecuteReturn<bool>(*info->callback, info->userData);
+ break;
+ }
case CommandType::DRAW:
{
if(currentPipeline)
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
TestGraphicsProgramImpl::TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms)
: mGl(gl),
+ mId(gl.CreateProgram()),
mCreateInfo(createInfo),
- mReflection(gl, vertexFormats, createInfo, customUniforms)
+ mReflection(gl, mId, vertexFormats, createInfo, customUniforms)
{
- mId = mGl.CreateProgram();
-
// Ensure active sampler uniforms are set
mGl.SetCustomUniforms(customUniforms);
- mGl.LinkProgram(mId);
+
+ // Don't need to re-register uniforms in GL side - now done in creation of mReflection.
+ // Was previously done in mGl.LinkProgram(mId);
}
bool TestGraphicsProgramImpl::GetParameter(uint32_t parameterId, void* outData)
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
UniformData("uViewMatrix", Property::Type::MATRIX),
UniformData("uLightCameraProjectionMatrix", Property::Type::MATRIX),
UniformData("uLightCameraViewMatrix", Property::Type::MATRIX),
+
+ // WARNING: IF YOU CHANGE THIS LIST, ALSO CHANGE mActiveUniforms IN test-gl-abstraction, Initialize
};
+
+/**
+ * Helper function that returns size of uniform datatypes based
+ * on property type.
+ */
+constexpr int GetSizeForType(Property::Type type)
+{
+ switch(type)
+ {
+ case Property::Type::BOOLEAN:
+ {
+ return sizeof(bool);
+ }
+ case Property::Type::FLOAT:
+ {
+ return sizeof(float);
+ }
+ case Property::Type::INTEGER:
+ {
+ return sizeof(int);
+ }
+ case Property::Type::VECTOR2:
+ {
+ return sizeof(Vector2);
+ }
+ case Property::Type::VECTOR3:
+ {
+ return sizeof(Vector3);
+ }
+ case Property::Type::VECTOR4:
+ {
+ return sizeof(Vector4);
+ }
+ case Property::Type::MATRIX3:
+ {
+ return sizeof(Matrix3);
+ }
+ case Property::Type::MATRIX:
+ {
+ return sizeof(Matrix);
+ }
+ default:
+ {
+ return 0;
+ }
+ };
}
-TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, Property::Array& vfs, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms)
+} // namespace
+
+TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t programId, Property::Array& vfs, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms)
: mGl(gl),
mCustomUniforms(customUniforms)
{
mDefaultUniformBlock.name = "";
mDefaultUniformBlock.members = {};
mDefaultUniformBlock.binding = 0;
- mDefaultUniformBlock.size = 64 * (UNIFORMS.size() + mCustomUniforms.size());
mDefaultUniformBlock.descriptorSet = 0;
mDefaultUniformBlock.members.clear();
- int loc = 0;
+
+ int offset = 0;
for(const auto& data : UNIFORMS)
{
mDefaultUniformBlock.members.emplace_back();
- auto& item = mDefaultUniformBlock.members.back();
- item.name = data.name;
- item.binding = 0;
- item.offset = loc * 64;
- item.location = loc++;
+ auto& item = mDefaultUniformBlock.members.back();
+ item.name = data.name;
+ item.binding = 0;
+ item.offsets.push_back(offset);
+ item.locations.push_back(gl.GetUniformLocation(programId, data.name.c_str()));
item.bufferIndex = 0;
item.uniformClass = Graphics::UniformClass::UNIFORM;
+ item.type = data.type;
+ offset += GetSizeForType(data.type);
}
for(const auto& data : mCustomUniforms)
{
fprintf(stderr, "\ncustom uniforms: %s\n", data.name.c_str());
- mDefaultUniformBlock.members.emplace_back();
- auto& item = mDefaultUniformBlock.members.back();
- item.name = data.name;
- item.binding = 0;
- item.offset = loc * 64;
- item.location = loc++;
- item.bufferIndex = 0;
- item.uniformClass = Graphics::UniformClass::UNIFORM;
+
+ auto iter = data.name.find("[", 0);
+ int numElements = 1;
+ if(iter != std::string::npos)
+ {
+ auto baseName = data.name.substr(0, iter);
+ iter++;
+ numElements = std::stoi(data.name.substr(iter));
+ if(numElements == 0)
+ {
+ numElements = 1;
+ }
+ iter = data.name.find("]");
+ std::string suffix;
+ if(iter != std::string::npos && iter + 1 != data.name.length())
+ {
+ suffix = data.name.substr(iter + 1); // If there is a suffix, it means it is an element of an array of struct
+ }
+
+ if(!suffix.empty())
+ {
+ // Write multiple items
+ for(int i = 0; i < numElements; ++i)
+ {
+ std::stringstream elementNameStream;
+ elementNameStream << baseName << "[" << i << "]" << suffix;
+ mDefaultUniformBlock.members.emplace_back();
+ auto& item = mDefaultUniformBlock.members.back();
+ item.name = elementNameStream.str();
+ item.binding = 0;
+ item.offsets.push_back(offset);
+ item.locations.push_back(gl.GetUniformLocation(programId, elementNameStream.str().c_str()));
+ item.bufferIndex = 0;
+ item.uniformClass = Graphics::UniformClass::UNIFORM;
+ item.type = data.type;
+ offset += GetSizeForType(data.type);
+ }
+ }
+ else
+ {
+ // Write 1 item with multiple elements
+ mDefaultUniformBlock.members.emplace_back();
+ auto& item = mDefaultUniformBlock.members.back();
+
+ item.name = baseName;
+ item.binding = 0;
+ item.bufferIndex = 0;
+ item.uniformClass = Graphics::UniformClass::UNIFORM;
+ item.type = data.type;
+ item.numElements = numElements;
+
+ for(int i = 0; i < numElements; ++i)
+ {
+ std::stringstream elementNameStream;
+ elementNameStream << baseName << "[" << i << "]";
+ item.locations.push_back(gl.GetUniformLocation(programId, elementNameStream.str().c_str()));
+ item.offsets.push_back(offset);
+ offset += GetSizeForType(data.type);
+ }
+ }
+ }
+ else
+ {
+ // Write 1 item with 1 element
+ mDefaultUniformBlock.members.emplace_back();
+ auto& item = mDefaultUniformBlock.members.back();
+ item.name = data.name;
+ item.binding = 0;
+ item.offsets.push_back(offset);
+ item.locations.push_back(gl.GetUniformLocation(programId, item.name.c_str()));
+ item.bufferIndex = 0;
+ item.uniformClass = Graphics::UniformClass::UNIFORM;
+ item.type = data.type;
+ offset += GetSizeForType(data.type);
+ }
}
+ mDefaultUniformBlock.size = offset;
mUniformBlocks.push_back(mDefaultUniformBlock);
}
uint32_t TestGraphicsReflection::GetUniformBlockSize(uint32_t index) const
{
- // 64 bytes per uniform (64 = 4x4 matrix)
- // TODO: fix if array will be used
- return 64 * (UNIFORMS.size() + mCustomUniforms.size());
+ if(index >= mUniformBlocks.size())
+ {
+ return 0;
+ }
+
+ const auto& block = mUniformBlocks[index];
+ return block.size;
}
bool TestGraphicsReflection::GetUniformBlock(uint32_t index, Dali::Graphics::UniformBlockInfo& out) const
out.members[i].name = memberUniform.name;
out.members[i].binding = block.binding;
out.members[i].uniformClass = Graphics::UniformClass::UNIFORM;
- out.members[i].offset = memberUniform.offset;
- out.members[i].location = memberUniform.location;
+ out.members[i].offset = memberUniform.offsets[0];
+ out.members[i].location = memberUniform.locations[0];
}
return true;
{
if(blockIndex < mUniformBlocks.size() && memberLocation < mUniformBlocks[blockIndex].members.size())
{
- return mUniformBlocks[blockIndex].members[memberLocation].offset;
+ return mUniformBlocks[blockIndex].members[memberLocation].offsets[0];
}
else
{
return Graphics::ShaderLanguage::GLSL_3_1;
}
-Dali::Property::Type TestGraphicsReflection::GetMemberType(int blockIndex, int location) const
-{
- return location < static_cast<int>(UNIFORMS.size()) ? UNIFORMS[location].type : mCustomUniforms[location - UNIFORMS.size()].type;
-}
-
} // namespace Dali
#define DALI_TEST_GRAPHICS_REFLECTION_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
class TestGraphicsReflection : public Graphics::Reflection
{
public:
- TestGraphicsReflection(TestGlAbstraction& gl, Property::Array& vertexFormats, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms);
+ TestGraphicsReflection(TestGlAbstraction& gl, uint32_t program_id, Property::Array& vertexFormats, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms);
uint32_t GetVertexAttributeLocation(const std::string& name) const override;
Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeFormat(uint32_t location) const override;
Graphics::ShaderLanguage GetLanguage() const override;
public: // Test methods
+ struct TestUniformInfo
+ {
+ std::string name{""}; // baseName in the case of arrays
+ Graphics::UniformClass uniformClass{Graphics::UniformClass::UNDEFINED};
+ uint32_t binding{0u};
+ uint32_t bufferIndex{0u};
+ std::vector<uint32_t> offsets{};
+ std::vector<uint32_t> locations{};
+ uint32_t numElements{0u}; // 0 elements means this isn't an array; 1 element means this is an array of size 1
+ Property::Type type;
+ };
+
+ struct TestUniformBlockInfo
+ {
+ std::string name{""};
+ uint32_t descriptorSet{0u};
+ uint32_t binding{0u};
+ uint32_t size{0u};
+ std::vector<TestUniformInfo> members{};
+ };
+
void SetAttributes(std::vector<std::string> locations)
{
mAttributes.clear();
}
}
- Dali::Property::Type GetMemberType(int blockIndex, int location) const;
+ const TestUniformBlockInfo& GetTestUniformBlock(uint32_t index) const
+ {
+ return mUniformBlocks[index];
+ }
TestGlAbstraction& mGl;
mutable std::vector<std::string> mAttributes;
std::vector<UniformData> mCustomUniforms;
- Graphics::UniformBlockInfo mDefaultUniformBlock{}; ///< The emulated UBO containing all the standalone uniforms
- std::vector<Graphics::UniformBlockInfo> mUniformBlocks{}; ///< List of uniform blocks
+ TestUniformBlockInfo mDefaultUniformBlock{}; ///< The emulated UBO containing all the standalone uniforms
+ std::vector<TestUniformBlockInfo> mUniformBlocks{}; ///< List of uniform blocks
};
} // namespace Dali
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
if(!found)
{
- fprintf(stderr, "Search for %s(%s) failed\n", method.c_str(), params.c_str());
+ fprintf(stderr, "Search for %s() failed\n", method.c_str());
+ }
+ return found;
+}
+
+bool TraceCallStack::FindMethodAndGetParameters(std::string method, NamedParams& params) const
+{
+ bool found = false;
+ for(size_t i = 0; i < mCallStack.size(); i++)
+ {
+ if(0 == mCallStack[i].method.compare(method))
+ {
+ found = true;
+ params = mCallStack[i].namedParams;
+ break;
+ }
+ }
+ if(!found)
+ {
+ fprintf(stderr, "Search for %s() failed\n", method.c_str());
}
return found;
}
bool FindMethodAndGetParameters(std::string method, std::string& params) const;
/**
+ * Search for a method in the stack and return its parameters if found
+ * @param[in] method The name of the method
+ * @param[out] params of the method
+ * @return true if the method was in the stack
+ */
+ bool FindMethodAndGetParameters(std::string method, NamedParams& params) const;
+
+ /**
* Count how many times a method was called
* @param[in] method The name of the method
* @return The number of times it was called
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
*/
-#include <stdlib.h>
-#include <unistd.h>
-#include <dali/dali.h>
#include <dali-test-suite-utils.h>
+#include <dali/dali.h>
#include <dali/public-api/adaptor-framework/encoded-image-buffer.h>
+#include <stdlib.h>
+#include <unistd.h>
using namespace Dali;
EncodedImageBuffer buffer = EncodedImageBuffer::New(tinybuffer());
EncodedImageBuffer bufferCopy(buffer);
- DALI_TEST_EQUALS( (bool)bufferCopy, true, TEST_LOCATION );
+ DALI_TEST_EQUALS((bool)bufferCopy, true, TEST_LOCATION);
END_TEST;
}
EncodedImageBuffer buffer = EncodedImageBuffer::New(tinybuffer());
EncodedImageBuffer buffer2;
- DALI_TEST_EQUALS( (bool)buffer2, false, TEST_LOCATION );
+ DALI_TEST_EQUALS((bool)buffer2, false, TEST_LOCATION);
buffer2 = buffer;
- DALI_TEST_EQUALS( (bool)buffer2, true, TEST_LOCATION );
+ DALI_TEST_EQUALS((bool)buffer2, true, TEST_LOCATION);
END_TEST;
}
EncodedImageBuffer::RawBufferType::Iterator jter = getBuffer.Begin();
for(; iter != originBuffer.End(); ++iter, ++jter)
{
- DALI_TEST_EQUALS(*iter, *jter, TEST_LOCATION );
+ DALI_TEST_EQUALS(*iter, *jter, TEST_LOCATION);
}
END_TEST;
}
+
+int UtcDaliEncodedImageBufferGetHash(void)
+{
+ EncodedImageBuffer buffer1 = EncodedImageBuffer::New(tinybuffer());
+ EncodedImageBuffer buffer2 = EncodedImageBuffer::New(tinybuffer());
+ EncodedImageBuffer buffer3 = EncodedImageBuffer::New(EncodedImageBuffer::RawBufferType()); //< EmptyBuffer
+
+ tet_infoline("Test different encoded buffer with same data has same hash value.");
+ DALI_TEST_CHECK(buffer1 != buffer2);
+ DALI_TEST_CHECK(buffer1.GetHash() == buffer2.GetHash());
+
+ tet_infoline("Test hash with empty buffer.");
+ DALI_TEST_CHECK(buffer1.GetHash() != buffer3.GetHash());
+ DALI_TEST_CHECK(buffer2.GetHash() != buffer3.GetHash());
+
+ END_TEST;
+}
\ No newline at end of file
{
// test gif image, resolution: 100*100, 5 frames, delay: 1 second, disposal method: none
static const char* gGif_100_None = TEST_RESOURCE_DIR "/canvas-none.gif";
-// test gif image, resolution: 100*100, 5 frames, delay: 1 second, disposal method: none for first frame and previous for the rest
-static const char* gGif_100_Prev = TEST_RESOURCE_DIR "/canvas-prev.gif";
-// test gif image, resolution: 100*100, 5 frames, delay: 1 second, disposal method: background
-static const char* gGif_100_Bgnd = TEST_RESOURCE_DIR "/canvas-bgnd.gif";
// this image if not exist, for negative test
static const char* gGifNonExist = "non-exist.gif";
-void VerifyLoad(std::vector<Dali::PixelData>& pixelDataList, Dali::Vector<uint32_t>& frameDelayList, uint32_t frameCount, uint32_t width, uint32_t height, uint32_t delay)
-{
- DALI_TEST_EQUALS(pixelDataList.size(), frameCount, TEST_LOCATION);
- DALI_TEST_EQUALS(frameDelayList.Size(), frameCount, TEST_LOCATION);
-
- for(uint32_t idx = 0; idx < frameCount; idx++)
- {
- // Check the image size and delay of each frame
- DALI_TEST_EQUALS(pixelDataList[idx].GetWidth(), width, TEST_LOCATION);
- DALI_TEST_EQUALS(pixelDataList[idx].GetHeight(), height, TEST_LOCATION);
- DALI_TEST_EQUALS(frameDelayList[idx], delay, TEST_LOCATION);
- }
-}
} // namespace
void utc_dali_animated_image_loader_startup(void)
test_return_value = TET_PASS;
}
-int UtcDaliAnimatedImageLoadingP(void)
-{
- std::vector<Dali::PixelData> pixelDataList;
- Dali::Vector<uint32_t> frameDelayList;
-
- Dali::AnimatedImageLoading animatedImageLoading = Dali::AnimatedImageLoading::New(gGif_100_None, true);
- bool succeed = animatedImageLoading.LoadNextNFrames(0u, animatedImageLoading.GetImageCount(), pixelDataList);
- frameDelayList.Clear();
- frameDelayList.Resize(animatedImageLoading.GetImageCount(), 0);
- for(uint32_t i = 0; i < animatedImageLoading.GetImageCount(); ++i)
- {
- frameDelayList[i] = animatedImageLoading.GetFrameInterval(i);
- }
-
- // Check that the loading succeed
- DALI_TEST_CHECK(succeed);
- VerifyLoad(pixelDataList, frameDelayList, 5u, 100u, 100u, 1000u);
-
- pixelDataList.clear();
- animatedImageLoading = Dali::AnimatedImageLoading::New(gGif_100_Prev, true);
- succeed = animatedImageLoading.LoadNextNFrames(0u, animatedImageLoading.GetImageCount(), pixelDataList);
- frameDelayList.Clear();
- frameDelayList.Resize(animatedImageLoading.GetImageCount(), 0);
- for(uint32_t i = 0; i < animatedImageLoading.GetImageCount(); ++i)
- {
- frameDelayList[i] = animatedImageLoading.GetFrameInterval(i);
- }
-
- // Check that the loading succeed
- DALI_TEST_CHECK(succeed);
- VerifyLoad(pixelDataList, frameDelayList, 5u, 100u, 100u, 1000u);
-
- pixelDataList.clear();
- animatedImageLoading = Dali::AnimatedImageLoading::New(gGif_100_Bgnd, true);
- succeed = animatedImageLoading.LoadNextNFrames(0u, animatedImageLoading.GetImageCount(), pixelDataList);
- frameDelayList.Clear();
- frameDelayList.Resize(animatedImageLoading.GetImageCount(), 0);
- for(uint32_t i = 0; i < animatedImageLoading.GetImageCount(); ++i)
- {
- frameDelayList[i] = animatedImageLoading.GetFrameInterval(i);
- }
-
- // Check that the loading succeed
- DALI_TEST_CHECK(succeed);
- VerifyLoad(pixelDataList, frameDelayList, 5u, 100u, 100u, 1000u);
-
- END_TEST;
-}
-
-int UtcDaliAnimatedImageLoadingN(void)
-{
- std::vector<Dali::PixelData> pixelDataList;
- Dali::Vector<uint32_t> frameDelayList;
-
- Dali::AnimatedImageLoading animatedImageLoading = Dali::AnimatedImageLoading::New(gGifNonExist, true);
- bool succeed = animatedImageLoading.LoadNextNFrames(0u, animatedImageLoading.GetImageCount(), pixelDataList);
-
- // Check that the loading failed
- DALI_TEST_CHECK(!succeed);
-
- // Check that both pixelDataList and frameDelayList are empty
- DALI_TEST_EQUALS(pixelDataList.size(), 0u, TEST_LOCATION);
-
- END_TEST;
-}
-
int UtcDaliAnimatedImageLoadingGetImageSizeP(void)
{
Dali::AnimatedImageLoading animatedImageLoading = Dali::AnimatedImageLoading::New(gGif_100_None, true);
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// resolution: 2000*2560, pixel format: RGB888
const char* IMAGE_LARGE_EXIF3_RGB = TEST_RESOURCE_DIR "/f-large-exif-3.jpg";
+// resolution: 2048*2048, pixel format: RGB888, YUV420
+const char* IMAGE_LARGE_2048_YUV_420 = TEST_RESOURCE_DIR "/lake_front.jpg";
+
// resolution: 55*64, pixel format: RGB888
const char* IMAGE_WIDTH_ODD_EXIF1_RGB = TEST_RESOURCE_DIR "/f-odd-exif-1.jpg";
// resolution: 55*64, pixel format: RGB888
Dali::Vector<uint8_t> FileToMemory(const char* filename)
{
Dali::Vector<uint8_t> buffer;
- FILE *fp;
+ FILE* fp;
fp = fopen(filename, "rb");
if(fp != NULL)
{
END_TEST;
}
+
+int UtcDaliLoadImagePlanesFromFileP(void)
+{
+ std::vector<Devel::PixelBuffer> pixelBuffers;
+
+ Dali::LoadImagePlanesFromFile(IMAGE_LARGE_2048_YUV_420, pixelBuffers);
+ DALI_TEST_EQUALS(pixelBuffers.size(), 3, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixelBuffers[0].GetWidth(), 2048u, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixelBuffers[0].GetHeight(), 2048u, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixelBuffers[0].GetPixelFormat(), Pixel::L8, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixelBuffers[1].GetPixelFormat(), Pixel::CHROMINANCE_U, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixelBuffers[2].GetPixelFormat(), Pixel::CHROMINANCE_V, TEST_LOCATION);
+
+ pixelBuffers.clear();
+
+ // Test not supported image format: png
+ Dali::LoadImagePlanesFromFile(IMAGE_34_RGBA, pixelBuffers);
+ DALI_TEST_EQUALS(pixelBuffers.size(), 1, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixelBuffers[0].GetWidth(), 34u, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixelBuffers[0].GetHeight(), 34u, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixelBuffers[0].GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION);
+
+ pixelBuffers.clear();
+
+ // Test notsupported chrominace subsampling case
+ Dali::LoadImagePlanesFromFile(IMAGE_128_RGB, pixelBuffers);
+ DALI_TEST_EQUALS(pixelBuffers.size(), 1, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixelBuffers[0].GetWidth(), 128u, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixelBuffers[0].GetHeight(), 128u, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixelBuffers[0].GetPixelFormat(), Pixel::RGB888, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliLoadImagePlanesFromFileN(void)
+{
+ std::vector<Devel::PixelBuffer> pixelBuffers;
+
+ Dali::LoadImagePlanesFromFile(IMAGENONEXIST, pixelBuffers);
+ DALI_TEST_CHECK(pixelBuffers.empty());
+
+ END_TEST;
+}
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
DALI_TEST_CHECK(pixelData);
DALI_TEST_EQUALS(pixelData.GetWidth(), 10, TEST_LOCATION);
DALI_TEST_EQUALS(pixelData.GetHeight(), 10, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixelData.GetStride(), 10, TEST_LOCATION);
DALI_TEST_EQUALS(pixelData.GetPixelFormat(), Pixel::RGB565, TEST_LOCATION);
// Try drawing it
FillCheckerboard(pixbuf);
DALI_TEST_EQUALS(pixbuf.GetWidth(), 10, TEST_LOCATION);
+ DALI_TEST_EQUALS(pixbuf.GetStride(), 10, TEST_LOCATION);
END_TEST;
}
END_TEST;
}
+int UtcDaliWindowSetMaximumSizeN(void)
+{
+ try
+ {
+ Dali::Window instance;
+ Dali::Window::WindowSize size(100, 100);
+ DevelWindow::SetMaximumSize(instance, size);
+ DALI_TEST_CHECK(false); // Should not reach here!
+ }
+ catch(...)
+ {
+ DALI_TEST_CHECK(true);
+ }
+
+ END_TEST;
+}
+
int UtcDaliWindowMinimizeN(void)
{
try
END_TEST;
}
+int UtcDaliWindowSetMimimumSizeN(void)
+{
+ try
+ {
+ Dali::Window instance;
+ Dali::Window::WindowSize size(100, 100);
+ DevelWindow::SetMimimumSize(instance, size);
+ DALI_TEST_CHECK(false); // Should not reach here!
+ }
+ catch(...)
+ {
+ DALI_TEST_CHECK(true);
+ }
+
+ END_TEST;
+}
+
int UtcDaliWindowAddAvailableOrientationN(void)
{
Dali::Window window;
OPTION(ENABLE_PKG_CONFIGURE "Use pkgconfig" ON)
OPTION(ENABLE_LINK_TEST "Enable the link test" ON)
OPTION(ENABLE_ATSPI "Enable AT-SPI accessibility" ON)
+OPTION(ENABLE_TRACE "Enable Trace" OFF)
# Include additional macros
INCLUDE( common.cmake )
SET( ENABLE_ATSPI OFF )
ENDIF()
+IF( ENABLE_TRACE )
+ ADD_DEFINITIONS("-DTRACE_ENABLED")
+ENDIF()
+
# Set up compiler flags and warnings
IF( UNIX )
ADD_COMPILE_OPTIONS( -Wall ${DALI_CFLAGS} )# -Wextra -Wno-unused-parameter )# -Wfloat-equal )
MESSAGE( STATUS "Use pkg configure: ${ENABLE_PKG_CONFIGURE}" )
MESSAGE( STATUS "Enable link test: ${ENABLE_LINK_TEST}" )
MESSAGE( STATUS "Enable AT-SPI: ${ENABLE_ATSPI}" )
+MESSAGE( STATUS "Enable Trace: ${ENABLE_TRACE}" )
MESSAGE( STATUS "Tizen Platform Config supported ${TIZEN_PLATFORM_CONFIG_SUPPORTED_LOGMSG}")
MESSAGE( STATUS "Compile flags: ${CMAKE_CXX_FLAGS}")
MESSAGE( STATUS "Compile flags: ${CMAKE_C_FLAGS}")
CHECK_MODULE_AND_SET( APPFW_WATCH capi-appfw-watch-application watch_available )
CHECK_MODULE_AND_SET( APPCORE_WATCH appcore-watch [] )
-CHECK_MODULE_AND_SET( CAPI_APPFW_APPLICATION appcore-ui [] )
+CHECK_MODULE_AND_SET( CAPI_APPFW_APPLICATION app-core-ui-cpp [] )
CHECK_MODULE_AND_SET( CAPI_APPFW_WIDGET_BASE appcore-widget-base [] )
CHECK_MODULE_AND_SET( CAPI_APPFW_COMMON capi-appfw-app-common [] )
CHECK_MODULE_AND_SET( CAPI_APPFW_CONTROL capi-appfw-app-control [] )
${ECORE_IMF_CFLAGS}
${FRIBIDI_CFLAGS}
${COMPONENT_BASED_CORE_BASE_CFLAGS}
+ ${SCREENCONNECTORPROVIDER_CFLAGS}
)
SET( DALI_LDFLAGS ${DALI_LDFLAGS}
${ECORE_IMF_LDFLAGS}
${FRIBIDI_LDFLAGS}
${COMPONENT_BASED_CORE_BASE_LDFLAGS}
+ ${SCREENCONNECTORPROVIDER_LDFLAGS}
)
ELSE()
SET( DALI_CFLAGS ${DALI_CFLAGS}
SET( DALI_CFLAGS ${DALI_CFLAGS}
${HAPTIC_CFLAGS}
${EFL_ASSIST_CFLAGS}
- ${SCREENCONNECTORPROVIDER_CFLAGS}
${APPCORE_WATCH_CFLAGS}
)
SET( DALI_LDFLAGS ${DALI_LDFLAGS}
- ${SCREENCONNECTORPROVIDER_LDFLAGS}
${APPCORE_WATCH_LDFLAGS}
)
ENDIF()
// Constructors
/**
- * @brief Constructs a new BitSet with all bits set to 0.
- *
- * Equivalent to the pseudocode:
- * @code
- * for(i = 0; i < max; ++i) bits[i] = 0;
- * @endcode
+ * @brief Constructor.
*/
- BitSet()
- {
- std::fill(mData.begin(), mData.end(), 0u);
- }
+ BitSet() = default;
BitSet(const BitSet&) = default;
/**
* @brief Constructs a new BitSet with all bits initialized with bits from the specified integer.
*
+ * This constructor is only available for BitSets with 32-bit capacity. Equivalent to the pseudocode:
+ * @code
+ * for(i = 0; i < 32; ++i) bits[i] = (data >> i) & 0x1;
+ * @endcode
+ *
+ * @param data 32-bit integer with the initial values.
+ */
+ template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 1u)>>
+ explicit BitSet(std::uint32_t data)
+ {
+ mData[0] = data;
+ }
+
+ /**
+ * @brief Constructs a new BitSet with all bits initialized with bits from the specified integer.
+ *
* This constructor is only available for BitSets with 64-bit capacity. Equivalent to the pseudocode:
* @code
* for(i = 0; i < 64; ++i) bits[i] = (data >> i) & 0x1;
/**
* @brief Obtains a copy of the internal storage serialized as a single integer.
*
+ * This method is only available for BitSets with 32-bit capacity.
+ *
+ * @return A copy of the internal storage.
+ *
+ * @see BitSet::BitSet(std::uint32_t)
+ */
+ template<std::size_t I = N, typename = std::enable_if_t<(I == N && N == 1u)>>
+ std::uint32_t GetRawData32() const
+ {
+ return mData[0];
+ }
+
+ /**
+ * @brief Obtains a copy of the internal storage serialized as a single integer.
+ *
* This method is only available for BitSets with 64-bit capacity.
*
* @return A copy of the internal storage.
return result;
}
- ArrayType mData;
+ ArrayType mData{};
};
/**
// Operators
/**
+ * @copydoc BitSet::operator~() const
+ */
+ EnumBitSet operator~() const
+ {
+ return BitSet<N>::operator~();
+ }
+
+ /**
+ * @copydoc BitSet::operator|(const BitSet&) const
+ */
+ EnumBitSet operator|(const EnumBitSet& other) const
+ {
+ return BitSet<N>::operator|(other);
+ }
+
+ /**
+ * @copydoc BitSet::operator&(const BitSet&) const
+ */
+ EnumBitSet operator&(const EnumBitSet& other) const
+ {
+ return BitSet<N>::operator&(other);
+ }
+
+ /**
+ * @copydoc BitSet::operator^(const BitSet&) const
+ */
+ EnumBitSet operator^(const EnumBitSet& other) const
+ {
+ return BitSet<N>::operator^(other);
+ }
+
+ /**
* @copydoc BitSet::operator[](IndexType) const
*/
bool operator[](Enum index) const
}
private:
+ // For operators '~|&^'
+ EnumBitSet(BitSet<N>&& bitSet)
+ : BitSet<N>(bitSet)
+ {
+ }
+
// No data members (non-virtual destructor)
};
virtual void SetApplicationName(std::string name) = 0;
/**
+ * @brief Sets the name of the GUI toolkit that AT-SPI clients can query.
+ *
+ * The default name is "dali".
+ *
+ * @param toolkitName The toolkit name
+ */
+ virtual void SetToolkitName(std::string_view toolkitName) = 0;
+
+ /**
* @brief Gets object being root of accessibility tree.
*
* @return handler to accessibility object
virtual void EmitMovedOutOfScreen(Accessible* obj, ScreenRelativeMoveType type) = 0;
/**
+ * @brief Emits "org.a11y.atspi.Socket.Available" event on AT-SPI bus.
+ *
+ * @param obj Accessible object
+ */
+ virtual void EmitSocketAvailable(Accessible* obj) = 0;
+
+ /**
* @brief Emits state-changed event on at-spi bus.
*
* @param[in] obj The accessible object
virtual bool IsEnabled() = 0;
/**
+ * @brief Calls socket.Embed(plug) via D-Bus.
+ *
+ * @param[in] plug The plug
+ * @param[in] socket The socket
+ *
+ * @return Address returned by the D-Bus call.
+ *
+ * @note Remote object pointed to by 'socket' must implement 'org.a11y.atspi.Socket'.
+ * @see UnembedSocket()
+ */
+ virtual Address EmbedSocket(const Address& plug, const Address& socket) = 0;
+
+ /**
+ * @brief Calls socket.Embedded(plug) via D-Bus.
+ *
+ * The "Embedded" D-Bus method is an ATK extension.
+ * See 'impl_Embedded' in AT_SPI2_ATK/atk-adaptor/adaptors/socket-adaptor.c for more information.
+ *
+ * @param[in] plug The plug
+ * @param[in] socket The socket
+ */
+ virtual void EmbedAtkSocket(const Address& plug, const Address& socket) = 0;
+
+ /**
+ * @brief Calls socket.Unmbed(plug) via D-Bus.
+ *
+ * @param[in] plug The plug
+ * @param[in] socket The socket
+ *
+ * @note Remote object pointed to by 'socket' must implement 'org.a11y.atspi.Socket'.
+ * @see EmbedSocket()
+ */
+ virtual void UnembedSocket(const Address& plug, const Address& socket) = 0;
+
+ /**
* @brief Returns instance of bridge singleton object.
*
* @return The current bridge object
return mDisabledSignal;
}
+ static Signal<void()>& ScreenReaderEnabledSignal()
+ {
+ return mScreenReaderEnabledSignal;
+ }
+
+ static Signal<void()>& ScreenReaderDisabledSignal()
+ {
+ return mScreenReaderDisabledSignal;
+ }
+
protected:
struct Data
{
inline static Signal<void()> mEnabledSignal;
inline static Signal<void()> mDisabledSignal;
+ inline static Signal<void()> mScreenReaderEnabledSignal;
+ inline static Signal<void()> mScreenReaderDisabledSignal;
/**
* @brief Registers accessible object to be known in bridge object.
/*
- * Copyright 2020 Samsung Electronics Co., Ltd
+ * Copyright 2022 Samsung Electronics Co., Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <dali/devel-api/adaptor-framework/proxy-accessible.h>
#include <dali/devel-api/adaptor-framework/window-devel.h>
#include <dali/devel-api/atspi-interfaces/accessible.h>
+#include <dali/devel-api/atspi-interfaces/action.h>
+#include <dali/devel-api/atspi-interfaces/application.h>
#include <dali/devel-api/atspi-interfaces/collection.h>
#include <dali/devel-api/atspi-interfaces/component.h>
+#include <dali/devel-api/atspi-interfaces/editable-text.h>
+#include <dali/devel-api/atspi-interfaces/hyperlink.h>
+#include <dali/devel-api/atspi-interfaces/hypertext.h>
+#include <dali/devel-api/atspi-interfaces/selection.h>
+#include <dali/devel-api/atspi-interfaces/socket.h>
+#include <dali/devel-api/atspi-interfaces/text.h>
+#include <dali/devel-api/atspi-interfaces/value.h>
#include <dali/internal/adaptor/common/adaptor-impl.h>
+#include <dali/internal/window-system/common/window-impl.h>
#include <dali/public-api/dali-adaptor-common.h>
using namespace Dali::Accessibility;
return std::string{it->second};
}
+AtspiInterfaces Accessible::GetInterfaces() const
+{
+ if(!mInterfaces)
+ {
+ mInterfaces = DoGetInterfaces();
+ DALI_ASSERT_DEBUG(mInterfaces); // There has to be at least AtspiInterface::ACCESSIBLE
+ }
+
+ return mInterfaces;
+}
+
+std::vector<std::string> Accessible::GetInterfacesAsStrings() const
+{
+ std::vector<std::string> ret;
+ AtspiInterfaces interfaces = GetInterfaces();
+
+ for(std::size_t i = 0u; i < static_cast<std::size_t>(AtspiInterface::MAX_COUNT); ++i)
+ {
+ auto interface = static_cast<AtspiInterface>(i);
+
+ if(interfaces[interface])
+ {
+ auto name = GetInterfaceName(interface);
+
+ DALI_ASSERT_DEBUG(!name.empty());
+ ret.emplace_back(std::move(name));
+ }
+ }
+
+ return ret;
+}
+
+AtspiInterfaces Accessible::DoGetInterfaces() const
+{
+ AtspiInterfaces interfaces;
+
+ interfaces[AtspiInterface::ACCESSIBLE] = true;
+ interfaces[AtspiInterface::ACTION] = dynamic_cast<const Action*>(this);
+ interfaces[AtspiInterface::APPLICATION] = dynamic_cast<const Application*>(this);
+ interfaces[AtspiInterface::COLLECTION] = dynamic_cast<const Collection*>(this);
+ interfaces[AtspiInterface::COMPONENT] = dynamic_cast<const Component*>(this);
+ interfaces[AtspiInterface::EDITABLE_TEXT] = dynamic_cast<const EditableText*>(this);
+ interfaces[AtspiInterface::HYPERLINK] = dynamic_cast<const Hyperlink*>(this);
+ interfaces[AtspiInterface::HYPERTEXT] = dynamic_cast<const Hypertext*>(this);
+ interfaces[AtspiInterface::SELECTION] = dynamic_cast<const Selection*>(this);
+ interfaces[AtspiInterface::SOCKET] = dynamic_cast<const Socket*>(this);
+ interfaces[AtspiInterface::TEXT] = dynamic_cast<const Text*>(this);
+ interfaces[AtspiInterface::VALUE] = dynamic_cast<const Value*>(this);
+
+ return interfaces;
+}
+
+std::string Accessible::GetInterfaceName(AtspiInterface interface)
+{
+ static const std::unordered_map<AtspiInterface, std::string_view> interfaceMap{
+ {AtspiInterface::ACCESSIBLE, "org.a11y.atspi.Accessible"},
+ {AtspiInterface::ACTION, "org.a11y.atspi.Action"},
+ {AtspiInterface::APPLICATION, "org.a11y.atspi.Application"},
+ {AtspiInterface::CACHE, "org.a11y.atspi.Cache"},
+ {AtspiInterface::COLLECTION, "org.a11y.atspi.Collection"},
+ {AtspiInterface::COMPONENT, "org.a11y.atspi.Component"},
+ {AtspiInterface::DEVICE_EVENT_CONTROLLER, "org.a11y.atspi.DeviceEventController"},
+ {AtspiInterface::DEVICE_EVENT_LISTENER, "org.a11y.atspi.DeviceEventListener"},
+ {AtspiInterface::DOCUMENT, "org.a11y.atspi.Document"},
+ {AtspiInterface::EDITABLE_TEXT, "org.a11y.atspi.EditableText"},
+ {AtspiInterface::EVENT_DOCUMENT, "org.a11y.atspi.Event.Document"},
+ {AtspiInterface::EVENT_FOCUS, "org.a11y.atspi.Event.Focus"},
+ {AtspiInterface::EVENT_KEYBOARD, "org.a11y.atspi.Event.Keyboard"},
+ {AtspiInterface::EVENT_MOUSE, "org.a11y.atspi.Event.Mouse"},
+ {AtspiInterface::EVENT_OBJECT, "org.a11y.atspi.Event.Object"},
+ {AtspiInterface::EVENT_TERMINAL, "org.a11y.atspi.Event.Terminal"},
+ {AtspiInterface::EVENT_WINDOW, "org.a11y.atspi.Event.Window"},
+ {AtspiInterface::HYPERLINK, "org.a11y.atspi.Hyperlink"},
+ {AtspiInterface::HYPERTEXT, "org.a11y.atspi.Hypertext"},
+ {AtspiInterface::IMAGE, "org.a11y.atspi.Image"},
+ {AtspiInterface::REGISTRY, "org.a11y.atspi.Registry"},
+ {AtspiInterface::SELECTION, "org.a11y.atspi.Selection"},
+ {AtspiInterface::SOCKET, "org.a11y.atspi.Socket"},
+ {AtspiInterface::TABLE, "org.a11y.atspi.Table"},
+ {AtspiInterface::TABLE_CELL, "org.a11y.atspi.TableCell"},
+ {AtspiInterface::TEXT, "org.a11y.atspi.Text"},
+ {AtspiInterface::VALUE, "org.a11y.atspi.Value"},
+ };
+
+ auto it = interfaceMap.find(interface);
+
+ if(it == interfaceMap.end())
+ {
+ return {};
+ }
+
+ return std::string{it->second};
+}
+
Dali::Actor Accessible::GetCurrentlyHighlightedActor()
{
return IsUp() ? Bridge::GetCurrentBridge()->mData->mCurrentlyHighlightedActor : Dali::Actor{};
bool GrabHighlight() override
{
- return false;
+ if(!IsUp())
+ {
+ return false;
+ }
+
+ // Only window accessible is able to grab and clear highlight
+ if(!mRoot)
+ {
+ return false;
+ }
+
+ auto self = Self();
+ auto oldHighlightedActor = GetCurrentlyHighlightedActor();
+ if(self == oldHighlightedActor)
+ {
+ return true;
+ }
+
+ // Clear the old highlight.
+ if(oldHighlightedActor)
+ {
+ auto oldHighlightedObject = Dali::Accessibility::Component::DownCast(Accessible::Get(oldHighlightedActor));
+ if(oldHighlightedObject)
+ {
+ oldHighlightedObject->ClearHighlight();
+ }
+ }
+
+ SetCurrentlyHighlightedActor(self);
+
+ auto window = Dali::DevelWindow::Get(self);
+ Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
+ windowImpl.EmitAccessibilityHighlightSignal(true);
+
+ return true;
}
bool ClearHighlight() override
{
- return false;
+ if(!IsUp())
+ {
+ return false;
+ }
+
+ // Only window accessible is able to grab and clear highlight
+ if(!mRoot)
+ {
+ return false;
+ }
+
+ auto self = Self();
+ if(self != GetCurrentlyHighlightedActor())
+ {
+ return false;
+ }
+
+ SetCurrentlyHighlightedActor({});
+
+ auto window = Dali::DevelWindow::Get(self);
+ Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
+ windowImpl.EmitAccessibilityHighlightSignal(false);
+
+ return true;
}
Role GetRole() const override
Attributes GetAttributes() const override
{
+ Attributes attributes;
+
+ if(mRoot)
+ {
+ Dali::Window window = Dali::DevelWindow::Get(Self());
+ Dali::Internal::Adaptor::Window& windowImpl = Dali::GetImplementation(window);
+ attributes["resID"] = windowImpl.GetNativeResourceId();
+ }
+
Dali::TypeInfo type;
Self().GetTypeInfo(type);
- return {
- {"class", type.GetName()},
- };
+ attributes["class"] = type.GetName();
+
+ return attributes;
}
bool DoGesture(const GestureInfo& gestureInfo) override
convertingFunctor = functor;
}
-Accessible* Accessible::Get(Dali::Actor actor, bool isRoot)
+Accessible* Accessible::Get(Dali::Actor actor)
{
if(!actor)
{
auto pair = gAdaptorAccessibles.emplace(&actor.GetBaseObject(), nullptr);
if(pair.second)
{
+ bool isRoot = false;
+ Dali::Integration::Scene scene = Dali::Integration::Scene::Get(actor);
+ if(scene)
+ {
+ isRoot = (actor == scene.GetRootLayer());
+ }
pair.first->second.reset(new AdaptorAccessible(actor, isRoot));
}
accessible = pair.first->second.get();
{\r
namespace Accessibility\r
{\r
+class Accessible;\r
+\r
/**\r
* @brief Enumeration describing type of object move relative to the screen. Only outgoing moves are signalled to AT-clients.\r
*/\r
MAX_COUNT\r
};\r
\r
-using ReadingInfoTypes = EnumBitSet<ReadingInfoType, ReadingInfoType::MAX_COUNT>;\r
-using States = EnumBitSet<State, State::MAX_COUNT>;\r
-using Attributes = std::unordered_map<std::string, std::string>;\r
+/**\r
+ * @brief Enumeration of all AT-SPI interfaces.\r
+ *\r
+ * @see Dali::Accessibility::Accessible::GetInterfaceName()\r
+ * @see Dali::Accessibility::AtspiInterfaceType\r
+ */\r
+enum class AtspiInterface\r
+{\r
+ ACCESSIBLE,\r
+ ACTION,\r
+ APPLICATION,\r
+ CACHE,\r
+ COLLECTION,\r
+ COMPONENT,\r
+ DEVICE_EVENT_CONTROLLER,\r
+ DEVICE_EVENT_LISTENER,\r
+ DOCUMENT,\r
+ EDITABLE_TEXT,\r
+ EVENT_DOCUMENT,\r
+ EVENT_FOCUS,\r
+ EVENT_KEYBOARD,\r
+ EVENT_MOUSE,\r
+ EVENT_OBJECT,\r
+ EVENT_TERMINAL,\r
+ EVENT_WINDOW,\r
+ HYPERLINK,\r
+ HYPERTEXT,\r
+ IMAGE,\r
+ REGISTRY,\r
+ SELECTION,\r
+ SOCKET,\r
+ TABLE,\r
+ TABLE_CELL,\r
+ TEXT,\r
+ VALUE,\r
+ MAX_COUNT\r
+};\r
+\r
+/**\r
+ * @brief Enumeration of all AT-SPI events.\r
+ */\r
+enum class AtspiEvent\r
+{\r
+ PROPERTY_CHANGED,\r
+ BOUNDS_CHANGED,\r
+ LINK_SELECTED,\r
+ STATE_CHANGED,\r
+ CHILDREN_CHANGED,\r
+ VISIBLE_DATA_CHANGED,\r
+ SELECTION_CHANGED,\r
+ MODEL_CHANGED,\r
+ ACTIVE_DESCENDANT_CHANGED,\r
+ ROW_INSERTED,\r
+ ROW_REORDERED,\r
+ ROW_DELETED,\r
+ COLUMN_INSERTED,\r
+ COLUMN_REORDERED,\r
+ COLUMN_DELETED,\r
+ TEXT_BOUNDS_CHANGED,\r
+ TEXT_SELECTION_CHANGED,\r
+ TEXT_CHANGED,\r
+ TEXT_ATTRIBUTES_CHANGED,\r
+ TEXT_CARET_MOVED,\r
+ ATTRIBUTES_CHANGED,\r
+ MOVED_OUT,\r
+ WINDOW_CHANGED,\r
+ MAX_COUNT\r
+};\r
+\r
+using AtspiInterfaces = EnumBitSet<AtspiInterface, AtspiInterface::MAX_COUNT>;\r
+using AtspiEvents = EnumBitSet<AtspiEvent, AtspiEvent::MAX_COUNT>;\r
+using ReadingInfoTypes = EnumBitSet<ReadingInfoType, ReadingInfoType::MAX_COUNT>;\r
+using States = EnumBitSet<State, State::MAX_COUNT>;\r
+using Attributes = std::unordered_map<std::string, std::string>;\r
+\r
+namespace Internal\r
+{\r
+/*\r
+ * AT-SPI interfaces exposed as native C++ types should specialize this like so:\r
+ *\r
+ * template<>\r
+ * struct AtspiInterfaceTypeHelper<AtspiInterface::ACCESSIBLE>\r
+ * {\r
+ * using Type = Dali::Accessibility::Accessible;\r
+ * };\r
+ */\r
+template<AtspiInterface I>\r
+struct AtspiInterfaceTypeHelper; // no default definition\r
+\r
+} // namespace Internal\r
+\r
+/**\r
+ * @brief Resolves to the native C++ type that represents the given AT-SPI interface.\r
+ *\r
+ * For example, @code AtspiInterfaceType<AtspiInterface::ACCESSIBLE> @endcode is the same as\r
+ * @code Dali::Accessibility::Accessible @endcode. Not all AT-SPI interfaces have native C++\r
+ * representations (in which case, such an expression will not compile).\r
+ *\r
+ * @tparam I Enumeration value indicating the requested AT-SPI interface.\r
+ */\r
+template<AtspiInterface I>\r
+using AtspiInterfaceType = typename Internal::AtspiInterfaceTypeHelper<I>::Type;\r
\r
/**\r
* @brief Class representing unique object address on accessibility bus\r
\r
/**\r
* @brief Class representing accessibility relations\r
+ *\r
* Class connecting one source object with multiple target objects with usage\r
* of specific relation type.\r
- * @note std::string representing source and targets are string values of Accessibility::Address\r
- * @see Dali::Accessibility::Accessible::Address\r
+ *\r
+ * A remote target object (i.e. one belonging to a different process) can be\r
+ * represented in terms of a ProxyAccessible.\r
+ *\r
+ * @see Dali::Accessibility::Accessible::Accessible\r
* @see Dali::Accessibility::Accessible::RelationType\r
*/\r
struct DALI_ADAPTOR_API Relation\r
{\r
- Relation(RelationType relationType, std::vector<Address> targets)\r
- : relationType(relationType),\r
- targets(targets)\r
+ Relation(RelationType relationType, const std::vector<Accessible*>& targets)\r
+ : mRelationType(relationType),\r
+ mTargets(targets)\r
{\r
}\r
\r
- RelationType relationType;\r
- std::vector<Address> targets;\r
+ RelationType mRelationType;\r
+ std::vector<Accessible*> mTargets;\r
};\r
\r
} // namespace Accessibility\r
AnimatedImageLoading::~AnimatedImageLoading()
{
}
-
-bool AnimatedImageLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData)
-{
- return GetImplementation(*this).LoadNextNFrames(frameStartIndex, count, pixelData);
-}
-
Dali::Devel::PixelBuffer AnimatedImageLoading::LoadFrame(uint32_t frameIndex)
{
return GetImplementation(*this).LoadFrame(frameIndex);
~AnimatedImageLoading();
/**
- * @brief Load the next N Frames of the animated image.
- *
- * @note This function will load the entire animated image into memory if not already loaded.
- * @param[in] frameStartIndex The frame counter to start from. Will usually be the next frame
- * after the previous invocation of this method, or 0 to start.
- * @param[in] count The number of frames to load
- * @param[out] pixelData The vector in which to return the frame data
- * @return True if the frame data was successfully loaded
- */
- bool LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData);
-
- /**
* @brief Load a frame of the animated image.
*
* @note This function will load the entire animated image into memory if not already loaded.
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// INTERNAL INCLUDES
#include <dali/devel-api/adaptor-framework/application-devel.h>
#include <dali/integration-api/adaptor-framework/scene-holder.h>
+#include <dali/internal/adaptor/common/adaptor-impl.h>
#include <dali/internal/adaptor/common/application-impl.h>
+#include <dali/internal/network/common/network-service-impl.h>
namespace Dali
{
//Store only the value before adaptor is created
internal->StoreWindowPositionSize(positionSize);
- }
- else
+ } else
{
internal = Internal::Adaptor::Application::New(argc, argv, stylesheet, windowMode, positionSize, Internal::Adaptor::Framework::NORMAL, type);
}
return Application(dynamic_cast<Dali::Internal::Adaptor::Application*>(refObject));
}
+CustomCommandReceivedSignalType& CustomCommandReceivedSignal(Application application)
+{
+ DALI_ASSERT_ALWAYS(Adaptor::IsAvailable() && "Adaptor is not available")
+
+ Internal::Adaptor::NetworkServicePtr networkService = Internal::Adaptor::NetworkService::Get();
+
+ DALI_ASSERT_ALWAYS(networkService && "Network Service Unavailable");
+
+ return networkService->CustomCommandReceivedSignal();
+}
+
} // namespace DevelApplication
} // namespace Dali
#define DALI_APPLICATION_DEVEL_H
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// INTERNAL INCLUDES
#include <dali/public-api/adaptor-framework/application.h>
+#include <dali/public-api/dali-adaptor-common.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/signals/dali-signal.h>
namespace Dali
{
namespace DevelApplication
{
+
+using CustomCommandReceivedSignalType = Signal<void(const std::string&)>; ///< Signal signature for CustomCommandReceivedSignal
+
/**
* @brief This is the constructor for applications.
* Especially, it is for keyboard application.
*/
DALI_ADAPTOR_API Application DownCast(Dali::RefObject* refObject);
+/**
+ * @brief This signal will be triggered when a custom command is received.
+ *
+ * For this signal to be triggered, the adaptor must be built with -DENABLE_NETWORK_LOGGING=ON
+ * and when running, DALI_NETWORK_CONTROL=1 must also be set.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ * void YourCallbackName(const std::string&);
+ * @endcode
+ *
+ * @param[in] application A handle to the Application
+ * @return The signal when a custom command is received
+ */
+DALI_ADAPTOR_API CustomCommandReceivedSignalType& CustomCommandReceivedSignal(Application application);
+
} // namespace DevelApplication
} // namespace Dali
{
return Dali::Accessibility::IsUp();
}
+
+bool Dali::AtspiAccessibility::IsScreenReaderEnabled()
+{
+ return Dali::Accessibility::Bridge::GetCurrentBridge()->GetScreenReaderEnabled();
+}
\ No newline at end of file
*/
DALI_ADAPTOR_API bool IsEnabled();
+/**
+ * @brief Returns whether the state of Screen Reader is enabled or not.
+ *
+ * @return True if Screen Reader is enabled, false otherwise.
+ */
+DALI_ADAPTOR_API bool IsScreenReaderEnabled();
+
} //namespace AtspiAccessibility
} //namespace Dali
return Internal::Adaptor::GetDragAndDrop();
}
-bool DragAndDrop::StartDragAndDrop(Dali::Actor source, Dali::Actor shadow, const std::string& dragData)
+bool DragAndDrop::StartDragAndDrop(Dali::Actor source, Dali::Window shadowWindow, const DragData& dragData, Dali::DragAndDrop::SourceFunction callback)
{
- return GetImplementation(*this).StartDragAndDrop(source, shadow, dragData);
+ return GetImplementation(*this).StartDragAndDrop(source, shadowWindow, dragData, callback);
}
bool DragAndDrop::AddListener(Dali::Actor target, DragAndDropFunction callback)
return GetImplementation(*this).AddListener(target, callback);
}
+bool DragAndDrop::RemoveListener(Dali::Actor target)
+{
+ return GetImplementation(*this).RemoveListener(target);
+}
+
} // namespace Dali
*/
// EXTERNAL INCLUDES
+#include <dali/public-api/adaptor-framework/window.h>
#include <dali/public-api/actors/actor.h>
#include <dali/public-api/math/rect.h>
#include <dali/public-api/object/base-handle.h>
{
public:
/**
+ * @brief Enumeration for the drag source event type in the source object
+ */
+ enum class SourceEventType
+ {
+ START, ///< Drag and drop is started.
+ CANCEL, ///< Drag and drop is cancelled.
+ ACCEPT, ///< Drag and drop is accepted.
+ FINISH ///< Drag and drop is finished.
+ };
+
+ /**
* @brief Enumeration for the drag event type in the target object
*/
enum class DragType
{
DragEvent()
{
+ this->mimeType = nullptr;
this->data = nullptr;
}
- DragEvent(DragType type, Dali::Vector2 position, char* data = nullptr)
+ DragEvent(DragType type, Dali::Vector2 position, const char* mimeType = nullptr, char* data = nullptr)
{
this->type = type;
this->position = position;
+ this->mimeType = mimeType;
this->data = data;
}
{
return position;
}
+ void SetMimeType(const char* mimeType)
+ {
+ this->mimeType = mimeType;
+ }
+ const char* GetMimeType()
+ {
+ return mimeType;
+ }
void SetData(char* data)
{
this->data = data;
}
- char* GetData()
+ char* GetData() const
{
return data;
}
private:
DragType type{DragType::DROP}; ///< The drag event type.
Dali::Vector2 position; ///< The position of drag object.
+ const char* mimeType; ///< The mime type of drag object.
char* data{nullptr}; ///< The data of drag object.
};
+ /**
+ * @brief Structure that contains information about the drag data information.
+ */
+ struct DragData
+ {
+ void SetMimeType(const char* mimeType)
+ {
+ this->mimeType = mimeType;
+ }
+ const char* GetMimeType() const
+ {
+ return mimeType;
+ }
+ void SetData(const char* data)
+ {
+ this->data = data;
+ }
+ const char* GetData() const
+ {
+ return data;
+ }
+
+ private:
+ const char* mimeType{nullptr}; ///<The mime type of drag data.
+ const char* data{nullptr}; ///<The drag data.
+ };
+
using DragAndDropFunction = std::function<void(const DragEvent&)>;
+ using SourceFunction = std::function<void(enum SourceEventType)>;
/**
* @brief Create an uninitialized DragAndDrop.
* @brief Start the drag operation.
*
* @param[in] source The drag source object.
- * @param[in] shadow The shadow object for drag object.
+ * @param[in] shadowWindow The shadow window for drag object.
* @param[in] dragData The data to send to target object.
+ * @param[in] callback The drag source event callback.
* @return bool true if the drag operation is started successfully.
*/
- bool StartDragAndDrop(Dali::Actor source, Dali::Actor shadow, const std::string& dragData);
+ bool StartDragAndDrop(Dali::Actor source, Dali::Window shadowWindow, const DragData& dragData, Dali::DragAndDrop::SourceFunction callback);
/**
* @brief Add the listener for receiving the drag and drop events.
*/
bool AddListener(Dali::Actor target, DragAndDropFunction callback);
+ /**
+ * @brief Remove the listener.
+ *
+ * @param[in] target The drop target object.
+ * @return bool true if the listener is removed successfully.
+ */
+ bool RemoveListener(Dali::Actor target);
+
public:
/**
* @brief This constructor is used by Adaptor::GetDragAndDrop().
#define DALI_TIZEN_PLATFORM_IMAGE_LOADER_INPUT_H
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
};
using LoadBitmapFunction = bool (*)(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& pixelData);
+using LoadPlanesFunction = bool (*)(const Dali::ImageLoader::Input& input, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers);
using LoadBitmapHeaderFunction = bool (*)(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height);
/**
*/
struct BitmapLoader
{
- unsigned char magicByte1; ///< The first byte in the file should be this
- unsigned char magicByte2; ///< The second byte in the file should be this
- LoadBitmapFunction loader; ///< The function which decodes the file
- LoadBitmapHeaderFunction header; ///< The function which decodes the header of the file
- Dali::Integration::Bitmap::Profile profile; ///< The kind of bitmap to be created
- /// (addressable packed pixels or an opaque compressed blob).
+ unsigned char magicByte1; ///< The first byte in the file should be this
+ unsigned char magicByte2; ///< The second byte in the file should be this
+ LoadBitmapFunction loader; ///< The function which decodes the file
+ LoadPlanesFunction planeLoader; ///< The function which decodes the file to each plane
+ LoadBitmapHeaderFunction header; ///< The function which decodes the header of the file
+ Dali::Integration::Bitmap::Profile profile; ///< The kind of bitmap to be created
+ /// (addressable packed pixels or an opaque compressed blob).
};
} // namespace ImageLoader
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return Dali::Devel::PixelBuffer();
}
+void LoadImagePlanesFromFile(const std::string& url, std::vector<Devel::PixelBuffer>& buffers, ImageDimensions size, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection)
+{
+ Integration::BitmapResourceType resourceType(size, fittingMode, samplingMode, orientationCorrection);
+
+ Internal::Platform::FileReader fileReader(url);
+ FILE* const fp = fileReader.GetFile();
+ if(fp != NULL)
+ {
+ TizenPlatform::ImageLoader::ConvertStreamToPlanes(resourceType, url, fp, buffers);
+ }
+}
+
Devel::PixelBuffer LoadImageFromBuffer(const Dali::Vector<uint8_t>& buffer, ImageDimensions size, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection)
{
if(buffer.Empty())
{
Dali::Devel::PixelBuffer bitmap;
// Make path as empty string. Path information just for file format hint.
- bool success = TizenPlatform::ImageLoader::ConvertStreamToBitmap(resourceType, std::string(""), fp, bitmap);
+ bool success = TizenPlatform::ImageLoader::ConvertStreamToBitmap(resourceType, std::string(""), fp, bitmap);
if(success && bitmap)
{
return bitmap;
#define DALI_IMAGE_LOADING_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
bool orientationCorrection = true);
/**
+ * @brief Load an image and save each plane to a separate buffer synchronously from local file.
+ *
+ * @note This method is thread safe, i.e. can be called from any thread.
+ * If the image file doesn't support to load planes, this method returns a bitmap image instead.
+ *
+ * @param [in] url The URL of the image file to load.
+ * @param [out] buffers The loaded PixelBuffer object list or an empty list in case loading failed.
+ * @param [in] size The width and height to fit the loaded image to, 0.0 means whole image
+ * @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
+ * @param [in] samplingMode The filtering method used when sampling pixels from the input image while fitting it to desired size.
+ * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
+ */
+DALI_ADAPTOR_API void LoadImagePlanesFromFile(
+ const std::string& url,
+ std::vector<Devel::PixelBuffer>& buffers,
+ ImageDimensions size = ImageDimensions(0, 0),
+ FittingMode::Type fittingMode = FittingMode::DEFAULT,
+ SamplingMode::Type samplingMode = SamplingMode::BOX_THEN_LINEAR,
+ bool orientationCorrection = true);
+
+/**
* @brief Load an image synchronously from encoded buffer.
*
* @note This method is thread safe, i.e. can be called from any thread.
#define DALI_INPUT_METHOD_CONTEXT_H
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
COMMIT, ///< Commit recieved
DELETE_SURROUNDING, ///< Event to delete a range of characters from the string
GET_SURROUNDING, ///< Event to query string and cursor position
- PRIVATE_COMMAND ///< Private command sent from the input panel
+ PRIVATE_COMMAND, ///< Private command sent from the input panel
+ SELECTION_SET ///< input method needs to set the selection
};
/**
: predictiveString(),
eventName(VOID),
cursorOffset(0),
- numberOfChars(0){};
+ numberOfChars(0),
+ startIndex(0),
+ endIndex(0){};
/**
* @brief Constructor
: predictiveString(aPredictiveString),
eventName(aEventName),
cursorOffset(aCursorOffset),
- numberOfChars(aNumberOfChars)
+ numberOfChars(aNumberOfChars),
+ startIndex(0),
+ endIndex(0)
+ {
+ }
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] aEventName The name of the event from the InputMethodContext.
+ * @param[in] aStartIndex The start index of selection.
+ * @param[in] aEndIndex The end index of selection.
+ */
+ EventData(EventType aEventName, int aStartIndex, int aEndIndex)
+ : predictiveString(),
+ eventName(aEventName),
+ cursorOffset(0),
+ numberOfChars(0),
+ startIndex(aStartIndex),
+ endIndex(aEndIndex)
{
}
EventType eventName; ///< The name of the event from the InputMethodContext.
int cursorOffset; ///< Start position from the current cursor position to start deleting characters.
int numberOfChars; ///< number of characters to delete from the cursorOffset.
+ int startIndex; ///< The start index of selection.
+ int endIndex; ///< The end index of selection.
};
/**
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
NativeImageSourceQueuePtr NativeImageSourceQueue::New(Any nativeImageSourceQueue)
{
//ColorFormat will be ignored.
- NativeImageSourceQueuePtr image = new NativeImageSourceQueue(0, 0, ColorFormat::RGBA8888, nativeImageSourceQueue);
+ NativeImageSourceQueuePtr image = new NativeImageSourceQueue(0, 0, ColorFormat::BGRA8888, nativeImageSourceQueue);
if(image->mImpl)
{
return image;
#define DALI_NATIVE_IMAGE_SOURCE_QUEUE_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
public:
/**
* @brief Enumeration for the instance when creating a native image, the color depth has to be specified.
+ * @note This ColorFormat follows pixel byte order.
*/
enum class ColorFormat
{
RGB888, /// 8 red bits, 8 green bits, 8 blue bits
RGBA8888, /// 8 red bits, 8 green bits, 8 blue bits, alpha 8 bits
- RGBX8888 /// 8 red bits, 8 green bits, 8 blue bits, and 8 ignored bits
+ RGBX8888, /// 8 red bits, 8 green bits, 8 blue bits, and 8 ignored bits
+ BGR888, /// 8 blue bits, 8 green bits, 8 red bits
+ BGRA8888, /// 8 blue bits, 8 green bits, 8 red bits, alpha 8 bits
+ BGRX8888, /// 8 blue bits, 8 green bits, 8 red bits, and 8 ignored bits
};
/**
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return GetImplementation(*this).GetHeight();
}
+unsigned int PixelBuffer::GetStride() const
+{
+ return GetImplementation(*this).GetStride();
+}
+
Pixel::Format PixelBuffer::GetPixelFormat() const
{
return GetImplementation(*this).GetPixelFormat();
#define DALI_PIXEL_BUFFER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
unsigned int GetHeight() const;
/**
+ * @brief Gets the stride of the buffer in pixels.
+ *
+ * @SINCE_2_1.17
+ * @return The stride of the buffer in pixels. 0 means the buffer is tightly packed.
+ */
+ unsigned int GetStride() const;
+
+ /**
* @brief Gets the pixel format.
*
* @SINCE_1_2.46
// INTERNAL INCLUDES
#include <dali/devel-api/adaptor-framework/accessibility.h>
#include <dali/devel-api/atspi-interfaces/accessible.h>
+#include <dali/devel-api/atspi-interfaces/component.h>
namespace Dali::Accessibility
{
*
* To be used as a proxy object, in those situations where you want to return an address in
* a different bridge (embedding for example), but the object itself isn't planned to be used
- * otherwise. This object has no parent, no children, an empty name and so on.
+ * otherwise. This object has a settable parent, no children, an empty name and so on.
*/
-class DALI_ADAPTOR_API ProxyAccessible : public virtual Accessible
+class DALI_ADAPTOR_API ProxyAccessible : public virtual Accessible, public virtual Component
{
public:
- ProxyAccessible() = default;
-
- ProxyAccessible(Address address)
- : mAddress(std::move(address))
+ ProxyAccessible()
+ : mAddress{},
+ mParent{nullptr}
{
}
void SetAddress(Address address)
{
- this->mAddress = std::move(address);
+ mAddress = std::move(address);
+ }
+
+ void SetParent(Accessible* parent)
+ {
+ mParent = parent;
}
std::string GetName() const override
Accessible* GetParent() override
{
- return nullptr;
+ return mParent;
}
size_t GetChildCount() const override
return {};
}
+ bool IsProxy() const override
+ {
+ return true;
+ }
+
Address GetAddress() const override
{
return mAddress;
return Dali::Actor{};
}
+ Rect<> GetExtents(CoordinateType type) const override
+ {
+ auto* parent = Component::DownCast(mParent);
+
+ return parent ? parent->GetExtents(type) : Rect<>{};
+ }
+
+ ComponentLayer GetLayer() const override
+ {
+ return ComponentLayer::WINDOW;
+ }
+
+ int16_t GetMdiZOrder() const override
+ {
+ return false;
+ }
+
+ bool GrabFocus() override
+ {
+ return false;
+ }
+
+ double GetAlpha() const override
+ {
+ return 0.0;
+ }
+
+ bool GrabHighlight() override
+ {
+ return false;
+ }
+
+ bool ClearHighlight() override
+ {
+ return false;
+ }
+
+ bool IsScrollable() const override
+ {
+ return false;
+ }
+
private:
- Address mAddress;
+ Address mAddress;
+ Accessible* mParent;
};
} // namespace Dali::Accessibility
+++ /dev/null
-#ifndef DALI_VECTOR_IMAGE_RENDERER_PLUGIN_H
-#define DALI_VECTOR_IMAGE_RENDERER_PLUGIN_H
-
-/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-// INTERNAL INCLUDES
-#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
-
-namespace Dali
-{
-/**
- * VectorImageRendererPlugin is an abstract interface, used by dali-adaptor to render a vector image(SVG).
- * A concrete implementation must be created for each platform and provided as a dynamic library which
- * will be loaded at run time by the adaptor.
- */
-class VectorImageRendererPlugin
-{
-public:
- /**
- * @brief Constructor
- */
- VectorImageRendererPlugin()
- {
- }
-
- /**
- * @brief Destructor
- */
- virtual ~VectorImageRendererPlugin()
- {
- }
-
- /**
- * @brief Load vector image data directly.
- *
- * @param[in] data The memory data of vector image
- * @return True if the load success, false otherwise.
- */
- virtual bool Load(const Vector<uint8_t>& data) = 0;
-
- /**
- * @brief Rasterizes the content to the target buffer synchronously.
- *
- * @param[in] buffer The target buffer
- * @return True if the rendering succeeds, false otherwise.
- */
- virtual bool Rasterize(Dali::Devel::PixelBuffer& buffer) = 0;
-
- /**
- * @brief Gets the default size of the file.
- *
- * @param[out] width The default width of the file
- * @param[out] height The default height of the file
- */
- virtual void GetDefaultSize(uint32_t& width, uint32_t& height) const = 0;
-
- /**
- * @brief Function pointer called in adaptor to create a plugin instance.
- */
- using CreateVectorImageRendererFunction = VectorImageRendererPlugin* (*)();
-};
-
-} // namespace Dali
-
-#endif // DALI_VECTOR_IMAGE_RENDERER_PLUGIN_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return GetImplementation(*this).Load(data, dpi);
}
-bool VectorImageRenderer::Rasterize(Dali::Devel::PixelBuffer& buffer, float scale)
+bool VectorImageRenderer::IsLoaded() const
{
- return GetImplementation(*this).Rasterize(buffer, scale);
+ return GetImplementation(*this).IsLoaded();
+}
+
+Dali::Devel::PixelBuffer VectorImageRenderer::Rasterize(uint32_t width, uint32_t height)
+{
+ return GetImplementation(*this).Rasterize(width, height);
}
void VectorImageRenderer::GetDefaultSize(uint32_t& width, uint32_t& height) const
#define DALI_VECTOR_IMAGE_RENDERER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
bool Load(const Vector<uint8_t>& data, float dpi);
/**
- * @brief Rasterizes the content to the target buffer synchronously.
+ * @brief Query whether the vector image is loaded.
*
- * @param[in] buffer The target buffer
- * @param[in] scale The target image scale factor
- * @return True if the rendering succeeds, false otherwise.
+ * @return True if the image is loaded, false other wise.
*/
- bool Rasterize(Dali::Devel::PixelBuffer& buffer, float scale);
+ bool IsLoaded() const;
+
+ /**
+ * @brief Rasterizes the content to the pixel buffer synchronously.
+ *
+ * @param[in] width The pixel buffer width
+ * @param[in] height The pixel buffer height
+ * @return The handle to the rasterized PixelBuffer object or an empty handle in case failed.
+ */
+ Dali::Devel::PixelBuffer Rasterize(uint32_t width, uint32_t height);
/**
* @brief Gets the default size of the file.
#include <memory>
// INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/accessibility.h>
#include <dali/devel-api/adaptor-framework/web-engine-hit-test.h>
#include <dali/devel-api/common/bitwise-enum.h>
#include <dali/public-api/adaptor-framework/native-image-source.h>
virtual void ActivateAccessibility(bool activated) = 0;
/**
+ * @brief Get the accessibility address (bus and path) for embedding.
+ * @return Accessibility address of the root web content element.
+ */
+ virtual Accessibility::Address GetAccessibilityAddress() = 0;
+
+ /**
* @brief Request to set the current page's visibility.
* @param[in] visible Visible or not.
*
GetImplementation(*this).ActivateAccessibility(activated);
}
+Accessibility::Address WebEngine::GetAccessibilityAddress()
+{
+ return GetImplementation(*this).GetAccessibilityAddress();
+}
+
bool WebEngine::SetVisibility(bool visible)
{
return GetImplementation(*this).SetVisibility(visible);
#include <dali/public-api/object/base-handle.h>
//INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/accessibility.h>
#include <dali/devel-api/adaptor-framework/web-engine-plugin.h>
#include <dali/public-api/dali-adaptor-common.h>
void ActivateAccessibility(bool activated);
/**
+ * @brief Get the accessibility address (bus and path) for embedding.
+ * @return Accessibility address of the root web content element.
+ */
+ Accessibility::Address GetAccessibilityAddress();
+
+ /**
* @brief Request to set the current page's visibility.
* @param[in] visible Visible or not.
*
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return GetImplementation(window).AuxiliaryMessageSignal();
}
+AccessibilityHighlightSignalType& AccessibilityHighlightSignal(Window window)
+{
+ return GetImplementation(window).AccessibilityHighlightSignal();
+}
+
void SetParent(Window window, Window parent)
{
GetImplementation(window).SetParent(parent);
return GetImplementation(window).IsMaximized();
}
+void SetMaximumSize(Window window, Dali::Window::WindowSize size)
+{
+ GetImplementation(window).SetMaximumSize(size);
+}
+
void Minimize(Window window, bool miniimize)
{
GetImplementation(window).Minimize(miniimize);
return GetImplementation(window).IsMinimized();
}
+void SetMimimumSize(Window window, Dali::Window::WindowSize size)
+{
+ GetImplementation(window).SetMimimumSize(size);
+}
+
+bool IsWindowRotating(Window window)
+{
+ return GetImplementation(window).IsWindowRotating();
+}
+
+const KeyEvent& GetLastKeyEvent(Window window)
+{
+ return GetImplementation(window).GetLastKeyEvent();
+}
+
+const TouchEvent& GetLastTouchEvent(Window window)
+{
+ return GetImplementation(window).GetLastTouchEvent();
+}
+
+InterceptKeyEventSignalType& InterceptKeyEventSignal(Window window)
+{
+ return GetImplementation(window).InterceptKeyEventSignal();
+}
+
} // namespace DevelWindow
} // namespace Dali
#define DALI_WINDOW_DEVEL_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
namespace DevelWindow
{
-
-typedef Signal<void()> EventProcessingFinishedSignalType; ///< Event Processing finished signal type
-
-typedef Signal<void(const KeyEvent&)> KeyEventSignalType; ///< Key event signal type
-
-typedef Signal<void(const TouchEvent&)> TouchEventSignalType; ///< Touch signal type
-
-typedef Signal<void(const WheelEvent&)> WheelEventSignalType; ///< Touched signal type
-
-typedef Signal<void(Window, bool)> VisibilityChangedSignalType; ///< Visibility changed signal type
-
-typedef Signal<void(Window, WindowEffectState, WindowEffectType)> TransitionEffectEventSignalType; ///< Effect signal type and state
-
-typedef Signal<void()> KeyboardRepeatSettingsChangedSignalType; ///< Keyboard repeat settings changed signal type
-
-typedef Signal<void(const std::string&, const std::string&, const Property::Array&)> AuxiliaryMessageSignalType; ///< Auxiliary message signal type
+typedef Signal<void()> EventProcessingFinishedSignalType; ///< Event Processing finished signal type
+typedef Signal<void(const KeyEvent&)> KeyEventSignalType; ///< Key event signal type
+typedef Signal<void(const TouchEvent&)> TouchEventSignalType; ///< Touch signal type
+typedef Signal<void(const WheelEvent&)> WheelEventSignalType; ///< Wheel signal type
+typedef Signal<void(Window, bool)> VisibilityChangedSignalType; ///< Visibility changed signal type
+typedef Signal<void(Window, WindowEffectState, WindowEffectType)> TransitionEffectEventSignalType; ///< Effect signal type and state
+typedef Signal<void()> KeyboardRepeatSettingsChangedSignalType; ///< Keyboard repeat settings changed signal type
+typedef Signal<void(const std::string&, const std::string&, const Property::Array&)> AuxiliaryMessageSignalType; ///< Auxiliary message signal type
+typedef Signal<void(Window, bool)> AccessibilityHighlightSignalType; ///< Accessibility Highlight signal type
+typedef Signal<bool(const KeyEvent&)> InterceptKeyEventSignalType; ///< Intercept Key event signal type
/**
* @brief Creates an initialized handle to a new Window.
DALI_ADAPTOR_API AuxiliaryMessageSignalType& AuxiliaryMessageSignal(Window window);
/**
+ * @brief This signal is emitted when the window needs to grab or clear accessibility highlight.
+ * The highlight indicates that it is an object to interact with the user regardless of focus.
+ * After setting the highlight on the object, you can do things that the object can do, such as
+ * giving or losing focus.
+ *
+ * This signal is emitted by Dali::Accessibility::Component::GrabHighlight
+ * and Dali::Accessibility::Component::ClearHighlight
+ *
+ * A callback of the following type may be connected:
+ * @code
+ * void YourCallbackName( Window window, bool highlight );
+ * @endcode
+ *
+ * @param[in] window The window instance
+ * @return The signal to connect to
+ */
+DALI_ADAPTOR_API AccessibilityHighlightSignalType& AccessibilityHighlightSignal(Window window);
+
+/**
* @brief Sets parent window of the window.
*
* After setting that, these windows do together when raise-up, lower and iconified/deiconified.
DALI_ADAPTOR_API bool IsMaximized(Window window);
/**
+ * @brief Sets window's maximum size.
+ *
+ * It is to set the maximized size when window is maximized or the window's size is increased by RequestResizeToServer().
+ * Although the size is set by this function, window's size can be increased over the limitation by SetPositionSize() or SetSize().
+ *
+ * After setting, if Maximize() is called, window is resized with the setting size and move the center.
+ *
+ * @param[in] window The window instance.
+ * @param[in] size the maximum size
+ */
+DALI_ADAPTOR_API void SetMaximumSize(Window window, Dali::Window::WindowSize size);
+
+/**
* @brief Minimizes window's size.
* If this function is called with true, window will be iconified.
* Otherwise window will be activated.
*/
DALI_ADAPTOR_API bool IsMinimized(Window window);
+/**
+ * @brief Sets window's minimum size.
+ *
+ * It is to set the minimum size when window's size is decreased by RequestResizeToServer().
+ * Although the size is set by this function, window's size can be decreased over the limitation by SetPositionSize() or SetSize().
+ *
+ * @param[in] window The window instance.
+ * @param[in] size the minimum size
+ */
+DALI_ADAPTOR_API void SetMimimumSize(Window window, Dali::Window::WindowSize size);
+
+/**
+ * @brief Query whether window is rotating or not.
+ *
+ * @param[in] window The window instance.
+ * @return true if window is rotating, false otherwise.
+ */
+DALI_ADAPTOR_API bool IsWindowRotating(Window window);
+
+/**
+ * @brief Gets the last key event the window gets.
+ *
+ * @param[in] window The window instance.
+ * @return The last key event the window gets.
+ */
+DALI_ADAPTOR_API const KeyEvent& GetLastKeyEvent(Window window);
+
+/**
+ * @brief Gets the last touch event the window gets.
+ *
+ * @param[in] window The window instance.
+ * @return The last touch event the window gets.
+ * @note It returns the raw event the window gets. There is no hit-actor and local position information.
+ */
+DALI_ADAPTOR_API const TouchEvent& GetLastTouchEvent(Window window);
+
+/**
+ * @brief The user would connect to this signal to intercept a KeyEvent at window.
+ *
+ * Intercepts KeyEvents in the window before dispatching KeyEvents to the control.
+ * If a KeyEvent is consumed, no KeyEvent is delivered to the control.
+ *
+ * @param[in] window The window instance.
+ * @return The signal to connect to
+ */
+DALI_ADAPTOR_API InterceptKeyEventSignalType& InterceptKeyEventSignal(Window window);
+
} // namespace DevelWindow
} // namespace Dali
void EmitMovedOutOfScreen(ScreenRelativeMoveType type);
/**
+ * @brief Emits "org.a11y.atspi.Socket.Available" signal.
+ */
+ // This belongs to Dali::Accessibility::Socket. However, all Emit*() helpers
+ // are here in Accessible, regardless of what interface they belong to (perhaps
+ // to spare a dynamic_cast if used like this: Accessible::Get()->Emit*(...)).
+ void EmitSocketAvailable();
+
+ /**
* @brief Emits "highlighted" event.
*
* @param[in] event The enumerated window event
virtual std::vector<Relation> GetRelationSet() = 0;
/**
- * @brief Gets internal Actor to be saved before.
+ * @brief Gets the Actor associated with this Accessible (if there is one).
*
* @return The internal Actor
*/
/**
* @brief Gets all implemented interfaces.
*
- * @return The collection of strings with implemented interfaces
+ * Override DoGetInterfaces() to customize the return value of this method.
+ *
+ * @return The collection of implemented interfaces
+ *
+ * @see DoGetInterfaces()
+ */
+ AtspiInterfaces GetInterfaces() const;
+
+ /**
+ * @brief Gets all implemented interfaces.
+ *
+ * Converts all interfaces returned by GetInterfaces() to their DBus names
+ * using GetInterfaceName().
+ *
+ * @return The collection of names of implemented interfaces
+ *
+ * @see GetInterfaces()
+ * @see GetInterfaceName()
*/
- std::vector<std::string> GetInterfaces() const;
+ std::vector<std::string> GetInterfacesAsStrings() const;
/**
* @brief Checks if object is on root level.
return mIsOnRootLevel;
}
+ /**
+ * @brief Gets all suppressed events.
+ *
+ * @return All suppressed events
+ */
+ AtspiEvents GetSuppressedEvents() const
+ {
+ return mSuppressedEvents;
+ }
+
+ /**
+ * @brief Gets all suppressed events.
+ *
+ * @return All suppressed events
+ */
+ AtspiEvents& GetSuppressedEvents()
+ {
+ return mSuppressedEvents;
+ }
+
protected:
Accessible();
Accessible(const Accessible&) = delete;
Accessible& operator=(Accessible&&) = delete;
std::shared_ptr<Bridge::Data> GetBridgeData() const;
+ /**
+ * @brief Returns the collection of AT-SPI interfaces implemented by this Accessible.
+ *
+ * This method is called only once and its return value is cached. The default implementation
+ * uses dynamic_cast to determine which interfaces are implemented. Override this if you
+ * conceptually provide fewer interfaces than dynamic_cast can see.
+ *
+ * @return The collection of implemented interfaces
+ *
+ * @see GetInterfaces()
+ * @see GetInterfaceName()
+ */
+ virtual AtspiInterfaces DoGetInterfaces() const;
+
public:
/**
* @brief Gets the highlight actor.
* @brief Acquires Accessible object from Actor object.
*
* @param[in] actor Actor object
- * @param[in] isRoot True, if it's top level object (window)
*
* @return The handle to Accessible object
*/
- static Accessible* Get(Dali::Actor actor, bool isRoot = false);
+ static Accessible* Get(Dali::Actor actor);
+
+ /**
+ * @brief Obtains the DBus interface name for the specified AT-SPI interface.
+ *
+ * @param interface AT-SPI interface identifier (e.g. AtspiInterface::ACCESSIBLE)
+ * @return AT-SPI interface name (e.g. "org.a11y.atspi.Accessible")
+ */
+ static std::string GetInterfaceName(AtspiInterface interface);
+
+ /**
+ * @brief Downcasts an Accessible pointer to an AT-SPI interface pointer.
+ *
+ * @tparam I Desired AT-SPI interface
+ *
+ * @param obj Object to cast.
+ *
+ * @return Pointer to an AT-SPI interface or null if the interface is not implemented.
+ */
+ template<AtspiInterface I>
+ static AtspiInterfaceType<I>* DownCast(Accessible* obj)
+ {
+ if(!obj || !obj->GetInterfaces()[I])
+ {
+ return nullptr;
+ }
+
+ return dynamic_cast<AtspiInterfaceType<I>*>(obj);
+ }
private:
friend class Bridge;
mutable std::weak_ptr<Bridge::Data> mBridgeData;
+ mutable AtspiInterfaces mInterfaces;
+ AtspiEvents mSuppressedEvents;
bool mIsOnRootLevel = false;
}; // Accessible class
+namespace Internal
+{
+template<>
+struct AtspiInterfaceTypeHelper<AtspiInterface::ACCESSIBLE>
+{
+ using Type = Accessible;
+};
+} // namespace Internal
+
} // namespace Dali::Accessibility
#endif // DALI_ADAPTOR_ATSPI_ACCESSIBLE_H
* @return true on success, false otherwise
*/
virtual bool DoAction(const std::string& name) = 0;
+
+ /**
+ * @brief Downcasts an Accessible to an Action.
+ *
+ * @param obj The Accessible
+ * @return An Action or null
+ *
+ * @see Dali::Accessibility::Accessible::DownCast()
+ */
+ static inline Action* DownCast(Accessible* obj);
};
+namespace Internal
+{
+template<>
+struct AtspiInterfaceTypeHelper<AtspiInterface::ACTION>
+{
+ using Type = Action;
+};
+} // namespace Internal
+
+inline Action* Action::DownCast(Accessible* obj)
+{
+ return Accessible::DownCast<AtspiInterface::ACTION>(obj);
+}
+
} // namespace Dali::Accessibility
#endif // DALI_ADAPTOR_ATSPI_ACTION_H
* @return String with version
*/
virtual std::string GetVersion() const = 0;
+
+ /**
+ * @brief Downcasts an Accessible to an Application.
+ *
+ * @param obj The Accessible
+ * @return An Application or null
+ *
+ * @see Dali::Accessibility::Accessible::DownCast()
+ */
+ static inline Application* DownCast(Accessible* obj);
};
+namespace Internal
+{
+template<>
+struct AtspiInterfaceTypeHelper<AtspiInterface::APPLICATION>
+{
+ using Type = Application;
+};
+} // namespace Internal
+
+inline Application* Application::DownCast(Accessible* obj)
+{
+ return Accessible::DownCast<AtspiInterface::APPLICATION>(obj);
+}
+
} // namespace Dali::Accessibility
#endif // DALI_ADAPTOR_ATSPI_APPLICATION_H
/**
* @brief Interface enabling advanced quering of accessibility objects.
*
- * @note since all mathods can be implemented inside bridge,
- * none methods have to be overrided
+ * @note Since all methods can be implemented inside bridge,
+ * no methods have to be overriden.
*/
class DALI_ADAPTOR_API Collection : public virtual Accessible
{
+ /**
+ * @brief Downcasts an Accessible to a Collection.
+ *
+ * @param obj The Accessible
+ * @return A Collection or null
+ *
+ * @see Dali::Accessibility::Accessible::DownCast()
+ */
+ static inline Collection* DownCast(Accessible* obj);
};
+namespace Internal
+{
+template<>
+struct AtspiInterfaceTypeHelper<AtspiInterface::COLLECTION>
+{
+ using Type = Collection;
+};
+} // namespace Internal
+
+inline Collection* Collection::DownCast(Accessible* obj)
+{
+ return Accessible::DownCast<AtspiInterface::COLLECTION>(obj);
+}
+
} // namespace Dali::Accessibility
#endif // DALI_ADAPTOR_ATSPI_COLLECTION_H
* @see Dali::Accessibility::Point
*/
virtual bool IsAccessibleContainingPoint(Point point, CoordinateType type) const;
+
+ /**
+ * @brief Downcasts an Accessible to a Component.
+ *
+ * @param obj The Accessible
+ * @return A Component or null
+ *
+ * @see Dali::Accessibility::Accessible::DownCast()
+ */
+ static inline Component* DownCast(Accessible* obj);
};
+namespace Internal
+{
+template<>
+struct AtspiInterfaceTypeHelper<AtspiInterface::COMPONENT>
+{
+ using Type = Component;
+};
+} // namespace Internal
+
+inline Component* Component::DownCast(Accessible* obj)
+{
+ return Accessible::DownCast<AtspiInterface::COMPONENT>(obj);
+}
+
} // namespace Dali::Accessibility
#endif // DALI_ADAPTOR_ATSPI_COMPONENT_H
* @return true on success, false otherwise
*/
virtual bool SetTextContents(std::string newContents) = 0;
+
+ /**
+ * @brief Downcasts an Accessible to an EditableText.
+ *
+ * @param obj The Accessible
+ * @return An EditableText or null
+ *
+ * @see Dali::Accessibility::Accessible::DownCast()
+ */
+ static inline EditableText* DownCast(Accessible* obj);
};
+namespace Internal
+{
+template<>
+struct AtspiInterfaceTypeHelper<AtspiInterface::EDITABLE_TEXT>
+{
+ using Type = EditableText;
+};
+} // namespace Internal
+
+inline EditableText* EditableText::DownCast(Accessible* obj)
+{
+ return Accessible::DownCast<AtspiInterface::EDITABLE_TEXT>(obj);
+}
+
} // namespace Dali::Accessibility
#endif // DALI_ADAPTOR_ATSPI_EDITABLE_TEXT_H
* @return True if hyperlink object is valid, false otherwise
*/
virtual bool IsValid() const = 0;
+
+ /**
+ * @brief Downcasts an Accessible to a Hyperlink.
+ *
+ * @param obj The Accessible
+ * @return A Hyperlink or null
+ *
+ * @see Dali::Accessibility::Accessible::DownCast()
+ */
+ static inline Hyperlink* DownCast(Accessible* obj);
};
+namespace Internal
+{
+template<>
+struct AtspiInterfaceTypeHelper<AtspiInterface::HYPERLINK>
+{
+ using Type = Hyperlink;
+};
+} // namespace Internal
+
+inline Hyperlink* Hyperlink::DownCast(Accessible* obj)
+{
+ return Accessible::DownCast<AtspiInterface::HYPERLINK>(obj);
+}
+
} // namespace Dali::Accessibility
#endif // DALI_ADAPTOR_ATSPI_HYPERLINK_H
* @return The number of hyperlinks (zero if none or -1 if the number cannot be determined)
*/
virtual std::int32_t GetLinkCount() const = 0;
+
+ /**
+ * @brief Downcasts an Accessible to a Hypertext.
+ *
+ * @param obj The Accessible
+ * @return A Hypertext or null
+ *
+ * @see Dali::Accessibility::Accessible::DownCast()
+ */
+ static inline Hypertext* DownCast(Accessible* obj);
};
+namespace Internal
+{
+template<>
+struct AtspiInterfaceTypeHelper<AtspiInterface::HYPERTEXT>
+{
+ using Type = Hypertext;
+};
+} // namespace Internal
+
+inline Hypertext* Hypertext::DownCast(Accessible* obj)
+{
+ return Accessible::DownCast<AtspiInterface::HYPERTEXT>(obj);
+}
+
} // namespace Dali::Accessibility
#endif // DALI_ADAPTOR_ATSPI_HYPERTEXT_H
* @see Dali::Accessibility::Selection::DeselectSelectedChild
*/
virtual bool DeselectChild(int childIndex) = 0;
+
+ /**
+ * @brief Downcasts an Accessible to a Selection.
+ *
+ * @param obj The Accessible
+ * @return A Selection or null
+ *
+ * @see Dali::Accessibility::Accessible::DownCast()
+ */
+ static inline Selection* DownCast(Accessible* obj);
};
+namespace Internal
+{
+template<>
+struct AtspiInterfaceTypeHelper<AtspiInterface::SELECTION>
+{
+ using Type = Selection;
+};
+} // namespace Internal
+
+inline Selection* Selection::DownCast(Accessible* obj)
+{
+ return Accessible::DownCast<AtspiInterface::SELECTION>(obj);
+}
+
} // namespace Dali::Accessibility
#endif // DALI_ADAPTOR_ATSPI_SELECTION_H
--- /dev/null
+#ifndef DALI_ADAPTOR_ATSPI_SOCKET_H
+#define DALI_ADAPTOR_ATSPI_SOCKET_H
+
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/atspi-interfaces/accessible.h>
+
+namespace Dali::Accessibility
+{
+/**
+ * @brief A Socket is the root of the AT-SPI tree that can be embedded as a subtree
+ * in the tree belonging to another process (the Plug). The Plug initiates the Plug-Socket
+ * connection by calling Embed() and terminates it by calling Unembed().
+ *
+ * See AT_SPI2_CORE/xml/Socket.xml for a description of this interface in XML format.
+ */
+class DALI_ADAPTOR_API Socket : public virtual Accessible
+{
+public:
+ /**
+ * @brief Establishes the Plug-Socket connection.
+ *
+ * @param plug Address of the Plug (remote parent)
+ * @return Address of the Socket
+ */
+ virtual Address Embed(Address plug) = 0;
+
+ /**
+ * @brief Terminates the Plug-Socket connection.
+ *
+ * @param plug Address of the Plug (remote parent)
+ */
+ virtual void Unembed(Address plug) = 0;
+
+ /**
+ * @brief Downcasts an Accessible to a Socket.
+ *
+ * @param obj The Accessible
+ * @return A Socket or null
+ *
+ * @see Dali::Accessibility::Accessible::DownCast()
+ */
+ static inline Socket* DownCast(Accessible* obj);
+};
+
+namespace Internal
+{
+template<>
+struct AtspiInterfaceTypeHelper<AtspiInterface::SOCKET>
+{
+ using Type = Socket;
+};
+} // namespace Internal
+
+inline Socket* Socket::DownCast(Accessible* obj)
+{
+ return Accessible::DownCast<AtspiInterface::SOCKET>(obj);
+}
+
+} // namespace Dali::Accessibility
+
+#endif // DALI_ADAPTOR_ATSPI_SOCKET_H
// EXTERNAL INCLUDES
#include <string>
+#include <dali/public-api/math/rect.h>
// INTERNAL INCLUDES
#include <dali/devel-api/adaptor-framework/accessibility.h>
* @remarks This method is `SetSelection` in DBus method.
*/
virtual bool SetRangeOfSelection(std::size_t selectionIndex, std::size_t startOffset, std::size_t endOffset) = 0;
+
+ /**
+ * @brief Gets the bounding box for text within a range in text.
+ *
+ * @param[in] startOffset The index of first character
+ * @param[in] endOffset The index of first character after the last one expected
+ * @param[in] type The enumeration with type of coordinate system
+ *
+ * @return Rect<> giving the position and size of the specified range of text
+ * @remarks This method is `GetRangeExtents` in DBus method.
+ */
+ virtual Rect<> GetRangeExtents(std::size_t startOffset, std::size_t endOffset, CoordinateType type) = 0;
+
+ /**
+ * @brief Downcasts an Accessible to a Text.
+ *
+ * @param obj The Accessible
+ * @return A Text or null
+ *
+ * @see Dali::Accessibility::Accessible::DownCast()
+ */
+ static inline Text* DownCast(Accessible* obj);
};
+namespace Internal
+{
+template<>
+struct AtspiInterfaceTypeHelper<AtspiInterface::TEXT>
+{
+ using Type = Text;
+};
+} // namespace Internal
+
+inline Text* Text::DownCast(Accessible* obj)
+{
+ return Accessible::DownCast<AtspiInterface::TEXT>(obj);
+}
+
} // namespace Dali::Accessibility
#endif // DALI_ADAPTOR_ATSPI_TEXT_H
* @return The lowest increment
*/
virtual double GetMinimumIncrement() const = 0;
+
+ /**
+ * @brief Downcasts an Accessible to a Value.
+ *
+ * @param obj The Accessible
+ * @return A Value or null
+ *
+ * @see Dali::Accessibility::Accessible::DownCast()
+ */
+ static inline Value* DownCast(Accessible* obj);
+};
+
+namespace Internal
+{
+template<>
+struct AtspiInterfaceTypeHelper<AtspiInterface::VALUE>
+{
+ using Type = Value;
};
+} // namespace Internal
+
+inline Value* Value::DownCast(Accessible* obj)
+{
+ return Accessible::DownCast<AtspiInterface::VALUE>(obj);
+}
} // namespace Dali::Accessibility
${adaptor_devel_api_dir}/adaptor-framework/vector-animation-renderer.h
${adaptor_devel_api_dir}/adaptor-framework/vector-animation-renderer-plugin.h
${adaptor_devel_api_dir}/adaptor-framework/vector-image-renderer.h
- ${adaptor_devel_api_dir}/adaptor-framework/vector-image-renderer-plugin.h
${adaptor_devel_api_dir}/adaptor-framework/video-player.h
${adaptor_devel_api_dir}/adaptor-framework/video-player-plugin.h
${adaptor_devel_api_dir}/adaptor-framework/web-engine.h
${adaptor_devel_api_dir}/atspi-interfaces/hyperlink.h
${adaptor_devel_api_dir}/atspi-interfaces/hypertext.h
${adaptor_devel_api_dir}/atspi-interfaces/selection.h
+ ${adaptor_devel_api_dir}/atspi-interfaces/socket.h
${adaptor_devel_api_dir}/atspi-interfaces/text.h
${adaptor_devel_api_dir}/atspi-interfaces/value.h
)
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <dali/devel-api/text-abstraction/font-client.h>
// INTERNAL INCLUDES
+#include <dali/internal/imaging/common/image-operations.h>
#include <dali/internal/text/text-abstraction/font-client-impl.h>
namespace Dali
const uint32_t FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE = 64u; //Found this value from toolkit
+// FontClient::GlyphBufferData
+
FontClient::GlyphBufferData::GlyphBufferData()
: buffer{nullptr},
width{0u},
outlineOffsetX{0},
outlineOffsetY{0},
format{Pixel::A8},
+ compressionType(CompressionType::NO_COMPRESSION),
isColorEmoji{false},
- isColorBitmap{false}
+ isColorBitmap{false},
+ isBufferOwned{false}
{
}
FontClient::GlyphBufferData::~GlyphBufferData()
{
-}
+ if(isBufferOwned)
+ {
+ free(buffer);
+ }
+}
+
+size_t FontClient::GlyphBufferData::Compress(const uint8_t* const __restrict__ inBuffer, GlyphBufferData& __restrict__ outBufferData)
+{
+ size_t bufferSize = 0u;
+ uint8_t*& __restrict__ compressedBuffer = outBufferData.buffer;
+ switch(outBufferData.compressionType)
+ {
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION:
+ {
+ bufferSize = outBufferData.width * outBufferData.height * Pixel::GetBytesPerPixel(outBufferData.format);
+
+ compressedBuffer = (uint8_t*)malloc(bufferSize);
+ if(DALI_UNLIKELY(compressedBuffer == nullptr))
+ {
+ return 0u;
+ }
+ outBufferData.isBufferOwned = true;
+
+ // Copy buffer without compress
+ memcpy(compressedBuffer, inBuffer, bufferSize);
+ break;
+ }
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4:
+ {
+ const uint32_t widthByte = outBufferData.width * Pixel::GetBytesPerPixel(outBufferData.format);
+ const uint32_t componentCount = (widthByte >> 1);
+ const bool considerPadding = (widthByte & 1) ? true : false;
+
+ // For BIT_PER_PIXEL_4 type, we can know final compressed buffer size immediatly.
+ bufferSize = outBufferData.height * (componentCount + (considerPadding ? 1 : 0));
+ compressedBuffer = (uint8_t*)malloc(bufferSize);
+ if(DALI_UNLIKELY(compressedBuffer == nullptr))
+ {
+ return 0u;
+ }
+ outBufferData.isBufferOwned = true;
+
+ uint8_t* __restrict__ outBufferPtr = compressedBuffer;
+ const uint8_t* __restrict__ inBufferPtr = inBuffer;
+
+ // Compress for each line
+ for(uint32_t y = 0; y < outBufferData.height; ++y)
+ {
+ for(uint32_t x = 0; x < componentCount; ++x)
+ {
+ const uint8_t v0 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
+ const uint8_t v1 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
+
+ *(outBufferPtr++) = (v0 << 4) | v1;
+ }
+ if(considerPadding)
+ {
+ *(outBufferPtr++) = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
+ }
+ }
+ break;
+ }
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4:
+ {
+ const uint32_t widthByte = outBufferData.width * Pixel::GetBytesPerPixel(outBufferData.format);
+
+ // Allocate temperal buffer. Note that RLE4 can be bigger than original buffer.
+ uint8_t* __restrict__ tempBuffer = (uint8_t*)malloc(outBufferData.height * (widthByte + 1));
+ if(DALI_UNLIKELY(tempBuffer == nullptr))
+ {
+ return 0u;
+ }
+
+ uint8_t* __restrict__ outBufferPtr = tempBuffer;
+ const uint8_t* __restrict__ inBufferPtr = inBuffer;
+
+ bufferSize = 0u;
+
+ // Compress for each line
+ for(uint32_t y = 0; y < outBufferData.height; ++y)
+ {
+ uint32_t encodedByte = 0;
+ while(encodedByte < widthByte)
+ {
+ // Case 1 : Remain only 1 byte
+ if(DALI_UNLIKELY(encodedByte + 1 == widthByte))
+ {
+ const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v0 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
+ *(outBufferPtr++) = v0;
+ ++encodedByte;
+ ++bufferSize;
+ }
+ // Case 2 : Remain only 2 byte
+ else if(DALI_UNLIKELY(encodedByte + 2 == widthByte))
+ {
+ const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v0 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
+ const uint8_t prev1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v1 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev1) & 0x0f; // Intented underflow
+ encodedByte += 2;
+ if(v0 == v1)
+ {
+ *(outBufferPtr++) = 0x80 | v0;
+ ++bufferSize;
+ }
+ else
+ {
+ *(outBufferPtr++) = 0x10 | v0;
+ *(outBufferPtr++) = v1 << 4;
+ bufferSize += 2;
+ }
+ }
+ // Case 3 : Normal case. Remain byte bigger or equal than 3.
+ else
+ {
+ // Compress rule -
+ // Read 2 byte as v0 and v1.
+ // - If v0 == v1, We can compress. mark the first bit as 1. and remain 3 bit mark as the "runLength - 2".
+ // runLength can be maximum 9.
+ // - If v0 != v1, We cannot compress. mark the first bit as 0. and remain 3 bit mark as the "(nonRunLength - 1) / 2"
+ // Due to the BitPerPixel is 4, nonRunLength should be odd value.
+ // nonRunLength cutted if v0 == v1.
+ // nonRunLength can be maximum 15.
+
+ const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v0 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
+ const uint8_t prev1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v1 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev1) & 0x0f; // Intented underflow
+ encodedByte += 2;
+ // We can compress by RLE
+ if(v0 == v1)
+ {
+ uint8_t runLength = 2;
+ while(encodedByte < widthByte && runLength < 9)
+ {
+ const uint8_t prev2 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v2 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prev2) & 0x0f; // Intented underflow
+ if(v2 == v0)
+ {
+ ++inBufferPtr;
+ ++encodedByte;
+ ++runLength;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // Update (runLength - 2) result.
+ *(outBufferPtr++) = ((0x8 | (runLength - 2)) << 4) | v0;
+ ++bufferSize;
+ }
+ // We cannot compress by RLE.
+ else
+ {
+ // Read one more value.
+ const uint8_t prev2 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v2 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev2) & 0x0f; // Intented underflow
+ ++encodedByte;
+
+ uint8_t nonRunLength = 3;
+ uint8_t* nonRunLengthHeaderPtr = outBufferPtr;
+ *(outBufferPtr++) = v0;
+ *(outBufferPtr++) = (v1 << 4) | v2;
+ bufferSize += 2;
+ while(encodedByte < widthByte && nonRunLength < 15)
+ {
+ if(DALI_LIKELY(encodedByte + 1 < widthByte))
+ {
+ const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t w0 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prew0) & 0x0f; // Intented underflow
+ const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr + 1 - widthByte));
+ const uint8_t w1 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr + 1)) - prew1) & 0x0f; // Intented underflow
+ if(w0 == w1)
+ {
+ // Stop non-compress logic.
+ break;
+ }
+ else
+ {
+ ++bufferSize;
+ *(outBufferPtr++) = (w0 << 4) | w1;
+ inBufferPtr += 2;
+ encodedByte += 2;
+ nonRunLength += 2;
+ }
+ }
+ else
+ {
+ // Edge case. There is only one pixel remained.
+ const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t w0 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prew0) & 0x0f; // Intented underflow
+ {
+ ++bufferSize;
+ *(outBufferPtr++) = (w0 << 4);
+ ++encodedByte;
+ ++inBufferPtr;
+ // Increase nonRunLength 2 even latest value is invalid.
+ nonRunLength += 2;
+ }
+ }
+ }
+
+ // Update (nonRunLength-1)/2 result into header.
+ *(nonRunLengthHeaderPtr) |= (nonRunLength >> 1) << 4;
+ }
+ }
+ }
+ }
+
+ // Allocate and copy data
+ compressedBuffer = (uint8_t*)malloc(bufferSize);
+ if(DALI_UNLIKELY(compressedBuffer == nullptr))
+ {
+ free(tempBuffer);
+ return 0u;
+ }
+ outBufferData.isBufferOwned = true;
+
+ memcpy(compressedBuffer, tempBuffer, bufferSize);
+ free(tempBuffer);
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return bufferSize;
+}
+
+void FontClient::GlyphBufferData::Decompress(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer)
+{
+ if(DALI_UNLIKELY(outBuffer == nullptr))
+ {
+ return;
+ }
+
+ switch(inBufferData.compressionType)
+ {
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION:
+ {
+ const auto bufferSize = inBufferData.width * inBufferData.height * Pixel::GetBytesPerPixel(inBufferData.format);
+
+ // Copy buffer without compress
+ memcpy(outBuffer, inBufferData.buffer, bufferSize);
+ break;
+ }
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4:
+ {
+ const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+ const uint32_t componentCount = (widthByte >> 1);
+ const bool considerPadding = (widthByte & 1) ? true : false;
+
+ uint8_t* __restrict__ outBufferPtr = outBuffer;
+ const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer;
+
+ // Compress for each line
+ for(uint32_t y = 0; y < inBufferData.height; ++y)
+ {
+ for(uint32_t x = 0; x < componentCount; ++x)
+ {
+ const uint8_t v = *(inBufferPtr++);
+ const uint8_t v0 = (v >> 4) & 0x0f;
+ const uint8_t v1 = v & 0x0f;
+
+ *(outBufferPtr++) = (v0 << 4) | v0;
+ *(outBufferPtr++) = (v1 << 4) | v1;
+ }
+ if(considerPadding)
+ {
+ const uint8_t v = *(inBufferPtr++);
+ *(outBufferPtr++) = (v << 4) | v;
+ }
+ }
+ break;
+ }
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4:
+ {
+ const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+
+ uint8_t* __restrict__ outBufferPtr = outBuffer;
+ const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer;
+ // Compress for each line
+ for(uint32_t y = 0; y < inBufferData.height; ++y)
+ {
+ uint32_t x = 0;
+ uint32_t decodedByte = 0;
+ while(decodedByte < widthByte)
+ {
+ const uint8_t v = *(inBufferPtr++);
+ ++x;
+ // Compress by RLE
+ if(v & 0x80)
+ {
+ const uint8_t runLength = ((v >> 4) & 0x07) + 2u;
+ decodedByte += runLength;
+ const uint8_t repeatValue = v & 0x0f;
+ for(uint8_t iter = 0; iter < runLength; ++iter)
+ {
+ const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+ const uint8_t v0 = (prev0 + repeatValue) & 0x0f;
+ *(outBufferPtr++) = (v0 << 4) | v0;
+ }
+ }
+ // Not compress by RLE
+ else
+ {
+ const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
+ decodedByte += nonRunLength;
+ // First value.
+ const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+ const uint8_t v0 = (prev0 + (v & 0x0f)) & 0x0f;
+ *(outBufferPtr++) = (v0 << 4) | v0;
+
+ const bool ignoreLastValue = decodedByte > widthByte ? true : false;
+ if(DALI_UNLIKELY(ignoreLastValue))
+ {
+ --decodedByte;
+ for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
+ {
+ const uint8_t w = *(inBufferPtr++);
+ const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+ const uint8_t w0 = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
+ const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte + 1)) & 0x0f;
+ const uint8_t w1 = (prew1 + (w & 0x0f)) & 0x0f;
+ ++x;
+
+ *(outBufferPtr++) = (w0 << 4) | w0;
+ *(outBufferPtr++) = (w1 << 4) | w1;
+ }
+ // Last value.
+ {
+ const uint8_t w = ((*(inBufferPtr++)) >> 4) & 0x0f;
+ const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+ const uint8_t w0 = (prew0 + w) & 0x0f;
+ ++x;
+
+ *(outBufferPtr++) = (w0 << 4) | w0;
+ }
+ }
+ else
+ {
+ for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
+ {
+ const uint8_t w = *(inBufferPtr++);
+ const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+ const uint8_t w0 = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
+ const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte + 1)) & 0x0f;
+ const uint8_t w1 = (prew1 + (w & 0x0f)) & 0x0f;
+ ++x;
+
+ *(outBufferPtr++) = (w0 << 4) | w0;
+ *(outBufferPtr++) = (w1 << 4) | w1;
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void FontClient::GlyphBufferData::DecompressScanline(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer, uint32_t& __restrict__ offset)
+{
+ switch(inBufferData.compressionType)
+ {
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION:
+ {
+ const auto bufferSize = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+
+ // Copy buffer without compress
+ memcpy(outBuffer, inBufferData.buffer + offset, bufferSize);
+
+ // Update offset
+ offset += bufferSize;
+ break;
+ }
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4:
+ {
+ const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+ const uint32_t componentCount = (widthByte >> 1);
+ const bool considerPadding = (widthByte & 1) ? true : false;
+
+ uint8_t* __restrict__ outBufferPtr = outBuffer;
+ const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
+
+ // Decompress scanline
+ for(uint32_t x = 0; x < componentCount; ++x)
+ {
+ const uint8_t v = *(inBufferPtr++);
+ const uint8_t v0 = (v >> 4) & 0x0f;
+ const uint8_t v1 = v & 0x0f;
+
+ *(outBufferPtr++) = (v0 << 4) | v0;
+ *(outBufferPtr++) = (v1 << 4) | v1;
+ }
+ if(considerPadding)
+ {
+ const uint8_t v = *(inBufferPtr++);
+ *(outBufferPtr++) = (v << 4) | v;
+ }
+
+ // Update offset
+ offset += (widthByte + 1u) >> 1u;
+ break;
+ }
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4:
+ {
+ const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+
+ uint8_t* __restrict__ outBufferPtr = outBuffer;
+ const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
+
+ // If offset is zero, fill outBuffer as 0 first.
+ if(DALI_UNLIKELY(offset == 0))
+ {
+ memset(outBufferPtr, 0, widthByte);
+ }
+
+ // Decompress scanline
+ uint32_t decodedByte = 0;
+ while(decodedByte < widthByte)
+ {
+ const uint8_t v = *(inBufferPtr++);
+ ++offset;
+ // Compress by RLE
+ if(v & 0x80)
+ {
+ const uint8_t runLength = ((v >> 4) & 0x07) + 2u;
+ decodedByte += runLength;
+ const uint8_t repeatValue = (v & 0x0f);
+ for(uint8_t iter = 0; iter < runLength; ++iter)
+ {
+ const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
+ const uint8_t v0 = (prev0 + repeatValue) & 0x0f;
+ *(outBufferPtr++) = (v0 << 4) | v0;
+ }
+ }
+ // Not compress by RLE
+ else
+ {
+ const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
+ decodedByte += nonRunLength;
+ // First value.
+ const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
+ const uint8_t v0 = (prev0 + (v & 0x0f)) & 0x0f;
+ *(outBufferPtr++) = (v0 << 4) | v0;
+
+ const bool ignoreLastValue = decodedByte > widthByte ? true : false;
+ if(DALI_UNLIKELY(ignoreLastValue))
+ {
+ --decodedByte;
+ for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
+ {
+ const uint8_t w = *(inBufferPtr++);
+ const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
+ const uint8_t w0 = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
+ const uint8_t prew1 = (*(outBufferPtr + 1)) & 0x0f;
+ const uint8_t w1 = (prew1 + (w & 0x0f)) & 0x0f;
+ ++offset;
+
+ *(outBufferPtr++) = (w0 << 4) | w0;
+ *(outBufferPtr++) = (w1 << 4) | w1;
+ }
+ // Last value.
+ {
+ const uint8_t w = ((*(inBufferPtr++)) >> 4) & 0x0f;
+ const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
+ const uint8_t w0 = (prew0 + w) & 0x0f;
+ ++offset;
+
+ *(outBufferPtr++) = (w0 << 4) | w0;
+ }
+ }
+ else
+ {
+ for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
+ {
+ const uint8_t w = *(inBufferPtr++);
+ const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
+ const uint8_t w0 = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
+ const uint8_t prew1 = (*(outBufferPtr + 1)) & 0x0f;
+ const uint8_t w1 = (prew1 + (w & 0x0f)) & 0x0f;
+ ++offset;
+
+ *(outBufferPtr++) = (w0 << 4) | w0;
+ *(outBufferPtr++) = (w1 << 4) | w1;
+ }
+ }
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+// FontClient
FontClient FontClient::Get()
{
#define DALI_PLATFORM_TEXT_ABSTRACTION_FONT_CLIENT_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
~GlyphBufferData();
- unsigned char* buffer; ///< The glyph's bitmap buffer data.
- unsigned int width; ///< The width of the bitmap.
- unsigned int height; ///< The height of the bitmap.
- int outlineOffsetX; ///< The additional horizontal offset to be added for the glyph's position for outline.
- int outlineOffsetY; ///< The additional vertical offset to be added for the glyph's position for outline.
- Pixel::Format format; ///< The pixel's format of the bitmap.
- bool isColorEmoji : 1; ///< Whether the glyph is an emoji.
- bool isColorBitmap : 1; ///< Whether the glyph is a color bitmap.
+ // Compression method of buffer. Each buffer compressed line by line
+ enum class CompressionType
+ {
+ NO_COMPRESSION = 0, // No compression
+ BPP_4 = 1, // Compress as 4 bit. Color become value * 17 (0x00, 0x11, 0x22, ... 0xee, 0xff).
+ // Only works for Pixel::L8 format
+ RLE_4 = 2, // Compress as 4 bit, and Run-Length-Encode. For more high compress rate, we store difference between previous scanline.
+ // Only works for Pixel::L8 format
+ };
+
+ /**
+ * @brief Helper static function to compress raw buffer from inBuffer to outBufferData.buffer.
+ * outBufferData will have it's own buffer.
+ *
+ * @pre outBufferData must not have it's own buffer.
+ * @param[in] inBuffer The input raw data.
+ * @param[in, out] outBufferData The output glyph buffer data.
+ * @return Size of compressed out buffer, Or 0 if compress failed.
+ */
+ static size_t Compress(const uint8_t* const inBuffer, GlyphBufferData& outBufferData);
+
+ /**
+ * @brief Helper static function to decompress raw buffer from inBuffer to outBufferPtr.
+ * If outBuffer is nullptr, Do nothing.
+ *
+ * @pre outBuffer memory should be allocated.
+ * @param[in] inBufferData The input glyph buffer data.
+ * @param[in, out] outBuffer The output pointer of raw buffer data.
+ */
+ static void Decompress(const GlyphBufferData& inBufferData, uint8_t* outBuffer);
+
+ /**
+ * @brief Special Helper static function to decompress raw buffer from inBuffer to outBuffer one scanline.
+ * After decompress one scanline successed, offset will be changed.
+ *
+ * @pre outBuffer memory should be allocated.
+ * @pre if inBufferData's compression type is RLE4, outBuffer memory should store the previous scanline data.
+ * @param[in] inBufferData The input glyph buffer data.
+ * @param[in, out] outBuffer The output pointer of raw buffer data.
+ * @param[in, out] offset The offset of input. It will be changed as next scanline's offset.
+ */
+ static void DecompressScanline(const GlyphBufferData& inBufferData, uint8_t* outBuffer, uint32_t& offset);
+
+ uint8_t* buffer; ///< The glyph's bitmap buffer data.
+ uint32_t width; ///< The width of the bitmap.
+ uint32_t height; ///< The height of the bitmap.
+ int outlineOffsetX; ///< The additional horizontal offset to be added for the glyph's position for outline.
+ int outlineOffsetY; ///< The additional vertical offset to be added for the glyph's position for outline.
+ Pixel::Format format; ///< The pixel's format of the bitmap.
+ CompressionType compressionType : 3; ///< The type of buffer compression.
+ bool isColorEmoji : 1; ///< Whether the glyph is an emoji.
+ bool isColorBitmap : 1; ///< Whether the glyph is a color bitmap.
+ bool isBufferOwned : 1; ///< Whether the glyph's bitmap buffer data owned by this class or not. Becareful when you use non-owned buffer data.
};
/**
#define DALI_TEXT_ABSTRACTION_FONT_LIST_H
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
}
+ FontDescription(const FontPath& path,
+ const FontFamily& family,
+ const FontWidth::Type& width,
+ const FontWeight::Type& weight,
+ const FontSlant::Type& slant,
+ const Type& type)
+ : path(path),
+ family(family),
+ width(width),
+ weight(weight),
+ slant(slant),
+ type(type)
+ {
+ }
+
~FontDescription()
{
}
}
/**
+ * @copydoc Dali::Integration::SceneHolder::InterceptKeyEventSignal()
+ */
+ Dali::Integration::SceneHolder::KeyEventGeneratedSignalType& InterceptKeyEventSignal()
+ {
+ return mScene.InterceptKeyEventSignal();
+ }
+
+ /**
* @copydoc Dali::Integration::SceneHolder::TouchedSignal()
*/
Dali::Integration::SceneHolder::TouchEventSignalType& TouchedSignal()
return GetImplementation(*this).KeyEventGeneratedSignal();
}
+SceneHolder::KeyEventGeneratedSignalType& SceneHolder::InterceptKeyEventSignal()
+{
+ return GetImplementation(*this).InterceptKeyEventSignal();
+}
+
SceneHolder::TouchEventSignalType& SceneHolder::TouchedSignal()
{
return GetImplementation(*this).TouchedSignal();
KeyEventGeneratedSignalType& KeyEventGeneratedSignal();
/**
+ * @brief This signal is emitted when key event is received.
+ * Intercepts KeyEvents in the window before dispatching KeyEvents to the control.
+ * If a KeyEvent is consumed, no KeyEvent is delivered to the control.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ * bool YourCallbackName(const KeyEvent& event);
+ * @endcode
+ * @return The signal to connect to
+ */
+ KeyEventGeneratedSignalType& InterceptKeyEventSignal();
+
+ /**
* @brief This signal is emitted when the screen is touched and when the touch ends
* (i.e. the down & up touch events only).
*
#include <dali/internal/accessibility/bridge/dbus.h>
#include <dali/public-api/dali-adaptor-common.h>
-/* DBus Interfaces */
+// DBus names
#define A11yDbusName "org.a11y.Bus"
-#define A11yDbusPath "/org/a11y/bus"
#define A11yDbusStatusInterface "org.a11y.Status"
#define AtspiDbusNameRegistry "org.a11y.atspi.Registry"
+#define DirectReadingDBusName "org.tizen.ScreenReader"
+#define DirectReadingDBusInterface "org.tizen.DirectReading"
+
+// DBus paths
+
+#define A11yDbusPath "/org/a11y/bus"
+#define AtspiDbusPathCache "/org/a11y/atspi/cache"
+#define AtspiDbusPathDec "/org/a11y/atspi/registry/deviceeventcontroller"
#define AtspiDbusPathRegistry "/org/a11y/atspi/registry"
-#define AtspiDbusInterfaceRegistry "org.a11y.atspi.Registry"
#define AtspiDbusPathRoot "/org/a11y/atspi/accessible/root"
-#define AtspiDbusInterfaceSocket "org.a11y.atspi.Socket"
#define AtspiPath "/org/a11y/atspi/accessible"
-#define AtspiDbusInterfaceAccessible "org.a11y.atspi.Accessible"
-#define AtspiDbusInterfaceAction "org.a11y.atspi.Action"
-#define AtspiDbusInterfaceApplication "org.a11y.atspi.Application"
-#define AtspiDbusInterfaceCache "org.a11y.atspi.Cache"
-#define AtspiDbusPathCache "/org/a11y/atspi/cache"
-#define AtspiDbusInterfaceCollection "org.a11y.atspi.Collection"
-#define AtspiDbusInterfaceComponent "org.a11y.atspi.Component"
-#define AtspiDbusInterfaceDocument "org.a11y.atspi.Document"
-#define AtspiDbusInterfaceEditableText "org.a11y.atspi.EditableText"
-#define AtspiDbusInterfaceEventKeyboard "org.a11y.atspi.Event.Keyboard"
-#define AtspiDbusInterfaceEventMouse "org.a11y.atspi.Event.Mouse"
-#define AtspiDbusInterfaceEventObject "org.a11y.atspi.Event.Object"
-#define AtspiDbusInterfaceHyperlink "org.a11y.atspi.Hyperlink"
-#define AtspiDbusInterfaceHypertext "org.a11y.atspi.Hypertext"
-#define AtspiDbusInterfaceImage "org.a11y.atspi.Image"
-#define AtspiDbusInterfaceSelection "org.a11y.atspi.Selection"
-#define AtspiDbusInterfaceTable "org.a11y.atspi.Table"
-#define AtspiDbusInterfaceTableCell "org.a11y.atspi.TableCell"
-#define AtspiDbusInterfaceText "org.a11y.atspi.Text"
-#define AtspiDbusInterfaceValue "org.a11y.atspi.Value"
-#define AtspiDbusInterfaceSocket "org.a11y.atspi.Socket"
-#define AtspiDbusInterfaceEventWindow "org.a11y.atspi.Event.Window"
-
-#define AtspiDbusPathDec "/org/a11y/atspi/registry/deviceeventcontroller"
-#define AtspiDbusInterfaceDec "org.a11y.atspi.DeviceEventController"
-#define AtspiDbusInterfaceDeviceEventListener "org.a11y.atspi.DeviceEventListener"
-
-#define DirectReadingDBusName "org.tizen.ScreenReader"
#define DirectReadingDBusPath "/org/tizen/DirectReading"
-#define DirectReadingDBusInterface "org.tizen.DirectReading"
struct ObjectPath;
//INTERNAL INCLUDES
#include <dali/devel-api/atspi-interfaces/accessible.h>
-#include <dali/devel-api/atspi-interfaces/action.h>
-#include <dali/devel-api/atspi-interfaces/application.h>
-#include <dali/devel-api/atspi-interfaces/collection.h>
-#include <dali/devel-api/atspi-interfaces/component.h>
-#include <dali/devel-api/atspi-interfaces/editable-text.h>
-#include <dali/devel-api/atspi-interfaces/hyperlink.h>
-#include <dali/devel-api/atspi-interfaces/hypertext.h>
-#include <dali/devel-api/atspi-interfaces/selection.h>
-#include <dali/devel-api/atspi-interfaces/text.h>
-#include <dali/devel-api/atspi-interfaces/value.h>
#include <dali/devel-api/adaptor-framework/accessibility-bridge.h>
+#include <dali/devel-api/atspi-interfaces/socket.h>
#include <dali/internal/accessibility/bridge/accessibility-common.h>
#include <third-party/libunibreak/linebreak.h>
#include <third-party/libunibreak/wordbreak.h>
using namespace Dali::Accessibility;
-std::vector<std::string> Accessible::GetInterfaces() const
-{
- std::vector<std::string> tmp;
- tmp.push_back(AtspiDbusInterfaceAccessible);
- if(dynamic_cast<const Collection*>(this))
- {
- tmp.push_back(AtspiDbusInterfaceCollection);
- }
- if(dynamic_cast<const Text*>(this))
- {
- tmp.push_back(AtspiDbusInterfaceText);
- }
- if(dynamic_cast<const EditableText*>(this))
- {
- tmp.push_back(AtspiDbusInterfaceEditableText);
- }
- if(dynamic_cast<const Value*>(this))
- {
- tmp.push_back(AtspiDbusInterfaceValue);
- }
- if(dynamic_cast<const Component*>(this))
- {
- tmp.push_back(AtspiDbusInterfaceComponent);
- }
- if(auto action = dynamic_cast<const Action*>(this))
- {
- if(action->GetActionCount() > 0)
- {
- tmp.push_back(AtspiDbusInterfaceAction);
- }
- }
- if(dynamic_cast<const Selection*>(this))
- {
- tmp.push_back(AtspiDbusInterfaceSelection);
- }
- if(dynamic_cast<const Hypertext*>(this))
- {
- tmp.push_back(AtspiDbusInterfaceHypertext);
- }
- if(dynamic_cast<const Hyperlink*>(this))
- {
- tmp.push_back(AtspiDbusInterfaceHyperlink);
- }
- return tmp;
-}
-
Accessible::Accessible()
{
}
}
}
+void Accessible::EmitSocketAvailable()
+{
+ DALI_ASSERT_DEBUG(Socket::DownCast(this));
+
+ if(auto bridgeData = GetBridgeData())
+ {
+ bridgeData->mBridge->EmitSocketAvailable(this);
+ }
+}
+
void Accessible::Emit(WindowEvent event, unsigned int detail)
{
if(auto bridgeData = GetBridgeData())
for(const auto& it : relations)
{
- if(it.relationType == RelationType::CONTROLLED_BY)
+ if(it.mRelationType == RelationType::CONTROLLED_BY)
{
return false;
}
void BridgeAccessible::RegisterInterfaces()
{
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceAccessible};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::ACCESSIBLE)};
AddGetPropertyToInterface(desc, "ChildCount", &BridgeAccessible::GetChildCount);
AddGetPropertyToInterface(desc, "Name", &BridgeAccessible::GetName);
AddGetPropertyToInterface(desc, "Description", &BridgeAccessible::GetDescription);
AddFunctionToInterface(desc, "GetLocalizedRoleName", &BridgeAccessible::GetLocalizedRoleName);
AddFunctionToInterface(desc, "GetState", &BridgeAccessible::GetStates);
AddFunctionToInterface(desc, "GetAttributes", &BridgeAccessible::GetAttributes);
- AddFunctionToInterface(desc, "GetInterfaces", &BridgeAccessible::GetInterfaces);
+ AddFunctionToInterface(desc, "GetInterfaces", &BridgeAccessible::GetInterfacesAsStrings);
AddFunctionToInterface(desc, "GetChildAtIndex", &BridgeAccessible::GetChildAtIndex);
AddFunctionToInterface(desc, "GetChildren", &BridgeAccessible::GetChildren);
AddFunctionToInterface(desc, "GetIndexInParent", &BridgeAccessible::GetIndexInParent);
mDbusServer.addInterface("/", desc, true);
}
+Accessible* BridgeAccessible::FindSelf() const
+{
+ return FindCurrentObject();
+}
+
Component* BridgeAccessible::GetObjectInRelation(Accessible* obj, RelationType relationType)
{
if(!obj)
for(auto& relation : obj->GetRelationSet())
{
- if(relation.relationType == relationType)
+ if(relation.mRelationType == relationType)
{
- for(auto& address : relation.targets)
+ for(auto& target : relation.mTargets)
{
- auto component = dynamic_cast<Component*>(Find(address));
+ auto component = dynamic_cast<Component*>(target);
if(component)
{
return component;
auto relation = std::find_if(relations.begin(),
relations.end(),
[relationType](const Dali::Accessibility::Relation& relation) -> bool {
- return relation.relationType == relationType;
+ return relation.mRelationType == relationType;
});
- return relations.end() != relation && !relation->targets.empty() ? Find(relation->targets.back()) : nullptr;
+ return relations.end() != relation && !relation->mTargets.empty() ? relation->mTargets.back() : nullptr;
};
auto labellingObject = findObjectByRelationType(RelationType::LABELLED_BY);
double minimumIncrement = 0.0;
double maximumValue = 0.0;
double minimumValue = 0.0;
- auto* valueInterface = dynamic_cast<Dali::Accessibility::Value*>(self);
+ auto* valueInterface = Value::DownCast(self);
if(valueInterface)
{
currentValue = valueInterface->GetCurrent();
int32_t firstSelectedChildIndex = -1;
int32_t selectedChildCount = 0;
- auto* selfSelectionInterface = dynamic_cast<Dali::Accessibility::Selection*>(self);
+ auto* selfSelectionInterface = Selection::DownCast(self);
if(selfSelectionInterface)
{
selectedChildCount = selfSelectionInterface->GetSelectedChildrenCount();
listChildrenCount = GetItemCountOfFirstDescendantContainer(self, Role::POPUP_MENU, Role::MENU_ITEM, false);
}
- auto* textInterface = dynamic_cast<Dali::Accessibility::Text*>(self);
+ auto* textInterface = Text::DownCast(self);
std::string nameFromTextInterface = "";
if(textInterface)
{
auto parentChildCount = parent ? static_cast<int32_t>(parent->GetChildCount()) : 0;
auto parentStateSet = parent ? parent->GetStates() : States{};
bool isSelectedInParent = false;
- auto* parentSelectionInterface = dynamic_cast<Dali::Accessibility::Selection*>(parent);
+ auto* parentSelectionInterface = Selection::DownCast(parent);
if(parentSelectionInterface)
{
isSelectedInParent = parentSelectionInterface->IsChildSelected(indexInParent);
return attributes;
}
-DBus::ValueOrError<std::vector<std::string>> BridgeAccessible::GetInterfaces()
+DBus::ValueOrError<std::vector<std::string>> BridgeAccessible::GetInterfacesAsStrings()
{
- return FindSelf()->GetInterfaces();
+ return FindSelf()->GetInterfacesAsStrings();
}
int BridgeAccessible::GetChildCount()
for(auto& it : relations)
{
- ret.emplace_back(Relation{static_cast<uint32_t>(it.relationType), it.targets});
+ ret.emplace_back(Relation{static_cast<uint32_t>(it.mRelationType), it.mTargets});
}
return ret;
*/
void RegisterInterfaces();
+ /**
+ * @brief Returns the Accessible object of the currently executed DBus method call.
+ *
+ * @return The Accessible object
+ */
+ Dali::Accessibility::Accessible* FindSelf() const;
+
public:
/**
* @brief Enumeration for NeighborSearchMode.
Dali::Accessibility::Accessible* // describedByObject
>;
- using Relation = std::tuple<uint32_t, std::vector<Dali::Accessibility::Address>>;
+ using Relation = std::tuple<uint32_t, std::vector<Dali::Accessibility::Accessible*>>;
/**
* @copydoc Dali::Accessibility::Accessible::GetChildCount()
DBus::ValueOrError<std::unordered_map<std::string, std::string>> GetAttributes();
/**
- * @copydoc Dali::Accessibility::Accessible::GetInterfaces()
+ * @copydoc Dali::Accessibility::Accessible::GetInterfacesAsStrings()
*/
- DBus::ValueOrError<std::vector<std::string>> GetInterfaces();
+ DBus::ValueOrError<std::vector<std::string>> GetInterfacesAsStrings();
/**
* @brief Gets Accessible object on which surface lies the point with given coordinates.
// CLASS HEADER
#include <dali/internal/accessibility/bridge/bridge-action.h>
-// EXTERNAL INCLUDES
-#include <iostream>
-
using namespace Dali::Accessibility;
void BridgeAction::RegisterInterfaces()
{
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceAction};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::ACTION)};
AddGetPropertyToInterface(desc, "NActions", &BridgeAction::GetActionCount);
Action* BridgeAction::FindSelf() const
{
- auto self = BridgeBase::FindSelf();
- assert(self);
- auto actionInterface = dynamic_cast<Action*>(self);
- if(!actionInterface)
- {
- throw std::domain_error{"object " + self->GetAddress().ToString() + " doesn't have Action interface"};
- }
- return actionInterface;
+ return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::ACTION>();
}
DBus::ValueOrError<std::string> BridgeAction::GetActionName(int32_t index)
void BridgeApplication::RegisterInterfaces()
{
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceApplication};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::APPLICATION)};
AddGetPropertyToInterface(desc, "ToolkitName", &BridgeApplication::GetToolkitName);
AddGetPropertyToInterface(desc, "Version", &BridgeApplication::GetVersion);
mDbusServer.addInterface("/", desc, true);
Application* BridgeApplication::FindSelf() const
{
- auto self = BridgeBase::FindSelf();
- assert(self);
- auto appInterface = dynamic_cast<Application*>(self);
- if(!appInterface)
- {
- throw std::domain_error{"object " + self->GetAddress().ToString() + " doesn't have Application interface"};
- }
- return appInterface;
+ return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::APPLICATION>();
}
std::string BridgeApplication::GetToolkitName()
BridgeBase::~BridgeBase()
{
mApplication.mChildren.clear();
- mApplication.mWindows.clear();
}
void BridgeBase::AddFilteredEvent(FilteredEvents kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor)
mDbusServer = {mConnectionPtr};
{
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceCache};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::CACHE)};
AddFunctionToInterface(desc, "GetItems", &BridgeBase::GetItems);
mDbusServer.addInterface(AtspiDbusPathCache, desc);
}
{
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceApplication};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::APPLICATION)};
AddGetSetPropertyToInterface(desc, "Id", &BridgeBase::GetId, &BridgeBase::SetId);
mDbusServer.addInterface(AtspiPath, desc);
}
- mRegistry = {AtspiDbusNameRegistry, AtspiDbusPathRegistry, AtspiDbusInterfaceRegistry, mConnectionPtr};
+ mRegistry = {AtspiDbusNameRegistry, AtspiDbusPathRegistry, Accessible::GetInterfaceName(AtspiInterface::REGISTRY), mConnectionPtr};
UpdateRegisteredEvents();
}
}
-void BridgeBase::OnWindowVisibilityChanged(Dali::Window window, bool visible)
-{
- if(visible)
- {
- // TODO : Should we check 'out of screen' here? -> Then, we need an actor of this change.
- Dali::Accessibility::Bridge::GetCurrentBridge()->WindowShown(window); // Called when Window is shown.
- }
- else
- {
- Dali::Accessibility::Bridge::GetCurrentBridge()->WindowHidden(window); // Called when Window is hidden and iconified.
- }
-}
-
-void BridgeBase::OnWindowFocusChanged(Dali::Window window, bool focusIn)
-{
- if(focusIn)
- {
- Dali::Accessibility::Bridge::GetCurrentBridge()->WindowFocused(window); // Called when Window is focused.
- }
- else
- {
- Dali::Accessibility::Bridge::GetCurrentBridge()->WindowUnfocused(window); // Called when Window is out of focus.
- }
-}
-
void BridgeBase::AddTopLevelWindow(Accessible* windowAccessible)
{
if(windowAccessible->GetInternalActor() == nullptr)
SetIsOnRootLevel(windowAccessible);
RegisterDefaultLabel(windowAccessible);
-
- Dali::Window window = Dali::DevelWindow::Get(windowAccessible->GetInternalActor());
- if(window)
- {
- mApplication.mWindows.push_back(window);
- Dali::DevelWindow::VisibilityChangedSignal(window).Connect(this, &BridgeBase::OnWindowVisibilityChanged);
- window.FocusChangeSignal().Connect(this, &BridgeBase::OnWindowFocusChanged);
- }
}
void BridgeBase::RemoveTopLevelWindow(Accessible* windowAccessible)
{
- for(auto i = 0u; i < mApplication.mWindows.size(); ++i)
- {
- if(windowAccessible->GetInternalActor() == mApplication.mWindows[i].GetRootLayer())
- {
- Dali::DevelWindow::VisibilityChangedSignal(mApplication.mWindows[i]).Disconnect(this, &BridgeBase::OnWindowVisibilityChanged);
- mApplication.mWindows[i].FocusChangeSignal().Disconnect(this, &BridgeBase::OnWindowFocusChanged);
- mApplication.mWindows.erase(mApplication.mWindows.begin() + i);
- break;
- }
- }
-
UnregisterDefaultLabel(windowAccessible);
for(auto i = 0u; i < mApplication.mChildren.size(); ++i)
return Find(ptr.GetPath());
}
-Accessible* BridgeBase::FindSelf() const
+Accessible* BridgeBase::FindCurrentObject() const
{
auto path = DBus::DBusServer::getCurrentObjectPath();
auto size = strlen(AtspiPath);
root->GetAddress(),
parent ? parent->GetAddress() : Address{},
children,
- item->GetInterfaces(),
+ item->GetInterfacesAsStrings(),
item->GetName(),
item->GetRole(),
item->GetDescription(),
public:
Dali::Accessibility::ProxyAccessible mParent;
std::vector<Dali::Accessibility::Accessible*> mChildren;
- std::vector<Dali::Window> mWindows;
std::string mName;
+ std::string mToolkitName{"dali"};
std::string GetName() const override
{
std::string GetToolkitName() const override
{
- return {"dali"};
+ return mToolkitName;
}
std::string GetVersion() const override
/**
* @brief Returns the target object of the currently executed DBus method call.
*
- * And any subclasses redefine `FindSelf` with a different return type as a convenient wrapper around dynamic_cast.
* @return The Accessible object
* @note When a DBus method is called on some object, this target object (`currentObject`) is temporarily saved by the bridge,
* because DBus handles the invocation target separately from the method arguments.
* We then use the saved object inside the 'glue' method (e.g. BridgeValue::GetMinimum)
* to call the equivalent method on the respective C++ object (this could be ScrollBar::AccessibleImpl::GetMinimum in the example given).
*/
- Dali::Accessibility::Accessible* FindSelf() const;
+ Dali::Accessibility::Accessible* FindCurrentObject() const;
+
+ /**
+ * @brief Returns the target object of the currently executed DBus method call.
+ *
+ * This method tries to downcast the return value of FindCurrentObject() to the requested type,
+ * issuing an error reply to the DBus caller if the requested type is not implemented. Whether
+ * a given type is implemented is decided based on the return value of Accessible::GetInterfaces()
+ * for the current object.
+ *
+ * @tparam I The requested AT-SPI interface
+ * @return The Accessible object (cast to a more derived type)
+ *
+ * @see FindCurrentObject()
+ * @see Dali::Accessibility::AtspiInterface
+ * @see Dali::Accessibility::AtspiInterfaceType
+ * @see Dali::Accessibility::Accessible::GetInterfaces()
+ */
+ template<Dali::Accessibility::AtspiInterface I>
+ auto* FindCurrentObjectWithInterface() const
+ {
+ using Type = Dali::Accessibility::AtspiInterfaceType<I>;
+
+ Type* result;
+ auto* currentObject = FindCurrentObject();
+ DALI_ASSERT_DEBUG(currentObject); // FindCurrentObject() throws domain_error
+
+ if(!(result = Dali::Accessibility::Accessible::DownCast<I>(currentObject)))
+ {
+ std::stringstream s;
+
+ s << "Object " << currentObject->GetAddress().ToString();
+ s << " does not implement ";
+ s << Dali::Accessibility::Accessible::GetInterfaceName(I);
+
+ throw std::domain_error{s.str()};
+ }
+
+ return result;
+ }
/**
* @copydoc Dali::Accessibility::Bridge::FindByPath()
mApplication.mName = std::move(name);
}
+ /**
+ * @copydoc Dali::Accessibility::Bridge::SetToolkitName()
+ */
+ void SetToolkitName(std::string_view toolkitName) override
+ {
+ mApplication.mToolkitName = std::string{toolkitName};
+ }
+
protected:
mutable ApplicationAccessible mApplication;
std::vector<Dali::Accessibility::Accessible*> mDefaultLabels;
// EXTERNAL INCLUDES
#include <algorithm>
-#include <iostream>
#include <unordered_set>
#include <vector>
void BridgeCollection::RegisterInterfaces()
{
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceCollection};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::COLLECTION)};
AddFunctionToInterface(desc, "GetMatches", &BridgeCollection::GetMatches);
mDbusServer.addInterface("/", desc, true);
}
Collection* BridgeCollection::FindSelf() const
{
- auto self = BridgeBase::FindSelf();
- assert(self);
- auto collectionInterface = dynamic_cast<Collection*>(self);
- if(!collectionInterface)
- {
- throw std::domain_error{"object " + self->GetAddress().ToString() + " doesn't have Collection interface"};
- }
- return collectionInterface;
+ return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::COLLECTION>();
}
/**
void Update(Accessible* obj)
{
mObject.clear();
- for(auto& interface : obj->GetInterfaces())
+ for(auto& interface : obj->GetInterfacesAsStrings())
{
mObject.insert(std::move(interface));
}
DBus::ValueOrError<std::vector<Accessible*> > BridgeCollection::GetMatches(MatchRule rule, uint32_t sortBy, int32_t count, bool traverse)
{
std::vector<Accessible*> res;
- auto self = BridgeBase::FindSelf();
+ auto self = BridgeBase::FindCurrentObject();
auto matcher = Comparer{&rule};
VisitNodes(self, res, matcher, count);
// CLASS HEADER
#include <dali/internal/accessibility/bridge/bridge-component.h>
-// EXTERNAL INCLUDES
-#include <iostream>
-
#define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties"
using namespace Dali::Accessibility;
// Screen Reader will call the methods with the exact names as specified in the AT-SPI Component interface:
// https://gitlab.gnome.org/GNOME/at-spi2-core/-/blob/master/xml/Component.xml
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceComponent};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::COMPONENT)};
AddFunctionToInterface(desc, "Contains", &BridgeComponent::IsAccessibleContainingPoint);
AddFunctionToInterface(desc, "GetAccessibleAtPoint", &BridgeComponent::GetAccessibleAtPoint);
AddFunctionToInterface(desc, "GetExtents", &BridgeComponent::GetExtents);
Component* BridgeComponent::FindSelf() const
{
- auto self = BridgeBase::FindSelf();
- assert(self);
- auto componentInterface = dynamic_cast<Component*>(self);
- if(!componentInterface)
- {
- throw std::domain_error{"object " + self->GetAddress().ToString() + " doesn't have Component interface"};
- }
- return componentInterface;
+ return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::COMPONENT>();
}
DBus::ValueOrError<bool> BridgeComponent::IsAccessibleContainingPoint(int32_t x, int32_t y, uint32_t coordType)
void BridgeEditableText::RegisterInterfaces()
{
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceEditableText};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::EDITABLE_TEXT)};
AddFunctionToInterface(desc, "CopyText", &BridgeEditableText::CopyText);
AddFunctionToInterface(desc, "CutText", &BridgeEditableText::CutText);
AddFunctionToInterface(desc, "DeleteText", &BridgeEditableText::DeleteText);
EditableText* BridgeEditableText::FindSelf() const
{
- auto self = BridgeBase::FindSelf();
- assert(self);
- auto editableTextInterface = dynamic_cast<EditableText*>(self);
- if(!editableTextInterface)
- {
- throw std::domain_error{"object " + self->GetAddress().ToString() + " doesn't have Text interface"};
- }
- return editableTextInterface;
+ return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::EDITABLE_TEXT>();
}
DBus::ValueOrError<bool> BridgeEditableText::CopyText(int32_t startPosition, int32_t endPosition)
void BridgeHyperlink::RegisterInterfaces()
{
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceHyperlink};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::HYPERLINK)};
AddGetPropertyToInterface(desc, "NAnchors", &BridgeHyperlink::GetAnchorCount);
AddGetPropertyToInterface(desc, "StartIndex", &BridgeHyperlink::GetStartIndex);
AddGetPropertyToInterface(desc, "EndIndex", &BridgeHyperlink::GetEndIndex);
Hyperlink* BridgeHyperlink::FindSelf() const
{
- auto self = BridgeBase::FindSelf();
- assert(self);
- auto hyperlinkInterface = dynamic_cast<Hyperlink*>(self);
- if(!hyperlinkInterface)
- {
- throw std::domain_error{"object " + self->GetAddress().ToString() + " doesn't have Hyperlink interface"};
- }
- return hyperlinkInterface;
+ return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::HYPERLINK>();
}
DBus::ValueOrError<int32_t> BridgeHyperlink::GetEndIndex()
void BridgeHypertext::RegisterInterfaces()
{
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceHypertext};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::HYPERTEXT)};
AddFunctionToInterface(desc, "GetNLinks", &BridgeHypertext::GetLinkCount);
AddFunctionToInterface(desc, "GetLink", &BridgeHypertext::GetLink);
AddFunctionToInterface(desc, "GetLinkIndex", &BridgeHypertext::GetLinkIndex);
Hypertext* BridgeHypertext::FindSelf() const
{
- auto self = BridgeBase::FindSelf();
- assert(self);
- auto hypertextInterface = dynamic_cast<Hypertext*>(self);
- if(!hypertextInterface)
- {
- throw std::domain_error{"object " + self->GetAddress().ToString() + " doesn't have Hypertext interface"};
- }
- return hypertextInterface;
+ return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::HYPERTEXT>();
}
DBus::ValueOrError<int32_t> BridgeHypertext::GetLinkCount()
#include <dali/internal/accessibility/bridge/bridge-hyperlink.h>
#include <dali/internal/accessibility/bridge/bridge-object.h>
#include <dali/internal/accessibility/bridge/bridge-selection.h>
+#include <dali/internal/accessibility/bridge/bridge-socket.h>
#include <dali/internal/accessibility/bridge/bridge-text.h>
#include <dali/internal/accessibility/bridge/bridge-value.h>
#include <dali/internal/accessibility/bridge/bridge-application.h>
public BridgeSelection,
public BridgeApplication,
public BridgeHypertext,
- public BridgeHyperlink
+ public BridgeHyperlink,
+ public BridgeSocket
{
DBus::DBusClient mAccessibilityStatusClient;
DBus::DBusClient mRegistryClient;
DBus::DBusClient mDirectReadingClient;
bool mIsScreenReaderEnabled = false;
bool mIsEnabled = false;
- bool mIsShown = false;
std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks;
Dali::Actor mHighlightedActor;
std::function<void(Dali::Actor)> mHighlightClearAction;
mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
if(!msg)
{
- LOG() << "Direct reading command failed (" << msg.getError().message << ")";
+ LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
}
},
true);
mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
if(!msg)
{
- LOG() << "Direct reading command failed (" << msg.getError().message << ")";
+ LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
}
},
false);
mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
if(!msg)
{
- LOG() << "Direct reading command failed (" << msg.getError().message << ")";
+ LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
}
},
alsoNonDiscardable);
mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
if(!msg)
{
- LOG() << "Direct reading command failed (" << msg.getError().message << ")";
+ LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
}
else if(callback)
{
mData->mHighlightActor = {};
mDisabledSignal.Emit();
+ UnembedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
}
+
mHighlightedActor = {};
mHighlightClearAction = {};
BridgeAccessible::ForceDown();
mDirectReadingClient = {};
mDirectReadingCallbacks.clear();
mApplication.mChildren.clear();
- mApplication.mWindows.clear();
ClearTimer();
}
{
if(mData)
{
+ // The ~Window() after this point cannot emit DESTROY, because Bridge is not available. So emit DESTROY here.
+ for(auto windowAccessible : mApplication.mChildren)
+ {
+ BridgeObject::Emit(windowAccessible, WindowEvent::DESTROY);
+ }
mData->mCurrentlyHighlightedActor = {};
mData->mHighlightActor = {};
}
BridgeApplication::RegisterInterfaces();
BridgeHypertext::RegisterInterfaces();
BridgeHyperlink::RegisterInterfaces();
+ BridgeSocket::RegisterInterfaces();
RegisterOnBridge(&mApplication);
- mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, mConnectionPtr};
+ mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, Accessible::GetInterfaceName(AtspiInterface::DEVICE_EVENT_CONTROLLER), mConnectionPtr};
mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
}
});
- auto proxy = DBus::DBusClient{AtspiDbusNameRegistry, AtspiDbusPathRoot, AtspiDbusInterfaceSocket, mConnectionPtr};
- Address root{"", "root"};
- auto res = proxy.method<Address(Address)>("Embed").call(root);
- if(!res)
- {
- LOG() << "Call to Embed failed: " << res.getError().message;
- }
- assert(res);
-
- mApplication.mParent.SetAddress(std::move(std::get<0>(res)));
-
+ auto parentAddress = EmbedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
+ mApplication.mParent.SetAddress(std::move(parentAddress));
mEnabledSignal.Emit();
- if(mIsShown)
- {
- auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer();
- auto window = Dali::DevelWindow::Get(rootLayer);
- EmitActivate(window); // Currently, sends a signal that the default window is activated here.
- }
-
return ForceUpResult::JUST_STARTED;
}
*/
void WindowShown(Dali::Window window) override
{
- if(!mIsShown && IsUp())
+ if(IsUp())
{
EmitShown(window);
}
- mIsShown = true;
}
/**
*/
void WindowHidden(Dali::Window window) override
{
- if(mIsShown && IsUp())
+ if(IsUp())
{
EmitHidden(window);
}
- mIsShown = false;
}
/**
*/
void WindowFocused(Dali::Window window) override
{
- if(mIsShown && IsUp())
+ if(IsUp())
{
EmitActivate(window);
}
*/
void WindowUnfocused(Dali::Window window) override
{
- if(mIsShown && IsUp())
+ if(IsUp())
{
EmitDeactivate(window);
}
});
}
+ void EmitScreenReaderEnabledSignal()
+ {
+ if (mIsScreenReaderEnabled)
+ {
+ mScreenReaderEnabledSignal.Emit();
+ }
+ else
+ {
+ mScreenReaderDisabledSignal.Emit();
+ }
+ }
+
void ListenScreenReaderEnabledProperty()
{
mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
mIsScreenReaderEnabled = res;
+ EmitScreenReaderEnabledSignal();
SwitchBridge();
});
}
{
return mIsEnabled;
}
+
+ Address EmbedSocket(const Address& plug, const Address& socket) override
+ {
+ auto client = CreateSocketClient(socket);
+ auto reply = client.method<Address(Address)>("Embed").call(plug);
+
+ if(!reply)
+ {
+ DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
+ return {};
+ }
+
+ return std::get<0>(reply.getValues());
+ }
+
+ void EmbedAtkSocket(const Address& plug, const Address& socket) override
+ {
+ auto client = CreateSocketClient(socket);
+
+ client.method<void(std::string)>("Embedded").call(ATSPI_PREFIX_PATH + plug.GetPath());
+ }
+
+ void UnembedSocket(const Address& plug, const Address& socket) override
+ {
+ auto client = CreateSocketClient(socket);
+
+ client.method<void(Address)>("Unembed").call(plug);
+ }
+
+private:
+ DBus::DBusClient CreateSocketClient(const Address& socket)
+ {
+ return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
+ }
}; // BridgeImpl
namespace // unnamed namespace
auto window = Dali::DevelWindow::Get(rootLayer);
auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
- auto accessible = Accessibility::Accessible::Get(rootLayer, true);
+ auto accessible = Accessibility::Accessible::Get(rootLayer);
auto bridge = Bridge::GetCurrentBridge();
bridge->AddTopLevelWindow(accessible);
void BridgeObject::RegisterInterfaces()
{
- // DBus::DBusInterfaceDescription desc{ AtspiDbusInterfaceEventObject };
+ // DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT)};
// mStateChanged = addSignal<std::string, int, int, DBus::EldbusVariant<int>, Accessible*>(desc, "StateChanged");
// mDbusServer.addInterface("/", desc, true);
}
void BridgeObject::EmitActiveDescendantChanged(Accessible* obj, Accessible* child)
{
- if(!IsUp() || obj->IsHidden() || child->IsHidden())
+ if(!IsUp() || obj->IsHidden() || obj->GetSuppressedEvents()[AtspiEvent::ACTIVE_DESCENDANT_CHANGED] || child->IsHidden())
{
return;
}
mDbusServer.emit2<std::string, int, int, DBus::EldbusVariant<Address>, Address>(
GetAccessiblePath(obj),
- AtspiDbusInterfaceEventObject,
+ Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT),
"ActiveDescendantChanged",
"",
index,
{ObjectPropertyChangeEvent::ROLE, "accessible-role"},
};
- if(!IsUp() || obj->IsHidden())
+ if(!IsUp() || obj->IsHidden() || obj->GetSuppressedEvents()[AtspiEvent::PROPERTY_CHANGED])
{
return;
}
{
mDbusServer.emit2<std::string, int, int, DBus::EldbusVariant<int>, Address>(
GetAccessiblePath(obj),
- AtspiDbusInterfaceEventObject,
+ Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT),
"PropertyChange",
std::string{eventName->second},
0,
{WindowEvent::RESTYLE, "Restyle"},
};
- if(!IsUp() || obj->IsHidden())
+ if(!IsUp() || obj->IsHidden() || obj->GetSuppressedEvents()[AtspiEvent::WINDOW_CHANGED])
{
return;
}
{
mDbusServer.emit2<std::string, int, int, DBus::EldbusVariant<int>, Address>(
GetAccessiblePath(obj),
- AtspiDbusInterfaceEventWindow,
+ Accessible::GetInterfaceName(AtspiInterface::EVENT_WINDOW),
std::string{eventName->second},
"",
detail,
{State::HIGHLIGHTABLE, "highlightable"},
};
- if(!IsUp() || obj->IsHidden())
+ if(!IsUp() || obj->IsHidden() || obj->GetSuppressedEvents()[AtspiEvent::STATE_CHANGED]) // separate ?
{
return;
}
{
mDbusServer.emit2<std::string, int, int, DBus::EldbusVariant<int>, Address>(
GetAccessiblePath(obj),
- AtspiDbusInterfaceEventObject,
+ Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT),
"StateChanged",
std::string{stateName->second},
newValue,
void BridgeObject::EmitBoundsChanged(Accessible* obj, Dali::Rect<> rect)
{
- if(!IsUp() || !IsBoundsChangedEventAllowed || obj->IsHidden())
+ if(!IsUp() || !IsBoundsChangedEventAllowed || obj->IsHidden() || obj->GetSuppressedEvents()[AtspiEvent::BOUNDS_CHANGED])
{
return;
}
AddFilteredEvent(FilteredEvents::BOUNDS_CHANGED, obj, 1.0f, [=]() {
mDbusServer.emit2<std::string, int, int, DBus::EldbusVariant<std::tuple<int32_t, int32_t, int32_t, int32_t> >, Address>(
GetAccessiblePath(obj),
- AtspiDbusInterfaceEventObject,
+ Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT),
"BoundsChanged",
"",
0,
void BridgeObject::EmitCursorMoved(Accessible* obj, unsigned int cursorPosition)
{
- if(!IsUp() || obj->IsHidden())
+ if(!IsUp() || obj->IsHidden() || obj->GetSuppressedEvents()[AtspiEvent::TEXT_CARET_MOVED])
{
return;
}
mDbusServer.emit2<std::string, int, int, DBus::EldbusVariant<int>, Address>(
GetAccessiblePath(obj),
- AtspiDbusInterfaceEventObject,
+ Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT),
"TextCaretMoved",
"",
cursorPosition,
{TextChangedState::DELETED, "delete"},
};
- if(!IsUp() || obj->IsHidden())
+ if(!IsUp() || obj->IsHidden() || obj->GetSuppressedEvents()[AtspiEvent::TEXT_CHANGED])
{
return;
}
{
mDbusServer.emit2<std::string, int, int, DBus::EldbusVariant<std::string>, Address>(
GetAccessiblePath(obj),
- AtspiDbusInterfaceEventObject,
+ Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT),
"TextChanged",
std::string{stateName->second},
position,
void BridgeObject::EmitMovedOutOfScreen(Accessible* obj, ScreenRelativeMoveType type)
{
- if(!IsUp() || obj->IsHidden())
+ if(!IsUp() || obj->IsHidden() || obj->GetSuppressedEvents()[AtspiEvent::MOVED_OUT])
{
return;
}
mDbusServer.emit2<std::string, int, int, DBus::EldbusVariant<int>, Address>(
GetAccessiblePath(obj),
- AtspiDbusInterfaceEventObject,
+ Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT),
"MoveOuted",
"",
static_cast<int>(type),
{0},
{"", "root"});
}
+
+void BridgeObject::EmitSocketAvailable(Accessible* obj)
+{
+ if(!IsUp() || obj->IsHidden()) //TODO Suppress SocketAvailable event
+ {
+ return;
+ }
+
+ mDbusServer.emit2<Address, Address>(
+ GetAccessiblePath(obj),
+ Accessible::GetInterfaceName(AtspiInterface::SOCKET),
+ "Available",
+ obj->GetAddress(),
+ {"", "root"});
+}
/**
* @copydoc Dali::Accessibility::Bridge::Emit()
*/
- void Emit(Dali::Accessibility::Accessible* obj, Dali::Accessibility::WindowEvent event, unsigned int detail) override;
+ void Emit(Dali::Accessibility::Accessible* obj, Dali::Accessibility::WindowEvent event, unsigned int detail = 0) override;
/**
* @copydoc Dali::Accessibility::Bridge::Emit()
*/
void EmitMovedOutOfScreen(Dali::Accessibility::Accessible* obj, Dali::Accessibility::ScreenRelativeMoveType type) override;
+ /**
+ * @copydoc Dali::Accessibility::Bridge::EmitSocketAvailable()
+ */
+ void EmitSocketAvailable(Dali::Accessibility::Accessible* obj) override;
+
protected:
DBus::DBusInterfaceDescription::SignalId mStateChanged;
};
void BridgeSelection::RegisterInterfaces()
{
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceSelection};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::SELECTION)};
AddGetPropertyToInterface(desc, "NSelectedChildren", &BridgeSelection::GetSelectedChildrenCount);
AddFunctionToInterface(desc, "GetSelectedChild", &BridgeSelection::GetSelectedChild);
AddFunctionToInterface(desc, "SelectChild", &BridgeSelection::SelectChild);
Selection* BridgeSelection::FindSelf() const
{
- auto self = BridgeBase::FindSelf();
- assert(self);
- auto selectionInterface = dynamic_cast<Selection*>(self);
- if(!selectionInterface)
- {
- throw std::domain_error{"object " + self->GetAddress().ToString() + " doesn't have Selection interface"};
- }
- return selectionInterface;
+ return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::SELECTION>();
}
DBus::ValueOrError<int32_t> BridgeSelection::GetSelectedChildrenCount()
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/internal/accessibility/bridge/bridge-socket.h>
+
+using namespace Dali::Accessibility;
+
+void BridgeSocket::RegisterInterfaces()
+{
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::SOCKET)};
+
+ AddFunctionToInterface(desc, "Embed", &BridgeSocket::Embed);
+ AddFunctionToInterface(desc, "Unembed", &BridgeSocket::Unembed);
+
+ mDbusServer.addInterface("/", desc, true);
+}
+
+Socket* BridgeSocket::FindSelf() const
+{
+ return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::SOCKET>();
+}
+
+DBus::ValueOrError<Address> BridgeSocket::Embed(Address plug)
+{
+ return FindSelf()->Embed(plug);
+}
+
+DBus::ValueOrError<void> BridgeSocket::Unembed(Address plug)
+{
+ FindSelf()->Unembed(plug);
+ return {};
+}
--- /dev/null
+#ifndef DALI_INTERNAL_ACCESSIBILITY_BRIDGE_SOCKET_H
+#define DALI_INTERNAL_ACCESSIBILITY_BRIDGE_SOCKET_H
+
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/atspi-interfaces/socket.h>
+#include <dali/internal/accessibility/bridge/bridge-base.h>
+
+/**
+ * @brief The BridgeSocket class contains glue code for Accessibility::Socket.
+ */
+class BridgeSocket : public virtual BridgeBase
+{
+protected:
+ BridgeSocket() = default;
+
+ /**
+ * @brief Registers Socket methods as a DBus interface.
+ */
+ void RegisterInterfaces();
+
+ /**
+ * @brief Returns the Socket object of the currently executed DBus method call.
+ *
+ * @return The Socket object
+ */
+ Dali::Accessibility::Socket* FindSelf() const;
+
+public:
+ /**
+ * @copydoc Dali::Accessibility::Socket::Embed()
+ */
+ DBus::ValueOrError<Dali::Accessibility::Address> Embed(Dali::Accessibility::Address plug);
+
+ /**
+ * @copydoc Dali::Accessibility::Socket::Unembed()
+ */
+ DBus::ValueOrError<void> Unembed(Dali::Accessibility::Address plug);
+};
+
+#endif // DALI_INTERNAL_ACCESSIBILITY_BRIDGE_SOCKET_H
// Screen Reader will call the methods with the exact names as specified in the AT-SPI Text interface:
// https://gitlab.gnome.org/GNOME/at-spi2-core/-/blob/master/xml/Text.xml
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceText};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::TEXT)};
AddFunctionToInterface(desc, "GetText", &BridgeText::GetText);
AddGetPropertyToInterface(desc, "CharacterCount", &BridgeText::GetCharacterCount);
AddGetPropertyToInterface(desc, "CaretOffset", &BridgeText::GetCursorOffset);
AddFunctionToInterface(desc, "GetSelection", &BridgeText::GetRangeOfSelection);
AddFunctionToInterface(desc, "SetSelection", &BridgeText::SetRangeOfSelection);
AddFunctionToInterface(desc, "RemoveSelection", &BridgeText::RemoveSelection);
+ AddFunctionToInterface(desc, "GetRangeExtents", &BridgeText::GetRangeExtents);
mDbusServer.addInterface("/", desc, true);
}
Text* BridgeText::FindSelf() const
{
- auto self = BridgeBase::FindSelf();
- assert(self);
- auto textInterface = dynamic_cast<Text*>(self);
- if(!textInterface)
- {
- throw std::domain_error{"Object " + self->GetAddress().ToString() + " doesn't have Text interface"};
- }
- return textInterface;
+ return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::TEXT>();
}
DBus::ValueOrError<std::string> BridgeText::GetText(int startOffset, int endOffset)
{
return FindSelf()->SetRangeOfSelection(selectionIndex, startOffset, endOffset);
}
+
+DBus::ValueOrError<int32_t, int32_t, int32_t, int32_t> BridgeText::GetRangeExtents(int32_t startOffset, int32_t endOffset, uint32_t coordType)
+{
+ auto rect = FindSelf()->GetRangeExtents(startOffset, endOffset, static_cast<CoordinateType>(coordType));
+ return {static_cast<int32_t>(rect.x), static_cast<int32_t>(rect.y), static_cast<int32_t>(rect.width), static_cast<int32_t>(rect.height)};
+}
* @copydoc Dali::Accessibility::Text::SetRangeOfSelection()
*/
DBus::ValueOrError<bool> SetRangeOfSelection(int32_t selectionIndex, int32_t startOffset, int32_t endOffset);
+
+ /**
+ * @copydoc Dali::Accessibility::Text::GetRangeExtents()
+ */
+ DBus::ValueOrError<int32_t, int32_t, int32_t, int32_t> GetRangeExtents(int32_t startOffset, int32_t endOffset, uint32_t coordType);
};
#endif // DALI_INTERNAL_ACCESSIBILITY_BRIDGE_TEXT_H
// CLASS HEADER
#include <dali/internal/accessibility/bridge/bridge-value.h>
-// EXTERNAL INCLUDES
-#include <iostream>
-
using namespace Dali::Accessibility;
BridgeValue::BridgeValue()
void BridgeValue::RegisterInterfaces()
{
- DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceValue};
+ DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::VALUE)};
AddGetSetPropertyToInterface(desc, "CurrentValue", &BridgeValue::GetCurrentValue, &BridgeValue::SetCurrentValue);
AddGetPropertyToInterface(desc, "MaximumValue", &BridgeValue::GetMaximumValue);
AddGetPropertyToInterface(desc, "MinimumIncrement", &BridgeValue::GetMinimumIncrement);
Value* BridgeValue::FindSelf() const
{
- auto self = BridgeBase::FindSelf();
- assert(self);
- auto valueInterface = dynamic_cast<Value*>(self);
- if(!valueInterface)
- {
- throw std::domain_error{"object " + self->GetAddress().ToString() + " doesn't have Value interface"};
- }
- return valueInterface;
+ return FindCurrentObjectWithInterface<Dali::Accessibility::AtspiInterface::VALUE>();
}
double BridgeValue::GetCurrentValue()
{
}
+void Accessibility::Accessible::EmitSocketAvailable()
+{
+}
+
void Accessibility::Accessible::FindWordSeparationsUtf8(const utf8_t* string, size_t length, const char* language, char* breaks)
{
}
{
}
+ void SetToolkitName(std::string_view toolkitName) override
+ {
+ }
+
Accessibility::Accessible* GetApplication() const override
{
return nullptr;
{
}
+ void EmitSocketAvailable(Accessibility::Accessible* obj) override
+ {
+ }
+
void EmitStateChanged(Accessibility::Accessible* obj, Accessibility::State state, int newValue, int reserved) override
{
}
{
return false;
}
+
+ Address EmbedSocket(const Address& plug, const Address& socket) override
+ {
+ return {};
+ }
+
+ void EmbedAtkSocket(const Address& plug, const Address& socket) override
+ {
+ }
+
+ void UnembedSocket(const Address& plug, const Address& socket) override
+ {
+ }
};
} // namespace Dali::Accessibility
${adaptor_accessibility_dir}/bridge/bridge-impl.cpp
${adaptor_accessibility_dir}/bridge/bridge-object.cpp
${adaptor_accessibility_dir}/bridge/bridge-selection.cpp
+ ${adaptor_accessibility_dir}/bridge/bridge-socket.cpp
${adaptor_accessibility_dir}/bridge/bridge-text.cpp
${adaptor_accessibility_dir}/bridge/bridge-value.cpp
${adaptor_accessibility_dir}/bridge/component.cpp
// EXTERNAL INCLUDES
#include <dali/devel-api/common/singleton-service.h>
#include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
#include <dali/public-api/object/object-registry.h>
// INTERNAL INCLUDES
{
namespace Adaptor
{
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_APPLICATION, true);
+
ApplicationPtr Application::gPreInitializedApplication(NULL);
ApplicationPtr Application::New(
}
// Run the adaptor
+ DALI_TRACE_BEGIN(gTraceFilter, "DALI_APP_ADAPTOR_START");
mAdaptor->Start();
+ DALI_TRACE_END(gTraceFilter, "DALI_APP_ADAPTOR_START");
Accessibility::Accessible::SetObjectRegistry(mAdaptor->GetObjectRegistry());
if(!mStylesheet.empty())
LanguageChangedSignal().Connect(&GetImplementation(lifecycleController), &LifecycleController::OnLanguageChanged);
Dali::Application application(this);
+
+ DALI_TRACE_BEGIN(gTraceFilter, "DALI_APP_EMIT_INIT_SIGNAL");
mInitSignal.Emit(application);
+ DALI_TRACE_END(gTraceFilter, "DALI_APP_EMIT_INIT_SIGNAL");
mAdaptor->NotifySceneCreated();
}
#define DALI_INTERNAL_COMBINED_UPDATE_RENDER_CONTROLLER_DEBUG_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// EXTERNAL INCLUDES
#include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
namespace Dali
{
#define LOG_EVENT_TRACE_FMT(format, ...)
#endif
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_COMBINED, false);
+#define TRACE_UPDATE_RENDER_BEGIN(tag) DALI_TRACE_BEGIN(gTraceFilter, tag)
+#define TRACE_UPDATE_RENDER_END(tag) DALI_TRACE_END(gTraceFilter, tag)
+
} // unnamed namespace
} // namespace Adaptor
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
LOG_UPDATE_RENDER_TRACE;
+ // For thread safe
+ bool uploadOnly = mUploadWithoutRendering;
+ unsigned int surfaceResized = mSurfaceResized;
+
// Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
AddPerformanceMarker(PerformanceInterface::VSYNC);
Integration::UpdateStatus updateStatus;
AddPerformanceMarker(PerformanceInterface::UPDATE_START);
+ TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
mCore.Update(frameDelta,
currentTime,
nextFrameTime,
updateStatus,
renderToFboEnabled,
- isRenderingToFbo);
+ isRenderingToFbo,
+ uploadOnly);
+ TRACE_UPDATE_RENDER_END("DALI_UPDATE");
AddPerformanceMarker(PerformanceInterface::UPDATE_END);
unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
Integration::RenderStatus renderStatus;
AddPerformanceMarker(PerformanceInterface::RENDER_START);
+ TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
// Upload shared resources
- mCore.PreRender(renderStatus, mForceClear, mUploadWithoutRendering);
+ mCore.PreRender(renderStatus, mForceClear);
- if(!mUploadWithoutRendering)
+ if(!uploadOnly || surfaceResized)
{
// Go through each window
WindowContainer windows;
}
}
- if(!mUploadWithoutRendering)
+ if(!uploadOnly)
{
graphics.PostRender();
}
- mCore.PostRender(mUploadWithoutRendering);
+ mCore.PostRender();
//////////////////////////////
// DELETE SURFACE
SurfaceDeleted();
}
+ TRACE_UPDATE_RENDER_END("DALI_RENDER");
AddPerformanceMarker(PerformanceInterface::RENDER_END);
mForceClear = false;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// EXTERNAL INCLUDES
#include <app_common.h>
#include <app_control_internal.h>
-#include <appcore_ui_base.h>
#include <bundle.h>
-#include <dali/internal/system/linux/dali-ecore.h>
-
#include <bundle_internal.h>
#include <system_info.h>
#include <system_settings.h>
#include <widget_base.h>
+#include <app_core_ui_base.hh>
+#include <app_event_internal.hh>
// CONDITIONAL INCLUDES
#ifdef APPCORE_WATCH_AVAILABLE
#include <appcore-watch/watch_app.h>
#endif
#include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
// INTERNAL INCLUDES
#include <dali/internal/system/common/callback-manager.h>
+#include <dali/internal/system/linux/dali-ecore.h>
+
+using namespace tizen_cpp;
namespace Dali
{
#if defined(DEBUG_ENABLED)
Integration::Log::Filter* gDBusLogging = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_ADAPTOR_EVENTS_DBUS");
#endif
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_FRAMEWORK, true);
bool IsWidgetFeatureEnabled()
{
}
}
+DeviceStatus::Memory::Status GetMemoryStatus(app_event_low_memory_status_e memoryStatus)
+{
+ switch(memoryStatus)
+ {
+ case APP_EVENT_LOW_MEMORY_SOFT_WARNING: // 0x02
+ {
+ return Dali::DeviceStatus::Memory::Status::LOW;
+ }
+ case APP_EVENT_LOW_MEMORY_HARD_WARNING: // 0x04
+ {
+ return Dali::DeviceStatus::Memory::Status::CRITICALLY_LOW;
+ }
+ default: // APP_EVENT_LOW_MEMORY_NORMAL 0x01
+ {
+ return Dali::DeviceStatus::Memory::Status::NORMAL;
+ }
+ }
+}
+
+DeviceStatus::Battery::Status GetBatteryStatus(app_event_low_battery_status_e batteryStatus)
+{
+ switch(batteryStatus)
+ {
+ case APP_EVENT_LOW_BATTERY_POWER_OFF: // 1
+ {
+ return Dali::DeviceStatus::Battery::Status::POWER_OFF;
+ }
+ case APP_EVENT_LOW_BATTERY_CRITICAL_LOW: // 2
+ {
+ return Dali::DeviceStatus::Battery::Status::CRITICALLY_LOW;
+ }
+ default:
+ {
+ return Dali::DeviceStatus::Battery::Status::NORMAL;
+ }
+ }
+}
+
} // namespace AppCore
/**
*/
struct Framework::Impl
{
+ class UiAppContext : public AppCoreUiBase
+ {
+ public:
+ explicit UiAppContext(unsigned int hint, Framework* framework)
+ : AppCoreUiBase(hint),
+ mFramework(framework)
+ {
+ mLanguageChanged = std::make_shared<AppEvent>(IAppCore::IEvent::Type::LANG_CHANGE, OnLanguageChanged, this);
+ AddEvent(mLanguageChanged);
+
+ mDeviceOrientationChanged = std::make_shared<AppEvent>(IAppCore::IEvent::Type::DEVICE_ORIENTATION_CHANGED, OnDeviceOrientationChanged, this);
+ AddEvent(mDeviceOrientationChanged);
+
+ mRegionFormatChanged = std::make_shared<AppEvent>(IAppCore::IEvent::Type::REGION_CHANGE, OnRegionFormatChanged, this);
+ AddEvent(mRegionFormatChanged);
+
+ mLowMemory = std::make_shared<AppEvent>(IAppCore::IEvent::Type::LOW_MEMORY, OnLowMemory, this);
+ AddEvent(mLowMemory);
+
+ mLowBattery = std::make_shared<AppEvent>(IAppCore::IEvent::Type::LOW_BATTERY, OnLowBattery, this);
+ AddEvent(mLowBattery);
+ }
+
+ virtual ~UiAppContext()
+ {
+ RemoveEvent(mLowBattery);
+ RemoveEvent(mLowMemory);
+ RemoveEvent(mRegionFormatChanged);
+ RemoveEvent(mDeviceOrientationChanged);
+ RemoveEvent(mLanguageChanged);
+ }
+
+ int OnCreate() override
+ {
+ AppCoreUiBase::OnCreate();
+ mFramework->Create();
+ return 0;
+ }
+
+ int OnTerminate() override
+ {
+ AppCoreUiBase::OnTerminate();
+ auto* observer = &mFramework->mObserver;
+ observer->OnTerminate();
+ return 0;
+ }
+
+ int OnPause() override
+ {
+ AppCoreUiBase::OnPause();
+ auto* observer = &mFramework->mObserver;
+ observer->OnPause();
+ return 0;
+ }
+
+ int OnResume() override
+ {
+ AppCoreUiBase::OnResume();
+ auto* observer = &mFramework->mObserver;
+ observer->OnResume();
+ return 0;
+ }
+
+ int OnControl(tizen_base::Bundle b) override
+ {
+ AppCoreUiBase::OnControl(b);
+
+ app_control_h appControl = nullptr;
+
+ auto* bundleData = b.GetHandle();
+ if(bundleData)
+ {
+ if(app_control_create_event(bundleData, &appControl) != TIZEN_ERROR_NONE)
+ {
+ DALI_LOG_ERROR("Failed to create an app_control handle");
+ return 0;
+ }
+ }
+ else
+ {
+ if(app_control_create(&appControl) != TIZEN_ERROR_NONE)
+ {
+ DALI_LOG_ERROR("Failed to create an app_control handle");
+ return 0;
+ }
+ }
+
+ auto* observer = &mFramework->mObserver;
+ ProcessBundle(mFramework, bundleData);
+ observer->OnReset();
+ observer->OnAppControl(appControl);
+ app_control_destroy(appControl);
+ return 0;
+ }
+
+ void OnLoopInit(int argc, char** argv) override
+ {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+ ecore_init();
+ ecore_app_args_set(argc, (const char**)argv);
+#pragma GCC diagnostic pop
+
+#ifdef DALI_ELDBUS_AVAILABLE
+ // Initialize ElDBus.
+ DALI_LOG_INFO(gDBusLogging, Debug::General, "Starting DBus Initialization\n");
+ eldbus_init();
+#endif
+ }
+
+ void OnLoopFinish() override
+ {
+ ecore_shutdown();
+
+ if(getenv("AUL_LOADER_INIT"))
+ {
+ setenv("AUL_LOADER_INIT", "0", 1);
+ ecore_shutdown();
+ }
+
+#ifdef DALI_ELDBUS_AVAILABLE
+ // Shutdown ELDBus.
+ DALI_LOG_INFO(gDBusLogging, Debug::General, "Shutting down DBus\n");
+ eldbus_shutdown();
+#endif
+ }
+
+ void OnLoopRun() override
+ {
+ ecore_main_loop_begin();
+ }
+
+ void OnLoopExit() override
+ {
+ ecore_main_loop_quit();
+ }
+
+ private:
+ static void OnLanguageChanged(app_event_info_h event_info, void* user_data)
+ {
+ auto* context = static_cast<UiAppContext*>(user_data);
+ auto* framework = context->mFramework;
+ Observer* observer = &framework->mObserver;
+
+ char* lang = nullptr;
+ app_event_get_language(event_info, &lang);
+ if(lang)
+ {
+ framework->SetLanguage(std::string(lang));
+ observer->OnLanguageChanged();
+ free(lang);
+ }
+ else
+ {
+ DALI_LOG_ERROR("NULL pointer in Language changed event\n");
+ }
+ }
+
+ static void OnDeviceOrientationChanged(app_event_info_h event_info, void* user_data)
+ {
+ }
+
+ static void OnRegionFormatChanged(app_event_info_h event_info, void* user_data)
+ {
+ auto* context = static_cast<UiAppContext*>(user_data);
+ auto* framework = context->mFramework;
+ Observer* observer = &framework->mObserver;
+
+ char* region = nullptr;
+ app_event_get_region_format(event_info, ®ion);
+ if(region)
+ {
+ framework->SetRegion(std::string(region));
+ observer->OnRegionChanged();
+ free(region);
+ }
+ else
+ {
+ DALI_LOG_ERROR("NULL pointer in Region changed event\n");
+ }
+ }
+
+ static void OnLowBattery(app_event_info_h event_info, void* user_data)
+ {
+ auto* context = static_cast<UiAppContext*>(user_data);
+ auto* framework = context->mFramework;
+ Observer* observer = &framework->mObserver;
+
+ app_event_low_battery_status_e status;
+ app_event_get_low_battery_status(event_info, &status);
+ Dali::DeviceStatus::Battery::Status result = AppCore::GetBatteryStatus(status);
+ observer->OnBatteryLow(result);
+ }
+
+ static void OnLowMemory(app_event_info_h event_info, void* user_data)
+ {
+ auto* context = static_cast<UiAppContext*>(user_data);
+ auto* framework = context->mFramework;
+ Observer* observer = &framework->mObserver;
+
+ app_event_low_memory_status_e status;
+ app_event_get_low_memory_status(event_info, &status);
+ Dali::DeviceStatus::Memory::Status result = AppCore::GetMemoryStatus(status);
+ observer->OnMemoryLow(result);
+ }
+
+ void ProcessBundle(Framework* framework, bundle* bundleData)
+ {
+ if(bundleData == nullptr)
+ {
+ return;
+ }
+
+ // get bundle name
+ char* bundleName = const_cast<char*>(bundle_get_val(bundleData, "name"));
+ if(bundleName != nullptr)
+ {
+ framework->SetBundleName(bundleName);
+ }
+
+ // get bundle? id
+ char* bundleId = const_cast<char*>(bundle_get_val(bundleData, "id"));
+ if(bundleId != nullptr)
+ {
+ framework->SetBundleId(bundleId);
+ }
+ }
+
+ private:
+ Framework* mFramework;
+ std::shared_ptr<AppEvent> mLanguageChanged;
+ std::shared_ptr<AppEvent> mDeviceOrientationChanged;
+ std::shared_ptr<AppEvent> mRegionFormatChanged;
+ std::shared_ptr<AppEvent> mLowBattery;
+ std::shared_ptr<AppEvent> mLowMemory;
+ };
+
// Constructor
Impl(void* data, Type type)
: mAbortCallBack(NULL),
#endif
mApplicationType = type;
mCallbackManager = CallbackManager::New();
-
- char* region = nullptr;
- char* language = nullptr;
- system_settings_get_value_string(SYSTEM_SETTINGS_KEY_LOCALE_COUNTRY, ®ion);
- system_settings_get_value_string(SYSTEM_SETTINGS_KEY_LOCALE_LANGUAGE, &language);
-
- if(region != nullptr)
- {
- mRegion = std::string(region);
- free(region);
- }
-
- if(language != nullptr)
- {
- mLanguage = std::string(language);
- free(language);
- }
}
~Impl()
int AppMain()
{
+ // TODO: The app-core-cpp has to be applied to the other app types.
int ret;
switch(mApplicationType)
{
mRegion = region;
}
- std::string GetLanguage() const
+ std::string GetLanguage()
{
+ if(mLanguage.empty())
+ {
+ char* language = nullptr;
+ system_settings_get_value_string(SYSTEM_SETTINGS_KEY_LOCALE_LANGUAGE, &language);
+
+ if(language != nullptr)
+ {
+ mLanguage = std::string(language);
+ free(language);
+ }
+ }
return mLanguage;
}
- std::string GetRegion() const
+ std::string GetRegion()
{
+ if(mRegion.empty())
+ {
+ char* region = nullptr;
+ system_settings_get_value_string(SYSTEM_SETTINGS_KEY_LOCALE_COUNTRY, ®ion);
+
+ if(region != nullptr)
+ {
+ mRegion = std::string(region);
+ free(region);
+ }
+ }
return mRegion;
}
Type mApplicationType;
CallbackBase* mAbortCallBack;
CallbackManager* mCallbackManager;
- std::string mLanguage;
- std::string mRegion;
+ std::string mLanguage{};
+ std::string mRegion{};
- Framework* mFramework;
- AppCore::AppEventHandlerPtr handlers[5];
+ Framework* mFramework;
+ AppCore::AppEventHandlerPtr handlers[5];
+ std::unique_ptr<UiAppContext> mUiAppContext;
#ifdef APPCORE_WATCH_AVAILABLE
watch_app_lifecycle_callback_s mWatchCallback;
app_event_handler_h watchHandlers[5];
#endif
- static int AppCreate(void* data)
- {
- appcore_ui_base_on_create();
- return static_cast<int>(static_cast<Framework*>(data)->Create());
- }
-
- static int AppTerminate(void* data)
- {
- appcore_ui_base_on_terminate();
- Observer* observer = &static_cast<Framework*>(data)->mObserver;
-
- observer->OnTerminate();
-
- return 0;
- }
-
- static int AppPause(void* data)
- {
- appcore_ui_base_on_pause();
- Observer* observer = &static_cast<Framework*>(data)->mObserver;
-
- observer->OnPause();
-
- return 0;
- }
-
- static int AppResume(void* data)
- {
- appcore_ui_base_on_resume();
- Observer* observer = &static_cast<Framework*>(data)->mObserver;
-
- observer->OnResume();
-
- return 0;
- }
-
static void ProcessBundle(Framework* framework, bundle* bundleData)
{
if(bundleData == NULL)
}
}
- /**
- * Called by AppCore when the application is launched from another module (e.g. homescreen).
- * @param[in] b the bundle data which the launcher module sent
- */
- static int AppControl(bundle* bundleData, void* data)
- {
- app_control_h appControl = NULL;
-
- appcore_ui_base_on_control(bundleData);
-
- if(bundleData)
- {
- if(app_control_create_event(bundleData, &appControl) != TIZEN_ERROR_NONE)
- {
- DALI_LOG_ERROR("Failed to create an app_control handle");
- }
- }
- else
- {
- if(app_control_create(&appControl) != TIZEN_ERROR_NONE)
- {
- DALI_LOG_ERROR("Failed to create an app_control handle");
- }
- }
-
- Framework* framework = static_cast<Framework*>(data);
- Observer* observer = &framework->mObserver;
-
- ProcessBundle(framework, bundleData);
-
- observer->OnReset();
- observer->OnAppControl(appControl);
-
- app_control_destroy(appControl);
-
- return 0;
- }
-
static void AppInit(int argc, char** argv, void* data)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
-
ecore_init();
ecore_app_args_set(argc, (const char**)argv);
-
#pragma GCC diagnostic pop
+
+#ifdef DALI_ELDBUS_AVAILABLE
+ // Initialize ElDBus.
+ DALI_LOG_INFO(gDBusLogging, Debug::General, "Starting DBus Initialization\n");
+ eldbus_init();
+#endif
}
static void AppFinish(void)
setenv("AUL_LOADER_INIT", "0", 1);
ecore_shutdown();
}
+
+#ifdef DALI_ELDBUS_AVAILABLE
+ // Shutdown ELDBus.
+ DALI_LOG_INFO(gDBusLogging, Debug::General, "Shutting down DBus\n");
+ eldbus_shutdown();
+#endif
}
static void AppRun(void* data)
int AppNormalMain()
{
- int ret;
-
- AppCore::AppAddEventHandler(&handlers[AppCore::LOW_BATTERY], AppCore::LOW_BATTERY, AppBatteryLow, mFramework);
- AppCore::AppAddEventHandler(&handlers[AppCore::LOW_MEMORY], AppCore::LOW_MEMORY, AppMemoryLow, mFramework);
- AppCore::AppAddEventHandler(&handlers[AppCore::DEVICE_ORIENTATION_CHANGED], AppCore::DEVICE_ORIENTATION_CHANGED, AppDeviceRotated, mFramework);
- AppCore::AppAddEventHandler(&handlers[AppCore::LANGUAGE_CHANGED], AppCore::LANGUAGE_CHANGED, AppLanguageChanged, mFramework);
- AppCore::AppAddEventHandler(&handlers[AppCore::REGION_FORMAT_CHANGED], AppCore::REGION_FORMAT_CHANGED, AppRegionChanged, mFramework);
-
- appcore_ui_base_ops ops = appcore_ui_base_get_default_ops();
-
- /* override methods */
- ops.base.create = AppCreate;
- ops.base.control = AppControl;
- ops.base.terminate = AppTerminate;
- ops.pause = AppPause;
- ops.resume = AppResume;
- ops.base.init = AppInit;
- ops.base.finish = AppFinish;
- ops.base.run = AppRun;
- ops.base.exit = AppExit;
-
- ret = appcore_ui_base_init(ops, *mFramework->mArgc, *mFramework->mArgv, mFramework, APPCORE_UI_BASE_HINT_WINDOW_GROUP_CONTROL | APPCORE_UI_BASE_HINT_WINDOW_STACK_CONTROL | APPCORE_UI_BASE_HINT_BG_LAUNCH_CONTROL | APPCORE_UI_BASE_HINT_HW_ACC_CONTROL | APPCORE_UI_BASE_HINT_WINDOW_AUTO_CONTROL);
-
- if(ret != TIZEN_ERROR_NONE)
- return ret;
+ if(mUiAppContext.get() == nullptr)
+ {
+ unsigned int hint = AppCoreUiBase::HINT_WINDOW_GROUP_CONTROL |
+ AppCoreUiBase::HINT_WINDOW_STACK_CONTROL |
+ AppCoreUiBase::HINT_BG_LAUNCH_CONTROL |
+ AppCoreUiBase::HINT_HW_ACC_CONTROL |
+ AppCoreUiBase::HINT_WINDOW_AUTO_CONTROL;
- appcore_ui_base_fini();
+ mUiAppContext = std::make_unique<UiAppContext>(hint, mFramework);
+ }
+ mUiAppContext->Run(*mFramework->mArgc, *mFramework->mArgv);
return TIZEN_ERROR_NONE;
}
void AppNormalExit()
{
- appcore_ui_base_exit();
+ if(mUiAppContext.get() == nullptr)
+ {
+ return;
+ }
+
+ mUiAppContext->Exit();
}
void AppWidgetExit()
{
set_last_result(TIZEN_ERROR_NOT_SUPPORTED);
}
-#ifdef DALI_ELDBUS_AVAILABLE
- // Initialize ElDBus.
- DALI_LOG_INFO(gDBusLogging, Debug::General, "Starting DBus Initialization\n");
- eldbus_init();
-#endif
+
InitThreads();
mImpl = new Impl(this, type);
Quit();
}
-#ifdef DALI_ELDBUS_AVAILABLE
- // Shutdown ELDBus.
- DALI_LOG_INFO(gDBusLogging, Debug::General, "Shutting down DBus\n");
- eldbus_shutdown();
-#endif
-
delete mImpl;
}
mRunning = true;
int ret;
+ DALI_TRACE_BEGIN(gTraceFilter, "DALI_APPMAIN");
ret = mImpl->AppMain();
+ DALI_TRACE_END(gTraceFilter, "DALI_APPMAIN");
if(ret != APP_ERROR_NONE)
{
DALI_LOG_ERROR("Framework::Run(), ui_app_main() is failed. err = %d\n", ret);
resourcePath += "/";
}
-#endif //TIZEN_PLATFORM_CONFIG_SUPPORTED
+#endif // TIZEN_PLATFORM_CONFIG_SUPPORTED
return resourcePath;
}
/**
* @copydoc Dali::DragAndDrop::StartDragAndDrop()
*/
- virtual bool StartDragAndDrop(Dali::Actor source, Dali::Actor shadow, const std::string& dragData) = 0;
+ virtual bool StartDragAndDrop(Dali::Actor source, Dali::Window shadowWindow, const Dali::DragAndDrop::DragData& data, Dali::DragAndDrop::SourceFunction callback) = 0;
/**
* @copydoc Dali::DragAndDrop::AddListener()
virtual bool AddListener(Dali::Actor target, Dali::DragAndDrop::DragAndDropFunction callback) = 0;
/**
+ * @copydoc Dali::DragAndDrop::RemoveListener()
+ */
+ virtual bool RemoveListener(Dali::Actor target) = 0;
+
+ /**
* @copydoc Dali::DragAndDrop::SendData()
*/
virtual void SendData(void* event) = 0;
{
}
-bool DragAndDropGeneric::StartDragAndDrop(Dali::Actor source, Dali::Actor shadow, const std::string& dragData)
+bool DragAndDropGeneric::StartDragAndDrop(Dali::Actor source, Dali::Window shadowWindow, const Dali::DragAndDrop::DragData& dragData, Dali::DragAndDrop::SourceFunction callback)
{
return true;
}
return true;
}
+bool DragAndDropGeneric::RemoveListener(Dali::Actor target)
+{
+ return true;
+}
+
void DragAndDropGeneric::SendData(void* event)
{
return;
/**
* @copydoc Dali::DragAndDrop::StartDragAndDrop()
*/
- bool StartDragAndDrop(Dali::Actor source, Dali::Actor shadow, const std::string& dragData) override;
+ bool StartDragAndDrop(Dali::Actor source, Dali::Window shadowWindow, const Dali::DragAndDrop::DragData& data, Dali::DragAndDrop::SourceFunction callback) override;
/**
* @copydoc Dali::DragAndDrop::AddListener()
bool AddListener(Dali::Actor target, Dali::DragAndDrop::DragAndDropFunction callback) override;
/**
+ * @copydoc Dali::DragAndDrop::RemoveListener()
+ */
+ bool RemoveListener(Dali::Actor target) override;
+
+ /**
* @copydoc Dali::DragAndDrop::SendData()
*/
void SendData(void* event) override;
return ECORE_CALLBACK_PASS_ON;
}
+static Eina_Bool EcoreEventDataSourceEnd(void* data, int type, void* event)
+{
+ Ecore_Wl2_Event_Data_Source_End *ev = reinterpret_cast<Ecore_Wl2_Event_Data_Source_End*>(event);
+ DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
+ if(ev->cancelled)
+ {
+ dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::CANCEL);
+ }
+ else
+ {
+ dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::ACCEPT);
+ }
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool EcoreEventDataSourceDrop(void* data, int type, void* event)
+{
+ DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
+ dndImpl->CallSourceEvent(Dali::DragAndDrop::SourceEventType::FINISH);
+ return ECORE_CALLBACK_PASS_ON;
+}
+
static Eina_Bool EcoreEventOfferDataReady(void* data, int type, void* event)
{
DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
return ECORE_CALLBACK_PASS_ON;
}
+static Eina_Bool EcoreEventDataEnter(void* data, int type, void* event)
+{
+ Ecore_Wl2_Event_Dnd_Enter* ev = reinterpret_cast<Ecore_Wl2_Event_Dnd_Enter*>(event);
+
+ // Set default offer is reject
+ ecore_wl2_offer_accept(ev->offer, NULL);
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool EcoreEventDataLeave(void* data, int type, void* event)
+{
+ DragAndDropEcoreWl* dndImpl = reinterpret_cast<DragAndDropEcoreWl*>(data);
+ dndImpl->ResetDropTargets();
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
Dali::DragAndDrop GetDragAndDrop()
{
Dali::DragAndDrop dnd;
DragAndDropEcoreWl::DragAndDropEcoreWl()
{
- mSendHandler = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_SEND, EcoreEventDataSend, this);
- mReceiveHandler = ecore_event_handler_add(ECORE_WL2_EVENT_OFFER_DATA_READY, EcoreEventOfferDataReady, this);
- mMotionHandler = ecore_event_handler_add(ECORE_WL2_EVENT_DND_MOTION, EcoreEventDataMotion, this);
- mDropHandler = ecore_event_handler_add(ECORE_WL2_EVENT_DND_DROP, EcoreEventDataDrop, this);
+ // Source Events
+ mSendHandler = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_SEND, EcoreEventDataSend, this);
+ mSourceEndHandler = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_END, EcoreEventDataSourceEnd, this);
+ mSourceDropHandler = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_DROP, EcoreEventDataSourceDrop, this);
+
+ // Target Events
+ mReceiveHandler = ecore_event_handler_add(ECORE_WL2_EVENT_OFFER_DATA_READY, EcoreEventOfferDataReady, this);
+ mMotionHandler = ecore_event_handler_add(ECORE_WL2_EVENT_DND_MOTION, EcoreEventDataMotion, this);
+ mDropHandler = ecore_event_handler_add(ECORE_WL2_EVENT_DND_DROP, EcoreEventDataDrop, this);
+ mEnterHandler = ecore_event_handler_add(ECORE_WL2_EVENT_DND_ENTER, EcoreEventDataEnter, this);
+ mLeaveHandler = ecore_event_handler_add(ECORE_WL2_EVENT_DND_LEAVE, EcoreEventDataLeave, this);
}
DragAndDropEcoreWl::~DragAndDropEcoreWl()
{
+ // Source Events
ecore_event_handler_del(mSendHandler);
+ ecore_event_handler_del(mSourceEndHandler);
+ ecore_event_handler_del(mSourceDropHandler);
+
+ // Target Events
ecore_event_handler_del(mReceiveHandler);
ecore_event_handler_del(mMotionHandler);
ecore_event_handler_del(mDropHandler);
+ ecore_event_handler_del(mEnterHandler);
}
-void DragAndDropEcoreWl::SetData(std::string data)
-{
- // Save Data
- mData = data;
-}
-
-bool DragAndDropEcoreWl::StartDragAndDrop(Dali::Actor source, Dali::Actor shadow, const std::string& dragData)
+bool DragAndDropEcoreWl::StartDragAndDrop(Dali::Actor source, Dali::Window shadowWindow, const Dali::DragAndDrop::DragData& data, Dali::DragAndDrop::SourceFunction callback)
{
// Get Parent Window
auto parent = Dali::DevelWindow::Get(source);
// Set Drag Source Data
- SetData(dragData);
+ mMimeType = data.GetMimeType();
+ mData = data.GetData();
- // Apply Shadow Property
- shadow.SetProperty(Dali::Actor::Property::SIZE, Vector2(150, 150));
- shadow.SetProperty(Dali::Actor::Property::OPACITY, 0.9f);
+ // Set Source Event
+ mSourceCallback = callback;
- // Create Drag Window
- mDragWindow = Dali::Window::New(Dali::PositionSize(0, 0, 150, 150), "DragWindow", "class", true);
- mDragWindow.SetTransparency(true);
- mDragWindow.SetSize(Dali::Window::WindowSize(150, 150));
- mDragWindow.SetBackgroundColor(Color::TRANSPARENT);
- mDragWindow.Add(shadow);
+ // Set Drag Window
+ mDragWindow = shadowWindow;
// Start Drag and Drop
Ecore_Wl2_Window* parentWindow = AnyCast<Ecore_Wl2_Window*>(parent.GetNativeHandle());
Ecore_Wl2_Display* display = ecore_wl2_connected_display_get(NULL);
Ecore_Wl2_Input* input = ecore_wl2_input_default_input_get(display);
- // TODO: Makes mime-type common
- char* mimeTypes[2] = {"text/plain"};
- mimeTypes[1] = NULL;
+ // Disable Default Cursor
+ ecore_wl2_input_pointer_set(input, NULL, 0, 0);
+
+ // Set mime type for drag and drop
+ const char* mimeTypes[2];
+ mimeTypes[0] = mMimeType.c_str();
+ mimeTypes[1] = NULL;
- // Set mimetype
+ // Set mime type
ecore_wl2_dnd_drag_types_set(input, (const char**)mimeTypes);
// Start wayland drag and drop
mSerial = ecore_wl2_dnd_drag_start(input, parentWindow, dragWindow);
+ // Call Start Event
+ CallSourceEvent(Dali::DragAndDrop::SourceEventType::START);
+
return true;
}
bool DragAndDropEcoreWl::AddListener(Dali::Actor target, Dali::DragAndDrop::DragAndDropFunction callback)
{
+ std::vector<DropTarget>::iterator itr;
+ for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
+ {
+ if((*itr).target == target)
+ {
+ return false;
+ }
+ }
+
DropTarget targetData;
targetData.target = target;
targetData.callback = callback;
return true;
}
+bool DragAndDropEcoreWl::RemoveListener(Dali::Actor target)
+{
+ std::vector<DropTarget>::iterator itr;
+ for(itr = mDropTargets.begin(); itr < mDropTargets.end(); itr++)
+ {
+ if((*itr).target == target)
+ {
+ mDropTargets.erase(itr);
+ break;
+ }
+ }
+
+ return true;
+}
+
+void DragAndDropEcoreWl::CallSourceEvent(Dali::DragAndDrop::SourceEventType type)
+{
+ if(mSourceCallback)
+ {
+ mSourceCallback(type);
+ }
+}
+
+void DragAndDropEcoreWl::ResetDropTargets()
+{
+ for(std::size_t i = 0; i < mDropTargets.size(); i++)
+ {
+ mDropTargets[i].inside = false;
+ }
+}
+
+
void DragAndDropEcoreWl::SendData(void* event)
{
Ecore_Wl2_Event_Data_Source_Send* ev = reinterpret_cast<Ecore_Wl2_Event_Data_Source_Send*>(event);
return;
}
- int len = strlen(mData.c_str());
- char* buf = new char[len + 1];
- strncpy(buf, mData.c_str(), len);
- buf[len] = '\0';
+ int dataLength = strlen(mData.c_str());
+ int bufferSize = dataLength;
+ if((mMimeType.find("text") != std::string::npos) ||
+ (mMimeType.find("markup") != std::string::npos) ||
+ (mMimeType.find("image") != std::string::npos))
+ {
+ bufferSize += 1;
+ }
+
+ char* buffer = new char[bufferSize];
+ if(!buffer)
+ {
+ return;
+ }
+
+ memcpy(buffer, mData.c_str(), dataLength);
+ buffer[dataLength] = '\0';
+
+ auto ret = write(ev->fd, buffer, bufferSize);
+ if(DALI_UNLIKELY(ret != bufferSize))
+ {
+ DALI_LOG_ERROR("write(ev->fd) return %d! Pleacse check it\n", static_cast<int>(ret));
+ }
- // Write source object data to target object
- write(ev->fd, buf, len + 1);
close(ev->fd);
if(mDragWindow)
mDragWindow.Hide();
}
- if(buf)
- {
- delete[] buf;
- }
+ delete[] buffer;
}
void DragAndDropEcoreWl::ReceiveData(void* event)
if(mTargetIndex != -1)
{
- Dali::DragAndDrop::DragEvent dragEvent(Dali::DragAndDrop::DragType::DROP, mPosition, ev->data);
+ Dali::DragAndDrop::DragEvent dragEvent(Dali::DragAndDrop::DragType::DROP, mPosition, ev->mimetype, ev->data);
mDropTargets[mTargetIndex].callback(dragEvent);
mDropTargets[mTargetIndex].inside = false;
+ ecore_wl2_offer_finish(ev->offer);
}
mTargetIndex = -1;
}
Dali::DragAndDrop::DragEvent dragEvent;
Dali::Vector2 curPosition(ev->x, ev->y);
- for(int i = 0; i < mDropTargets.size(); i++)
+ for(std::size_t i = 0; i < mDropTargets.size(); i++)
{
Vector2 position = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::POSITION);
Vector2 size = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SIZE);
dragEvent.SetAction(Dali::DragAndDrop::DragType::ENTER);
dragEvent.SetPosition(curPosition);
mDropTargets[i].callback(dragEvent);
+ // Accept Offer
+ ecore_wl2_offer_mimes_set(ev->offer, ecore_wl2_offer_mimes_get(ev->offer));
}
else if(!currentInside && mDropTargets[i].inside)
{
dragEvent.SetAction(Dali::DragAndDrop::DragType::LEAVE);
dragEvent.SetPosition(curPosition);
mDropTargets[i].callback(dragEvent);
+ // Reject Offer
+ ecore_wl2_offer_accept(ev->offer, NULL);
}
else if(currentInside && mDropTargets[i].inside)
{
// Check the target object region
mTargetIndex = -1;
- for(int i = 0; i < mDropTargets.size(); i++)
+ for(std::size_t i = 0; i < mDropTargets.size(); i++)
{
Vector2 position = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::POSITION);
Vector2 size = mDropTargets[i].target.GetProperty<Vector2>(Dali::Actor::Property::SIZE);
char* mimetype = (char*)eina_array_data_get(ecore_wl2_offer_mimes_get(ev->offer), 0);
if(mimetype)
{
- ecore_wl2_offer_accept(ev->offer, mimetype);
ecore_wl2_offer_receive(ev->offer, mimetype);
Ecore_Wl2_Display* display = ecore_wl2_connected_display_get(NULL);
Ecore_Wl2_Input* input = ecore_wl2_input_default_input_get(display);
/**
* @copydoc Dali::DragAndDrop::StartDragAndDrop()
*/
- bool StartDragAndDrop(Dali::Actor source, Dali::Actor shadow, const std::string& dragData) override;
+ bool StartDragAndDrop(Dali::Actor source, Dali::Window shadowWindow, const Dali::DragAndDrop::DragData& data, Dali::DragAndDrop::SourceFunction callback) override;
/**
* @copydoc Dali::DragAndDrop::AddListener()
bool AddListener(Dali::Actor target, Dali::DragAndDrop::DragAndDropFunction callback) override;
/**
- * @copydoc Dali::DragAndDrop::SetData()
+ * @copydoc Dali::DragAndDrop::RemoveListener()
*/
- void SetData(std::string data);
+ bool RemoveListener(Dali::Actor target) override;
/**
* @copydoc Dali::DragAndDrop::SendData()
*/
bool CalculateViewRegion(void* event) override;
+ /**
+ * @brief Call drag source events.
+ */
+ void CallSourceEvent(Dali::DragAndDrop::SourceEventType type);
+
+ /**
+ * @brief Reset drop targets.
+ */
+ void ResetDropTargets();
+
private:
DragAndDropEcoreWl(const DragAndDropEcoreWl&) = delete;
DragAndDropEcoreWl& operator=(DragAndDropEcoreWl&) = delete;
DragAndDropEcoreWl& operator=(DragAndDropEcoreWl&&) = delete;
private:
- Dali::Window mDragWindow;
- uint32_t mSerial{0u};
- Ecore_Event_Handler* mSendHandler{nullptr};
- Ecore_Event_Handler* mReceiveHandler{nullptr};
- Ecore_Event_Handler* mMotionHandler{nullptr};
- Ecore_Event_Handler* mDropHandler{nullptr};
- int mTargetIndex{0};
- Dali::Vector2 mPosition;
- std::string mData;
+ Dali::Window mDragWindow;
+ uint32_t mSerial{0u};
+ Ecore_Event_Handler* mSendHandler{nullptr};
+ Ecore_Event_Handler* mSourceEndHandler{nullptr};
+ Ecore_Event_Handler* mSourceDropHandler{nullptr};
+ Ecore_Event_Handler* mReceiveHandler{nullptr};
+ Ecore_Event_Handler* mMotionHandler{nullptr};
+ Ecore_Event_Handler* mDropHandler{nullptr};
+ Ecore_Event_Handler* mEnterHandler{nullptr};
+ Ecore_Event_Handler* mLeaveHandler{nullptr};
+ int mTargetIndex{0};
+ std::string mMimeType;
+ std::string mData;
+ int mDataSize{0};
+ Dali::Vector2 mPosition;
+ Dali::DragAndDrop::SourceFunction mSourceCallback{nullptr};
std::vector<DropTarget> mDropTargets;
}; // class DragAndDropEcoreWl
#define DALI_INTERNAL_EGL_IMAGE_EXTENSIONS_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void InitializeEglImageKHR();
private:
+ struct Impl;
+ Impl* mImpl{nullptr};
+
EglImplementation* mEglImplementation;
bool mImageKHRInitialized; ///< Flag for whether extended KHR functions loaded
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
fprintf(output, "{\"Cmd\":\"DRAW_INDEXED\"}\n");
break;
}
+ case GLES::CommandType::DRAW_NATIVE:
+ {
+ fprintf(output, "{\"Cmd\":\"DRAW_NATIVE\"}\n");
+ break;
+ }
case GLES::CommandType::DRAW_INDEXED_INDIRECT:
{
fprintf(output, "{\"Cmd\":\"DRAW_INDEXED_INDIRECT\"}\n");
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
break;
}
+ case GLES::CommandType::DRAW_NATIVE:
+ {
+ auto* info = &cmd.drawNative.drawNativeInfo;
+
+ mCurrentContext->PrepareForNativeRendering();
+
+ CallbackBase::ExecuteReturn<bool>(*info->callback, info->userData);
+
+ mCurrentContext->RestoreFromNativeRendering();
+ break;
+ }
}
}
}
}
mGlAbstraction->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ mGlAbstraction->PixelStorei(GL_UNPACK_ROW_LENGTH, info.srcStride);
+
mCurrentContext->BindTexture(bindTarget, texture->GetTextureTypeId(), texture->GetGLTexture());
if(!isSubImage)
}
// If upload buffer exceeds maximum size, flush.
- if(mTextureUploadTotalCPUMemoryUsed > TEXTURE_UPLOAD_MAX_BUFER_SIZE_MB * 1024)
+ if(mTextureUploadTotalCPUMemoryUsed > TEXTURE_UPLOAD_MAX_BUFER_SIZE_MB * 1024 * 1024)
{
Flush();
mTextureUploadTotalCPUMemoryUsed = 0;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "gles-graphics-render-pass.h"
#include "gles-graphics-render-target.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
#include <map>
+
namespace Dali::Graphics::GLES
{
struct Context::Impl
{
- Impl(EglGraphicsController& controller)
+ explicit Impl(EglGraphicsController& controller)
: mController(controller)
{
}
GLStateCache mGlStateCache{}; ///< GL status cache
bool mGlContextCreated{false}; ///< True if the OpenGL context has been created
+
+ EGLContext mNativeDrawContext{0u}; ///< Native rendering EGL context compatible with window context
+
+ EGLSurface mCacheDrawReadSurface{0u}; ///< cached 'read' surface
+ EGLSurface mCacheDrawWriteSurface{0u}; ///< cached 'write' surface
+ EGLContext mCacheEGLGraphicsContext{0u}; ///< cached window context
};
Context::Context(EglGraphicsController& controller)
mImpl = std::make_unique<Impl>(controller);
}
-Context::~Context() = default;
+Context::~Context()
+{
+ // Destroy native rendering context if one exists
+ if(mImpl->mNativeDrawContext)
+ {
+ eglDestroyContext(eglGetCurrentDisplay(), mImpl->mNativeDrawContext);
+ mImpl->mNativeDrawContext = EGL_NO_CONTEXT;
+ }
+}
void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
{
}
}
+void Context::PrepareForNativeRendering()
+{
+ // this should be pretty much constant
+ auto display = eglGetCurrentDisplay();
+ auto drawSurface = eglGetCurrentSurface(EGL_DRAW);
+ auto readSurface = eglGetCurrentSurface(EGL_READ);
+ auto context = eglGetCurrentContext();
+
+ // push the surface and context data to the impl
+ // It's needed to restore context
+ if(!mImpl->mCacheEGLGraphicsContext)
+ {
+ mImpl->mCacheDrawWriteSurface = drawSurface;
+ mImpl->mCacheDrawReadSurface = readSurface;
+ mImpl->mCacheEGLGraphicsContext = context;
+ }
+
+ if(!mImpl->mNativeDrawContext)
+ {
+ EGLint configId{0u};
+ EGLint size{0u};
+ eglGetConfigs(display, nullptr, 0, &size);
+ std::vector<EGLConfig> configs;
+ configs.resize(size);
+ eglGetConfigs(display, configs.data(), configs.size(), &size);
+
+ eglQueryContext(display, context, EGL_CONFIG_ID, &configId);
+
+ auto version = int(mImpl->mController.GetGLESVersion());
+
+ std::vector<EGLint> attribs;
+ attribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
+ attribs.push_back(version / 10);
+ attribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
+ attribs.push_back(version % 10);
+ attribs.push_back(EGL_NONE);
+
+ mImpl->mNativeDrawContext = eglCreateContext(display, configs[configId], EGL_NO_CONTEXT, attribs.data());
+ }
+
+ eglMakeCurrent(display, drawSurface, readSurface, mImpl->mNativeDrawContext);
+}
+
+void Context::RestoreFromNativeRendering()
+{
+ auto display = eglGetCurrentDisplay();
+
+ // bring back original context
+ eglMakeCurrent(display, mImpl->mCacheDrawWriteSurface, mImpl->mCacheDrawReadSurface, mImpl->mCacheEGLGraphicsContext);
+}
+
} // namespace Dali::Graphics::GLES
#define DALI_GRAPHICS_GLES_CONTEXT_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
void InvalidateCachedPipeline(GLES::Pipeline* pipeline);
+ /**
+ * @brief Sets up EGL context for native rendering
+ *
+ * - The native rendering uses dedicated context
+ * - There is one EGL native rendering context per GLES::Context object
+ * - Native rendering context is compatible with the window/surface context
+ * - Native rendering context dies with GLES::Context object
+ *
+ * When native rendering is about to be executed, the dedicated EGL context
+ * is acquired (created or reused) and made current. The Window/Surface context
+ * is cached to be restored afterwards.
+ */
+ void PrepareForNativeRendering();
+
+ /**
+ * @brief Restores window/surface context after native rendering.
+ */
+ void RestoreFromNativeRendering();
+
void ActiveTexture(uint32_t textureBindingIndex);
void BindTexture(GLenum target, BoundTextureType textureTypeId, uint32_t textureId);
void GenerateMipmap(GLenum target);
class CommandPool
{
static constexpr uint32_t COMMAND_POOL_DEFAULT_INCREMENT = 1024 * 32 / sizeof(Command); // 32kb banks
- static const uint32_t MEMORY_POOL_DEFAULT_INCREMENT = 1024; // 1kb memory pool increment
- static const uint32_t MEMORY_POOL_DEFAULT_ALIGNMENT = 64; // 64bytes alignment
+ static const uint32_t MEMORY_POOL_DEFAULT_INCREMENT = 1024; // 1kb memory pool increment
+ static const uint32_t MEMORY_POOL_DEFAULT_ALIGNMENT = 64; // 64bytes alignment
template<class T>
struct Block
CommandBuffer::~CommandBuffer() = default;
-void CommandBuffer::BindVertexBuffers(uint32_t firstBinding,
- std::vector<const Graphics::Buffer*> buffers,
- std::vector<uint32_t> offsets)
+void CommandBuffer::BindVertexBuffers(uint32_t firstBinding,
+ const std::vector<const Graphics::Buffer*>& buffers,
+ const std::vector<uint32_t>& offsets)
{
auto command = mCommandPool->AllocateCommand(CommandType::BIND_VERTEX_BUFFERS);
command->bindVertexBuffers.vertexBufferBindingsCount = firstBinding + buffers.size();
command->bindPipeline.pipeline = static_cast<const GLES::Pipeline*>(&pipeline);
}
-void CommandBuffer::BindTextures(std::vector<TextureBinding>& textureBindings)
+void CommandBuffer::BindTextures(const std::vector<TextureBinding>& textureBindings)
{
auto command = mCommandPool->AllocateCommand(CommandType::BIND_TEXTURES);
auto& bindTexturesCmd = command->bindTextures;
memcpy(bindTexturesCmd.textureBindings.Ptr(), textureBindings.data(), sizeof(TextureBinding) * textureBindings.size());
}
-void CommandBuffer::BindSamplers(std::vector<SamplerBinding>& samplerBindings)
+void CommandBuffer::BindSamplers(const std::vector<SamplerBinding>& samplerBindings)
{
auto command = mCommandPool->AllocateCommand(CommandType::BIND_SAMPLERS);
auto& bindSamplersCmd = command->bindSamplers;
}
void CommandBuffer::BeginRenderPass(
- Graphics::RenderPass* renderPass,
- Graphics::RenderTarget* renderTarget,
- Rect2D renderArea,
- std::vector<ClearValue> clearValues)
+ Graphics::RenderPass* renderPass,
+ Graphics::RenderTarget* renderTarget,
+ Rect2D renderArea,
+ const std::vector<ClearValue>& clearValues)
{
auto command = mCommandPool->AllocateCommand(CommandType::BEGIN_RENDERPASS);
auto& cmd = *command;
cmd.drawIndexedIndirect.stride = stride;
}
+void CommandBuffer::DrawNative(const DrawNativeInfo* drawNativeInfo)
+{
+ auto command = mCommandPool->AllocateCommand(CommandType::DRAW_NATIVE);
+ auto& cmd = command->drawNative;
+ memcpy(&cmd.drawNativeInfo, drawNativeInfo, sizeof(DrawNativeInfo));
+}
+
void CommandBuffer::Reset()
{
mCommandPool->Rollback(false);
#define DALI_GRAPHICS_GLES_COMMAND_BUFFER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
SET_DEPTH_COMPARE_OP,
SET_DEPTH_TEST_ENABLE,
SET_DEPTH_WRITE_ENABLE,
+ DRAW_NATIVE,
};
/**
{
bool enabled;
} colorMask;
+
+ struct
+ {
+ DrawNativeInfo drawNativeInfo;
+ } drawNative;
};
};
/**
* @copydoc Dali::Graphics::CommandBuffer::BindVertexBuffers
*/
- void BindVertexBuffers(uint32_t firstBinding,
- std::vector<const Graphics::Buffer*> buffers,
- std::vector<uint32_t> offsets) override;
+ void BindVertexBuffers(uint32_t firstBinding,
+ const std::vector<const Graphics::Buffer*>& buffers,
+ const std::vector<uint32_t>& offsets) override;
/**
* @copydoc Dali::Graphics::CommandBuffer::BindUniformBuffers
/**
* @copydoc Dali::Graphics::CommandBuffer::BindTextures
*/
- void BindTextures(std::vector<TextureBinding>& textureBindings) override;
+ void BindTextures(const std::vector<TextureBinding>& textureBindings) override;
/**
* @copydoc Dali::Graphics::CommandBuffer::BindSamplers
*/
- void BindSamplers(std::vector<SamplerBinding>& samplerBindings) override;
+ void BindSamplers(const std::vector<SamplerBinding>& samplerBindings) override;
/**
* @copydoc Dali::Graphics::CommandBuffer::BindPushConstants
* @copydoc Dali::Graphics::CommandBuffer::BeginRenderPass
*/
void BeginRenderPass(
- Graphics::RenderPass* renderPass,
- Graphics::RenderTarget* renderTarget,
- Rect2D renderArea,
- std::vector<ClearValue> clearValues) override;
+ Graphics::RenderPass* renderPass,
+ Graphics::RenderTarget* renderTarget,
+ Rect2D renderArea,
+ const std::vector<ClearValue>& clearValues) override;
/**
* @copydoc Dali::Graphics::CommandBuffer::EndRenderPass
uint32_t stride) override;
/**
+ * @copydoc Dali::Graphics::CommandBuffer::DrawNative
+ */
+ void DrawNative(const DrawNativeInfo* drawNativeInfo) override;
+
+ /**
* @copydoc Dali::Graphics::CommandBuffer::Reset
*/
void Reset() override;
namespace
{
+struct StringSize
+{
+ const char* const mString;
+ const uint32_t mLength;
+
+ template<uint32_t kLength>
+ constexpr StringSize(const char (&string)[kLength])
+ : mString(string),
+ mLength(kLength - 1) // remove terminating null; N.B. there should be no other null.
+ {
+ }
+
+ operator const char*() const
+ {
+ return mString;
+ }
+};
+
+bool operator==(const StringSize& lhs, const char* rhs)
+{
+ return strncmp(lhs.mString, rhs, lhs.mLength) == 0;
+}
+
+const char* const DELIMITERS = " \t\n";
+constexpr StringSize UNIFORM{"uniform"};
+constexpr StringSize SAMPLER_PREFIX{"sampler"};
+constexpr StringSize SAMPLER_TYPES[] = {"2D", "Cube", "ExternalOES"};
+constexpr auto END_SAMPLER_TYPES = SAMPLER_TYPES + std::extent<decltype(SAMPLER_TYPES)>::value;
+
Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeTypeFormat(GLenum type)
{
switch(type)
return a.location < b.location;
}
-struct StringSize
+std::string GetShaderSource(Dali::Graphics::ShaderState shaderState)
{
- const char* const mString;
- const uint32_t mLength;
+ std::vector<uint8_t> data;
+ auto* shader = static_cast<const Dali::Graphics::GLES::Shader*>(shaderState.shader);
+ auto& shaderCreateInfo = shader->GetCreateInfo();
+ data.resize(shaderCreateInfo.sourceSize + 1);
+ std::memcpy(&data[0], shaderCreateInfo.sourceData, shaderCreateInfo.sourceSize);
+ data[shaderCreateInfo.sourceSize] = 0;
- template<uint32_t kLength>
- constexpr StringSize(const char (&string)[kLength])
- : mString(string),
- mLength(kLength - 1) // remove terminating null; N.B. there should be no other null.
- {
- }
+ return std::string(reinterpret_cast<char*>(&data[0]));
+}
- operator const char*() const
+void ParseShaderSamplers(std::string shaderSource, std::vector<Dali::Graphics::UniformInfo>& uniformOpaques, int& samplerPosition, std::vector<int>& samplerPositions)
+{
+ if(!shaderSource.empty())
{
- return mString;
- }
-};
+ char* shaderStr = strdup(shaderSource.c_str());
+ char* uniform = strstr(shaderStr, UNIFORM);
-bool operator==(const StringSize& lhs, const char* rhs)
-{
- return strncmp(lhs.mString, rhs, lhs.mLength) == 0;
-}
+ while(uniform)
+ {
+ char* outerToken = strtok_r(uniform + UNIFORM.mLength, ";", &uniform);
-const char* const DELIMITERS = " \t\n";
-constexpr StringSize UNIFORM{"uniform"};
-constexpr StringSize SAMPLER_PREFIX{"sampler"};
-constexpr StringSize SAMPLER_TYPES[] = {"2D", "Cube", "ExternalOES"};
-constexpr auto END_SAMPLER_TYPES = SAMPLER_TYPES + std::extent<decltype(SAMPLER_TYPES)>::value;
+ char* nextPtr = nullptr;
+ char* token = strtok_r(outerToken, DELIMITERS, &nextPtr);
+ while(token)
+ {
+ if(SAMPLER_PREFIX == token)
+ {
+ token += SAMPLER_PREFIX.mLength;
+ if(std::find(SAMPLER_TYPES, END_SAMPLER_TYPES, token) != END_SAMPLER_TYPES)
+ {
+ bool found(false);
+ token = strtok_r(nullptr, DELIMITERS, &nextPtr);
+
+ for(uint32_t i = 0; i < static_cast<uint32_t>(uniformOpaques.size()); ++i)
+ {
+ if(samplerPositions[i] == -1 &&
+ strncmp(token, uniformOpaques[i].name.c_str(), uniformOpaques[i].name.size()) == 0)
+ {
+ samplerPositions[i] = uniformOpaques[i].offset = samplerPosition++;
+ found = true;
+ break;
+ }
+ }
+
+ if(!found)
+ {
+ DALI_LOG_ERROR("Sampler uniform %s declared but not used in the shader\n", token);
+ }
+ break;
+ }
+ }
+
+ token = strtok_r(nullptr, DELIMITERS, &nextPtr);
+ }
+
+ uniform = strstr(uniform, UNIFORM);
+ }
+ free(shaderStr);
+ }
+}
} // anonymous namespace
auto& programCreateInfo = mProgram.GetCreateInfo();
std::vector<uint8_t> data;
+ std::string vertShader;
std::string fragShader;
for(auto& shaderState : *programCreateInfo.shaderState)
{
- if(shaderState.pipelineStage == PipelineStage::FRAGMENT_SHADER)
+ if(shaderState.pipelineStage == PipelineStage::VERTEX_SHADER)
{
- auto* shader = static_cast<const GLES::Shader*>(shaderState.shader);
- auto& shaderCreateInfo = shader->GetCreateInfo();
- data.resize(shaderCreateInfo.sourceSize + 1);
- std::memcpy(&data[0], shaderCreateInfo.sourceData, shaderCreateInfo.sourceSize);
- data[shaderCreateInfo.sourceSize] = 0;
- fragShader = std::string(reinterpret_cast<char*>(&data[0]));
- break;
+ vertShader = GetShaderSource(shaderState);
}
- }
-
- if(!fragShader.empty())
- {
- char* shaderStr = strdup(fragShader.c_str());
- char* uniform = strstr(shaderStr, UNIFORM);
- int samplerPosition = 0;
- std::vector<int> samplerPositions(mUniformOpaques.size(), -1);
-
- while(uniform)
+ else if(shaderState.pipelineStage == PipelineStage::FRAGMENT_SHADER)
{
- char* outerToken = strtok_r(uniform + UNIFORM.mLength, ";", &uniform);
-
- char* nextPtr = nullptr;
- char* token = strtok_r(outerToken, DELIMITERS, &nextPtr);
- while(token)
- {
- if(SAMPLER_PREFIX == token)
- {
- token += SAMPLER_PREFIX.mLength;
- if(std::find(SAMPLER_TYPES, END_SAMPLER_TYPES, token) != END_SAMPLER_TYPES)
- {
- bool found(false);
- token = strtok_r(nullptr, DELIMITERS, &nextPtr);
-
- for(uint32_t i = 0; i < static_cast<uint32_t>(mUniformOpaques.size()); ++i)
- {
- if(samplerPositions[i] == -1 &&
- strncmp(token, mUniformOpaques[i].name.c_str(), mUniformOpaques[i].name.size()) == 0)
- {
- samplerPositions[i] = mUniformOpaques[i].offset = samplerPosition++;
- found = true;
- break;
- }
- }
+ fragShader = GetShaderSource(shaderState);
+ }
+ }
- if(!found)
- {
- DALI_LOG_ERROR("Sampler uniform %s declared but not used in the shader\n", token);
- }
- break;
- }
- }
+ int samplerPosition = 0;
+ std::vector<int> samplerPositions(mUniformOpaques.size(), -1);
- token = strtok_r(nullptr, DELIMITERS, &nextPtr);
- }
+ ParseShaderSamplers(vertShader, mUniformOpaques, samplerPosition, samplerPositions);
+ ParseShaderSamplers(fragShader, mUniformOpaques, samplerPosition, samplerPositions);
- uniform = strstr(uniform, UNIFORM);
- }
- free(shaderStr);
- }
std::sort(mUniformOpaques.begin(), mUniformOpaques.end(), [](const UniformInfo& a, const UniformInfo& b) { return a.offset < b.offset; });
}
{
DRAW,
DRAW_INDEXED,
- DRAW_INDEXED_INDIRECT
+ DRAW_INDEXED_INDIRECT,
};
Type type{}; ///< Type of the draw call
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// EXTERNAL INCLUDES
#include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
#include <dali/public-api/common/dali-vector.h>
#include <sstream>
const std::string EGL_KHR_PARTIAL_UPDATE = "EGL_KHR_partial_update";
const std::string EGL_KHR_SWAP_BUFFERS_WITH_DAMAGE = "EGL_KHR_swap_buffers_with_damage";
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_EGL, true);
} // namespace
namespace Dali
if(mSwapBufferCountAfterResume < THRESHOLD_SWAPBUFFER_COUNT)
{
DALI_LOG_RELEASE_INFO("EglImplementation::eglSwapBuffers started. eglSurface(%p)\n", eglSurface);
+ DALI_TRACE_BEGIN(gTraceFilter, "DALI_EGL_SWAP_BUFFERS");
}
#endif //DALI_PROFILE_UBUNTU
#ifndef DALI_PROFILE_UBUNTU
if(mSwapBufferCountAfterResume < THRESHOLD_SWAPBUFFER_COUNT)
{
+ DALI_TRACE_END(gTraceFilter, "DALI_EGL_SWAP_BUFFERS");
DALI_LOG_RELEASE_INFO("EglImplementation::eglSwapBuffers finished.\n");
mSwapBufferCountAfterResume++;
}
DALI_LOG_ERROR("eglQuerySurface(%d)\n", eglGetError());
age = 0;
}
-
- // 0 - invalid buffer
- // 1, 2, 3
- if(age > 3)
- {
- DALI_LOG_ERROR("EglImplementation::GetBufferAge() buffer age %d > 3\n", age);
- age = 0; // shoudn't be more than 3 back buffers, if there is just reset, I don't want to add extra history level
- }
-
return age;
}
if(mSwapBufferCountAfterResume < THRESHOLD_SWAPBUFFER_COUNT)
{
DALI_LOG_RELEASE_INFO("EglImplementation::eglSwapBuffersWithDamageKHR started. eglSurface(%p)\n", eglSurface);
+ DALI_TRACE_BEGIN(gTraceFilter, "DALI_EGL_SWAP_BUFFERS_KHR");
}
#endif //DALI_PROFILE_UBUNTU
#ifndef DALI_PROFILE_UBUNTU
if(mSwapBufferCountAfterResume < THRESHOLD_SWAPBUFFER_COUNT)
{
+ DALI_TRACE_END(gTraceFilter, "DALI_EGL_SWAP_BUFFERS_KHR");
DALI_LOG_RELEASE_INFO("EglImplementation::eglSwapBuffersWithDamageKHR finished.\n");
mSwapBufferCountAfterResume++;
}
static constexpr const char* DEFAULT_SAMPLER_TYPE = "sampler2D";
static constexpr const char* FRAGMENT_SHADER_ADVANCED_BLEND_EQUATION_PREFIX =
+ "#ifdef GL_KHR_blend_equation_advanced\n"
"#extension GL_KHR_blend_equation_advanced : enable\n"
+ "#endif\n"
- "#if GL_KHR_blend_equation_advanced==1 || __VERSION__>=320\n"
+ "#if defined(GL_KHR_blend_equation_advanced) || __VERSION__>=320\n"
" layout(blend_support_all_equations) out;\n"
"#endif\n";
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <EGL/eglext.h>
+#include <tbm_bufmgr.h>
+#include <tbm_surface.h>
+#include <tbm_surface_internal.h>
+
#include <dali/integration-api/debug.h>
// INTERNAL INCLUDES
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHRProc = 0;
PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHRProc = 0;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOESProc = 0;
+
+const std::string EGL_TIZEN_IMAGE_NATIVE_SURFACE = "EGL_TIZEN_image_native_surface";
+const std::string EGL_EXT_IMAGE_DMA_BUF_IMPORT = "EGL_EXT_image_dma_buf_import";
+
} // unnamed namespace
namespace Dali
{
namespace Adaptor
{
+struct EglImageExtensions::Impl
+{
+ bool mIsTizenImageNativeSurfaceSupported{false};
+ bool mIsExtImageDmaBufImportSupported{false};
+};
+
EglImageExtensions::EglImageExtensions(EglImplementation* eglImpl)
-: mEglImplementation(eglImpl),
+: mImpl(new Impl()),
+ mEglImplementation(eglImpl),
mImageKHRInitialized(false),
mImageKHRInitializeFailed(false)
{
EglImageExtensions::~EglImageExtensions()
{
+ delete mImpl;
}
void* EglImageExtensions::CreateImageKHR(EGLClientBuffer clientBuffer)
return NULL;
}
+ EGLImageKHR eglImage = EGL_NO_IMAGE_KHR;
+
// Use the EGL image extension
- const EGLint attribs[] =
+ if(mImpl->mIsTizenImageNativeSurfaceSupported)
+ {
+ // If EGL_TIZEN_image_native_surface is supported
+ const EGLint attribs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+
+ eglImage = eglCreateImageKHRProc(mEglImplementation->GetDisplay(),
+ EGL_NO_CONTEXT,
+ EGL_NATIVE_SURFACE_TIZEN,
+ clientBuffer,
+ attribs);
+ }
+ else if(mImpl->mIsExtImageDmaBufImportSupported)
+ {
+ // Else then use EGL_EXT_image_dma_buf_import
+ tbm_surface_info_s info;
+ tbm_surface_h tbmSurface = reinterpret_cast<tbm_surface_h>(clientBuffer);
+
+ if(tbm_surface_get_info(tbmSurface, &info) != TBM_SURFACE_ERROR_NONE)
{
- EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+ return NULL;
+ }
+
+ // We support only 1 plane
+ tbm_bo tbmBo = tbm_surface_internal_get_bo(tbmSurface, tbm_surface_internal_get_plane_bo_idx(tbmSurface, 0));
- EGLImageKHR eglImage = eglCreateImageKHRProc(mEglImplementation->GetDisplay(),
- EGL_NO_CONTEXT,
- EGL_NATIVE_SURFACE_TIZEN,
- clientBuffer,
- attribs);
+ // clang-format off
+ const EGLint attribs[] = {EGL_WIDTH, static_cast<EGLint>(info.width),
+ EGL_HEIGHT, static_cast<EGLint>(info.height),
+ EGL_LINUX_DRM_FOURCC_EXT, static_cast<EGLint>(info.format),
+ EGL_DMA_BUF_PLANE0_FD_EXT, static_cast<EGLint>(reinterpret_cast<size_t>(tbm_bo_get_handle(tbmBo, TBM_DEVICE_3D).ptr)),
+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, static_cast<EGLint>(info.planes[0].offset),
+ EGL_DMA_BUF_PLANE0_PITCH_EXT, static_cast<EGLint>(info.planes[0].stride),
+ EGL_NONE};
+ // clang-format on
+
+ eglImage = eglCreateImageKHRProc(mEglImplementation->GetDisplay(),
+ EGL_NO_CONTEXT,
+ EGL_LINUX_DMA_BUF_EXT,
+ nullptr,
+ attribs);
+ }
+ else
+ {
+ DALI_LOG_ERROR("Not supported\n");
+ return EGL_NO_IMAGE_KHR;
+ }
if(EGL_NO_IMAGE_KHR == eglImage)
{
{
mImageKHRInitializeFailed = true;
}
+
+ std::string extensionStr = eglQueryString(mEglImplementation->GetDisplay(), EGL_EXTENSIONS);
+
+ auto found = extensionStr.find(EGL_TIZEN_IMAGE_NATIVE_SURFACE);
+ if(found != std::string::npos)
+ {
+ mImpl->mIsTizenImageNativeSurfaceSupported = true;
+ }
+
+ found = extensionStr.find(EGL_EXT_IMAGE_DMA_BUF_IMPORT);
+ if(found != std::string::npos)
+ {
+ mImpl->mIsExtImageDmaBufImportSupported = true;
+ }
}
} // namespace Adaptor
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <dali/internal/imaging/common/image-operations.h>
#include <dali/internal/imaging/common/pixel-buffer-impl.h>
#include <dali/internal/imaging/common/pixel-manipulation.h>
-#include <dali/public-api/images/image-operations.h> // For ImageDimensions
+#include <dali/public-api/images/image-operations.h> // For ImageDimensions and MultiplyAndNormalizeColor
namespace Dali
{
unsigned char* destBuffer = buffer.GetBuffer();
unsigned int destBytesPerPixel = Dali::Pixel::GetBytesPerPixel(buffer.GetPixelFormat());
-
- int srcOffset = 0;
- int destOffset = 0;
-
- float srcAlphaValue = 1.0f;
+ unsigned int srcStrideBytes = mask.GetStride() * srcBytesPerPixel;
+ unsigned int destStrideBytes = buffer.GetStride() * destBytesPerPixel;
// if image is premultiplied, the other channels of the image need to multiply by alpha.
if(buffer.IsAlphaPreMultiplied())
{
- for(unsigned int row = 0; row < buffer.GetHeight(); ++row)
+ // Collect all valid channel list before lookup whole buffer
+ std::vector<Channel> validChannelList;
+ for(const Channel& channel : {Adaptor::RED, Adaptor::GREEN, Adaptor::BLUE, Adaptor::LUMINANCE, Adaptor::ALPHA})
{
- for(unsigned int col = 0; col < buffer.GetWidth(); ++col)
+ if(HasChannel(destPixelFormat, channel))
{
- auto srcAlpha = ReadChannel(srcBuffer + srcOffset, srcPixelFormat, Adaptor::ALPHA);
- auto destRed = ReadChannel(destBuffer + destOffset, destPixelFormat, Adaptor::RED);
- auto destGreen = ReadChannel(destBuffer + destOffset, destPixelFormat, Adaptor::GREEN);
- auto destBlue = ReadChannel(destBuffer + destOffset, destPixelFormat, Adaptor::BLUE);
- auto destLuminance = ReadChannel(destBuffer + destOffset, destPixelFormat, Adaptor::LUMINANCE);
- auto destAlpha = ReadChannel(destBuffer + destOffset, destPixelFormat, Adaptor::ALPHA);
-
- WriteChannel(destBuffer + destOffset, destPixelFormat, Adaptor::RED, destRed * srcAlpha / 255);
- WriteChannel(destBuffer + destOffset, destPixelFormat, Adaptor::GREEN, destGreen * srcAlpha / 255);
- WriteChannel(destBuffer + destOffset, destPixelFormat, Adaptor::BLUE, destBlue * srcAlpha / 255);
- WriteChannel(destBuffer + destOffset, destPixelFormat, Adaptor::LUMINANCE, destLuminance * srcAlpha / 255);
- WriteChannel(destBuffer + destOffset, destPixelFormat, Adaptor::ALPHA, destAlpha * srcAlpha / 255);
-
- srcOffset += srcBytesPerPixel;
- destOffset += destBytesPerPixel;
+ validChannelList.emplace_back(channel);
+ }
+ }
+ if(DALI_LIKELY(!validChannelList.empty()))
+ {
+ for(unsigned int row = 0; row < buffer.GetHeight(); ++row)
+ {
+ int srcOffset = 0;
+ int destOffset = 0;
+
+ for(unsigned int col = 0; col < buffer.GetWidth(); ++col)
+ {
+ auto srcAlpha = srcBuffer[srcOffset + srcAlphaByteOffset] & srcAlphaMask;
+ if(srcAlpha < 255)
+ {
+ // If alpha is 255, we don't need to change color. Skip current pixel
+ // But if alpha is not 255, we should change color.
+ if(srcAlpha > 0)
+ {
+ for(const Channel& channel : validChannelList)
+ {
+ auto color = ReadChannel(destBuffer + destOffset, destPixelFormat, channel);
+ WriteChannel(destBuffer + destOffset, destPixelFormat, channel, Platform::MultiplyAndNormalizeColor(color, srcAlpha));
+ }
+ }
+ else
+ {
+ // If alpha is 0, just set all pixel as zero.
+ memset(destBuffer + destOffset, 0, destBytesPerPixel);
+ }
+ }
+
+ srcOffset += srcBytesPerPixel;
+ destOffset += destBytesPerPixel;
+ }
+ srcBuffer += srcStrideBytes;
+ destBuffer += destStrideBytes;
}
}
}
{
for(unsigned int row = 0; row < buffer.GetHeight(); ++row)
{
+ int srcOffset = 0;
+ int destOffset = 0;
+
for(unsigned int col = 0; col < buffer.GetWidth(); ++col)
{
- unsigned char alpha = srcBuffer[srcOffset + srcAlphaByteOffset] & srcAlphaMask;
- srcAlphaValue = float(alpha) / 255.0f;
+ uint8_t srcAlpha = srcBuffer[srcOffset + srcAlphaByteOffset] & srcAlphaMask;
+ uint8_t destAlpha = destBuffer[destOffset + destAlphaByteOffset] & destAlphaMask;
+
+ destAlpha = Platform::MultiplyAndNormalizeColor(srcAlpha, destAlpha);
- unsigned char destAlpha = destBuffer[destOffset + destAlphaByteOffset] & destAlphaMask;
- float destAlphaValue = Clamp(float(destAlpha) * srcAlphaValue, 0.0f, 255.0f);
- destAlpha = destAlphaValue;
destBuffer[destOffset + destAlphaByteOffset] &= ~destAlphaMask;
destBuffer[destOffset + destAlphaByteOffset] |= (destAlpha & destAlphaMask);
srcOffset += srcBytesPerPixel;
destOffset += destBytesPerPixel;
}
+ srcBuffer += srcStrideBytes;
+ destBuffer += destStrideBytes;
}
}
}
srcAlphaMask = 0xFF;
}
- unsigned int srcBytesPerPixel = Dali::Pixel::GetBytesPerPixel(srcPixelFormat);
unsigned char* srcBuffer = mask.GetBuffer();
+ unsigned int srcBytesPerPixel = Dali::Pixel::GetBytesPerPixel(srcPixelFormat);
+ unsigned int srcStrideBytes = mask.GetStride() * srcBytesPerPixel;
// Set up source color offsets
Dali::Pixel::Format srcColorPixelFormat = buffer.GetPixelFormat();
unsigned int srcColorBytesPerPixel = Dali::Pixel::GetBytesPerPixel(srcColorPixelFormat);
+ unsigned int srcColorStrideBytes = buffer.GetStride() * srcColorBytesPerPixel;
// Setup destination offsets
Dali::Pixel::Format destPixelFormat = Dali::Pixel::RGBA8888;
int destAlphaMask = 0;
Dali::Pixel::GetAlphaOffsetAndMask(destPixelFormat, destAlphaByteOffset, destAlphaMask);
- PixelBufferPtr newPixelBuffer = PixelBuffer::New(buffer.GetWidth(), buffer.GetHeight(), destPixelFormat);
- unsigned char* destBuffer = newPixelBuffer->GetBuffer();
- unsigned char* oldBuffer = buffer.GetBuffer();
+ PixelBufferPtr newPixelBuffer = PixelBuffer::New(buffer.GetWidth(), buffer.GetHeight(), destPixelFormat);
+ unsigned char* destBuffer = newPixelBuffer->GetBuffer();
+ unsigned char* oldBuffer = buffer.GetBuffer();
+ unsigned int destStrideBytes = newPixelBuffer->GetStride() * destBytesPerPixel;
- int srcAlphaOffset = 0;
- int srcColorOffset = 0;
- int destOffset = 0;
- bool hasAlpha = Dali::Pixel::HasAlpha(buffer.GetPixelFormat());
+ bool hasAlpha = Dali::Pixel::HasAlpha(buffer.GetPixelFormat());
- float srcAlphaValue = 1.0f;
- unsigned char destAlpha = 0;
+ unsigned char destAlpha = 0;
for(unsigned int row = 0; row < buffer.GetHeight(); ++row)
{
+ int srcAlphaOffset = 0;
+ int srcColorOffset = 0;
+ int destOffset = 0;
for(unsigned int col = 0; col < buffer.GetWidth(); ++col)
{
- unsigned char alpha = srcBuffer[srcAlphaOffset + srcAlphaByteOffset] & srcAlphaMask;
- srcAlphaValue = float(alpha) / 255.0f;
+ unsigned char srcAlpha = srcBuffer[srcAlphaOffset + srcAlphaByteOffset] & srcAlphaMask;
ConvertColorChannelsToRGBA8888(oldBuffer, srcColorOffset, srcColorPixelFormat, destBuffer, destOffset);
if(hasAlpha)
{
- destAlpha = ConvertAlphaChannelToA8(oldBuffer, srcColorOffset, srcColorPixelFormat);
- float destAlphaValue = Clamp(float(destAlpha) * srcAlphaValue, 0.0f, 255.0f);
- destAlpha = destAlphaValue;
+ destAlpha = ConvertAlphaChannelToA8(oldBuffer, srcColorOffset, srcColorPixelFormat);
+ destAlpha = Platform::MultiplyAndNormalizeColor(srcAlpha, destAlpha);
}
else
{
- destAlpha = floorf(Clamp(srcAlphaValue * 255.0f, 0.0f, 255.0f));
+ destAlpha = srcAlpha;
}
destBuffer[destOffset + destAlphaByteOffset] &= ~destAlphaMask;
srcAlphaOffset += srcBytesPerPixel;
destOffset += destBytesPerPixel;
}
+ oldBuffer += srcColorStrideBytes;
+ srcBuffer += srcStrideBytes;
+ destBuffer += destStrideBytes;
}
return newPixelBuffer;
#define DALI_INTERNAL_ADAPTOR_ALPHA_MASK_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
namespace Adaptor
{
/**
- * Apply the mask to a buffer's alpha channel
+ * @brief Apply the mask to a buffer's alpha channel
+ * @note It works well only if mask's alpha mask is 8bit
* @param[in] buffer The buffer to apply the mask to
* @param[in] mask The mask to apply
*/
void ApplyMaskToAlphaChannel(PixelBuffer& buffer, const PixelBuffer& mask);
/**
- * Create a new PixelBuffer with an alpha channel large enough to handle the alpha from
+ * @brief Create a new PixelBuffer with an alpha channel large enough to handle the alpha from
* the mask, converting the color values to the new size, and either multiplying the mask's
* alpha into the existing alpha value, or writing the mask's alpha value directly into
* the new buffer's alpha channel.
+ * @note It works well only if mask's alpha mask is 8bit
*
* @param[in] buffer The buffer to apply the mask to
* @param[in] mask The mask to apply
~AnimatedImageLoading() override = default;
/**
- * @copydoc Dali::AnimatedImageLoading::LoadNextNFrames()
- */
- virtual bool LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData) = 0;
-
- /**
* @copydoc Dali::AnimatedImageLoading::LoadFrame()
*/
virtual Dali::Devel::PixelBuffer LoadFrame(uint32_t frameIndex) = 0;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// CLASS HEADER
#include "encoded-image-buffer-impl.h"
+// EXTERNAL INCLUDE
+#include <dali/devel-api/common/hash.h>
+
namespace Dali
{
namespace Internal
{
-
EncodedImageBuffer::EncodedImageBuffer(const RawBufferType& buffer)
: mBuffer(buffer)
{
+ mBufferHash = CalculateHash(mBuffer);
}
EncodedImageBuffer::~EncodedImageBuffer()
return mBuffer;
}
+const std::size_t EncodedImageBuffer::GetHash() const
+{
+ return mBufferHash;
+}
+
} // namespace Internal
} // namespace Dali
#define DALI_ENCODED_IMAGE_BUFFER_IMPL_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
// EXTERNAL INCLUDES
+#include <dali/public-api/common/dali-vector.h>
#include <dali/public-api/common/intrusive-ptr.h>
#include <dali/public-api/object/base-object.h>
-#include <dali/public-api/common/dali-vector.h>
// INTERNAL INCLUDES
-#include <dali/public-api/dali-adaptor-common.h>
#include <dali/public-api/adaptor-framework/encoded-image-buffer.h>
+#include <dali/public-api/dali-adaptor-common.h>
namespace Dali
{
*/
const RawBufferType& GetRawBuffer() const;
+ /**
+ * @copydoc Dali::EncodedImageBuffer::GetHash
+ */
+ const std::size_t GetHash() const;
+
protected:
/**
* Destructor
private:
Dali::Vector<uint8_t> mBuffer;
+ std::size_t mBufferHash;
};
} // namespace Internal
}
dataBuffer.ResizeUninitialized(dataSize);
- size_t offset = 0;
- for(size_t i = 0; i < chunks.size(); ++i)
+ if(DALI_LIKELY(dataSize > 0))
{
- memcpy(&dataBuffer[offset], &chunks[i].data[0], chunks[i].data.capacity());
- offset += chunks[i].data.capacity();
+ std::uint8_t* dataBufferPtr = dataBuffer.Begin();
+ for(size_t i = 0; i < chunks.size(); ++i)
+ {
+ memcpy(dataBufferPtr, &chunks[i].data[0], chunks[i].data.capacity());
+ dataBufferPtr += chunks[i].data.capacity();
+ }
}
return result;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
#include <memory.h>
#include <cmath>
unsigned char* outBuffer,
const unsigned int bufferWidth,
const unsigned int bufferHeight,
+ const unsigned int inBufferStride,
+ const unsigned int outBufferStride,
const float blurRadius)
{
// Calculate the weights for gaussian blur
for(unsigned int y = 0; y < bufferHeight; y++)
{
unsigned int targetPixelIndex = y;
- unsigned int ioffset = y * bufferWidth;
+ unsigned int ioffset = y * inBufferStride;
for(unsigned int x = 0; x < bufferWidth; x++)
{
float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f;
outBuffer[targetPixelIndex * 4 + 2] = std::max(0, std::min(static_cast<int>(b + 0.5f), 255));
outBuffer[targetPixelIndex * 4 + 3] = std::max(0, std::min(static_cast<int>(a + 0.5f), 255));
- targetPixelIndex += bufferHeight;
+ targetPixelIndex += outBufferStride;
}
}
{
unsigned int bufferWidth = buffer.GetWidth();
unsigned int bufferHeight = buffer.GetHeight();
+ unsigned int bufferStride = buffer.GetStride();
+
+ if(bufferWidth == 0 || bufferHeight == 0 || bufferStride == 0 || buffer.GetPixelFormat() != Pixel::RGBA8888)
+ {
+ DALI_LOG_ERROR("Invalid buffer!\n");
+ return;
+ }
// Create a temporary buffer for the two-pass blur
PixelBufferPtr softShadowImageBuffer = PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
- memcpy(softShadowImageBuffer->GetBuffer(), buffer.GetBuffer(), 4u * bufferWidth * bufferHeight);
// We perform the blur first but write its output image buffer transposed, so that we
// can just do it in two passes. The first pass blurs horizontally and transposes, the
// second pass does the same, but as the image is now transposed, it's really doing a
// vertical blur. The second transposition makes the image the right way up again. This
// is much faster than doing a 2D convolution.
- ConvoluteAndTranspose(buffer.GetBuffer(), softShadowImageBuffer->GetBuffer(), bufferWidth, bufferHeight, blurRadius);
- ConvoluteAndTranspose(softShadowImageBuffer->GetBuffer(), buffer.GetBuffer(), bufferHeight, bufferWidth, blurRadius);
+ ConvoluteAndTranspose(buffer.GetBuffer(), softShadowImageBuffer->GetBuffer(), bufferWidth, bufferHeight, bufferStride, bufferHeight, blurRadius);
+ ConvoluteAndTranspose(softShadowImageBuffer->GetBuffer(), buffer.GetBuffer(), bufferHeight, bufferWidth, bufferHeight, bufferStride, blurRadius);
// On leaving scope, softShadowImageBuffer will get destroyed.
}
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
const int IMG_MAX_SIZE = 65000;
constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
+constexpr int LOCAL_CACHED_COLOR_GENERATE_THRESHOLD = 64; ///< Generate color map optimize only if colorCount * threshold < width * height, So we don't loop if image is small
+
#if GIFLIB_MAJOR < 5
const int DISPOSE_BACKGROUND = 2; /* Set area too background color */
const int DISPOSE_PREVIOUS = 3; /* Restore to previous content */
bool animated;
};
+struct GifCachedColorData
+{
+ GifCachedColorData() = default;
+
+ // precalculated colormap table
+ std::vector<std::uint32_t> globalCachedColor{};
+ std::vector<std::uint32_t> localCachedColor{};
+};
+
// Forward declaration
struct GifAccessor;
FileData fileData;
GifAnimationData animated;
+ GifCachedColorData cachedColor;
std::unique_ptr<GifAccessor> gifAccessor{nullptr};
int imageNumber{0};
FileInfo fileInfo;
{
LoaderInfo::FileInfo* fi = reinterpret_cast<LoaderInfo::FileInfo*>(gifFileType->UserData);
- if(fi->position >= fi->length)
+ if(DALI_UNLIKELY(fi->position >= fi->length))
{
return 0; // if at or past end - no
}
{
Internal::Platform::FileReader fileReader(fileName);
FILE* fp = fileReader.GetFile();
- if(fp == NULL)
+ if(DALI_UNLIKELY(fp == NULL))
{
return false;
}
- if(fseek(fp, 0, SEEK_END) <= -1)
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END) <= -1))
{
return false;
}
length = ftell(fp);
- if(length <= -1)
+ if(DALI_UNLIKELY(length <= -1))
{
return false;
}
- if((!fseek(fp, 0, SEEK_SET)))
+ if(DALI_LIKELY(!fseek(fp, 0, SEEK_SET)))
{
globalMap = reinterpret_cast<GifByteType*>(malloc(sizeof(GifByteType) * length));
length = fread(globalMap, sizeof(GifByteType), length, fp);
size_t dataSize;
succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory(fileName, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE);
- if(succeeded)
+ if(DALI_LIKELY(succeeded))
{
size_t blobSize = dataBuffer.Size();
- if(blobSize > 0U)
+ if(DALI_LIKELY(blobSize > 0U))
{
// Open a file handle on the memory buffer:
Dali::Internal::Platform::FileReader fileReader(dataBuffer, blobSize);
FILE* const fp = fileReader.GetFile();
- if(NULL != fp)
+ if(DALI_LIKELY(NULL != fp))
{
- if((!fseek(fp, 0, SEEK_SET)))
+ if(DALI_LIKELY(!fseek(fp, 0, SEEK_SET)))
{
globalMap = reinterpret_cast<GifByteType*>(malloc(sizeof(GifByteType) * blobSize));
length = fread(globalMap, sizeof(GifByteType), blobSize, fp);
*
* @param[in] animated A structure containing GIF animation data
* @param[in] index Frame index to be searched in GIF
- * @return A pointer to the ImageFrame.
+ * @return single 32-bit (ABGR) value.
*/
-inline int CombinePixelABGR(int a, int r, int g, int b)
+inline std::uint32_t CombinePixelABGR(const std::uint32_t& a, const std::uint32_t& r, const std::uint32_t& g, const std::uint32_t& b)
{
return (((a) << 24) + ((b) << 16) + ((g) << 8) + (r));
}
-inline int PixelLookup(ColorMapObject* colorMap, int index)
+inline std::uint32_t PixelLookup(const ColorMapObject* const& colorMap, int index)
{
return CombinePixelABGR(0xFF, colorMap->Colors[index].Red, colorMap->Colors[index].Green, colorMap->Colors[index].Blue);
}
/**
+ * @brief Get the Background Color from frameInfo
+ *
+ * @param[in] gif A pointer pointing to GIF File Type
+ * @param[in] frameInfo A pointer pointing to Frame Information data
+ * @return single 32-bit (ABGR) value. of background color
+ */
+std::uint32_t GetBackgroundColor(GifFileType* gif, FrameInfo* frameInfo)
+{
+ if(frameInfo->transparent < 0)
+ {
+ ColorMapObject* colorMap;
+ int backGroundColor;
+
+ // work out color to use from colorMap
+ if(gif->Image.ColorMap)
+ {
+ colorMap = gif->Image.ColorMap;
+ }
+ else
+ {
+ colorMap = gif->SColorMap;
+ }
+ backGroundColor = gif->SBackGroundColor;
+ // Get background color from colormap
+ return PixelLookup(colorMap, backGroundColor);
+ }
+ else
+ {
+ // transparent
+ return 0;
+ }
+}
+
+/**
* @brief Brute force find frame index - gifs are normally small so ok for now.
*
* @param[in] animated A structure containing GIF animation data
* @brief Fill in an image with a specific rgba color value.
*
* @param[in] data A pointer pointing to an image data
- * @param[in] row A int containing the number of rows in an image
+ * @param[in] stride A int containing the number of stride in an image
* @param[in] val A uint32_t containing rgba color value
* @param[in] x X-coordinate used an offset to calculate pixel position
* @param[in] y Y-coordinate used an offset to calculate pixel position
* @param[in] width Width of the image
* @param[in] height Height of the image
*/
-void FillImage(uint32_t* data, int row, uint32_t val, int x, int y, int width, int height)
+void FillImage(uint32_t* data, int stride, uint32_t val, int x, int y, int width, int height)
{
- int xAxis, yAxis;
uint32_t* pixelPosition;
- for(yAxis = 0; yAxis < height; yAxis++)
- {
- pixelPosition = data + ((y + yAxis) * row) + x;
- for(xAxis = 0; xAxis < width; xAxis++)
- {
- *pixelPosition = val;
- pixelPosition++;
- }
- }
-}
-
-/**
- * @brief Fill a rgba data pixle blob with a frame color (bg or trans)
- *
- * @param[in] data A pointer pointing to an image data
- * @param[in] row A int containing the number of rows in an image
- * @param[in] gif A pointer pointing to GIF File Type
- * @param[in] frameInfo A pointer pointing to Frame Information data
- * @param[in] x X-coordinate used an offset to calculate pixel position
- * @param[in] y Y-coordinate used an offset to calculate pixel position
- * @param[in] width Width of the image
- * @param[in] height Height of the image
- */
-void FillFrame(uint32_t* data, int row, GifFileType* gif, FrameInfo* frameInfo, int x, int y, int w, int h)
-{
- // solid color fill for pre frame region
- if(frameInfo->transparent < 0)
+ // Boost time if stride == width and x == 0. We can assume that all pointer is continuous.
+ if(x == 0 && stride == width)
{
- ColorMapObject* colorMap;
- int backGroundColor;
-
- // work out color to use from colorMap
- if(gif->Image.ColorMap)
+ pixelPosition = data + (y * stride);
+ // Clear as white or transparent
+ // Special case. we can use memset.
+ if(val == 0x00 || val == 0xffffffffu)
{
- colorMap = gif->Image.ColorMap;
+ const std::int8_t setupVal = val & 0xff;
+ memset(pixelPosition, setupVal, width * height * sizeof(std::uint32_t));
}
else
{
- colorMap = gif->SColorMap;
+ for(int byteCount = 0; byteCount < width * height; ++byteCount)
+ {
+ *pixelPosition = val;
+ ++pixelPosition;
+ }
}
- backGroundColor = gif->SBackGroundColor;
- // and do the fill
- FillImage(data, row, CombinePixelABGR(0xff, colorMap->Colors[backGroundColor].Red, colorMap->Colors[backGroundColor].Green, colorMap->Colors[backGroundColor].Blue), x, y, w, h);
}
- // fill in region with 0 (transparent)
else
{
- FillImage(data, row, 0, x, y, w, h);
+ for(int yAxis = 0; yAxis < height; ++yAxis)
+ {
+ pixelPosition = data + ((y + yAxis) * stride) + x;
+ for(int xAxis = 0; xAxis < width; ++xAxis)
+ {
+ *pixelPosition = val;
+ ++pixelPosition;
+ }
+ }
}
}
* @brief Decode a gif image into rows then expand to 32bit into the destination
* data pointer.
*/
-bool DecodeImage(GifFileType* gif, uint32_t* data, int rowpix, int xin, int yin, int transparent, int x, int y, int w, int h, bool fill)
+bool DecodeImage(GifFileType* gif, GifCachedColorData& gifCachedColor, uint32_t* data, int rowpix, int xin, int yin, int transparent, int x, int y, int w, int h, bool fill, uint32_t fillColor = 0u)
{
int intoffset[] = {0, 4, 2, 1};
int intjump[] = {8, 8, 4, 2};
ColorMapObject* colorMap;
uint32_t* p;
+ // cached color data.
+ const std::uint32_t* cachedColorPtr = nullptr;
+
// what we need is image size.
SavedImage* sp;
sp = &gif->SavedImages[gif->ImageCount - 1];
gifW = sp->ImageDesc.Width;
gifH = sp->ImageDesc.Height;
- if((gifW < w) || (gifH < h))
+ if(DALI_UNLIKELY((gifW < w) || (gifH < h)))
{
DALI_LOG_ERROR("gifW : %d, w : %d, gifH : %d, h : %d\n", gifW, w, gifH, h);
DALI_ASSERT_DEBUG(false && "Dimensions are bigger than the Gif image size");
// build a blob of memory to have pointers to rows of pixels
// AND store the decoded gif pixels (1 byte per pixel) as welll
rows = static_cast<GifRowType*>(malloc((gifH * sizeof(GifRowType)) + (gifW * gifH * sizeof(GifPixelType))));
- if(!rows)
+ if(DALI_UNLIKELY(!rows))
{
goto on_error;
}
{
for(yy = intoffset[i]; yy < gifH; yy += intjump[i])
{
- if(DGifGetLine(gif, rows[yy], gifW) != GIF_OK)
+ if(DALI_UNLIKELY(DGifGetLine(gif, rows[yy], gifW) != GIF_OK))
{
goto on_error;
}
{
for(yy = 0; yy < gifH; yy++)
{
- if(DGifGetLine(gif, rows[yy], gifW) != GIF_OK)
+ if(DALI_UNLIKELY(DGifGetLine(gif, rows[yy], gifW) != GIF_OK))
{
goto on_error;
}
if(gif->Image.ColorMap)
{
colorMap = gif->Image.ColorMap;
+ // if w * h is big enough, generate local cached color.
+ if(colorMap->ColorCount * LOCAL_CACHED_COLOR_GENERATE_THRESHOLD < w * h)
+ {
+ gifCachedColor.localCachedColor.resize(colorMap->ColorCount);
+ for(i = 0; i < colorMap->ColorCount; ++i)
+ {
+ gifCachedColor.localCachedColor[i] = PixelLookup(colorMap, i);
+ }
+
+ cachedColorPtr = gifCachedColor.localCachedColor.data();
+ }
}
else
{
- colorMap = gif->SColorMap;
+ colorMap = gif->SColorMap;
+ cachedColorPtr = gifCachedColor.globalCachedColor.data();
}
+ // HARD-CODING optimize
// if we need to deal with transparent pixels at all...
if(transparent >= 0)
{
// if we are told to FILL (overwrite with transparency kept)
if(fill)
{
- for(yy = 0; yy < h; yy++)
+ // if we use cachedColor, use it
+ if(cachedColorPtr)
{
- p = data + ((y + yy) * rowpix) + x;
- for(xx = 0; xx < w; xx++)
+ for(yy = 0; yy < h; yy++)
{
- pix = rows[yin + yy][xin + xx];
- if(pix != transparent)
+ p = data + ((y + yy) * rowpix) + x;
+ for(xx = 0; xx < w; xx++)
{
- *p = PixelLookup(colorMap, pix);
+ pix = rows[yin + yy][xin + xx];
+ if(pix != transparent)
+ {
+ *p = cachedColorPtr[pix];
+ }
+ else
+ {
+ *p = fillColor;
+ }
+ p++;
}
- else
+ }
+ }
+ // we don't have cachedColor. use PixelLookup function.
+ else
+ {
+ for(yy = 0; yy < h; yy++)
+ {
+ p = data + ((y + yy) * rowpix) + x;
+ for(xx = 0; xx < w; xx++)
{
- *p = 0;
+ pix = rows[yin + yy][xin + xx];
+ if(pix != transparent)
+ {
+ *p = PixelLookup(colorMap, pix);
+ }
+ else
+ {
+ *p = fillColor;
+ }
+ p++;
}
- p++;
}
}
}
// paste on top with transparent pixels untouched
else
{
- for(yy = 0; yy < h; yy++)
+ // if we use cachedColor, use it
+ if(cachedColorPtr)
{
- p = data + ((y + yy) * rowpix) + x;
- for(xx = 0; xx < w; xx++)
+ for(yy = 0; yy < h; yy++)
{
- pix = rows[yin + yy][xin + xx];
- if(pix != transparent)
+ p = data + ((y + yy) * rowpix) + x;
+ for(xx = 0; xx < w; xx++)
{
- *p = PixelLookup(colorMap, pix);
+ pix = rows[yin + yy][xin + xx];
+ if(pix != transparent)
+ {
+ *p = cachedColorPtr[pix];
+ }
+ p++;
+ }
+ }
+ }
+ // we don't have cachedColor. use PixelLookup function.
+ else
+ {
+ for(yy = 0; yy < h; yy++)
+ {
+ p = data + ((y + yy) * rowpix) + x;
+ for(xx = 0; xx < w; xx++)
+ {
+ pix = rows[yin + yy][xin + xx];
+ if(pix != transparent)
+ {
+ *p = PixelLookup(colorMap, pix);
+ }
+ p++;
}
- p++;
}
}
}
}
else
{
- // walk pixels without worring about transparency at all
- for(yy = 0; yy < h; yy++)
+ // if we use cachedColor, use it
+ if(cachedColorPtr)
+ {
+ // walk pixels without worring about transparency at all
+ for(yy = 0; yy < h; yy++)
+ {
+ p = data + ((y + yy) * rowpix) + x;
+ for(xx = 0; xx < w; xx++)
+ {
+ pix = rows[yin + yy][xin + xx];
+ *p = cachedColorPtr[pix];
+ p++;
+ }
+ }
+ }
+ // we don't have cachedColor. use PixelLookup function.
+ else
{
- p = data + ((y + yy) * rowpix) + x;
- for(xx = 0; xx < w; xx++)
+ // walk pixels without worring about transparency at all
+ for(yy = 0; yy < h; yy++)
{
- pix = rows[yin + yy][xin + xx];
- *p = PixelLookup(colorMap, pix);
- p++;
+ p = data + ((y + yy) * rowpix) + x;
+ for(xx = 0; xx < w; xx++)
+ {
+ pix = rows[yin + yy][xin + xx];
+ *p = PixelLookup(colorMap, pix);
+ p++;
+ }
}
}
}
*
* @param[in] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
* @param[out] prop A ImageProperties structure for storing information about GIF data.
- * @param[out] error Error code
* @return The true or false whether reading was successful or not.
*/
bool ReadHeader(LoaderInfo& loaderInfo,
- ImageProperties& prop, //output struct
- int* error)
+ ImageProperties& prop)
{
- GifAnimationData& animated = loaderInfo.animated;
- LoaderInfo::FileData& fileData = loaderInfo.fileData;
- bool success = false;
+ GifAnimationData& animated = loaderInfo.animated;
+ GifCachedColorData& cachedColor = loaderInfo.cachedColor;
+ LoaderInfo::FileData& fileData = loaderInfo.fileData;
+ bool success = false;
LoaderInfo::FileInfo fileInfo;
GifRecordType rec;
bool full = true;
success = fileData.LoadFile();
- if(!success || !fileData.globalMap)
+ if(DALI_UNLIKELY(!success || !fileData.globalMap))
{
success = false;
DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
prop.h = gifAccessor.gif->SHeight;
// if size is invalid - abort here
- if((prop.w < 1) || (prop.h < 1) || (prop.w > IMG_MAX_SIZE) || (prop.h > IMG_MAX_SIZE) || IMG_TOO_BIG(prop.w, prop.h))
+ if(DALI_UNLIKELY((prop.w < 1) || (prop.h < 1) || (prop.w > IMG_MAX_SIZE) || (prop.h > IMG_MAX_SIZE) || IMG_TOO_BIG(prop.w, prop.h)))
{
if(IMG_TOO_BIG(prop.w, prop.h))
{
GifByteType* img;
// get image desc
- if(DGifGetImageDesc(gifAccessor.gif) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetImageDesc(gifAccessor.gif) == GIF_ERROR))
{
success = false;
DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
break;
}
// skip decoding and just walk image to next
- if(DGifGetCode(gifAccessor.gif, &img_code, &img) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetCode(gifAccessor.gif, &img_code, &img) == GIF_ERROR))
{
success = false;
DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
{
// allocate and save frame with field data
frameInfo = NewFrame(animated, -1, 0, 0, imageNumber + 1);
- if(!frameInfo)
+ if(DALI_UNLIKELY(!frameInfo))
{
success = false;
DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
int disposeMode = (ext[1] >> 2) & 0x7;
int delay = (int(ext[3]) << 8) | int(ext[2]);
frameInfo = NewFrame(animated, transparencyIndex, disposeMode, delay, imageNumber + 1);
- if(!frameInfo)
+ if(DALI_UNLIKELY(!frameInfo))
{
success = false;
DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
animated.currentFrame = 1;
- // no errors in header scan etc. so set err and return value
- *error = 0;
+ // cache global color map
+ ColorMapObject* colorMap = gifAccessor.gif->SColorMap;
+ if(colorMap)
+ {
+ cachedColor.globalCachedColor.resize(colorMap->ColorCount);
+ for(int i = 0; i < colorMap->ColorCount; ++i)
+ {
+ cachedColor.globalCachedColor[i] = PixelLookup(colorMap, i);
+ }
+ }
}
}
}
index = animated.currentFrame;
// if index is invalid for animated image - error out
- if((animated.animated) && ((index <= 0) || (index > animated.frameCount)))
+ if(DALI_UNLIKELY((animated.animated) && ((index <= 0) || (index > animated.frameCount))))
{
DALI_LOG_ERROR("LOAD_ERROR_GENERIC");
return false;
// find the given frame index
frame = FindFrame(animated, index);
- if(!frame)
+ if(DALI_UNLIKELY(!frame))
{
DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
return false;
loaderInfo.fileInfo.map = fileData.globalMap;
loaderInfo.fileInfo.length = fileData.length;
loaderInfo.fileInfo.position = 0;
- if(!loaderInfo.fileInfo.map)
+ if(DALI_UNLIKELY(!loaderInfo.fileInfo.map))
{
DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
return false;
}
std::unique_ptr<GifAccessor> gifAccessor = std::make_unique<GifAccessor>(loaderInfo.fileInfo);
- if(!gifAccessor->gif)
+ if(DALI_UNLIKELY(!gifAccessor->gif))
{
DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
return false;
// walk through gif records in file to figure out info
do
{
- if(DGifGetRecordType(loaderInfo.gifAccessor->gif, &rec) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetRecordType(loaderInfo.gifAccessor->gif, &rec) == GIF_ERROR))
{
DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
return false;
ImageFrame* thisFrame = NULL;
// get image desc
- if(DGifGetImageDesc(loaderInfo.gifAccessor->gif) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetImageDesc(loaderInfo.gifAccessor->gif) == GIF_ERROR))
{
DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
return false;
// allocate it
thisFrame->data = new uint32_t[prop.w * prop.h];
- if(!thisFrame->data)
+ if(DALI_UNLIKELY(!thisFrame->data))
{
DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
return false;
}
+ // Lazy fill background color feature.
+ // DecodeImage function draw range is EQUAL with previous FillImage range,
+ // We don't need to fill background that time.
+ // It will try to reduce the number of FillImage API call
+ // Note : We might check overlapping. But that operation looks expensive
+ // So, just optimize only if EQUAL case.
+ bool updateBackgroundColorLazy = false;
+ uint32_t backgroundColor = 0u;
+ int prevX = 0;
+ int prevY = 0;
+ int prevW = 0;
+ int prevH = 0;
+
// if we have no prior frame OR prior frame data... empty
if((!previousFrame) || (!previousFrame->data))
{
- first = true;
- frameInfo = &(thisFrame->info);
- memset(thisFrame->data, 0, prop.w * prop.h * sizeof(uint32_t));
+ first = true;
+ frameInfo = &(thisFrame->info);
+ updateBackgroundColorLazy = true;
+ backgroundColor = 0u;
+ prevX = 0;
+ prevY = 0;
+ prevW = prop.w;
+ prevH = prop.h;
}
// we have a prior frame to copy data from...
else
// if dispose mode is "background" then fill with bg
if(frameInfo->dispose == DISPOSE_BACKGROUND)
{
- FillFrame(thisFrame->data, prop.w, loaderInfo.gifAccessor->gif, frameInfo, x, y, w, h);
+ updateBackgroundColorLazy = true;
+ backgroundColor = GetBackgroundColor(loaderInfo.gifAccessor->gif, frameInfo);
+ prevX = x;
+ prevY = y;
+ prevW = w;
+ prevH = h;
}
else if(frameInfo->dispose == DISPOSE_PREVIOUS) // GIF_DISPOSE_RESTORE
{
{
// Find last preserved frame.
lastPreservedFrame = FindFrame(animated, imageNumber - prevIndex);
- if(!lastPreservedFrame)
+ if(DALI_UNLIKELY(!lastPreservedFrame))
{
DALI_LOG_ERROR("LOAD_ERROR_LAST_PRESERVED_FRAME_NOT_FOUND");
return false;
// now draw this frame on top
frameInfo = &(thisFrame->info);
ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
- if(!DecodeImage(loaderInfo.gifAccessor->gif, thisFrame->data, prop.w, xin, yin, frameInfo->transparent, x, y, w, h, first))
+
+ if(updateBackgroundColorLazy)
+ {
+ // If this frame's x,y,w,h is not equal with previous x,y,w,h, FillImage. else, don't fill
+ if(prevX != x || prevY != y || prevW != w || prevH != h)
+ {
+ FillImage(thisFrame->data, prop.w, backgroundColor, prevX, prevY, prevW, prevH);
+ // Don't send background color information to DecodeImage function.
+ updateBackgroundColorLazy = false;
+ }
+ }
+ if(DALI_UNLIKELY(!DecodeImage(loaderInfo.gifAccessor->gif, loaderInfo.cachedColor, thisFrame->data, prop.w, xin, yin, frameInfo->transparent, x, y, w, h, first || updateBackgroundColorLazy, backgroundColor)))
{
DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
return false;
frameInfo = &(thisFrame->info);
ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
- // clear out all pixels
- FillFrame(reinterpret_cast<uint32_t*>(pixels), prop.w, loaderInfo.gifAccessor->gif, frameInfo, 0, 0, prop.w, prop.h);
+ // clear out all pixels only if x,y,w,h is not whole image.
+ if(x != 0 || y != 0 || w != static_cast<int>(prop.w) || h != static_cast<int>(prop.h))
+ {
+ const std::uint32_t backgroundColor = GetBackgroundColor(loaderInfo.gifAccessor->gif, frameInfo);
+ FillImage(reinterpret_cast<uint32_t*>(pixels), prop.w, backgroundColor, 0, 0, prop.w, prop.h);
+ }
// and decode the gif with overwriting
- if(!DecodeImage(loaderInfo.gifAccessor->gif, reinterpret_cast<uint32_t*>(pixels), prop.w, xin, yin, frameInfo->transparent, x, y, w, h, true))
+ if(DALI_UNLIKELY(!DecodeImage(loaderInfo.gifAccessor->gif, loaderInfo.cachedColor, reinterpret_cast<uint32_t*>(pixels), prop.w, xin, yin, frameInfo->transparent, x, y, w, h, true, 0u)))
{
DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
return false;
else
{
// skip decoding and just walk image to next
- if(DGifGetCode(loaderInfo.gifAccessor->gif, &img_code, &img) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetCode(loaderInfo.gifAccessor->gif, &img_code, &img) == GIF_ERROR))
{
DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
return false;
public:
Impl(const std::string& url, bool isLocalResource)
: mUrl(url),
- mLoadSucceeded(true),
+ mLoadSucceeded(false),
mMutex()
{
loaderInfo.gifAccessor = nullptr;
- int error;
loaderInfo.fileData.fileName = mUrl.c_str();
loaderInfo.fileData.isLocalResource = isLocalResource;
+ }
- mLoadSucceeded = ReadHeader(loaderInfo, imageProperties, &error);
+ bool LoadGifInformation()
+ {
+ // Block to do not load this file again.
+ Mutex::ScopedLock lock(mMutex);
+ if(DALI_LIKELY(mLoadSucceeded))
+ {
+ return mLoadSucceeded;
+ }
+
+ mLoadSucceeded = ReadHeader(loaderInfo, imageProperties);
+ return mLoadSucceeded;
}
// Moveable but not copyable
delete mImpl;
}
-bool GifLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData)
-{
- int error;
- bool ret = false;
-
- Mutex::ScopedLock lock(mImpl->mMutex);
- if(!mImpl->mLoadSucceeded)
- {
- return false;
- }
-
- const int bufferSize = mImpl->imageProperties.w * mImpl->imageProperties.h * sizeof(uint32_t);
-
- DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count);
-
- for(int i = 0; i < count; ++i)
- {
- auto pixelBuffer = new unsigned char[bufferSize];
-
- mImpl->loaderInfo.animated.currentFrame = 1 + ((frameStartIndex + i) % mImpl->loaderInfo.animated.frameCount);
-
- if(ReadNextFrame(mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer, &error))
- {
- if(pixelBuffer)
- {
- pixelData.push_back(Dali::PixelData::New(pixelBuffer, bufferSize, mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY));
- ret = true;
- }
- }
- }
-
- return ret;
-}
-
Dali::Devel::PixelBuffer GifLoading::LoadFrame(uint32_t frameIndex)
{
int error;
Dali::Devel::PixelBuffer pixelBuffer;
- if(!mImpl->mLoadSucceeded)
+
+ DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
+
+ // If Gif file is still not loaded, Load the information.
+ if(DALI_UNLIKELY(!mImpl->LoadGifInformation()))
{
return pixelBuffer;
}
Mutex::ScopedLock lock(mImpl->mMutex);
- DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
-
pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888);
mImpl->loaderInfo.animated.currentFrame = 1 + (frameIndex % mImpl->loaderInfo.animated.frameCount);
ImageDimensions GifLoading::GetImageSize() const
{
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadGifInformation();
+ }
return ImageDimensions(mImpl->imageProperties.w, mImpl->imageProperties.h);
}
uint32_t GifLoading::GetImageCount() const
{
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadGifInformation();
+ }
return mImpl->loaderInfo.animated.frameCount;
}
uint32_t GifLoading::GetFrameInterval(uint32_t frameIndex) const
{
- return mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10;
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadGifInformation();
+ }
+
+ uint32_t interval = 0u;
+ if(DALI_LIKELY(mImpl->mLoadSucceeded))
+ {
+ interval = mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10;
+ }
+ return interval;
}
std::string GifLoading::GetUrl() const
~GifLoading() override;
/**
- * @brief Load the next N Frames of the gif.
- *
- * @note This function will load the entire gif into memory if not already loaded.
- * @param[in] frameStartIndex The frame counter to start from. Will usually be the next frame
- * after the previous invocation of this method, or 0 to start.
- * @param[in] count The number of frames to load
- * @param[out] pixelData The vector in which to return the frame data
- * @return True if the frame data was successfully loaded
- */
- bool LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData) override;
-
- /**
* @brief Load the next Frame of the animated image.
*
* @note This function will load the entire animated image into memory if not already loaded.
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// clang-format off
const Dali::ImageLoader::BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
{
- {Png::MAGIC_BYTE_1, Png::MAGIC_BYTE_2, LoadBitmapFromPng, LoadPngHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
- {Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadJpegHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
- {Bmp::MAGIC_BYTE_1, Bmp::MAGIC_BYTE_2, LoadBitmapFromBmp, LoadBmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
- {Gif::MAGIC_BYTE_1, Gif::MAGIC_BYTE_2, LoadBitmapFromGif, LoadGifHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
- {Webp::MAGIC_BYTE_1, Webp::MAGIC_BYTE_2, LoadBitmapFromWebp, LoadWebpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
- {Ktx::MAGIC_BYTE_1, Ktx::MAGIC_BYTE_2, LoadBitmapFromKtx, LoadKtxHeader, Bitmap::BITMAP_COMPRESSED },
- {Astc::MAGIC_BYTE_1, Astc::MAGIC_BYTE_2, LoadBitmapFromAstc, LoadAstcHeader, Bitmap::BITMAP_COMPRESSED },
- {Ico::MAGIC_BYTE_1, Ico::MAGIC_BYTE_2, LoadBitmapFromIco, LoadIcoHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
- {0x0, 0x0, LoadBitmapFromWbmp, LoadWbmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
+ {Png::MAGIC_BYTE_1, Png::MAGIC_BYTE_2, LoadBitmapFromPng, nullptr, LoadPngHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
+ {Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadPlanesFromJpeg, LoadJpegHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
+ {Bmp::MAGIC_BYTE_1, Bmp::MAGIC_BYTE_2, LoadBitmapFromBmp, nullptr, LoadBmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
+ {Gif::MAGIC_BYTE_1, Gif::MAGIC_BYTE_2, LoadBitmapFromGif, nullptr, LoadGifHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
+ {Webp::MAGIC_BYTE_1, Webp::MAGIC_BYTE_2, LoadBitmapFromWebp, nullptr, LoadWebpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
+ {Ktx::MAGIC_BYTE_1, Ktx::MAGIC_BYTE_2, LoadBitmapFromKtx, nullptr, LoadKtxHeader, Bitmap::BITMAP_COMPRESSED },
+ {Astc::MAGIC_BYTE_1, Astc::MAGIC_BYTE_2, LoadBitmapFromAstc, nullptr, LoadAstcHeader, Bitmap::BITMAP_COMPRESSED },
+ {Ico::MAGIC_BYTE_1, Ico::MAGIC_BYTE_2, LoadBitmapFromIco, nullptr, LoadIcoHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
+ {0x0, 0x0, LoadBitmapFromWbmp, nullptr, LoadWbmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
};
// clang-format on
bool GetBitmapLoaderFunctions(FILE* fp,
FileFormats format,
Dali::ImageLoader::LoadBitmapFunction& loader,
+ Dali::ImageLoader::LoadPlanesFunction& planeLoader,
Dali::ImageLoader::LoadBitmapHeaderFunction& header,
Bitmap::Profile& profile,
const std::string& filename)
// if a loader was found set the outputs
if(loaderFound)
{
- loader = lookupPtr->loader;
- header = lookupPtr->header;
- profile = lookupPtr->profile;
+ loader = lookupPtr->loader;
+ planeLoader = lookupPtr->planeLoader;
+ header = lookupPtr->header;
+ profile = lookupPtr->profile;
}
// Reset to the start of the file.
namespace ImageLoader
{
-bool ConvertStreamToBitmap(const BitmapResourceType& resource, std::string path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer)
+bool ConvertStreamToBitmap(const BitmapResourceType& resource, const std::string& path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer)
{
DALI_LOG_TRACE_METHOD(gLogFilter);
if(fp != NULL)
{
Dali::ImageLoader::LoadBitmapFunction function;
+ Dali::ImageLoader::LoadPlanesFunction planeLoader;
Dali::ImageLoader::LoadBitmapHeaderFunction header;
Bitmap::Profile profile;
if(GetBitmapLoaderFunctions(fp,
GetFormatHint(path),
function,
+ planeLoader,
header,
profile,
path))
return result;
}
+bool ConvertStreamToPlanes(const Integration::BitmapResourceType& resource, const std::string& path, FILE* const fp, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers)
+{
+ DALI_LOG_TRACE_METHOD(gLogFilter);
+
+ bool result = false;
+
+ if(fp != NULL)
+ {
+ Dali::ImageLoader::LoadBitmapFunction loader;
+ Dali::ImageLoader::LoadPlanesFunction planeLoader;
+ Dali::ImageLoader::LoadBitmapHeaderFunction header;
+
+ Bitmap::Profile profile;
+
+ if(GetBitmapLoaderFunctions(fp,
+ GetFormatHint(path),
+ loader,
+ planeLoader,
+ header,
+ profile,
+ path))
+ {
+ const Dali::ImageLoader::ScalingParameters scalingParameters(resource.size, resource.scalingMode, resource.samplingMode);
+ const Dali::ImageLoader::Input input(fp, scalingParameters, resource.orientationCorrection);
+
+ pixelBuffers.clear();
+
+ // Run the image type decoder:
+ if(planeLoader)
+ {
+ result = planeLoader(input, pixelBuffers);
+ if(!result)
+ {
+ DALI_LOG_ERROR("Unable to convert %s\n", path.c_str());
+ }
+ }
+ else
+ {
+ Dali::Devel::PixelBuffer pixelBuffer;
+ result = loader(input, pixelBuffer);
+ if(!result)
+ {
+ DALI_LOG_ERROR("Unable to convert %s\n", path.c_str());
+ return false;
+ }
+
+ pixelBuffer = Internal::Platform::ApplyAttributesToBitmap(pixelBuffer, resource.size, resource.scalingMode, resource.samplingMode);
+ if(pixelBuffer)
+ {
+ pixelBuffers.push_back(pixelBuffer);
+ }
+ else
+ {
+ DALI_LOG_ERROR("ApplyAttributesToBitmap is failed [%s]\n", path.c_str());
+ return false;
+ }
+ }
+ }
+ else
+ {
+ DALI_LOG_ERROR("Image Decoder for %s unavailable\n", path.c_str());
+ }
+ }
+
+ return result;
+}
+
ResourcePointer LoadImageSynchronously(const Integration::BitmapResourceType& resource, const std::string& path)
{
ResourcePointer result;
if(fp != NULL)
{
Dali::ImageLoader::LoadBitmapFunction loaderFunction;
+ Dali::ImageLoader::LoadPlanesFunction planeLoader;
Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
Bitmap::Profile profile;
if(GetBitmapLoaderFunctions(fp,
GetFormatHint(filename),
loaderFunction,
+ planeLoader,
headerFunction,
profile,
filename))
if(fp != NULL)
{
Dali::ImageLoader::LoadBitmapFunction loaderFunction;
+ Dali::ImageLoader::LoadPlanesFunction planeLoader;
Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
Bitmap::Profile profile;
if(GetBitmapLoaderFunctions(fp,
FORMAT_UNKNOWN,
loaderFunction,
+ planeLoader,
headerFunction,
profile,
""))
#define DALI_TIZEN_PLATFORM_IMAGE_LOADER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* @param[out] bitmap Pointer to write bitmap to
* @return true on success, false on failure
*/
-bool ConvertStreamToBitmap(const Integration::BitmapResourceType& resource, std::string path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer);
+bool ConvertStreamToBitmap(const Integration::BitmapResourceType& resource, const std::string& path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer);
/**
- * Convert a bitmap and write to a file stream.
+ * Convert a file stream into image planes.
+ * @param[in] resource The resource to convert.
* @param[in] path The path to the resource.
* @param[in] fp File Pointer. Closed on exit.
- * @param[out] pixelData Reference to PixelData object.
+ * @param[out] pixelBuffers Pointer to write buffer to
* @return true on success, false on failure
+ * @note If the image file doesn't support to load planes, this method returns one RGB bitmap image.
*/
-bool ConvertBitmapToStream(std::string path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer);
+bool ConvertStreamToPlanes(const Integration::BitmapResourceType& resource, const std::string& path, FILE* const fp, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers);
/**
* Loads an image synchronously
using Integration::Bitmap;
using Integration::BitmapPtr;
-typedef unsigned char PixelBuffer;
+typedef uint8_t PixelBuffer;
/**
* @brief 4 byte pixel structure.
* @param[in] pixelsIn The input buffer.
* @param[in] widthIn The width of the input buffer.
* @param[in] heightIn The height of the input buffer.
+ * @param[in] strideIn The stride of the input buffer.
* @param[in] pixelSize The size of the pixel.
* @param[out] pixelsOut The rotated output buffer.
* @param[out] widthOut The width of the output buffer.
bool Rotate90(const uint8_t* const pixelsIn,
unsigned int widthIn,
unsigned int heightIn,
+ unsigned int strideIn,
unsigned int pixelSize,
uint8_t*& pixelsOut,
unsigned int& widthOut,
heightOut = widthIn;
// Allocate memory for the rotated buffer.
+ // Output buffer is tightly packed
pixelsOut = static_cast<uint8_t*>(malloc(widthOut * heightOut * pixelSize));
if(nullptr == pixelsOut)
{
// Rotate the buffer.
for(unsigned int y = 0u; y < heightIn; ++y)
{
- const unsigned int srcLineIndex = y * widthIn;
+ const unsigned int srcLineIndex = y * strideIn;
const unsigned int dstX = y;
for(unsigned int x = 0u; x < widthIn; ++x)
{
* @param[in] pixelsIn The input buffer.
* @param[in] widthIn The width of the input buffer.
* @param[in] heightIn The height of the input buffer.
+ * @param[in] strideIn The stride of the input buffer.
* @param[in] pixelSize The size of the pixel.
* @param[out] pixelsOut The rotated output buffer.
*
bool Rotate180(const uint8_t* const pixelsIn,
unsigned int widthIn,
unsigned int heightIn,
+ unsigned int strideIn,
unsigned int pixelSize,
uint8_t*& pixelsOut)
{
// Allocate memory for the rotated buffer.
+ // Output buffer is tightly packed
pixelsOut = static_cast<uint8_t*>(malloc(widthIn * heightIn * pixelSize));
if(nullptr == pixelsOut)
{
// Rotate the buffer.
for(unsigned int y = 0u; y < heightIn; ++y)
{
- const unsigned int srcLineIndex = y * widthIn;
+ const unsigned int srcLineIndex = y * strideIn;
const unsigned int dstY = heightIn - y - 1u;
for(unsigned int x = 0u; x < widthIn; ++x)
{
* @param[in] pixelsIn The input buffer.
* @param[in] widthIn The width of the input buffer.
* @param[in] heightIn The height of the input buffer.
+ * @param[in] strideIn The stride of the input buffer.
* @param[in] pixelSize The size of the pixel.
* @param[out] pixelsOut The rotated output buffer.
* @param[out] widthOut The width of the output buffer.
bool Rotate270(const uint8_t* const pixelsIn,
unsigned int widthIn,
unsigned int heightIn,
+ unsigned int strideIn,
unsigned int pixelSize,
uint8_t*& pixelsOut,
unsigned int& widthOut,
heightOut = widthIn;
// Allocate memory for the rotated buffer.
+ // Output buffer is tightly packed
pixelsOut = static_cast<uint8_t*>(malloc(widthOut * heightOut * pixelSize));
if(nullptr == pixelsOut)
{
// Rotate the buffer.
for(unsigned int y = 0u; y < heightIn; ++y)
{
- const unsigned int srcLineIndex = y * widthIn;
+ const unsigned int srcLineIndex = y * strideIn;
const unsigned int dstX = widthOut - y - 1u;
for(unsigned int x = 0u; x < widthIn; ++x)
{
*
* @param[in] srcBufferPtr Pointer to the input pixel buffer.
* @param[in] srcWidth The width of the input pixel buffer.
+ * @param[in] srcStride The stride of the input pixel buffer.
* @param[in] pixelSize The size of the pixel.
* @param[in,out] dstPixelBuffer Pointer to the output pixel buffer.
* @param[in] dstWidth The width of the output pixel buffer.
*/
void HorizontalSkew(const uint8_t* const srcBufferPtr,
int srcWidth,
+ int srcStride,
unsigned int pixelSize,
uint8_t*& dstBufferPtr,
int dstWidth,
for(i = 0u; i < srcWidth; ++i)
{
// Loop through row pixels
- const unsigned int srcIndex = pixelSize * (row * srcWidth + i);
+ const unsigned int srcIndex = pixelSize * (row * srcStride + i);
unsigned char src[4u] = {0u, 0u, 0u, 0u};
for(unsigned int channel = 0u; channel < pixelSize; ++channel)
* @param[in] srcBufferPtr Pointer to the input pixel buffer.
* @param[in] srcWidth The width of the input pixel buffer.
* @param[in] srcHeight The height of the input pixel buffer.
+ * @param[in] srcStride The stride of the input pixel buffer.
* @param[in] pixelSize The size of the pixel.
* @param[in,out] dstPixelBuffer Pointer to the output pixel buffer.
* @param[in] dstWidth The width of the output pixel buffer.
void VerticalSkew(const uint8_t* const srcBufferPtr,
int srcWidth,
int srcHeight,
+ int srcStride,
unsigned int pixelSize,
uint8_t*& dstBufferPtr,
int dstWidth,
for(i = 0; i < srcHeight; ++i)
{
// Loop through column pixels
- const unsigned int srcIndex = pixelSize * (i * srcWidth + column);
+ const unsigned int srcIndex = pixelSize * (i * srcStride + column);
unsigned char src[4u] = {0u, 0u, 0u, 0u};
for(unsigned int channel = 0u; channel < pixelSize; ++channel)
{
const unsigned int inputWidth = bitmap.GetWidth();
const unsigned int inputHeight = bitmap.GetHeight();
+ const unsigned int inputStride = bitmap.GetStride();
if(desiredDimensions.GetWidth() < 1u || desiredDimensions.GetHeight() < 1u)
{
// Add some pre-calculated offsets to the bitmap pointers so this is not done within a loop.
// The cropping is added to the source pointer, and the padding is added to the destination.
const auto bytesPerPixel = Pixel::GetBytesPerPixel(pixelFormat);
- const PixelBuffer* const sourcePixels = bitmap.GetBuffer() + ((((scanlinesToCrop / 2) * inputWidth) + (columnsToCrop / 2)) * bytesPerPixel);
+ const PixelBuffer* const sourcePixels = bitmap.GetBuffer() + ((((scanlinesToCrop / 2) * inputStride) + (columnsToCrop / 2)) * bytesPerPixel);
PixelBuffer* const targetPixels = croppedBitmap.GetBuffer();
PixelBuffer* const targetPixelsActive = targetPixels + ((((scanlinesToPad / 2) * desiredWidth) + (columnsToPad / 2)) * bytesPerPixel);
DALI_ASSERT_DEBUG(sourcePixels && targetPixels);
// Copy the image data to the new bitmap.
// Optimize to a single memcpy if the left and right edges don't need a crop or a pad.
unsigned int outputSpan(desiredWidth * bytesPerPixel);
- if(columnsToCrop == 0 && columnsToPad == 0)
+ if(columnsToCrop == 0 && columnsToPad == 0 && inputStride == inputWidth)
{
memcpy(targetPixelsActive, sourcePixels, (desiredHeight - scanlinesToPad) * outputSpan);
}
{
// The width needs to change (due to either a crop or a pad), so we copy a scanline at a time.
// Precalculate any constants to optimize the inner loop.
- const unsigned int inputSpan(inputWidth * bytesPerPixel);
+ const unsigned int inputSpan(inputStride * bytesPerPixel);
const unsigned int copySpan((desiredWidth - columnsToPad) * bytesPerPixel);
const unsigned int scanlinesToCopy(desiredHeight - scanlinesToPad);
// Source dimensions as loaded from resources (e.g. filesystem):
auto bitmapWidth = bitmap.GetWidth();
auto bitmapHeight = bitmap.GetHeight();
+ auto bitmapStride = bitmap.GetStride();
// Desired dimensions (the rectangle to fit the source image to):
auto desiredWidth = desired.GetWidth();
auto desiredHeight = desired.GetHeight();
auto pixelFormat = bitmap.GetPixelFormat();
// Do the fast power of 2 iterated box filter to get to roughly the right side if the filter mode requests that:
- unsigned int shrunkWidth = -1, shrunkHeight = -1;
- DownscaleInPlacePow2(bitmap.GetBuffer(), pixelFormat, bitmapWidth, bitmapHeight, desiredWidth, desiredHeight, fittingMode, samplingMode, shrunkWidth, shrunkHeight);
+ unsigned int shrunkWidth = -1, shrunkHeight = -1, outStride = -1;
+ DownscaleInPlacePow2(bitmap.GetBuffer(), pixelFormat, bitmapWidth, bitmapHeight, bitmapStride, desiredWidth, desiredHeight, fittingMode, samplingMode, shrunkWidth, shrunkHeight, outStride);
// Work out the dimensions of the downscaled bitmap, given the scaling mode and desired dimensions:
const ImageDimensions filteredDimensions = FitToScalingMode(ImageDimensions(desiredWidth, desiredHeight), ImageDimensions(shrunkWidth, shrunkHeight), fittingMode);
{
if(samplingMode == SamplingMode::LINEAR || samplingMode == SamplingMode::BOX_THEN_LINEAR)
{
- LinearSample(bitmap.GetBuffer(), ImageDimensions(shrunkWidth, shrunkHeight), pixelFormat, outputBitmap.GetBuffer(), filteredDimensions);
+ LinearSample(bitmap.GetBuffer(), ImageDimensions(shrunkWidth, shrunkHeight), outStride, pixelFormat, outputBitmap.GetBuffer(), filteredDimensions);
}
else
{
- PointSample(bitmap.GetBuffer(), shrunkWidth, shrunkHeight, pixelFormat, outputBitmap.GetBuffer(), filteredWidth, filteredHeight);
+ PointSample(bitmap.GetBuffer(), shrunkWidth, shrunkHeight, outStride, pixelFormat, outputBitmap.GetBuffer(), filteredWidth, filteredHeight);
}
filtered = true;
}
// Copy out the 2^x downscaled, box-filtered pixels if no secondary filter (point or linear) was applied:
if(filtered == false && (shrunkWidth < bitmapWidth || shrunkHeight < bitmapHeight))
{
+ // The buffer is downscaled and it is tightly packed. We don't need to set a stride.
outputBitmap = MakePixelBuffer(bitmap.GetBuffer(), pixelFormat, shrunkWidth, shrunkHeight);
}
}
void DownscaleInPlacePow2Generic(unsigned char* const pixels,
const unsigned int inputWidth,
const unsigned int inputHeight,
+ const unsigned int inputStride,
const unsigned int desiredWidth,
const unsigned int desiredHeight,
BoxDimensionTest dimensionTest,
unsigned& outWidth,
- unsigned& outHeight)
+ unsigned& outHeight,
+ unsigned& outStride)
{
if(pixels == 0)
{
// Scale the image until it would be smaller than desired, stopping if the
// resulting height or width would be less than 1:
- unsigned int scaledWidth = inputWidth, scaledHeight = inputHeight;
+ unsigned int scaledWidth = inputWidth, scaledHeight = inputHeight, stride = inputStride;
while(ContinueScaling(dimensionTest, scaledWidth, scaledHeight, desiredWidth, desiredHeight))
{
- const unsigned int lastWidth = scaledWidth;
+ const unsigned int lastWidth = scaledWidth;
+ const unsigned int lastStride = stride;
scaledWidth >>= 1u;
scaledHeight >>= 1u;
+ stride = scaledWidth;
DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Scaling to %u\t%u.\n", scaledWidth, scaledHeight);
for(unsigned int y = 0; y <= lastScanlinePair; ++y)
{
// Scale two scanlines horizontally:
- HalveScanlineInPlace(&pixels[y * 2 * lastWidth * BYTES_PER_PIXEL], lastWidth);
- HalveScanlineInPlace(&pixels[(y * 2 + 1) * lastWidth * BYTES_PER_PIXEL], lastWidth);
+ HalveScanlineInPlace(&pixels[y * 2 * lastStride * BYTES_PER_PIXEL], lastWidth);
+ HalveScanlineInPlace(&pixels[(y * 2 + 1) * lastStride * BYTES_PER_PIXEL], lastWidth);
// Scale vertical pairs of pixels while the last two scanlines are still warm in
// the CPU cache(s):
// images but even a 4k wide RGB888 image will use just 24kB of cache (4k pixels
// * 3 Bpp * 2 scanlines) for two scanlines on the first iteration.
AverageScanlines(
- &pixels[y * 2 * lastWidth * BYTES_PER_PIXEL],
- &pixels[(y * 2 + 1) * lastWidth * BYTES_PER_PIXEL],
+ &pixels[y * 2 * lastStride * BYTES_PER_PIXEL],
+ &pixels[(y * 2 + 1) * lastStride * BYTES_PER_PIXEL],
&pixels[y * scaledWidth * BYTES_PER_PIXEL],
scaledWidth);
}
///@note: we could finish off with one of two mutually exclusive passes, one squashing horizontally as far as possible, and the other vertically, if we knew a following cpu point or bilinear filter would restore the desired aspect ratio.
outWidth = scaledWidth;
outHeight = scaledHeight;
+ outStride = stride;
}
} // namespace
const unsigned int lastPair = EvenDown(width - 2);
- for(unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel)
- {
- // Load all the byte pixel components we need:
- const unsigned int c11 = pixels[pixel * 3];
- const unsigned int c12 = pixels[pixel * 3 + 1];
- const unsigned int c13 = pixels[pixel * 3 + 2];
- const unsigned int c21 = pixels[pixel * 3 + 3];
- const unsigned int c22 = pixels[pixel * 3 + 4];
- const unsigned int c23 = pixels[pixel * 3 + 5];
-
- // Save the averaged byte pixel components:
- pixels[outPixel * 3] = static_cast<unsigned char>(AverageComponent(c11, c21));
- pixels[outPixel * 3 + 1] = static_cast<unsigned char>(AverageComponent(c12, c22));
- pixels[outPixel * 3 + 2] = static_cast<unsigned char>(AverageComponent(c13, c23));
+ /**
+ * @code
+ * for(unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel)
+ * {
+ * // Load all the byte pixel components we need:
+ * const unsigned int c11 = pixels[pixel * 3];
+ * const unsigned int c12 = pixels[pixel * 3 + 1];
+ * const unsigned int c13 = pixels[pixel * 3 + 2];
+ * const unsigned int c21 = pixels[pixel * 3 + 3];
+ * const unsigned int c22 = pixels[pixel * 3 + 4];
+ * const unsigned int c23 = pixels[pixel * 3 + 5];
+ *
+ * // Save the averaged byte pixel components:
+ * pixels[outPixel * 3] = static_cast<unsigned char>(AverageComponent(c11, c21));
+ * pixels[outPixel * 3 + 1] = static_cast<unsigned char>(AverageComponent(c12, c22));
+ * pixels[outPixel * 3 + 2] = static_cast<unsigned char>(AverageComponent(c13, c23));
+ * }
+ * @endcode
+ */
+ //@ToDo : Fix here if we found that collect 12 bytes == 3 uint32_t with 4 colors, and calculate in one-operation
+ std::uint8_t* inPixelPtr = pixels;
+ std::uint8_t* outPixelPtr = pixels;
+ for(std::uint32_t scanedPixelCount = 0; scanedPixelCount <= lastPair; scanedPixelCount += 2)
+ {
+ *(outPixelPtr + 0) = ((*(inPixelPtr + 0) ^ *(inPixelPtr + 3)) >> 1) + (*(inPixelPtr + 0) & *(inPixelPtr + 3));
+ *(outPixelPtr + 1) = ((*(inPixelPtr + 1) ^ *(inPixelPtr + 4)) >> 1) + (*(inPixelPtr + 1) & *(inPixelPtr + 4));
+ *(outPixelPtr + 2) = ((*(inPixelPtr + 2) ^ *(inPixelPtr + 5)) >> 1) + (*(inPixelPtr + 2) & *(inPixelPtr + 5));
+ inPixelPtr += 6;
+ outPixelPtr += 3;
}
}
for(unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel)
{
- const uint32_t averaged = AveragePixelRGB565(alignedPixels[pixel], alignedPixels[pixel + 1]);
+ const uint16_t averaged = AveragePixelRGB565(alignedPixels[pixel], alignedPixels[pixel + 1]);
alignedPixels[outPixel] = averaged;
}
}
for(unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel)
{
- // Load all the byte pixel components we need:
- const unsigned int c11 = pixels[pixel * 2];
- const unsigned int c12 = pixels[pixel * 2 + 1];
- const unsigned int c21 = pixels[pixel * 2 + 2];
- const unsigned int c22 = pixels[pixel * 2 + 3];
-
- // Save the averaged byte pixel components:
- pixels[outPixel * 2] = static_cast<unsigned char>(AverageComponent(c11, c21));
- pixels[outPixel * 2 + 1] = static_cast<unsigned char>(AverageComponent(c12, c22));
+ /**
+ * @code
+ * // Load all the byte pixel components we need:
+ * const unsigned int c11 = pixels[pixel * 2];
+ * const unsigned int c12 = pixels[pixel * 2 + 1];
+ * const unsigned int c21 = pixels[pixel * 2 + 2];
+ * const unsigned int c22 = pixels[pixel * 2 + 3];
+ *
+ * // Save the averaged byte pixel components:
+ * pixels[outPixel * 2] = static_cast<unsigned char>(AverageComponent(c11, c21));
+ * pixels[outPixel * 2 + 1] = static_cast<unsigned char>(AverageComponent(c12, c22));
+ * @endcode
+ */
+ // Note : We can assume that pixel is even number. So we can use | operation instead of + operation.
+ pixels[(outPixel << 1)] = ((pixels[(pixel << 1)] ^ pixels[(pixel << 1) | 2]) >> 1) + (pixels[(pixel << 1)] & pixels[(pixel << 1) | 2]);
+ pixels[(outPixel << 1) | 1] = ((pixels[(pixel << 1) | 1] ^ pixels[(pixel << 1) | 3]) >> 1) + (pixels[(pixel << 1) | 1] & pixels[(pixel << 1) | 3]);
}
}
for(unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel)
{
- // Load all the byte pixel components we need:
- const unsigned int c1 = pixels[pixel];
- const unsigned int c2 = pixels[pixel + 1];
-
- // Save the averaged byte pixel component:
- pixels[outPixel] = static_cast<unsigned char>(AverageComponent(c1, c2));
+ /**
+ * @code
+ * // Load all the byte pixel components we need:
+ * const unsigned int c1 = pixels[pixel];
+ * const unsigned int c2 = pixels[pixel + 1];
+ *
+ * // Save the averaged byte pixel component:
+ * pixels[outPixel] = static_cast<unsigned char>(AverageComponent(c1, c2));
+ * @endcode
+ */
+ // Note : We can assume that pixel is even number. So we can use | operation instead of + operation.
+ pixels[outPixel] = ((pixels[pixel] ^ pixels[pixel | 1]) >> 1) + (pixels[pixel] & pixels[pixel | 1]);
}
}
+// AverageScanline
+
+namespace
+{
/**
- * @ToDo: Optimise for ARM using a 4 bytes at a time loop wrapped around the single ARMV6 instruction: UHADD8 R4, R0, R5. Note, this is not neon. It runs in the normal integer pipeline so there is no downside like a stall moving between integer and copro, or extra power for clocking-up the idle copro.
- * if (widthInComponents >= 7) { word32* aligned1 = scanline1 + 3 & 3; word32* aligned1_end = scanline1 + widthInPixels & 3; while(aligned1 < aligned1_end) { UHADD8 *aligned1++, *aligned2++, *alignedoutput++ } .. + 0 to 3 spare pixels at each end.
+ * @copydoc AverageScanlines1
+ * @note This API average eight components in one operation.
+ * @note Only possible if each scanline pointer's address aligned
+ * It will give performance benifit.
*/
+inline void AverageScanlinesWithEightComponents(
+ const unsigned char* const scanline1,
+ const unsigned char* const __restrict__ scanline2,
+ unsigned char* const outputScanline,
+ const unsigned int totalComponentCount)
+{
+ unsigned int component = 0;
+ if(DALI_LIKELY(totalComponentCount >= 8))
+ {
+ // Note reinsterpret_cast from uint8_t to uint64_t and read/write only allowed
+ // If pointer of data is aligned well.
+ if(((reinterpret_cast<std::ptrdiff_t>(scanline1) & (sizeof(std::uint64_t) - 1)) == 0) &&
+ ((reinterpret_cast<std::ptrdiff_t>(scanline2) & (sizeof(std::uint64_t) - 1)) == 0) &&
+ ((reinterpret_cast<std::ptrdiff_t>(outputScanline) & (sizeof(std::uint64_t) - 1)) == 0))
+ {
+ // Jump 8 components in one step
+ const std::uint64_t* const scanline18Step = reinterpret_cast<const std::uint64_t* const>(scanline1);
+ const std::uint64_t* const scanline28Step = reinterpret_cast<const std::uint64_t* const>(scanline2);
+ std::uint64_t* const output8step = reinterpret_cast<std::uint64_t* const>(outputScanline);
+
+ const std::uint32_t totalStepCount = (totalComponentCount) >> 3;
+ component = totalStepCount << 3;
+
+ // and for each step, calculate average of 8 bytes.
+ for(std::uint32_t i = 0; i < totalStepCount; ++i)
+ {
+ const auto& c1 = *(scanline18Step + i);
+ const auto& c2 = *(scanline28Step + i);
+ *(output8step + i) = static_cast<std::uint64_t>((((c1 ^ c2) & 0xfefefefefefefefeull) >> 1) + (c1 & c2));
+ }
+ }
+ }
+ // remaining components calculate
+ for(; component < totalComponentCount; ++component)
+ {
+ const auto& c1 = scanline1[component];
+ const auto& c2 = scanline2[component];
+ outputScanline[component] = static_cast<std::uint8_t>(((c1 ^ c2) >> 1) + (c1 & c2));
+ }
+}
+
+} // namespace
+
void AverageScanlines1(const unsigned char* const scanline1,
const unsigned char* const __restrict__ scanline2,
unsigned char* const outputScanline,
{
DebugAssertDualScanlineParameters(scanline1, scanline2, outputScanline, width);
- for(unsigned int component = 0; component < width; ++component)
- {
- outputScanline[component] = static_cast<unsigned char>(AverageComponent(scanline1[component], scanline2[component]));
- }
+ /**
+ * @code
+ * for(unsigned int component = 0; component < width; ++component)
+ * {
+ * outputScanline[component] = static_cast<unsigned char>(AverageComponent(scanline1[component], scanline2[component]));
+ * }
+ * @endcode
+ */
+ AverageScanlinesWithEightComponents(scanline1, scanline2, outputScanline, width);
}
void AverageScanlines2(const unsigned char* const scanline1,
{
DebugAssertDualScanlineParameters(scanline1, scanline2, outputScanline, width * 2);
- for(unsigned int component = 0; component < width * 2; ++component)
- {
- outputScanline[component] = static_cast<unsigned char>(AverageComponent(scanline1[component], scanline2[component]));
- }
+ /**
+ * @code
+ * for(unsigned int component = 0; component < width * 2; ++component)
+ * {
+ * outputScanline[component] = static_cast<unsigned char>(AverageComponent(scanline1[component], scanline2[component]));
+ * }
+ * @endcode
+ */
+ AverageScanlinesWithEightComponents(scanline1, scanline2, outputScanline, width * 2);
}
void AverageScanlines3(const unsigned char* const scanline1,
{
DebugAssertDualScanlineParameters(scanline1, scanline2, outputScanline, width * 3);
- for(unsigned int component = 0; component < width * 3; ++component)
- {
- outputScanline[component] = static_cast<unsigned char>(AverageComponent(scanline1[component], scanline2[component]));
- }
+ /**
+ * @code
+ * for(unsigned int component = 0; component < width * 3; ++component)
+ * {
+ * outputScanline[component] = static_cast<unsigned char>(AverageComponent(scanline1[component], scanline2[component]));
+ * }
+ * @endcode
+ */
+ AverageScanlinesWithEightComponents(scanline1, scanline2, outputScanline, width * 3);
}
void AverageScanlinesRGBA8888(const unsigned char* const scanline1,
Pixel::Format pixelFormat,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned int desiredWidth,
unsigned int desiredHeight,
FittingMode::Type fittingMode,
SamplingMode::Type samplingMode,
unsigned& outWidth,
- unsigned& outHeight)
+ unsigned& outHeight,
+ unsigned& outStride)
{
outWidth = inputWidth;
outHeight = inputHeight;
+ outStride = inputStride;
// Perform power of 2 iterated 4:1 box filtering if the requested filter mode requires it:
if(samplingMode == SamplingMode::BOX || samplingMode == SamplingMode::BOX_THEN_NEAREST || samplingMode == SamplingMode::BOX_THEN_LINEAR)
{
{
const BoxDimensionTest dimensionTest = DimensionTestForScalingMode(fittingMode);
- if(pixelFormat == Pixel::RGBA8888)
- {
- Internal::Platform::DownscaleInPlacePow2RGBA8888(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
- }
- else if(pixelFormat == Pixel::RGB888)
- {
- Internal::Platform::DownscaleInPlacePow2RGB888(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
- }
- else if(pixelFormat == Pixel::RGB565)
- {
- Internal::Platform::DownscaleInPlacePow2RGB565(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
- }
- else if(pixelFormat == Pixel::LA88)
- {
- Internal::Platform::DownscaleInPlacePow2ComponentPair(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
- }
- else if(pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8)
- {
- Internal::Platform::DownscaleInPlacePow2SingleBytePerPixel(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
- }
- else
+ switch(pixelFormat)
{
- DALI_ASSERT_DEBUG(false && "Inner branch conditions don't match outer branch.");
+ case Pixel::RGBA8888:
+ {
+ Internal::Platform::DownscaleInPlacePow2RGBA8888(pixels, inputWidth, inputHeight, inputStride, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight, outStride);
+ break;
+ }
+ case Pixel::RGB888:
+ {
+ Internal::Platform::DownscaleInPlacePow2RGB888(pixels, inputWidth, inputHeight, inputStride, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight, outStride);
+ break;
+ }
+ case Pixel::RGB565:
+ {
+ Internal::Platform::DownscaleInPlacePow2RGB565(pixels, inputWidth, inputHeight, inputStride, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight, outStride);
+ break;
+ }
+ case Pixel::LA88:
+ {
+ Internal::Platform::DownscaleInPlacePow2ComponentPair(pixels, inputWidth, inputHeight, inputStride, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight, outStride);
+ break;
+ }
+ case Pixel::L8:
+ case Pixel::A8:
+ {
+ Internal::Platform::DownscaleInPlacePow2SingleBytePerPixel(pixels, inputWidth, inputHeight, inputStride, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight, outStride);
+ break;
+ }
+ default:
+ {
+ DALI_ASSERT_DEBUG(false && "Inner branch conditions don't match outer branch.");
+ }
}
}
}
void DownscaleInPlacePow2RGB888(unsigned char* pixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned int desiredWidth,
unsigned int desiredHeight,
BoxDimensionTest dimensionTest,
unsigned& outWidth,
- unsigned& outHeight)
+ unsigned& outHeight,
+ unsigned& outStride)
{
- DownscaleInPlacePow2Generic<3, HalveScanlineInPlaceRGB888, AverageScanlines3>(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
+ DownscaleInPlacePow2Generic<3, HalveScanlineInPlaceRGB888, AverageScanlines3>(pixels, inputWidth, inputHeight, inputStride, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight, outStride);
}
void DownscaleInPlacePow2RGBA8888(unsigned char* pixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned int desiredWidth,
unsigned int desiredHeight,
BoxDimensionTest dimensionTest,
unsigned& outWidth,
- unsigned& outHeight)
+ unsigned& outHeight,
+ unsigned& outStride)
{
DALI_ASSERT_DEBUG(((reinterpret_cast<ptrdiff_t>(pixels) & 3u) == 0u) && "Pointer should be 4-byte aligned for performance on some platforms.");
- DownscaleInPlacePow2Generic<4, HalveScanlineInPlaceRGBA8888, AverageScanlinesRGBA8888>(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
+ DownscaleInPlacePow2Generic<4, HalveScanlineInPlaceRGBA8888, AverageScanlinesRGBA8888>(pixels, inputWidth, inputHeight, inputStride, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight, outStride);
}
void DownscaleInPlacePow2RGB565(unsigned char* pixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned int desiredWidth,
unsigned int desiredHeight,
BoxDimensionTest dimensionTest,
unsigned int& outWidth,
- unsigned int& outHeight)
+ unsigned int& outHeight,
+ unsigned int& outStride)
{
- DownscaleInPlacePow2Generic<2, HalveScanlineInPlaceRGB565, AverageScanlinesRGB565>(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
+ DownscaleInPlacePow2Generic<2, HalveScanlineInPlaceRGB565, AverageScanlinesRGB565>(pixels, inputWidth, inputHeight, inputStride, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight, outStride);
}
/**
void DownscaleInPlacePow2ComponentPair(unsigned char* pixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned int desiredWidth,
unsigned int desiredHeight,
BoxDimensionTest dimensionTest,
unsigned& outWidth,
- unsigned& outHeight)
+ unsigned& outHeight,
+ unsigned& outStride)
{
- DownscaleInPlacePow2Generic<2, HalveScanlineInPlace2Bytes, AverageScanlines2>(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
+ DownscaleInPlacePow2Generic<2, HalveScanlineInPlace2Bytes, AverageScanlines2>(pixels, inputWidth, inputHeight, inputStride, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight, outStride);
}
void DownscaleInPlacePow2SingleBytePerPixel(unsigned char* pixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned int desiredWidth,
unsigned int desiredHeight,
BoxDimensionTest dimensionTest,
unsigned int& outWidth,
- unsigned int& outHeight)
+ unsigned int& outHeight,
+ unsigned int& outStride)
{
- DownscaleInPlacePow2Generic<1, HalveScanlineInPlace1Byte, AverageScanlines1>(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
+ DownscaleInPlacePow2Generic<1, HalveScanlineInPlace1Byte, AverageScanlines1>(pixels, inputWidth, inputHeight, inputStride, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight, outStride);
}
+// Point sampling group below
+
namespace
{
/**
inline void PointSampleAddressablePixels(const uint8_t* inPixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
uint8_t* outPixels,
unsigned int desiredWidth,
unsigned int desiredHeight)
{
DALI_ASSERT_DEBUG(((desiredWidth <= inputWidth && desiredHeight <= inputHeight) ||
- outPixels >= inPixels + inputWidth * inputHeight * sizeof(PIXEL) || outPixels <= inPixels - desiredWidth * desiredHeight * sizeof(PIXEL)) &&
+ outPixels >= inPixels + inputStride * inputHeight * sizeof(PIXEL) || outPixels <= inPixels - desiredWidth * desiredHeight * sizeof(PIXEL)) &&
"The input and output buffers must not overlap for an upscaling.");
DALI_ASSERT_DEBUG(reinterpret_cast<uint64_t>(inPixels) % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...).");
DALI_ASSERT_DEBUG(reinterpret_cast<uint64_t>(outPixels) % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...).");
{
// Round fixed point y coordinate to nearest integer:
const unsigned int integerY = (inY + (1u << 15u)) >> 16u;
- const PIXEL* const inScanline = &inAligned[inputWidth * integerY];
+ const PIXEL* const inScanline = &inAligned[inputStride * integerY];
PIXEL* const outScanline = &outAligned[desiredWidth * outY];
DALI_ASSERT_DEBUG(integerY < inputHeight);
- DALI_ASSERT_DEBUG(reinterpret_cast<const uint8_t*>(inScanline) < (inPixels + inputWidth * inputHeight * sizeof(PIXEL)));
+ DALI_ASSERT_DEBUG(reinterpret_cast<const uint8_t*>(inScanline) < (inPixels + inputStride * inputHeight * sizeof(PIXEL)));
DALI_ASSERT_DEBUG(reinterpret_cast<uint8_t*>(outScanline) < (outPixels + desiredWidth * desiredHeight * sizeof(PIXEL)));
unsigned int inX = 0;
void PointSample4BPP(const unsigned char* inPixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned char* outPixels,
unsigned int desiredWidth,
unsigned int desiredHeight)
{
- PointSampleAddressablePixels<uint32_t>(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
+ PointSampleAddressablePixels<uint32_t>(inPixels, inputWidth, inputHeight, inputStride, outPixels, desiredWidth, desiredHeight);
}
// RGB565, LA88
void PointSample2BPP(const unsigned char* inPixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned char* outPixels,
unsigned int desiredWidth,
unsigned int desiredHeight)
{
- PointSampleAddressablePixels<uint16_t>(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
+ PointSampleAddressablePixels<uint16_t>(inPixels, inputWidth, inputHeight, inputStride, outPixels, desiredWidth, desiredHeight);
}
// L8, A8
void PointSample1BPP(const unsigned char* inPixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned char* outPixels,
unsigned int desiredWidth,
unsigned int desiredHeight)
{
- PointSampleAddressablePixels<uint8_t>(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
+ PointSampleAddressablePixels<uint8_t>(inPixels, inputWidth, inputHeight, inputStride, outPixels, desiredWidth, desiredHeight);
}
/* RGB888
void PointSample3BPP(const uint8_t* inPixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
uint8_t* outPixels,
unsigned int desiredWidth,
unsigned int desiredHeight)
for(unsigned int outY = 0; outY < desiredHeight; ++outY)
{
const unsigned int integerY = (inY + (1u << 15u)) >> 16u;
- const uint8_t* const inScanline = &inPixels[inputWidth * integerY * BYTES_PER_PIXEL];
+ const uint8_t* const inScanline = &inPixels[inputStride * integerY * BYTES_PER_PIXEL];
uint8_t* const outScanline = &outPixels[desiredWidth * outY * BYTES_PER_PIXEL];
unsigned int inX = 0; //< 16.16 fixed-point input image x-coord.
void PointSample(const unsigned char* inPixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
Pixel::Format pixelFormat,
unsigned char* outPixels,
unsigned int desiredWidth,
// Check the pixel format is one that is supported:
if(pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGB565 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8)
{
- if(pixelFormat == Pixel::RGB888)
- {
- PointSample3BPP(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
- }
- else if(pixelFormat == Pixel::RGBA8888)
- {
- PointSample4BPP(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
- }
- else if(pixelFormat == Pixel::RGB565 || pixelFormat == Pixel::LA88)
- {
- PointSample2BPP(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
- }
- else if(pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8)
- {
- PointSample1BPP(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
- }
- else
+ switch(pixelFormat)
{
- DALI_ASSERT_DEBUG(0 == "Inner branch conditions don't match outer branch.");
+ case Pixel::RGB888:
+ {
+ PointSample3BPP(inPixels, inputWidth, inputHeight, inputStride, outPixels, desiredWidth, desiredHeight);
+ break;
+ }
+ case Pixel::RGBA8888:
+ {
+ PointSample4BPP(inPixels, inputWidth, inputHeight, inputStride, outPixels, desiredWidth, desiredHeight);
+ break;
+ }
+ case Pixel::RGB565:
+ case Pixel::LA88:
+ {
+ PointSample2BPP(inPixels, inputWidth, inputHeight, inputStride, outPixels, desiredWidth, desiredHeight);
+ break;
+ }
+ case Pixel::L8:
+ case Pixel::A8:
+ {
+ PointSample1BPP(inPixels, inputWidth, inputHeight, inputStride, outPixels, desiredWidth, desiredHeight);
+ break;
+ }
+ default:
+ {
+ DALI_ASSERT_DEBUG(0 == "Inner branch conditions don't match outer branch.");
+ }
}
}
else
bool DEBUG_ASSERT_ALIGNMENT>
inline void LinearSampleGeneric(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions)
{
const unsigned int desiredWidth = desiredDimensions.GetWidth();
const unsigned int desiredHeight = desiredDimensions.GetHeight();
- DALI_ASSERT_DEBUG(((outPixels >= inPixels + inputWidth * inputHeight * sizeof(PIXEL)) ||
+ DALI_ASSERT_DEBUG(((outPixels >= inPixels + inputStride * inputHeight * sizeof(PIXEL)) ||
(inPixels >= outPixels + desiredWidth * desiredHeight * sizeof(PIXEL))) &&
"Input and output buffers cannot overlap.");
if(DEBUG_ASSERT_ALIGNMENT)
// Find the two scanlines to blend and the weight to blend with:
const unsigned int integerY1 = inY >> 16u;
- const unsigned int integerY2 = integerY1 >= inputHeight ? integerY1 : integerY1 + 1;
+ const unsigned int integerY2 = integerY1 + 1 >= inputHeight ? integerY1 : integerY1 + 1;
const unsigned int inputYWeight = inY & 65535u;
DALI_ASSERT_DEBUG(integerY1 < inputHeight);
DALI_ASSERT_DEBUG(integerY2 < inputHeight);
- const PIXEL* const inScanline1 = &inAligned[inputWidth * integerY1];
- const PIXEL* const inScanline2 = &inAligned[inputWidth * integerY2];
+ const PIXEL* const inScanline1 = &inAligned[inputStride * integerY1];
+ const PIXEL* const inScanline2 = &inAligned[inputStride * integerY2];
unsigned int inX = 0;
for(unsigned int outX = 0; outX < desiredWidth; ++outX)
{
// Work out the two pixel scanline offsets for this cluster of four samples:
const unsigned int integerX1 = inX >> 16u;
- const unsigned int integerX2 = integerX1 >= inputWidth ? integerX1 : integerX1 + 1;
+ const unsigned int integerX2 = integerX1 + 1 >= inputWidth ? integerX1 : integerX1 + 1;
// Execute the loads:
const PIXEL pixel1 = inScanline1[integerX1];
void LinearSample1BPP(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions)
{
- LinearSampleGeneric<uint8_t, BilinearFilter1BPPByte, false>(inPixels, inputDimensions, outPixels, desiredDimensions);
+ LinearSampleGeneric<uint8_t, BilinearFilter1BPPByte, false>(inPixels, inputDimensions, inputStride, outPixels, desiredDimensions);
}
void LinearSample2BPP(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions)
{
- LinearSampleGeneric<Pixel2Bytes, BilinearFilter2Bytes, true>(inPixels, inputDimensions, outPixels, desiredDimensions);
+ LinearSampleGeneric<Pixel2Bytes, BilinearFilter2Bytes, true>(inPixels, inputDimensions, inputStride, outPixels, desiredDimensions);
}
void LinearSampleRGB565(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions)
{
- LinearSampleGeneric<PixelRGB565, BilinearFilterRGB565, true>(inPixels, inputDimensions, outPixels, desiredDimensions);
+ LinearSampleGeneric<PixelRGB565, BilinearFilterRGB565, true>(inPixels, inputDimensions, inputStride, outPixels, desiredDimensions);
}
void LinearSample3BPP(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions)
{
- LinearSampleGeneric<Pixel3Bytes, BilinearFilterRGB888, false>(inPixels, inputDimensions, outPixels, desiredDimensions);
+ LinearSampleGeneric<Pixel3Bytes, BilinearFilterRGB888, false>(inPixels, inputDimensions, inputStride, outPixels, desiredDimensions);
}
void LinearSample4BPP(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions)
{
- LinearSampleGeneric<Pixel4Bytes, BilinearFilter4Bytes, true>(inPixels, inputDimensions, outPixels, desiredDimensions);
+ LinearSampleGeneric<Pixel4Bytes, BilinearFilter4Bytes, true>(inPixels, inputDimensions, inputStride, outPixels, desiredDimensions);
+}
+
+// Dispatch to a format-appropriate linear sampling function:
+void LinearSample(const unsigned char* __restrict__ inPixels,
+ ImageDimensions inDimensions,
+ unsigned int inStride,
+ Pixel::Format pixelFormat,
+ unsigned char* __restrict__ outPixels,
+ ImageDimensions outDimensions)
+{
+ // Check the pixel format is one that is supported:
+ if(pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::RGB565)
+ {
+ switch(pixelFormat)
+ {
+ case Pixel::RGB888:
+ {
+ LinearSample3BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ break;
+ }
+ case Pixel::RGBA8888:
+ {
+ LinearSample4BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ break;
+ }
+ case Pixel::L8:
+ case Pixel::A8:
+ {
+ LinearSample1BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ break;
+ }
+ case Pixel::LA88:
+ {
+ LinearSample2BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ break;
+ }
+ case Pixel::RGB565:
+ {
+ LinearSampleRGB565(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ break;
+ }
+ default:
+ {
+ DALI_ASSERT_DEBUG(0 == "Inner branch conditions don't match outer branch.");
+ }
+ }
+ }
+ else
+ {
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not linear sampled: unsupported pixel format: %u.\n", unsigned(pixelFormat));
+ }
}
void Resample(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions,
Resampler::Filter filterType,
samples[i].ResizeUninitialized(srcWidth);
}
- const int srcPitch = srcWidth * numChannels;
+ const int srcPitch = inputStride * numChannels;
const int dstPitch = dstWidth * numChannels;
int dstY = 0;
void LanczosSample4BPP(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions)
{
- Resample(inPixels, inputDimensions, outPixels, desiredDimensions, Resampler::LANCZOS4, 4, true);
+ Resample(inPixels, inputDimensions, inputStride, outPixels, desiredDimensions, Resampler::LANCZOS4, 4, true);
}
void LanczosSample1BPP(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions)
{
// For L8 images
- Resample(inPixels, inputDimensions, outPixels, desiredDimensions, Resampler::LANCZOS4, 1, false);
+ Resample(inPixels, inputDimensions, inputStride, outPixels, desiredDimensions, Resampler::LANCZOS4, 1, false);
}
-// Dispatch to a format-appropriate linear sampling function:
-void LinearSample(const unsigned char* __restrict__ inPixels,
- ImageDimensions inDimensions,
- Pixel::Format pixelFormat,
- unsigned char* __restrict__ outPixels,
- ImageDimensions outDimensions)
+// Dispatch to a format-appropriate third-party resampling function:
+void LanczosSample(const unsigned char* __restrict__ inPixels,
+ ImageDimensions inDimensions,
+ unsigned int inStride,
+ Pixel::Format pixelFormat,
+ unsigned char* __restrict__ outPixels,
+ ImageDimensions outDimensions)
{
// Check the pixel format is one that is supported:
- if(pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::RGB565)
+ if(pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::BGRA8888 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8)
{
- if(pixelFormat == Pixel::RGB888)
- {
- LinearSample3BPP(inPixels, inDimensions, outPixels, outDimensions);
- }
- else if(pixelFormat == Pixel::RGBA8888)
- {
- LinearSample4BPP(inPixels, inDimensions, outPixels, outDimensions);
- }
- else if(pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8)
+ switch(pixelFormat)
{
- LinearSample1BPP(inPixels, inDimensions, outPixels, outDimensions);
- }
- else if(pixelFormat == Pixel::LA88)
- {
- LinearSample2BPP(inPixels, inDimensions, outPixels, outDimensions);
- }
- else if(pixelFormat == Pixel::RGB565)
- {
- LinearSampleRGB565(inPixels, inDimensions, outPixels, outDimensions);
- }
- else
- {
- DALI_ASSERT_DEBUG(0 == "Inner branch conditions don't match outer branch.");
+ case Pixel::RGBA8888:
+ case Pixel::BGRA8888:
+ {
+ LanczosSample4BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ break;
+ }
+ case Pixel::L8:
+ case Pixel::A8:
+ {
+ LanczosSample1BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ break;
+ }
+ default:
+ {
+ DALI_ASSERT_DEBUG(0 == "Inner branch conditions don't match outer branch.");
+ }
}
}
else
{
- DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not linear sampled: unsupported pixel format: %u.\n", unsigned(pixelFormat));
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not lanczos sampled: unsupported pixel format: %u.\n", unsigned(pixelFormat));
}
}
void RotateByShear(const uint8_t* const pixelsIn,
unsigned int widthIn,
unsigned int heightIn,
+ unsigned int strideIn,
unsigned int pixelSize,
float radians,
uint8_t*& pixelsOut,
fastRotationPerformed = Rotate90(pixelsIn,
widthIn,
heightIn,
+ strideIn,
pixelSize,
pixelsOut,
widthOut,
fastRotationPerformed = Rotate180(pixelsIn,
widthIn,
heightIn,
+ strideIn,
pixelSize,
pixelsOut);
fastRotationPerformed = Rotate270(pixelsIn,
widthIn,
heightIn,
+ strideIn,
pixelSize,
pixelsOut,
widthOut,
const uint8_t* const firstHorizontalSkewPixelsIn = fastRotationPerformed ? pixelsOut : pixelsIn;
std::unique_ptr<uint8_t, void (*)(void*)> tmpPixelsInPtr((fastRotationPerformed ? pixelsOut : nullptr), free);
+ unsigned int stride = fastRotationPerformed ? widthOut : strideIn;
+
// Reset the input/output
widthIn = widthOut;
heightIn = heightOut;
const float shear = angleTangent * ((angleTangent >= 0.f) ? (0.5f + static_cast<float>(y)) : (0.5f + static_cast<float>(y) - static_cast<float>(heightOut)));
const int intShear = static_cast<int>(floor(shear));
- HorizontalSkew(firstHorizontalSkewPixelsIn, widthIn, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast<float>(intShear));
+ HorizontalSkew(firstHorizontalSkewPixelsIn, widthIn, stride, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast<float>(intShear));
}
// Reset the 'pixel in' pointer with the output of the 'First Horizontal Skew' and free the memory allocated by the 'Fast Rotations'.
for(column = 0u; column < widthOut; ++column, offset -= angleSinus)
{
const int shear = static_cast<int>(floor(offset));
- VerticalSkew(tmpPixelsInPtr.get(), tmpWidthIn, tmpHeightIn, pixelSize, pixelsOut, widthOut, heightOut, column, shear, offset - static_cast<float>(shear));
+ VerticalSkew(tmpPixelsInPtr.get(), tmpWidthIn, tmpHeightIn, tmpWidthIn, pixelSize, pixelsOut, widthOut, heightOut, column, shear, offset - static_cast<float>(shear));
}
// Reset the 'pixel in' pointer with the output of the 'Vertical Skew' and free the memory allocated by the 'First Horizontal Skew'.
// Reset the input/output
for(unsigned int y = 0u; y < heightOut; ++y, offset += angleTangent)
{
const int shear = static_cast<int>(floor(offset));
- HorizontalSkew(tmpPixelsInPtr.get(), tmpWidthIn, pixelSize, pixelsOut, widthOut, y, shear, offset - static_cast<float>(shear));
+ HorizontalSkew(tmpPixelsInPtr.get(), tmpWidthIn, tmpWidthIn, pixelSize, pixelsOut, widthOut, y, shear, offset - static_cast<float>(shear));
}
// The deleter of the tmpPixelsInPtr unique pointer is called freeing the memory allocated by the 'Vertical Skew'.
void HorizontalShear(const uint8_t* const pixelsIn,
unsigned int widthIn,
unsigned int heightIn,
+ unsigned int strideIn,
unsigned int pixelSize,
float radians,
uint8_t*& pixelsOut,
const float shear = radians * ((radians >= 0.f) ? (0.5f + static_cast<float>(y)) : (0.5f + static_cast<float>(y) - static_cast<float>(heightOut)));
const int intShear = static_cast<int>(floor(shear));
- HorizontalSkew(pixelsIn, widthIn, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast<float>(intShear));
+ HorizontalSkew(pixelsIn, widthIn, strideIn, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast<float>(intShear));
}
}
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* @param[in] pixelFormat The format of the image pointed at by pixels.
* @param[in] inputWidth The width of the input image.
* @param[in] inputHeight The height of the input image.
+ * @param[in] inputStride The stride of the input image.
* @param[in] desiredWidth The width the client is requesting.
* @param[in] desiredHeight The height the client is requesting.
* @param[out] outWidth The resulting width after downscaling.
* @param[out] outHeight The resulting height after downscaling.
+ * @param[out] outStride The resulting stride after downscaling.
*/
void DownscaleInPlacePow2(unsigned char* const pixels,
Pixel::Format pixelFormat,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned int desiredWidth,
unsigned int desiredHeight,
FittingMode::Type fittingMode,
SamplingMode::Type samplingMode,
unsigned& outWidth,
- unsigned& outHeight);
+ unsigned& outHeight,
+ unsigned& outStride);
/**
* @brief Destructive in-place downscaling by a power of 2 factor.
* @param[in,out] pixels The buffer both to read from and write the result to.
* @param[in] inputWidth The width of the input image.
* @param[in] inputHeight The height of the input image.
+ * @param[in] inputStride The stride of the input image.
* @param[in] desiredWidth The width the client is requesting.
* @param[in] desiredHeight The height the client is requesting.
* @param[out] outWidth The resulting width after downscaling.
* @param[out] outHeight The resulting height after downscaling.
+ * @param[out] outStride The resulting stride after downscaling.
*/
void DownscaleInPlacePow2RGB888(unsigned char* pixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned int desiredWidth,
unsigned int desiredHeight,
BoxDimensionTest dimensionTest,
unsigned int& outWidth,
- unsigned int& outHeight);
+ unsigned int& outHeight,
+ unsigned int& outStride);
/**
* @copydoc DownscaleInPlacePow2RGB888
void DownscaleInPlacePow2RGBA8888(unsigned char* pixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned int desiredWidth,
unsigned int desiredHeight,
BoxDimensionTest dimensionTest,
unsigned int& outWidth,
- unsigned int& outHeight);
+ unsigned int& outHeight,
+ unsigned int& outStride);
/**
* @copydoc DownscaleInPlacePow2RGB888
void DownscaleInPlacePow2RGB565(unsigned char* pixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned int desiredWidth,
unsigned int desiredHeight,
BoxDimensionTest dimensionTest,
unsigned int& outWidth,
- unsigned int& outHeight);
+ unsigned int& outHeight,
+ unsigned int& outStride);
/**
* @copydoc DownscaleInPlacePow2RGB888
void DownscaleInPlacePow2ComponentPair(unsigned char* pixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned int desiredWidth,
unsigned int desiredHeight,
BoxDimensionTest dimensionTest,
unsigned int& outWidth,
- unsigned int& outHeight);
+ unsigned int& outHeight,
+ unsigned int& outStride);
/**
* @copydoc DownscaleInPlacePow2RGB888
void DownscaleInPlacePow2SingleBytePerPixel(unsigned char* pixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned int desiredWidth,
unsigned int desiredHeight,
BoxDimensionTest dimensionTest,
unsigned int& outWidth,
- unsigned int& outHeight);
+ unsigned int& outHeight,
+ unsigned int& outStride);
/**
* @brief Rescales an input image into the exact output dimensions passed-in.
void PointSample(const unsigned char* inPixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
Pixel::Format pixelFormat,
unsigned char* outPixels,
unsigned int desiredWidth,
void PointSample4BPP(const unsigned char* inPixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned char* outPixels,
unsigned int desiredWidth,
unsigned int desiredHeight);
void PointSample3BPP(const unsigned char* inPixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned char* outPixels,
unsigned int desiredWidth,
unsigned int desiredHeight);
void PointSample2BPP(const unsigned char* inPixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned char* outPixels,
unsigned int desiredWidth,
unsigned int desiredHeight);
void PointSample1BPP(const unsigned char* inPixels,
unsigned int inputWidth,
unsigned int inputHeight,
+ unsigned int inputStride,
unsigned char* outPixels,
unsigned int desiredWidth,
unsigned int desiredHeight);
*/
void LinearSample(const unsigned char* __restrict__ inPixels,
ImageDimensions inDimensions,
+ unsigned int inStride,
Pixel::Format pixelFormat,
unsigned char* __restrict__ outPixels,
ImageDimensions outDimensions);
*/
void LinearSample1BPP(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions);
*/
void LinearSample2BPP(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions);
*/
void LinearSampleRGB565(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions);
*/
void LinearSample3BPP(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions);
*/
void LinearSample4BPP(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions);
/**
+ * @brief Resample input image to output image using a Lanczos algorithm.
+ *
+ * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
+ * separate buffer from the output buffer.
+ *
+ * @param[in] inPixels Pointer to the input image buffer.
+ * @param[in] inputDimensions The input dimensions of the image.
+ * @param[in] inputStride The input stride of the image.
+ * @param[in] pixelFormat The format of the image pointed at by pixels.
+ * @param[out] outPixels Pointer to the output image buffer.
+ * @param[in] desiredDimensions The output dimensions of the image.
+ */
+void LanczosSample(const unsigned char* __restrict__ inPixels,
+ ImageDimensions inDimensions,
+ unsigned int inStride,
+ Pixel::Format pixelFormat,
+ unsigned char* __restrict__ outPixels,
+ ImageDimensions outDimensions);
+
+/**
* @brief Resamples the input image with the Lanczos algorithm.
*
* @pre @p inPixels must not alias @p outPixels. The input image should be a totally
*
* @param[in] inPixels Pointer to the input image buffer.
* @param[in] inputDimensions The input dimensions of the image.
+ * @param[in] inputStride The input stride of the image.
* @param[out] outPixels Pointer to the output image buffer.
* @param[in] desiredDimensions The output dimensions of the image.
*/
void LanczosSample4BPP(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions);
*
* @param[in] inPixels Pointer to the input image buffer.
* @param[in] inputDimensions The input dimensions of the image.
+ * @param[in] inputStride The input stride of the image.
* @param[out] outPixels Pointer to the output image buffer.
* @param[in] desiredDimensions The output dimensions of the image.
*/
void LanczosSample1BPP(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions);
*
* @param[in] inPixels Pointer to the input image buffer.
* @param[in] inputDimensions The input dimensions of the image.
+ * @param[in] inputStride The input stride of the image.
* @param[out] outPixels Pointer to the output image buffer.
* @param[in] desiredDimensions The output dimensions of the image.
*/
void Resample(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
+ unsigned int inputStride,
unsigned char* __restrict__ outPixels,
ImageDimensions desiredDimensions,
Resampler::Filter filterType,
* @param[in] pixelsIn The input buffer.
* @param[in] widthIn The width of the input buffer.
* @param[in] heightIn The height of the input buffer.
+ * @param[in] strideIn The stride of the input buffer.
* @param[in] pixelSize The size of the pixel.
* @param[in] radians The rotation angle in radians.
* @param[out] pixelsOut The rotated output buffer.
void RotateByShear(const uint8_t* const pixelsIn,
unsigned int widthIn,
unsigned int heightIn,
+ unsigned int strideIn,
unsigned int pixelSize,
float radians,
uint8_t*& pixelsOut,
* @param[in] pixelsIn The input buffer.
* @param[in] widthIn The width of the input buffer.
* @param[in] heightIn The height of the input buffer.
+ * @param[in] strideIn The stride of the input buffer.
* @param[in] pixelSize The size of the pixel.
* @param[in] radians The shear angle in radians.
* @param[out] pixelsOut The rotated output buffer.
void HorizontalShear(const uint8_t* const pixelsIn,
unsigned int widthIn,
unsigned int heightIn,
+ unsigned int strideIn,
unsigned int pixelSize,
float radians,
uint8_t*& pixelsOut,
**/
inline uint32_t AveragePixelRGBA8888(uint32_t a, uint32_t b)
{
- const unsigned int avg =
- ((AverageComponent((a & 0xff000000) >> 1u, (b & 0xff000000) >> 1u) << 1u) & 0xff000000) +
- (AverageComponent(a & 0x00ff0000, b & 0x00ff0000) & 0x00ff0000) +
- (AverageComponent(a & 0x0000ff00, b & 0x0000ff00) & 0x0000ff00) +
- (AverageComponent(a & 0x000000ff, b & 0x000000ff));
- return avg;
- ///@ToDo: Optimise by trying return (((a ^ b) & 0xfefefefeUL) >> 1) + (a & b);
+ /**
+ * @code
+ * const unsigned int avg =
+ * (AverageComponent((a & 0xff000000) >> 1u, (b & 0xff000000) >> 1u) << 1u) & 0xff000000) +
+ * (AverageComponent(a & 0x00ff0000, b & 0x00ff0000) & 0x00ff0000) +
+ * (AverageComponent(a & 0x0000ff00, b & 0x0000ff00) & 0x0000ff00) +
+ * (AverageComponent(a & 0x000000ff, b & 0x000000ff);
+ * return avg;
+ * @endcode
+ */
+ return (((a ^ b) & 0xfefefefeu) >> 1) + (a & b);
///@ToDo: Optimise for ARM using the single ARMV6 instruction: UHADD8 R4, R0, R5. This is not Neon. It runs in the normal integer pipeline so there is no downside like a stall moving between integer and copro.
}
**/
inline uint32_t AveragePixelRGB565(uint32_t a, uint32_t b)
{
- const unsigned int avg =
- (AverageComponent(a & 0xf800, b & 0xf800) & 0xf800) +
- (AverageComponent(a & 0x7e0, b & 0x7e0) & 0x7e0) +
- (AverageComponent(a & 0x1f, b & 0x1f));
- return avg;
+ /**
+ * @code
+ * const unsigned int avg =
+ * (AverageComponent(a & 0xf800, b & 0xf800) & 0xf800) +
+ * (AverageComponent(a & 0x7e0, b & 0x7e0) & 0x7e0) +
+ * (AverageComponent(a & 0x1f, b & 0x1f));
+ * return avg;
+ * @endcode
+ */
+ return (((a ^ b) & 0xf7deu) >> 1) + (a & b);
}
/** @return The weighted blend of two integers as a 16.16 fixed-point number, given a 0.16 fixed-point blending factor. */
inline unsigned int WeightedBlendIntToFixed1616(unsigned int a, unsigned int b, unsigned int fractBlend)
{
DALI_ASSERT_DEBUG(fractBlend <= 65535u && "Factor should be in 0.16 fixed-point.");
- const unsigned int weightedAFixed = a * (65535u - fractBlend);
- const unsigned int weightedBFixed = b * fractBlend;
- const unsigned blended = (weightedAFixed + weightedBFixed);
+ /**
+ * @code
+ * const unsigned int weightedAFixed = a * (65535u - fractBlend);
+ * const unsigned int weightedBFixed = b * fractBlend;
+ * const unsigned blended = (weightedAFixed + weightedBFixed);
+ * @endcode
+ */
+ const unsigned int blended = (a << 16) - a + (static_cast<int32_t>(b) - static_cast<int32_t>(a)) * fractBlend;
return blended;
}
inline uint64_t WeightedBlendFixed1616ToFixed1632(unsigned int a, unsigned int b, unsigned int fractBlend)
{
DALI_ASSERT_DEBUG(fractBlend <= 65535u && "Factor should be in 0.16 fixed-point.");
- // Blend while promoting intermediates to 16.32 fixed point:
- const uint64_t weightedAFixed = uint64_t(a) * (65535u - fractBlend);
- const uint64_t weightedBFixed = uint64_t(b) * fractBlend;
- const uint64_t blended = (weightedAFixed + weightedBFixed);
+ /**
+ * @code
+ * // Blend while promoting intermediates to 16.32 fixed point:
+ * const uint64_t weightedAFixed = uint64_t(a) * (65535u - fractBlend);
+ * const uint64_t weightedBFixed = uint64_t(b) * fractBlend;
+ * const uint64_t blended = (weightedAFixed + weightedBFixed);
+ * @endcode
+ */
+ const uint64_t blended = (static_cast<uint64_t>(a) << 16) - a + (static_cast<int64_t>(b) - static_cast<int64_t>(a)) * fractBlend;
return blended;
}
DALI_ASSERT_DEBUG(fractBlendHorizontal <= 65535u && "Factor should be in 0.16 fixed-point.");
DALI_ASSERT_DEBUG(fractBlendVertical <= 65535u && "Factor should be in 0.16 fixed-point.");
- const unsigned int topBlend = WeightedBlendIntToFixed1616(tl, tr, fractBlendHorizontal);
- const unsigned int botBlend = WeightedBlendIntToFixed1616(bl, br, fractBlendHorizontal);
- const uint64_t blended2x2 = WeightedBlendFixed1616ToFixed1632(topBlend, botBlend, fractBlendVertical);
+ /**
+ * @code
+ * const unsigned int topBlend = WeightedBlendIntToFixed1616(tl, tr, fractBlendHorizontal);
+ * const unsigned int botBlend = WeightedBlendIntToFixed1616(bl, br, fractBlendHorizontal);
+ * const uint64_t blended2x2 = WeightedBlendFixed1616ToFixed1632(topBlend, botBlend, fractBlendVertical);
+ * const unsigned int rounded = (blended2x2 + (1u << 31u)) >> 32u;
+ * @endcode
+ */
+
+ /**
+ * Hard-coding optimize!
+ *
+ * Let p = 65536, s.t we can optimze it as << 16.
+ * Let x = fractBlendHorizontal, y = fractBlendVertical.
+ * topBlend = (tl*p - tl - tl*x + tr*x)
+ * botBlend = (bl*p - bl - bl*x + br*x)
+ * blended2x2 = topBlend*p - topBlend - topBlend*y + botBlend*y
+ *
+ * And now we can split all values.
+ * tl*p*p - tl*p - tl*x*p + tr*x*p - tl*p + tl + tl*x - tr*x - tl*y*p + tl*y + tl*x*y - tr*x*y + bl*y*p - bl*y - bl*x*y + br*x*y;
+ * --> (collect by p, x, and y)
+ * (tl)*p*p + (-2tl + (-tl + tr)*x + (-tl+bl)*y)*p + tl + (tl - tr)*x + (tl - bl)*y + (tl - tr - bl + br)*x*y
+ *
+ * A = (tl - tr) * x;
+ * B = (tl - bl) * y;
+ * C = (tl - tr - bl + br) * x * y;
+ * D = (2*tl + A + B)
+ * -->
+ * (tl << 32) - (D << 16) + tl + A + B + C
+ *
+ * Becareful of overflow and negative value.
+ */
+ const int32_t A = (static_cast<int32_t>(tl) - static_cast<int32_t>(tr)) * static_cast<int32_t>(fractBlendHorizontal);
+ const int32_t B = (static_cast<int32_t>(tl) - static_cast<int32_t>(bl)) * static_cast<int32_t>(fractBlendVertical);
+ const int64_t C = (static_cast<int64_t>(tl) - static_cast<int64_t>(tr) - static_cast<int64_t>(bl) + static_cast<int64_t>(br)) * static_cast<int64_t>(fractBlendHorizontal) * static_cast<int64_t>(fractBlendVertical);
+ const int64_t D = ((static_cast<int64_t>(tl) << 1) + A + B);
+
+ const uint64_t blended2x2 = (static_cast<int64_t>(tl) << 32u) - (D << 16u) + tl + A + B + C;
const unsigned int rounded = (blended2x2 + (1u << 31u)) >> 32u;
return rounded;
}
+/**
+ * @brief Fast multiply & divide by 255. It wiil be useful when we applying alpha value in color
+ *
+ * @param x The value between [0..255]
+ * @param y The value between [0..255]
+ * @return (x*y)/255
+ */
+inline uint8_t MultiplyAndNormalizeColor(const uint8_t x, const uint8_t y) noexcept
+{
+ const uint32_t xy = static_cast<const uint32_t>(x) * y;
+ return ((xy << 15) + (xy << 7) + xy) >> 23;
+}
+
+/**
+ * @brief Fast division by 17 and roundup. It will be useful when we compress 8bit luminance value as 4bit for text glyph.
+ *
+ * @param x The value between [0..255]
+ * @return round(x / 17.0f).(same as (x+8)/17)
+ */
+inline uint8_t CompressBitPerPixel8To4(const uint8_t x) noexcept
+{
+ return ((((static_cast<const uint16_t>(x) << 4) - x + (x >> 4)) >> 7) + 1) >> 1;
+}
+
/**@}*/
} /* namespace Platform */
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
// Pull the bytes of the file header in as a block:
const unsigned int readLength = sizeof(AstcFileHeader);
- if(fread(&fileHeader, 1, readLength, filePointer) != readLength)
+ if(DALI_UNLIKELY(fread(&fileHeader, 1, readLength, filePointer) != readLength))
{
return false;
}
// Check the header contains the ASTC native file identifier.
bool headerIsValid = memcmp(fileHeader.magic, FileIdentifier, sizeof(fileHeader.magic)) == 0;
- if(!headerIsValid)
+ if(DALI_UNLIKELY(!headerIsValid))
{
DALI_LOG_ERROR("File is not a valid ASTC native file\n");
// Return here as otherwise, if not a valid ASTC file, we are likely to pick up other header errors spuriously.
const unsigned int zDepth = static_cast<unsigned int>(fileHeader.zsize[0]) + (static_cast<unsigned int>(fileHeader.zsize[1]) << 8) + (static_cast<unsigned int>(fileHeader.zsize[2]) << 16);
// Check image dimensions are within limits.
- if((width > MAX_TEXTURE_DIMENSION) || (height > MAX_TEXTURE_DIMENSION))
+ if(DALI_UNLIKELY((width > MAX_TEXTURE_DIMENSION) || (height > MAX_TEXTURE_DIMENSION)))
{
DALI_LOG_ERROR("ASTC file has larger than supported dimensions: %d,%d\n", width, height);
headerIsValid = false;
}
// Confirm the ASTC block does not have any Z depth.
- if(zDepth != 1)
+ if(DALI_UNLIKELY(zDepth != 1))
{
DALI_LOG_ERROR("ASTC files with z size other than 1 are not supported. Z size is: %d\n", zDepth);
headerIsValid = false;
bool LoadBitmapFromAstc(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
{
FILE* const filePointer = input.file;
- if(!filePointer)
+ if(DALI_UNLIKELY(!filePointer))
{
DALI_LOG_ERROR("Null file handle passed to ASTC compressed bitmap file loader.\n");
return false;
AstcFileHeader fileHeader;
unsigned int width, height;
- if(!LoadAstcHeader(filePointer, width, height, fileHeader))
+ if(DALI_UNLIKELY(!LoadAstcHeader(filePointer, width, height, fileHeader)))
{
DALI_LOG_ERROR("Could not load ASTC Header from file.\n");
return false;
// Retrieve the pixel format from the ASTC block size.
Pixel::Format pixelFormat = GetAstcPixelFormat(fileHeader);
- if(pixelFormat == Pixel::INVALID)
+ if(DALI_UNLIKELY(pixelFormat == Pixel::INVALID))
{
DALI_LOG_ERROR("No internal pixel format supported for ASTC file pixel format.\n");
return false;
}
// Retrieve the file size.
- if(fseek(filePointer, 0L, SEEK_END))
+ if(DALI_UNLIKELY(fseek(filePointer, 0L, SEEK_END)))
{
DALI_LOG_ERROR("Could not seek through file.\n");
return false;
}
off_t fileSize = ftell(filePointer);
- if(fileSize == -1L)
+ if(DALI_UNLIKELY(fileSize == -1L))
{
DALI_LOG_ERROR("Could not determine ASTC file size.\n");
return false;
}
- if(fseek(filePointer, sizeof(AstcFileHeader), SEEK_SET))
+ if(DALI_UNLIKELY(fseek(filePointer, sizeof(AstcFileHeader), SEEK_SET)))
{
DALI_LOG_ERROR("Could not seek through file.\n");
return false;
size_t imageByteCount = fileSize - sizeof(AstcFileHeader);
// Sanity-check the image data is not too large and that it is at less than 2 bytes per texel:
- if((imageByteCount > MAX_IMAGE_DATA_SIZE) || (imageByteCount > ((static_cast<size_t>(width) * height) << 1)))
+ if(DALI_UNLIKELY((imageByteCount > MAX_IMAGE_DATA_SIZE) || (imageByteCount > ((static_cast<size_t>(width) * height) << 1))))
{
DALI_LOG_ERROR("ASTC file has too large image-data field.\n");
return false;
const size_t bytesRead = fread(pixels, 1, imageByteCount, filePointer);
// Check the size of loaded data is what we expected.
- if(bytesRead != imageByteCount)
+ if(DALI_UNLIKELY(bytesRead != imageByteCount))
{
DALI_LOG_ERROR("Read of image pixel data failed.\n");
return false;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
const unsigned int readLength = sizeof(T);
// Load the information directly into our structure
- if(fread(&header, 1, readLength, fp) != readLength)
+ if(DALI_UNLIKELY(fread(&header, 1, readLength, fp) != readLength))
{
return false;
}
bool LoadBmpHeader(FILE* fp, unsigned int& width, unsigned int& height, BmpFileHeader& fileHeader, BmpInfoHeader& infoHeader)
{
- if(!ReadHeader(fp, fileHeader))
+ if(DALI_UNLIKELY(!ReadHeader(fp, fileHeader)))
{
DALI_LOG_ERROR("File header read failed\n");
return false;
}
- if(!ReadHeader(fp, infoHeader))
+ if(DALI_UNLIKELY(!ReadHeader(fp, infoHeader)))
{
DALI_LOG_ERROR("Info header read failed\n");
return false;
width = infoHeader.width;
height = abs(infoHeader.height);
- if(infoHeader.width == 0)
+ if(DALI_UNLIKELY(infoHeader.width == 0))
{
DALI_LOG_ERROR("Invalid header size\n");
return false;
unsigned int rowStride,
unsigned int padding)
{
- if(fp == NULL || pixels == NULL)
+ if(DALI_UNLIKELY(fp == NULL || pixels == NULL))
{
DALI_LOG_ERROR("Error decoding BMP_RGB24V5 format\n");
return false;
}
- if(fseek(fp, offset, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, offset, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking BMP_RGB24V5 data\n");
return false;
}
- for(unsigned int yPos = 0; yPos < height; yPos++)
+ for(std::uint32_t yPos = 0; yPos < height; ++yPos)
{
- unsigned char* pixelsPtr = NULL;
+ std::uint8_t* pixelsPtr = NULL;
if(topDown)
{
pixelsPtr = pixels + (yPos * rowStride);
{
pixelsPtr = pixels + (((height - 1) - yPos) * rowStride);
}
- if(fread(pixelsPtr, 1, rowStride, fp) != rowStride)
+ if(DALI_UNLIKELY(fread(pixelsPtr, 1, rowStride, fp) != rowStride))
{
DALI_LOG_ERROR("Error reading the BMP image\n");
return false;
}
- for(unsigned int i = 0; i < rowStride; i += 3)
+ for(std::uint32_t i = 0; i < rowStride; i += 3)
{
- unsigned char temp = pixelsPtr[i];
- pixelsPtr[i] = pixelsPtr[i + 2];
- pixelsPtr[i + 2] = temp;
+ std::uint8_t temp = pixelsPtr[i];
+ pixelsPtr[i] = pixelsPtr[i + 2];
+ pixelsPtr[i + 2] = temp;
}
if(padding)
{
// move past the padding.
- if(fseek(fp, padding, SEEK_CUR))
+ if(DALI_UNLIKELY(fseek(fp, padding, SEEK_CUR)))
{
DALI_LOG_ERROR("Error moving past BMP_RGB24V5 padding\n");
}
unsigned int rowStride,
unsigned int padding)
{
- if(fp == NULL || pixels == NULL)
+ if(DALI_UNLIKELY(fp == NULL || pixels == NULL))
{
DALI_LOG_ERROR("Error decoding BMP_BITFIELDS32V4 format\n");
return false;
}
- if(fseek(fp, offset, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, offset, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking BMP_BITFIELDS32V4 data\n");
return false;
}
- for(unsigned int yPos = 0; yPos < height; yPos++)
+ for(std::uint32_t yPos = 0; yPos < height; ++yPos)
{
- unsigned char* pixelsPtr = NULL;
+ std::uint8_t* pixelsPtr = NULL;
if(topDown)
{
pixelsPtr = pixels + (yPos * rowStride);
{
pixelsPtr = pixels + (((height - 1) - yPos) * rowStride);
}
- if(fread(pixelsPtr, 1, rowStride, fp) != rowStride)
+ if(DALI_UNLIKELY(fread(pixelsPtr, 1, rowStride, fp) != rowStride))
{
DALI_LOG_ERROR("Error reading the BMP image\n");
return false;
}
- for(unsigned int i = 0; i < rowStride; i += 4)
+ for(std::uint32_t i = 0; i < rowStride; i += 4)
{
- unsigned char temp = pixelsPtr[i];
- pixelsPtr[i] = pixelsPtr[i + 2];
- pixelsPtr[i + 2] = temp;
+ std::uint8_t temp = pixelsPtr[i];
+ pixelsPtr[i] = pixelsPtr[i + 2];
+ pixelsPtr[i + 2] = temp;
}
if(padding)
{
// move past the padding.
- if(fseek(fp, padding, SEEK_CUR))
+ if(DALI_UNLIKELY(fseek(fp, padding, SEEK_CUR)))
{
DALI_LOG_ERROR("Error moving past BMP_BITFIELDS32V4 padding\n");
}
unsigned int rowStride,
unsigned int padding)
{
- if(fp == NULL || pixels == NULL)
+ if(DALI_UNLIKELY(fp == NULL || pixels == NULL))
{
DALI_LOG_ERROR("Error decoding BMP_BITFIELDS32 format\n");
return false;
}
- if(fseek(fp, offset, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, offset, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking BMP_BITFIELDS32 data\n");
return false;
}
- for(unsigned int yPos = 0; yPos < height; yPos++)
+ for(std::uint32_t yPos = 0; yPos < height; ++yPos)
{
- unsigned char* pixelsPtr;
+ std::uint8_t* pixelsPtr;
if(topDown)
{
// the data in the file is top down, and we store the data top down
pixelsPtr = pixels + (((height - 1) - yPos) * rowStride);
}
- if(fread(pixelsPtr, 1, rowStride, fp) != rowStride)
+ if(DALI_UNLIKELY(fread(pixelsPtr, 1, rowStride, fp) != rowStride))
{
DALI_LOG_ERROR("Error reading the BMP image\n");
return false;
}
- for(unsigned int i = 0; i < rowStride; i += 4)
+ for(std::uint32_t i = 0; i < rowStride; i += 4)
{
- unsigned char temp = pixelsPtr[i];
- pixelsPtr[i] = pixelsPtr[i + 2];
- pixelsPtr[i + 2] = temp;
+ std::uint8_t temp = pixelsPtr[i];
+ pixelsPtr[i] = pixelsPtr[i + 2];
+ pixelsPtr[i + 2] = temp;
}
if(padding)
{
// move past the padding.
- if(fseek(fp, padding, SEEK_CUR))
+ if(DALI_UNLIKELY(fseek(fp, padding, SEEK_CUR)))
{
DALI_LOG_ERROR("Error moving past BMP_BITFIELDS32 padding\n");
}
unsigned int offset,
bool topDown)
{
- if(fp == NULL || pixels == NULL)
+ if(DALI_UNLIKELY(fp == NULL || pixels == NULL))
{
DALI_LOG_ERROR("Error decoding RGB565 format\n");
return false;
}
- if(fseek(fp, offset, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, offset, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking RGB565 data\n");
return false;
}
- width = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
- unsigned int rowStride = width * 2;
+ width = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
+ std::uint32_t rowStride = width * 2;
- for(unsigned int i = 0; i < height; i++)
+ for(std::uint32_t i = 0; i < height; ++i)
{
- unsigned char* pixelsPtr = NULL;
+ std::uint8_t* pixelsPtr = NULL;
if(topDown)
{
// the data in the file is top down, and we store the data top down
// the data in the file is bottom up, and we store the data top down
pixelsPtr = pixels + (((height - 1) - i) * rowStride);
}
- if(fread(pixelsPtr, 1, rowStride, fp) != rowStride)
+ if(DALI_UNLIKELY(fread(pixelsPtr, 1, rowStride, fp) != rowStride))
{
return false;
}
unsigned int offset,
bool topDown)
{
- if(fp == NULL || pixels == NULL)
+ if(DALI_UNLIKELY(fp == NULL || pixels == NULL))
{
DALI_LOG_ERROR("Error decoding BMP_BITFIELDS555 format\n");
return false;
}
- if(fseek(fp, offset, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, offset, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking BMP_BITFIELDS555 data\n");
return false;
width = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
- std::vector<char> raw(width * height * 2);
- unsigned int rawStride = width * 2;
- unsigned int rowStride = width * 3;
+ std::vector<std::uint8_t> raw(width * height * 2);
+ std::uint32_t rawStride = width * 2;
+ std::uint32_t rowStride = width * 3;
- char* rawPtr = NULL;
- for(unsigned int j = 0; j < height; j++)
+ std::uint8_t* rawPtr = NULL;
+ for(std::uint32_t j = 0; j < height; ++j)
{
rawPtr = &raw[0] + (j * rawStride);
- if(fread(rawPtr, 1, rawStride, fp) != rawStride)
+ if(DALI_UNLIKELY(fread(rawPtr, 1, rawStride, fp) != rawStride))
{
return false;
}
}
- for(unsigned int yPos = 0; yPos < height; yPos++)
+ for(std::uint32_t yPos = 0; yPos < height; ++yPos)
{
- unsigned char* pixelsPtr = NULL;
+ std::uint8_t* pixelsPtr = NULL;
if(topDown)
{
// the data in the file is top down, and we store the data top down
pixelsPtr = pixels + (((height - 1) - yPos) * rowStride);
}
- for(unsigned int k = 0; k < width; k++)
+ for(std::uint32_t k = 0; k < width; ++k)
{
- int index = yPos * rawStride + 2 * k;
+ std::uint32_t index = yPos * rawStride + 2 * k;
pixelsPtr[3 * k] = ((raw[index + 1] >> 2) & 0x1F) * 0xFF / 0x1F;
pixelsPtr[3 * k + 1] = (((raw[index + 1] & 0x03) << 3) | (raw[index] >> 5)) * 0xFF / 0x1F;
pixelsPtr[3 * k + 2] = (raw[index] & 0x1F) * 0xFF / 0x1F;
unsigned int offset,
bool topDown)
{
- if(fp == NULL || pixels == NULL)
+ if(DALI_UNLIKELY(fp == NULL || pixels == NULL))
{
DALI_LOG_ERROR("Error decoding BMP_RGB555 format\n");
return false;
}
- if(fseek(fp, offset, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, offset, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking BMP_RGB555 data\n");
return false;
}
width = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
- std::vector<char> raw(width * height * 2);
- unsigned int rawStride = width * 2;
- unsigned int rowStride = width * 3;
+ std::vector<std::uint8_t> raw(width * height * 2);
+ std::uint32_t rawStride = width * 2;
+ std::uint32_t rowStride = width * 3;
- char* rawPtr = NULL;
- for(unsigned int j = 0; j < height; j++)
+ std::uint8_t* rawPtr = NULL;
+ for(std::uint32_t j = 0; j < height; ++j)
{
rawPtr = &raw[0] + (j * rawStride);
- if(fread(rawPtr, 1, rawStride, fp) != rawStride)
+ if(DALI_UNLIKELY(fread(rawPtr, 1, rawStride, fp) != rawStride))
{
return false;
}
}
- for(unsigned int i = 0; i < height; i++)
+ for(std::uint32_t i = 0; i < height; ++i)
{
- unsigned char* pixelsPtr = NULL;
+ std::uint8_t* pixelsPtr = NULL;
if(topDown)
{
// the data in the file is top down, and we store the data top down
// the data in the file is bottom up, and we store the data top down
pixelsPtr = pixels + (((height - 1) - i) * rowStride);
}
- for(unsigned int k = 0; k < width; k++)
+ for(std::uint32_t k = 0; k < width; ++k)
{
- int index = i * rawStride + 2 * k;
+ std::uint32_t index = i * rawStride + 2 * k;
pixelsPtr[3 * k] = ((raw[index + 1] >> 2) & 0x1F) * 0xFF / 0x1F;
pixelsPtr[3 * k + 1] = (((raw[index + 1] & 0x03) << 3) | (raw[index] >> 5)) * 0xFF / 0x1F;
pixelsPtr[3 * k + 2] = (raw[index] & 0x1F) * 0xFF / 0x1F;
unsigned int offset,
bool topDown)
{
- if(fp == NULL || pixels == NULL)
+ if(DALI_UNLIKELY(fp == NULL || pixels == NULL))
{
DALI_LOG_ERROR("Error decoding BMP_RGB1 format\n");
return false;
}
- if(fseek(fp, offset, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, offset, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking BMP_RGB1 data\n");
return false;
}
- unsigned char colorTable[8] = {0};
- char cmd;
- unsigned int fillw = ((width & 63) != 0) ? width + 64 - (width & 63) : width;
- std::vector<char> colorIndex(fillw * height);
- unsigned int rowStride = fillw * 3; // RGB
+ std::uint8_t colorTable[8] = {0};
+ std::uint8_t cmd;
+ std::uint32_t fillw = ((width & 63) != 0) ? width + 64 - (width & 63) : width;
+ std::vector<std::uint8_t> colorIndex(fillw * height);
+ std::uint32_t rowStride = fillw * 3; // RGB
- if(fread(colorTable, 1, 8, fp) != 8)
+ if(DALI_UNLIKELY(fread(colorTable, 1, 8, fp) != 8))
{
return false;
}
- for(unsigned int i = 0; i < fillw * height; i += 8)
+ for(std::uint32_t i = 0; i < fillw * height; i += 8)
{
- if(fread(&cmd, 1, 1, fp) != 1)
+ if(DALI_UNLIKELY(fread(&cmd, 1, 1, fp) != 1))
{
return false;
}
colorIndex[i + 7] = (cmd & 0x01);
}
- for(unsigned int index = 0; index < height; index = index + 1)
+ for(std::uint32_t index = 0; index < height; ++index)
{
- unsigned char* pixelsPtr = NULL;
+ std::uint8_t* pixelsPtr = NULL;
if(topDown)
{
// the data in the file is top down, and we store the data top down
// the data in the file is bottom up, and we store the data top down
pixelsPtr = pixels + (((height - 1) - index) * rowStride);
}
- for(unsigned int j = 0; j < fillw; j++)
+ for(std::uint32_t j = 0; j < fillw; ++j)
{
- unsigned int ctIndex = 0;
+ std::uint32_t ctIndex = 0;
if((fillw * index + j) < (fillw * height))
{
ctIndex = colorIndex[fillw * index + j];
unsigned int offset,
bool topDown)
{
- if(fp == NULL || pixels == NULL)
+ if(DALI_UNLIKELY(fp == NULL || pixels == NULL))
{
DALI_LOG_ERROR("Error decoding BMP_RGB4 format\n");
return false;
}
- if(fseek(fp, offset, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, offset, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking BMP_RGB4 data\n");
return false;
}
- char colorTable[64];
- char cmd;
- unsigned int fillw = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
- std::vector<char> colorIndex(fillw * height);
- unsigned int rowStride = fillw * 3;
+ std::uint8_t colorTable[64];
+ std::uint8_t cmd;
+ std::uint32_t fillw = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
+ std::vector<std::uint8_t> colorIndex(fillw * height);
+ std::uint32_t rowStride = fillw * 3;
- if(fread(colorTable, 1, 64, fp) != 64)
+ if(DALI_UNLIKELY(fread(colorTable, 1, 64, fp) != 64))
{
return false;
}
- for(unsigned int i = 0; i < fillw * height; i += 2)
+ for(std::uint32_t i = 0; i < fillw * height; i += 2)
{
- if(fread(&cmd, 1, 1, fp) != 1)
+ if(DALI_UNLIKELY(fread(&cmd, 1, 1, fp) != 1))
{
return false;
}
colorIndex[i] = cmd >> 4;
colorIndex[i + 1] = cmd & (0x0F);
}
- unsigned int ctIndex = 0;
+ std::uint32_t ctIndex = 0;
- for(unsigned int index = 0; index < height; index = index + 1)
+ for(std::uint32_t index = 0; index < height; ++index)
{
- unsigned char* pixelsPtr = NULL;
+ std::uint8_t* pixelsPtr = NULL;
if(topDown)
{
// the data in the file is top down, and we store the data top down
// the data in the file is bottom up, and we store the data top down
pixelsPtr = pixels + (((height - 1) - index) * rowStride);
}
- for(unsigned int j = 0; j < fillw; j++)
+ for(std::uint32_t j = 0; j < fillw; ++j)
{
ctIndex = colorIndex[fillw * index + j];
pixelsPtr[3 * j] = colorTable[4 * ctIndex + 2];
unsigned int offset,
bool topDown)
{
- if(fp == NULL || pixels == NULL)
+ if(DALI_UNLIKELY(fp == NULL || pixels == NULL))
{
DALI_LOG_ERROR("Error decoding BMP_RGB8 format\n");
return false;
}
- if(fseek(fp, offset, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, offset, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking BMP_RGB8 data\n");
return false;
}
- std::vector<char> colorTable(1024);
+ std::vector<std::uint8_t> colorTable(1024);
width = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
- char cmd;
- std::vector<char> colorIndex(width * height);
- unsigned int rowStride = width * 3; //RGB8->RGB24
+ std::vector<std::uint8_t> colorIndex(width * height);
+ std::uint32_t rowStride = width * 3; //RGB8->RGB24
- if(fread(&colorTable[0], 1, 1024, fp) != 1024)
+ if(DALI_UNLIKELY(fread(&colorTable[0], 1, 1024, fp) != 1024))
{
return false;
}
- for(unsigned int i = 0; i < width * height; i++)
+ if(DALI_UNLIKELY(fread(&colorIndex[0], 1, width * height, fp) != width * height))
{
- if(fread(&cmd, 1, 1, fp) != 1)
- {
- return false;
- }
-
- colorIndex[i] = cmd;
+ return false;
}
- unsigned int ctIndex = 0;
- for(unsigned int index = 0; index < height; index = index + 1)
+ std::uint8_t ctIndex = 0;
+ for(std::uint32_t index = 0; index < height; ++index)
{
- unsigned char* pixelsPtr = NULL;
+ std::uint8_t* pixelsPtr = NULL;
if(topDown)
{
// the data in the file is top down, and we store the data top down
// the data in the file is bottom up, and we store the data top down
pixelsPtr = pixels + (((height - 1) - index) * rowStride);
}
- for(unsigned int j = 0; j < width; j++)
+ for(std::uint8_t j = 0; j < width; ++j)
{
ctIndex = colorIndex[width * index + j];
pixelsPtr[3 * j] = colorTable[4 * ctIndex + 2];
unsigned int offset,
bool topDown)
{
- if(fp == NULL || pixels == NULL)
+ if(DALI_UNLIKELY(fp == NULL || pixels == NULL))
{
DALI_LOG_ERROR("Error decoding BMP_RLE4 format\n");
return false;
}
- unsigned char* pixelsPtr = pixels;
- width = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
- char cmd[2];
- unsigned int cmdStride = 2;
- char colorTable[64];
- std::vector<char> colorIndex(width * height >> 1);
- std::vector<char> run;
- unsigned int x = 0;
- unsigned int y = 0;
- unsigned int dx = 0;
- unsigned int dy = 0;
+ std::uint8_t* pixelsPtr = pixels;
+ width = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
+ std::uint8_t cmd[2];
+ std::uint32_t cmdStride = 2;
+ std::uint8_t colorTable[64];
+ std::vector<std::uint8_t> colorIndex(width * height >> 1);
+ std::vector<std::uint8_t> run;
+ std::uint32_t x = 0;
+ std::uint32_t y = 0;
+ std::uint32_t dx = 0;
+ std::uint32_t dy = 0;
width += (width & 1);
width = width >> 1;
bool finish = false;
- if(fseek(fp, offset, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, offset, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking BMP_RLE4 data\n");
return false;
}
- if(fread(colorTable, 1, 64, fp) != 64)
+ if(DALI_UNLIKELY(fread(colorTable, 1, 64, fp) != 64))
{
return false;
}
{
break;
}
- if(fread(cmd, 1, cmdStride, fp) != cmdStride)
+ if(DALI_UNLIKELY(fread(cmd, 1, cmdStride, fp) != cmdStride))
{
return false;
}
y++;
break;
case 2: // delta
- if(fread(cmd, 1, cmdStride, fp) != cmdStride)
+ if(DALI_UNLIKELY(fread(cmd, 1, cmdStride, fp) != cmdStride))
{
DALI_LOG_ERROR("Error reading the BMP image\n");
return false;
break;
default:
// decode a literal run
- unsigned int length = cmd[1] & (0xFF);
+ std::uint32_t length = cmd[1] & (0xFF);
//size of run, which is word aligned
- unsigned int bytesize = length;
+ std::uint32_t bytesize = length;
bytesize += (bytesize & 1);
bytesize >>= 1;
bytesize += (bytesize & 1);
run.resize(bytesize);
- if(fread(&run[0], 1, bytesize, fp) != bytesize)
+ if(DALI_UNLIKELY(fread(&run[0], 1, bytesize, fp) != bytesize))
{
DALI_LOG_ERROR("Error reading the BMP image\n");
return false;
{
length += (length & 1);
length >>= 1;
- for(unsigned int i = 0; i < length; i += 1)
+ for(std::uint32_t i = 0; i < length; ++i)
{
colorIndex[(x >> 1) + width * (height - y - 1) + i] = run[i];
}
}
else
{
- for(unsigned int i = 0; i < length; i++)
+ for(std::uint32_t i = 0; i < length; ++i)
{
if((i & 1) == 0) //copy high to low
{
}
else
{
- unsigned int length = cmd[0] & (0xFF);
+ std::uint32_t length = cmd[0] & (0xFF);
if((x & 1) == 0)
{
length += (length & 1);
length >>= 1;
- for(unsigned int i = 0; i < length; i++)
+ for(std::uint32_t i = 0; i < length; ++i)
{
colorIndex[(height - y - 1) * width + i + (x >> 1)] = cmd[1];
}
}
else
{
- for(unsigned int i = 0; i < length; i++)
+ for(std::uint32_t i = 0; i < length; ++i)
{
if((i & 1) == 0)
{
}
}
- int ctIndexHigh = 0;
- int ctIndexLow = 0;
- for(unsigned int index = 0; index < (width * height); index = index + 1)
+ std::uint32_t ctIndexHigh = 0;
+ std::uint32_t ctIndexLow = 0;
+ for(std::uint32_t index = 0; index < (width * height); ++index)
{
ctIndexHigh = colorIndex[index] >> 4;
ctIndexLow = colorIndex[index] & (0x0F);
unsigned int offset,
bool topDown)
{
- if(fp == NULL || pixels == NULL)
+ if(DALI_UNLIKELY(fp == NULL || pixels == NULL))
{
DALI_LOG_ERROR("Error decoding BMP_RLE8 format\n");
return false;
}
- unsigned char* pixelsPtr = pixels;
- unsigned int x = 0;
- unsigned int y = 0;
- unsigned int cmdStride = 2;
+ std::uint8_t* pixelsPtr = pixels;
+ std::uint32_t x = 0;
+ std::uint32_t y = 0;
+ std::uint32_t cmdStride = 2;
width = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
- std::vector<char> colorTable(1024);
- char cmd[2];
- std::vector<char> colorIndex(width * height);
+ std::vector<std::uint8_t> colorTable(1024);
+ std::uint8_t cmd[2];
+ std::vector<std::uint8_t> colorIndex(width * height);
- if(fseek(fp, offset, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, offset, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking BMP_RLE8 data\n");
return false;
}
- if(fread(&colorTable[0], 1, 1024, fp) != 1024)
+ if(DALI_UNLIKELY(fread(&colorTable[0], 1, 1024, fp) != 1024))
{
return false;
}
- unsigned int dx = 0;
- unsigned int dy = 0;
- bool finish = false;
- unsigned int length = 0;
- unsigned int copylength = 0;
- std::vector<char> run;
+ std::uint32_t dx = 0;
+ std::uint32_t dy = 0;
+ bool finish = false;
+ std::uint32_t length = 0;
+ std::uint32_t copylength = 0;
+ std::vector<std::uint8_t> run;
while((x + y * width) < width * height)
{
- if(finish)
+ if(DALI_UNLIKELY(finish))
{
break;
}
- if(fread(cmd, 1, cmdStride, fp) != cmdStride)
+ if(DALI_UNLIKELY(fread(cmd, 1, cmdStride, fp) != cmdStride))
{
return false;
}
y++;
break;
case 2: // delta
- if(fread(cmd, 1, cmdStride, fp) != cmdStride)
+ if(DALI_UNLIKELY(fread(cmd, 1, cmdStride, fp) != cmdStride))
{
DALI_LOG_ERROR("Error reading the BMP image\n");
return false;
//absolute mode must be word-aligned
length += (length & 1);
run.resize(length);
- if(fread(&run[0], 1, length, fp) != length)
+ if(DALI_UNLIKELY(fread(&run[0], 1, length, fp) != length))
{
DALI_LOG_ERROR("Error reading the BMP image\n");
return false;
}
- for(unsigned int i = 0; i < length; i += 1)
+ for(std::uint32_t i = 0; i < length; ++i)
{
colorIndex[x + width * (height - y - 1) + i] = run[i];
}
else
{
length = cmd[0] & (0xFF);
- for(unsigned int i = 0; i < length; i++)
+ for(std::uint32_t i = 0; i < length; ++i)
{
colorIndex[(height - y - 1) * width + x] = cmd[1];
x++;
}
}
}
- int ctIndex = 0;
- for(unsigned int index = 0; index < width * height; index = index + 1)
+ std::uint32_t ctIndex = 0;
+ for(std::uint32_t index = 0; index < width * height; ++index)
{
ctIndex = colorIndex[index];
pixelsPtr[3 * index] = colorTable[4 * ctIndex + 2];
{
//DALI_ASSERT_DEBUG( bitmap.GetPackedPixelsProfile() != 0 && "Need a packed pixel bitmap to load into." );
FILE* const fp = input.file;
- if(fp == NULL)
+ if(DALI_UNLIKELY(fp == NULL))
{
DALI_LOG_ERROR("Error loading bitmap\n");
return false;
// Load the header info
unsigned int width, height;
- if(!LoadBmpHeader(fp, width, height, fileHeader, infoHeader))
+ if(DALI_UNLIKELY(!LoadBmpHeader(fp, width, height, fileHeader, infoHeader)))
{
return false;
}
{
if(infoHeader.bitsPerPixel == 16)
{
- if(fseek(fp, 14 + infoHeader.infoHeaderSize + 1, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, 14 + infoHeader.infoHeaderSize + 1, SEEK_SET)))
{
return false;
}
char mask;
- if(fread(&mask, 1, 1, fp) != 1)
+ if(DALI_UNLIKELY(fread(&mask, 1, 1, fp) != 1))
{
return false;
}
pixelsIterator = pixels + (((height - 1) - yPos) * rowStride);
}
- if(fread(pixelsIterator, 1, rowStride, fp) != rowStride)
+ if(DALI_UNLIKELY(fread(pixelsIterator, 1, rowStride, fp) != rowStride))
{
DALI_LOG_ERROR("Error reading the BMP image\n");
break;
if(padding)
{
- if(fseek(fp, padding, SEEK_CUR)) // move past the padding.
+ if(DALI_UNLIKELY(fseek(fp, padding, SEEK_CUR))) // move past the padding.
{
DALI_LOG_ERROR("Error moving past BMP padding\n");
}
}
} // switch
- if(!decodeResult)
+ if(DALI_UNLIKELY(!decodeResult))
{
DALI_LOG_ERROR("Decoding failed\n");
return false;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*gifInfo = DGifOpen(reinterpret_cast<void*>(fp), ReadDataFromGif);
#endif
- if(!(*gifInfo) || errorCode)
+ if(DALI_UNLIKELY(!(*gifInfo) || errorCode))
{
DALI_LOG_ERROR("GIF Loader: DGifOpen Error. Code: %d\n", errorCode);
return false;
height = (*gifInfo)->SHeight;
// No proper size in GIF.
- if(width <= 0 || height <= 0)
+ if(DALI_UNLIKELY(width <= 0 || height <= 0))
{
return false;
}
for(unsigned int currentByte = interlacePairPtr->startingByte; currentByte < height; currentByte += interlacePairPtr->incrementalByte)
{
unsigned char* row = decodedData + currentByte * bytesPerRow;
- if(DGifGetLine(gifInfo, row, width) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetLine(gifInfo, row, width) == GIF_ERROR))
{
DALI_LOG_ERROR("GIF Loader: Error reading Interlaced GIF\n");
return false;
for(unsigned int row = 0; row < height; ++row)
{
- if(DGifGetLine(gifInfo, decodedDataPtr, width) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetLine(gifInfo, decodedDataPtr, width) == GIF_ERROR))
{
DALI_LOG_ERROR("GIF Loader: Error reading non-interlaced GIF\n");
return false;
/// Called when we want to handle IMAGE_DESC_RECORD_TYPE
bool HandleImageDescriptionRecordType(Dali::Devel::PixelBuffer& bitmap, GifFileType* gifInfo, unsigned int width, unsigned int height, bool& finished)
{
- if(DGifGetImageDesc(gifInfo) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetImageDesc(gifInfo) == GIF_ERROR))
{
DALI_LOG_ERROR("GIF Loader: Error getting Image Description\n");
return false;
}
// Ensure there is at least 1 image in the GIF.
- if(gifInfo->ImageCount < 1)
+ if(DALI_UNLIKELY(gifInfo->ImageCount < 1))
{
DALI_LOG_ERROR("GIF Loader: No Images\n");
return false;
bitmap = Dali::Devel::PixelBuffer::New(actualWidth, actualHeight, pixelFormat);
// Decode the GIF Image
- if(!DecodeImage(gifInfo, decodedData, actualWidth, actualHeight, bytesPerRow))
+ if(DALI_UNLIKELY(!DecodeImage(gifInfo, decodedData, actualWidth, actualHeight, bytesPerRow)))
{
return false;
}
extensionByte != NULL;
extRetCode = DGifGetExtensionNext(gifInfo, &extensionByte))
{
- if(extRetCode == GIF_ERROR)
+ if(DALI_UNLIKELY(extRetCode == GIF_ERROR))
{
DALI_LOG_ERROR("GIF Loader: Error reading GIF Extension record.\n");
return false;
GifFileType* gifInfo(NULL);
unsigned int width(0);
unsigned int height(0);
- if(!LoadGifHeader(fp, width, height, &gifInfo))
+ if(DALI_UNLIKELY(!LoadGifHeader(fp, width, height, &gifInfo)))
{
return false;
}
!finished && recordType != TERMINATE_RECORD_TYPE;
returnCode = DGifGetRecordType(gifInfo, &recordType))
{
- if(returnCode == GIF_ERROR)
+ if(DALI_UNLIKELY(returnCode == GIF_ERROR))
{
DALI_LOG_ERROR("GIF Loader: Error getting Record Type\n");
return false;
if(IMAGE_DESC_RECORD_TYPE == recordType)
{
- if(!HandleImageDescriptionRecordType(bitmap, gifInfo, width, height, finished))
+ if(DALI_UNLIKELY(!HandleImageDescriptionRecordType(bitmap, gifInfo, width, height, finished)))
{
return false;
}
}
else if(EXTENSION_RECORD_TYPE == recordType)
{
- if(!HandleExtensionRecordType(gifInfo))
+ if(DALI_UNLIKELY(!HandleExtensionRecordType(gifInfo)))
{
return false;
}
#define ARGB_JOIN(a, r, g, b) \
(((a) << 24) + ((r) << 16) + ((g) << 8) + (b))
-bool read_ushort(unsigned char* map, size_t length, size_t* position, unsigned short* ret)
+bool read_ushort(const unsigned char* const& map, size_t length, size_t* position, unsigned short* ret)
{
unsigned char b[2];
- if(*position + 2 > length)
+ if(DALI_UNLIKELY(*position + 2 > length))
{
return false;
}
return true;
}
-bool read_uint(unsigned char* map, size_t length, size_t* position, unsigned int* ret)
+bool read_uint(const unsigned char* const& map, size_t length, size_t* position, unsigned int* ret)
{
unsigned char b[4];
unsigned int i;
- if(*position + 4 > length)
+ if(DALI_UNLIKELY(*position + 4 > length))
{
return false;
}
return true;
}
-bool read_uchar(unsigned char* map, size_t length, size_t* position, unsigned char* ret)
+bool read_uchar(const unsigned char* const& map, size_t length, size_t* position, unsigned char* ret)
{
- if(*position + 1 > length)
+ if(DALI_UNLIKELY(*position + 1 > length))
{
return false;
}
return true;
}
-bool read_mem(unsigned char* map, size_t length, size_t* position, void* buffer, int size)
+bool read_mem(const unsigned char* const& map, size_t length, size_t* position, void* buffer, int size)
{
- if(*position + size > length)
+ if(DALI_UNLIKELY(*position + size > length))
{
return false;
}
{
memset(&chosen, 0, sizeof(chosen));
- if(fp == NULL)
+ if(DALI_UNLIKELY(fp == NULL))
{
DALI_LOG_ERROR("Error loading bitmap\n");
return false;
unsigned short word;
unsigned char byte;
- if(fseek(fp, 0, SEEK_END))
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END)))
{
DALI_LOG_ERROR("Error seeking ICO data\n");
return false;
fsize = static_cast<unsigned int>(positionIndicator);
}
- if(0u == fsize)
+ if(DALI_UNLIKELY(0u == fsize))
{
return false;
}
- if(fseek(fp, 0, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking ICO data\n");
return false;
}
- if(fsize < (ICO_FILE_HEADER + ICO_IMAGE_INFO_HEADER)) //6 + 16 + 40
+ if(DALI_UNLIKELY(fsize < (ICO_FILE_HEADER + ICO_IMAGE_INFO_HEADER))) //6 + 16 + 40
{
return false;
}
map.ResizeUninitialized(fsize);
-
- if(fread(&map[0], 1, fsize, fp) != fsize)
+ if(DALI_UNLIKELY(fread(&map[0], 1, fsize, fp) != fsize))
{
DALI_LOG_WARNING("image file read opeation error!\n");
return false;
}
+ const std::uint8_t* const inputBufferPtr = &map[0];
+
int search = BIGGEST;
unsigned short reserved, type, count;
- if(!read_ushort(&map[0], fsize, &position, &reserved))
+ if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &reserved)))
{
return false;
}
- if(!read_ushort(&map[0], fsize, &position, &type))
+ if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &type)))
{
return false;
}
- if(!read_ushort(&map[0], fsize, &position, &count))
+ if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &count)))
{
return false;
}
- if(!((reserved == 0) &&
- ((type == ICON) || (type == CURSOR)) && (count != 0)))
+ if(DALI_UNLIKELY(!((reserved == 0) &&
+ ((type == ICON) || (type == CURSOR)) && (count != 0))))
{
return false;
}
for(unsigned short i = 0; i < count; i++)
{
unsigned char tw = 0, th = 0, tcols = 0;
- if(!read_uchar(&map[0], fsize, &position, &tw))
+ if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &tw)))
{
return false;
}
{
w = 256;
}
- if(!read_uchar(&map[0], fsize, &position, &th))
+ if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &th)))
{
return false;
}
{
h = 256;
}
- if(!read_uchar(&map[0], fsize, &position, &tcols))
+ if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &tcols)))
{
return false;
}
int cols = tcols;
- if(!read_uchar(&map[0], fsize, &position, &byte))
+ if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &byte)))
{
return false;
}
- if(!read_ushort(&map[0], fsize, &position, &word))
+ if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &word)))
{
return false;
}
planes = word;
}
//else hot_x = word;
- if(!read_ushort(&map[0], fsize, &position, &word))
+ if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &word)))
{
return false;
}
//else hot_y = word;
unsigned int bmoffset, bmsize;
- if(!read_uint(&map[0], fsize, &position, &bmsize))
+ if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &bmsize)))
{
return false;
}
- if(!read_uint(&map[0], fsize, &position, &bmoffset))
+ if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &bmoffset)))
{
return false;
}
- if((bmsize <= 0) || (bmoffset <= 0) || (bmoffset >= fsize))
+ if(DALI_UNLIKELY((bmsize <= 0) || (bmoffset <= 0) || (bmoffset >= fsize)))
{
return false;
}
}
}
- if(chosen.bmoffset == 0)
+ if(DALI_UNLIKELY(chosen.bmoffset == 0))
{
return false;
}
/**
* @brief Handle the different bits per pixel
* @param[in] bitcount The bit count
- * @param[in/out] map The map to use/set
+ * @param[in] inputBufferPtr The map to use
* @param[in/out] pix A reference to the pointer to the pix buffer
- * @param[in/out] surface A reference to the surface buffer
+ * @param[in/out] outputBufferPtr A reference to the surface buffer
* @param[in] width The width
* @param[in] height The height
* @param[in] fsize The file size
*/
bool HandleBitsPerPixel(
const unsigned int bitcount,
- Dali::Vector<unsigned char>& map,
+ const std::uint8_t* const& inputBufferPtr,
unsigned int*& pix,
- Dali::Vector<unsigned int>& surface,
+ std::uint32_t* const& outputBufferPtr,
const unsigned int width,
const unsigned int height,
const unsigned int fsize,
size_t& position,
- Dali::Vector<unsigned char>& pixbuf,
const unsigned int stride,
const Dali::Vector<unsigned int>& palette)
{
+ // Pixbuf only ever contains one scanline worth of data.
+ Dali::Vector<std::uint8_t> pixbuf;
+ pixbuf.ResizeUninitialized(stride);
+ std::uint8_t* lineBufferPtr = &pixbuf[0];
+
// Note: Switch is in order of most common format first.
switch(bitcount)
{
case 32:
{
- unsigned char* p = &map[position];
- pix = &surface[0] + ((height - 1) * width);
+ const std::uint8_t* p = inputBufferPtr + position;
+ pix = outputBufferPtr + ((height - 1) * width);
for(unsigned int i = 0; i < height; i++)
{
{
for(unsigned int i = 0; i < height; i++)
{
- pix = &surface[0] + ((height - 1 - i) * width);
- if(!read_mem(&map[0], fsize, &position, &pixbuf[0], stride))
+ pix = outputBufferPtr + ((height - 1 - i) * width);
+ if(DALI_UNLIKELY(!read_mem(inputBufferPtr, fsize, &position, lineBufferPtr, stride)))
{
return false;
}
- unsigned char* p = &pixbuf[0];
+ const std::uint8_t* p = lineBufferPtr;
for(unsigned int j = 0; j < width; j++)
{
*pix++ = ARGB_JOIN(0xff, p[0], p[1], p[2]);
{
for(unsigned int i = 0; i < height; i++)
{
- pix = &surface[0] + ((height - 1 - i) * width);
- if(!read_mem(&map[0], fsize, &position, &pixbuf[0], stride))
+ pix = outputBufferPtr + ((height - 1 - i) * width);
+ if(DALI_UNLIKELY(!read_mem(inputBufferPtr, fsize, &position, lineBufferPtr, stride)))
{
return false;
}
- unsigned char* p = &pixbuf[0];
+ const std::uint8_t* p = lineBufferPtr;
for(unsigned int j = 0; j < width; j++)
{
*pix++ = palette[*p++];
{
for(unsigned int i = 0; i < height; i++)
{
- pix = &surface[0] + ((height - 1 - i) * width);
- if(!read_mem(&map[0], fsize, &position, &pixbuf[0], stride))
+ pix = outputBufferPtr + ((height - 1 - i) * width);
+ if(DALI_UNLIKELY(!read_mem(inputBufferPtr, fsize, &position, lineBufferPtr, stride)))
{
return false;
}
- unsigned char* p = &pixbuf[0];
+ const std::uint8_t* p = lineBufferPtr;
for(unsigned int j = 0; j < width; j++)
{
if(j & 0x1)
case 1:
{
+ const std::uint32_t bytesPerWidth = width / 8;
+ const std::uint32_t bytesRemainingPerWidth = width & 7;
for(unsigned int i = 0; i < height; i++)
{
- pix = &surface[0] + ((height - 1 - i) * width);
- if(!read_mem(&map[0], fsize, &position, &pixbuf[0], stride))
+ pix = outputBufferPtr + ((height - 1 - i) * width);
+ if(DALI_UNLIKELY(!read_mem(inputBufferPtr, fsize, &position, lineBufferPtr, stride)))
{
return false;
}
- unsigned char* p = &pixbuf[0];
- for(unsigned int j = 0; j < width; j += 8)
+ const std::uint8_t* p = lineBufferPtr;
+ for(unsigned int j = 0; j < bytesPerWidth; ++j)
{
*pix++ = palette[*p >> 7];
*pix++ = palette[*p >> 6 & 0x01];
*pix++ = palette[*p >> 1 & 0x01];
*pix++ = palette[*p >> 0 & 0x01];
- p++;
+ ++p;
+ }
+ if(bytesRemainingPerWidth > 0)
+ {
+ for(std::uint32_t j = 0; j < bytesRemainingPerWidth; ++j)
+ {
+ *pix++ = palette[(*p >> (7 - j)) & 0x01];
+ }
+ ++p;
}
}
break;
/**
* @brief Apply the mask if required
- * @param[in/out] map The map to use/set
+ * @param[in] inputBufferPtr The map to use
* @param[in] fsize The file size
* @param[in/out] position The position in the file
- * @param[in//out] maskbuf The mask buffer
* @param[in] bitStride The stride
* @param[in] width The width
* @param[in] height The height
* @param[in/out] pix A reference to the pointer to the pix buffer
- * @param[in/out] surface A reference to the surface buffer
+ * @param[in/out] outputBufferPtr A reference to the surface buffer
*/
bool ApplyMask(
- Dali::Vector<unsigned char>& map,
- const unsigned int fsize,
- size_t& position,
- Dali::Vector<unsigned char>& maskbuf,
- const unsigned int bitStride,
- const unsigned int width,
- const unsigned int height,
- unsigned int*& pix,
- Dali::Vector<unsigned int>& surface)
+ const std::uint8_t* const& inputBufferPtr,
+ const unsigned int fsize,
+ size_t& position,
+ const unsigned int bitStride,
+ const unsigned int width,
+ const unsigned int height,
+ unsigned int*& pix,
+ std::uint32_t* const& outputBufferPtr)
{
- if(!read_mem(&map[0], fsize, &position, &maskbuf[0], bitStride * height))
- {
- return false;
- }
+ Dali::Vector<std::uint8_t> maskbuf;
+ maskbuf.ResizeUninitialized(bitStride);
+ std::uint8_t* lineBufferPtr = &maskbuf[0];
// Apply mask.
// Precalc to save time in the loops.
// Loop for each line of the image.
for(unsigned int i = 0; i < height; ++i)
{
- unsigned char* m = &maskbuf[0] + (bitStride * i);
- pix = &surface[0] + ((height - 1 - i) * width);
+ pix = outputBufferPtr + ((height - 1 - i) * width);
+ if(DALI_UNLIKELY(!read_mem(inputBufferPtr, fsize, &position, lineBufferPtr, bitStride)))
+ {
+ return false;
+ }
+ const std::uint8_t* m = lineBufferPtr;
// Do chunks of 8 pixels first so mask operations can be unrolled.
for(unsigned int j = 0; j < bytesPerWidth; ++j)
unsigned int fsize;
FILE* const fp = input.file;
- if(false == LoadIcoHeaderHelper(fp, chosen, map, fsize))
+ if(DALI_UNLIKELY(false == LoadIcoHeaderHelper(fp, chosen, map, fsize)))
{
return false;
}
unsigned int fsize;
FILE* const fp = input.file;
- if(false == LoadIcoHeaderHelper(fp, chosen, map, fsize))
+ if(DALI_UNLIKELY(false == LoadIcoHeaderHelper(fp, chosen, map, fsize)))
{
return false;
}
- Dali::Vector<unsigned int> pal;
- Dali::Vector<unsigned int> surface;
- Dali::Vector<unsigned char> maskbuf;
- Dali::Vector<unsigned char> pixbuf;
- pal.Resize(256 * 4);
-
unsigned int dword;
unsigned short word;
unsigned int h = chosen.h;
unsigned int cols = chosen.cols;
+ const std::uint8_t* const inputBufferPtr = &map[0];
+
// read bmp header time... let's do some checking
- if(!read_uint(&map[0], fsize, &position, &dword))
+ if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
{
return false; // headersize - dont care
}
- if(!read_uint(&map[0], fsize, &position, &dword))
+ if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
{
return false; // width
}
diff_size = 1;
}
}
- if(!read_uint(&map[0], fsize, &position, &dword))
+ if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
{
return false; // height
}
DALI_LOG_WARNING("Broken ICO file!\n");
}
- // Set up the surface as soon as we have the width and height, so we have a black image if there are any further errors.
- surface.ResizeUninitialized(w * h * 4);
- memset(&surface[0], 0, w * h * 4);
-
- if(!read_ushort(&map[0], fsize, &position, &word))
+ if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &word)))
{
return false; // planes
}
//planes2 = word;
- if(!read_ushort(&map[0], fsize, &position, &word))
+ if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &word)))
{
return false; // bitcount
}
unsigned int bitcount = word;
- if(!read_uint(&map[0], fsize, &position, &dword))
+ if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
{
return false; // compression
}
//compression = dword;
- if(!read_uint(&map[0], fsize, &position, &dword))
+ if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
{
return false; // imagesize
}
//imagesize = dword;
- if(!read_uint(&map[0], fsize, &position, &dword))
+ if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
{
return false; // z pixels per m
}
- if(!read_uint(&map[0], fsize, &position, &dword))
+ if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
{
return false; // y pizels per m
}
- if(!read_uint(&map[0], fsize, &position, &dword))
+ if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
{
return false; // colors used
}
//colorsused = dword;
- if(!read_uint(&map[0], fsize, &position, &dword))
+ if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
{
return false; // colors important
}
+ Dali::Vector<unsigned int> pal;
+ pal.Resize(256 * 4);
for(unsigned int i = 0; i < cols; i++)
{
unsigned char a, r, g, b;
- if(!read_uchar(&map[0], fsize, &position, &b))
+ if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &b)))
{
return false;
}
- if(!read_uchar(&map[0], fsize, &position, &g))
+ if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &g)))
{
return false;
}
- if(!read_uchar(&map[0], fsize, &position, &r))
+ if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &r)))
{
return false;
}
- if(!read_uchar(&map[0], fsize, &position, &a))
+ if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &a)))
{
return false;
}
pal[i] = ARGB_JOIN(0xff, b, g, r);
}
+ Dali::Vector<std::uint32_t> surface;
+
// This is the reference way of calculating the total number of bytes necessary to store one row of pixels.
unsigned int stride = (((bitcount * w) + 31) / 32) * 4;
unsigned int bitStride = ((w + 31) / 32) * 4;
+ // Set up the surface as soon as we have the width and height.
+ surface.ResizeUninitialized(w * h);
- // Pixbuf only ever contains one scanline worth of data.
- pixbuf.ResizeUninitialized(stride);
- maskbuf.ResizeUninitialized(bitStride * h);
+ std::uint32_t* const outputBufferPtr = &surface[0];
// Handle different bits-per-pixel.
- if(!HandleBitsPerPixel(bitcount, map, pix, surface, w, h, fsize, position, pixbuf, stride, pal))
+ if(DALI_UNLIKELY(!HandleBitsPerPixel(bitcount, inputBufferPtr, pix, outputBufferPtr, w, h, fsize, position, stride, pal)))
{
return false;
}
// From the spec: If bpp is less than 32, there will be a 1bpp mask bitmap also.
- if((bitcount < 32) && !ApplyMask(map, fsize, position, maskbuf, bitStride, w, h, pix, surface))
+ if(bitcount < 32)
{
- // Return false if not able to apply mask when the bpp is less than 32
- return false;
+ if(DALI_UNLIKELY(!ApplyMask(inputBufferPtr, fsize, position, bitStride, w, h, pix, outputBufferPtr)))
+ {
+ // Return false if not able to apply mask when the bpp is less than 32
+ return false;
+ }
}
bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::Format::RGBA8888);
auto pixels = bitmap.GetBuffer();
- memcpy(pixels, &surface[0], w * h * 4);
+ memcpy(pixels, outputBufferPtr, w * h * 4);
return true;
}
return true;
}
+bool IsJpegDecodingFailed()
+{
+ std::string errorString = tjGetErrorStr();
+
+ if(DALI_UNLIKELY(IsJpegErrorFatal(errorString)))
+ {
+ DALI_LOG_ERROR("%s\n", errorString.c_str());
+ return true;
+ }
+ else
+ {
+ DALI_LOG_WARNING("%s\n", errorString.c_str());
+ return false;
+ }
+}
+
// helpers for safe exif memory handling
using ExifHandle = std::unique_ptr<ExifData, decltype(exif_data_free)*>;
}
}
+void GetJpegPixelFormat(int jpegColorspace, TJPF& pixelLibJpegType, Pixel::Format& pixelFormat)
+{
+ pixelLibJpegType = TJPF_RGB;
+ pixelFormat = Pixel::RGB888;
+
+ switch(jpegColorspace)
+ {
+ case TJCS_RGB:
+ // YCbCr is not an absolute colorspace but rather a mathematical transformation of RGB designed solely for storage and transmission.
+ // YCbCr images must be converted to RGB before they can actually be displayed.
+ case TJCS_YCbCr:
+ {
+ pixelLibJpegType = TJPF_RGB;
+ pixelFormat = Pixel::RGB888;
+ break;
+ }
+ case TJCS_GRAY:
+ {
+ pixelLibJpegType = TJPF_GRAY;
+ pixelFormat = Pixel::L8;
+ break;
+ }
+ case TJCS_CMYK:
+ case TJCS_YCCK:
+ {
+ pixelLibJpegType = TJPF_CMYK;
+ pixelFormat = Pixel::RGBA8888;
+ break;
+ }
+ default:
+ {
+ pixelLibJpegType = TJPF_RGB;
+ pixelFormat = Pixel::RGB888;
+ break;
+ }
+ }
+}
+
+bool TransformBitmap(int scaledPreXformWidth, int scaledPreXformHeight, JpegTransform transform, uint8_t* bitmapPixelBuffer, Pixel::Format pixelFormat)
+{
+ const unsigned int bufferWidth = Dali::TizenPlatform::GetTextureDimension(scaledPreXformWidth);
+ const unsigned int bufferHeight = Dali::TizenPlatform::GetTextureDimension(scaledPreXformHeight);
+
+ bool result = false;
+
+ switch(transform)
+ {
+ case JpegTransform::NONE:
+ {
+ result = true;
+ break;
+ }
+ // 3 orientation changes for a camera held perpendicular to the ground or upside-down:
+ case JpegTransform::ROTATE_180:
+ {
+ static auto rotate180Functions = TransformFunctionArray{
+ &Rotate180<1>,
+ &Rotate180<3>,
+ &Rotate180<4>,
+ };
+ result = Transform(rotate180Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+ break;
+ }
+ case JpegTransform::ROTATE_270:
+ {
+ static auto rotate270Functions = TransformFunctionArray{
+ &Rotate270<1>,
+ &Rotate270<3>,
+ &Rotate270<4>,
+ };
+ result = Transform(rotate270Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+ break;
+ }
+ case JpegTransform::ROTATE_90:
+ {
+ static auto rotate90Functions = TransformFunctionArray{
+ &Rotate90<1>,
+ &Rotate90<3>,
+ &Rotate90<4>,
+ };
+ result = Transform(rotate90Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+ break;
+ }
+ case JpegTransform::FLIP_VERTICAL:
+ {
+ static auto flipVerticalFunctions = TransformFunctionArray{
+ &FlipVertical<1>,
+ &FlipVertical<3>,
+ &FlipVertical<4>,
+ };
+ result = Transform(flipVerticalFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+ break;
+ }
+ // Less-common orientation changes, since they don't correspond to a camera's physical orientation:
+ case JpegTransform::FLIP_HORIZONTAL:
+ {
+ static auto flipHorizontalFunctions = TransformFunctionArray{
+ &FlipHorizontal<1>,
+ &FlipHorizontal<3>,
+ &FlipHorizontal<4>,
+ };
+ result = Transform(flipHorizontalFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+ break;
+ }
+ case JpegTransform::TRANSPOSE:
+ {
+ static auto transposeFunctions = TransformFunctionArray{
+ &Transpose<1>,
+ &Transpose<3>,
+ &Transpose<4>,
+ };
+ result = Transform(transposeFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+ break;
+ }
+ case JpegTransform::TRANSVERSE:
+ {
+ static auto transverseFunctions = TransformFunctionArray{
+ &Transverse<1>,
+ &Transverse<3>,
+ &Transverse<4>,
+ };
+ result = Transform(transverseFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+ break;
+ }
+ default:
+ {
+ DALI_LOG_ERROR("Unsupported JPEG Orientation transformation: %x.\n", transform);
+ break;
+ }
+ }
+ return result;
+}
+
+bool LoadJpegFile(const Dali::ImageLoader::Input& input, Vector<uint8_t>& jpegBuffer, unsigned int& jpegBufferSize)
+{
+ FILE* const fp = input.file;
+
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END)))
+ {
+ DALI_LOG_ERROR("Error seeking to end of file\n");
+ return false;
+ }
+
+ long positionIndicator = ftell(fp);
+ jpegBufferSize = 0u;
+ if(positionIndicator > -1L)
+ {
+ jpegBufferSize = static_cast<unsigned int>(positionIndicator);
+ }
+
+ if(DALI_UNLIKELY(0u == jpegBufferSize))
+ {
+ DALI_LOG_ERROR("Jpeg buffer size error\n");
+ return false;
+ }
+
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
+ {
+ DALI_LOG_ERROR("Error seeking to start of file\n");
+ return false;
+ }
+
+ try
+ {
+ jpegBuffer.ResizeUninitialized(jpegBufferSize);
+ }
+ catch(...)
+ {
+ DALI_LOG_ERROR("Could not allocate temporary memory to hold JPEG file of size %uMB.\n", jpegBufferSize / 1048576U);
+ return false;
+ }
+ unsigned char* const jpegBufferPtr = jpegBuffer.Begin();
+
+ // Pull the compressed JPEG image bytes out of a file and into memory:
+ if(DALI_UNLIKELY(fread(jpegBufferPtr, 1, jpegBufferSize, fp) != jpegBufferSize))
+ {
+ DALI_LOG_ERROR("Error on image file read.\n");
+ return false;
+ }
+
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
+ {
+ DALI_LOG_ERROR("Error seeking to start of file\n");
+ return false;
+ }
+
+ return true;
+}
+
} // namespace
namespace Dali
{
namespace TizenPlatform
{
+bool DecodeJpeg(const Dali::ImageLoader::Input& input, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers, bool decodeToYuv);
JpegTransform ConvertExifOrientation(ExifData* exifData);
bool TransformSize(int requiredWidth, int requiredHeight, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, JpegTransform transform, int& preXformImageWidth, int& preXformImageHeight, int& postXformImageWidth, int& postXformImageHeight);
// On error exit from the JPEG lib, control will pass via JpegErrorHandler
// into this branch body for cleanup and error return:
- if(setjmp(jerr.jumpBuffer))
+ if(DALI_UNLIKELY(setjmp(jerr.jumpBuffer)))
{
DALI_LOG_ERROR("setjmp failed\n");
jpeg_destroy_decompress(&cinfo);
jpeg_stdio_src(&cinfo, fp);
// Check header to see if it is JPEG file
- if(jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
+ if(DALI_UNLIKELY(jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK))
{
DALI_LOG_ERROR("jpeg_read_header failed\n");
width = height = 0;
bool LoadBitmapFromJpeg(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
{
- const int flags = 0;
- FILE* const fp = input.file;
+ std::vector<Dali::Devel::PixelBuffer> pixelBuffers;
- if(fseek(fp, 0, SEEK_END))
+ bool result = DecodeJpeg(input, pixelBuffers, false);
+ if(!result && pixelBuffers.empty())
{
- DALI_LOG_ERROR("Error seeking to end of file\n");
- return false;
+ bitmap.Reset();
}
-
- long positionIndicator = ftell(fp);
- unsigned int jpegBufferSize = 0u;
- if(positionIndicator > -1L)
- {
- jpegBufferSize = static_cast<unsigned int>(positionIndicator);
- }
-
- if(0u == jpegBufferSize)
+ else
{
- DALI_LOG_ERROR("Jpeg buffer size error\n");
- return false;
+ bitmap = pixelBuffers[0];
}
+ return result;
+}
- if(fseek(fp, 0, SEEK_SET))
- {
- DALI_LOG_ERROR("Error seeking to start of file\n");
- return false;
- }
+bool LoadPlanesFromJpeg(const Dali::ImageLoader::Input& input, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers)
+{
+ return DecodeJpeg(input, pixelBuffers, true);
+}
- Vector<unsigned char> jpegBuffer;
- try
- {
- jpegBuffer.ResizeUninitialized(jpegBufferSize);
- }
- catch(...)
- {
- DALI_LOG_ERROR("Could not allocate temporary memory to hold JPEG file of size %uMB.\n", jpegBufferSize / 1048576U);
- return false;
- }
- unsigned char* const jpegBufferPtr = jpegBuffer.Begin();
+bool DecodeJpeg(const Dali::ImageLoader::Input& input, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers, bool decodeToYuv)
+{
+ Vector<uint8_t> jpegBuffer;
+ unsigned int jpegBufferSize = 0u;
- // Pull the compressed JPEG image bytes out of a file and into memory:
- if(fread(jpegBufferPtr, 1, jpegBufferSize, fp) != jpegBufferSize)
+ if(!LoadJpegFile(input, jpegBuffer, jpegBufferSize))
{
- DALI_LOG_ERROR("Error on image file read.\n");
+ DALI_LOG_ERROR("LoadJpegFile failed\n");
return false;
}
- if(fseek(fp, 0, SEEK_SET))
- {
- DALI_LOG_ERROR("Error seeking to start of file\n");
- }
-
auto jpeg = MakeJpegDecompressor();
-
- if(!jpeg)
+ if(DALI_UNLIKELY(!jpeg))
{
DALI_LOG_ERROR("%s\n", tjGetErrorStr());
return false;
}
- auto transform = JpegTransform::NONE;
+ uint8_t* const jpegBufferPtr = jpegBuffer.Begin();
+ auto transform = JpegTransform::NONE;
// extract exif data
auto exifData = MakeExifDataFromData(jpegBufferPtr, jpegBufferSize);
transform = ConvertExifOrientation(exifData.get());
}
- std::unique_ptr<Property::Map> exifMap;
- exifMap.reset(new Property::Map());
-
- for(auto k = 0u; k < EXIF_IFD_COUNT; ++k)
- {
- auto content = exifData->ifd[k];
- for(auto i = 0u; i < content->count; ++i)
- {
- auto&& tag = content->entries[i];
- const char* shortName = exif_tag_get_name_in_ifd(tag->tag, static_cast<ExifIfd>(k));
- if(shortName)
- {
- AddExifFieldPropertyMap(*exifMap, *tag, static_cast<ExifIfd>(k));
- }
- }
- }
-
// Push jpeg data in memory buffer through TurboJPEG decoder to make a raw pixel array:
int chrominanceSubsampling = -1;
int preXformImageWidth = 0, preXformImageHeight = 0;
-
- // In Ubuntu, the turbojpeg version is not correct. so build error occurs.
- // Temporarily separate Ubuntu and other profiles.
-#ifndef DALI_PROFILE_UBUNTU
int jpegColorspace = -1;
+
if(tjDecompressHeader3(jpeg.get(), jpegBufferPtr, jpegBufferSize, &preXformImageWidth, &preXformImageHeight, &chrominanceSubsampling, &jpegColorspace) == -1)
{
DALI_LOG_ERROR("%s\n", tjGetErrorStr());
// Do not set width and height to 0 or return early as this sometimes fails only on determining subsampling type.
}
-#else
- if(tjDecompressHeader2(jpeg.get(), jpegBufferPtr, jpegBufferSize, &preXformImageWidth, &preXformImageHeight, &chrominanceSubsampling) == -1)
- {
- DALI_LOG_ERROR("%s\n", tjGetErrorStr());
- // Do not set width and height to 0 or return early as this sometimes fails only on determining subsampling type.
- }
-#endif
- if(preXformImageWidth == 0 || preXformImageHeight == 0)
+ if(DALI_UNLIKELY(preXformImageWidth == 0 || preXformImageHeight == 0))
{
DALI_LOG_ERROR("Invalid Image!\n");
return false;
TransformSize(requiredWidth, requiredHeight, input.scalingParameters.scalingMode, input.scalingParameters.samplingMode, transform, scaledPreXformWidth, scaledPreXformHeight, scaledPostXformWidth, scaledPostXformHeight);
- // Colorspace conversion options
- TJPF pixelLibJpegType = TJPF_RGB;
- Pixel::Format pixelFormat = Pixel::RGB888;
-#ifndef DALI_PROFILE_UBUNTU
- switch(jpegColorspace)
+ bool result = false;
+
+ // Now we support YUV420 only
+ if(decodeToYuv && chrominanceSubsampling == TJSAMP_420 && transform == JpegTransform::NONE)
{
- case TJCS_RGB:
- // YCbCr is not an absolute colorspace but rather a mathematical transformation of RGB designed solely for storage and transmission.
- // YCbCr images must be converted to RGB before they can actually be displayed.
- case TJCS_YCbCr:
- {
- pixelLibJpegType = TJPF_RGB;
- pixelFormat = Pixel::RGB888;
- break;
- }
- case TJCS_GRAY:
- {
- pixelLibJpegType = TJPF_GRAY;
- pixelFormat = Pixel::L8;
- break;
- }
- case TJCS_CMYK:
- case TJCS_YCCK:
- {
- pixelLibJpegType = TJPF_CMYK;
- pixelFormat = Pixel::RGBA8888;
- break;
- }
- default:
+ unsigned char* planes[3];
+
+ // Allocate buffers for each plane and decompress the jpeg buffer into the buffers
+ for(int i = 0; i < 3; i++)
{
- pixelLibJpegType = TJPF_RGB;
- pixelFormat = Pixel::RGB888;
- break;
- }
- }
-#endif
- // Allocate a bitmap and decompress the jpeg buffer into its pixel buffer:
- bitmap = Dali::Devel::PixelBuffer::New(scaledPostXformWidth, scaledPostXformHeight, pixelFormat);
+ auto planeSize = tjPlaneSizeYUV(i, scaledPostXformWidth, 0, scaledPostXformHeight, chrominanceSubsampling);
- // set metadata
- GetImplementation(bitmap).SetMetadata(std::move(exifMap));
+ unsigned char* buffer = static_cast<unsigned char*>(malloc(planeSize));
+ if(!buffer)
+ {
+ DALI_LOG_ERROR("Buffer allocation is failed [%d]\n", planeSize);
+ pixelBuffers.clear();
+ return false;
+ }
- auto bitmapPixelBuffer = bitmap.GetBuffer();
+ int width, height, planeWidth;
+ Pixel::Format pixelFormat = Pixel::RGB888;
- if(tjDecompress2(jpeg.get(), jpegBufferPtr, jpegBufferSize, reinterpret_cast<unsigned char*>(bitmapPixelBuffer), scaledPreXformWidth, 0, scaledPreXformHeight, pixelLibJpegType, flags) == -1)
- {
- std::string errorString = tjGetErrorStr();
+ if(i == 0)
+ {
+ // luminance plane
+ width = scaledPostXformWidth;
+ height = scaledPostXformHeight;
+ planeWidth = tjPlaneWidth(i, scaledPostXformWidth, chrominanceSubsampling);
+ pixelFormat = Pixel::L8;
+ }
+ else
+ {
+ // chrominance plane
+ width = tjPlaneWidth(i, scaledPostXformWidth, chrominanceSubsampling);
+ height = tjPlaneHeight(i, scaledPostXformHeight, chrominanceSubsampling);
+ planeWidth = width;
+ pixelFormat = (i == 1 ? Pixel::CHROMINANCE_U : Pixel::CHROMINANCE_V);
+ }
- if(IsJpegErrorFatal(errorString))
- {
- DALI_LOG_ERROR("%s\n", errorString.c_str());
- return false;
+ Internal::Adaptor::PixelBufferPtr internal = Internal::Adaptor::PixelBuffer::New(buffer, planeSize, width, height, planeWidth, pixelFormat);
+ Dali::Devel::PixelBuffer bitmap = Devel::PixelBuffer(internal.Get());
+ planes[i] = buffer;
+ pixelBuffers.push_back(bitmap);
}
- else
+
+ const int flags = 0;
+
+ int decodeResult = tjDecompressToYUVPlanes(jpeg.get(), jpegBufferPtr, jpegBufferSize, reinterpret_cast<unsigned char**>(&planes), scaledPostXformWidth, nullptr, scaledPostXformHeight, flags);
+ if(decodeResult == -1 && IsJpegDecodingFailed())
{
- DALI_LOG_WARNING("%s\n", errorString.c_str());
+ pixelBuffers.clear();
+ return false;
}
+
+ result = true;
}
+ else
+ {
+ // Colorspace conversion options
+ TJPF pixelLibJpegType = TJPF_RGB;
+ Pixel::Format pixelFormat = Pixel::RGB888;
- const unsigned int bufferWidth = GetTextureDimension(scaledPreXformWidth);
- const unsigned int bufferHeight = GetTextureDimension(scaledPreXformHeight);
+ GetJpegPixelFormat(jpegColorspace, pixelLibJpegType, pixelFormat);
- bool result = false;
- switch(transform)
- {
- case JpegTransform::NONE:
- {
- result = true;
- break;
- }
- // 3 orientation changes for a camera held perpendicular to the ground or upside-down:
- case JpegTransform::ROTATE_180:
- {
- static auto rotate180Functions = TransformFunctionArray{
- &Rotate180<1>,
- &Rotate180<3>,
- &Rotate180<4>,
- };
- result = Transform(rotate180Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
- break;
- }
- case JpegTransform::ROTATE_270:
- {
- static auto rotate270Functions = TransformFunctionArray{
- &Rotate270<1>,
- &Rotate270<3>,
- &Rotate270<4>,
- };
- result = Transform(rotate270Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
- break;
- }
- case JpegTransform::ROTATE_90:
- {
- static auto rotate90Functions = TransformFunctionArray{
- &Rotate90<1>,
- &Rotate90<3>,
- &Rotate90<4>,
- };
- result = Transform(rotate90Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
- break;
- }
- case JpegTransform::FLIP_VERTICAL:
- {
- static auto flipVerticalFunctions = TransformFunctionArray{
- &FlipVertical<1>,
- &FlipVertical<3>,
- &FlipVertical<4>,
- };
- result = Transform(flipVerticalFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
- break;
- }
- // Less-common orientation changes, since they don't correspond to a camera's physical orientation:
- case JpegTransform::FLIP_HORIZONTAL:
- {
- static auto flipHorizontalFunctions = TransformFunctionArray{
- &FlipHorizontal<1>,
- &FlipHorizontal<3>,
- &FlipHorizontal<4>,
- };
- result = Transform(flipHorizontalFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
- break;
- }
- case JpegTransform::TRANSPOSE:
- {
- static auto transposeFunctions = TransformFunctionArray{
- &Transpose<1>,
- &Transpose<3>,
- &Transpose<4>,
- };
- result = Transform(transposeFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
- break;
- }
- case JpegTransform::TRANSVERSE:
+ // Allocate a bitmap and decompress the jpeg buffer into its pixel buffer:
+ Dali::Devel::PixelBuffer bitmap = Dali::Devel::PixelBuffer::New(scaledPostXformWidth, scaledPostXformHeight, pixelFormat);
+
+ // Set metadata
+ if(DALI_LIKELY(exifData))
{
- static auto transverseFunctions = TransformFunctionArray{
- &Transverse<1>,
- &Transverse<3>,
- &Transverse<4>,
- };
- result = Transform(transverseFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
- break;
+ std::unique_ptr<Property::Map> exifMap = std::make_unique<Property::Map>();
+
+ for(auto k = 0u; k < EXIF_IFD_COUNT; ++k)
+ {
+ auto content = exifData->ifd[k];
+ for(auto i = 0u; i < content->count; ++i)
+ {
+ auto&& tag = content->entries[i];
+ const char* shortName = exif_tag_get_name_in_ifd(tag->tag, static_cast<ExifIfd>(k));
+ if(shortName)
+ {
+ AddExifFieldPropertyMap(*exifMap, *tag, static_cast<ExifIfd>(k));
+ }
+ }
+ }
+
+ GetImplementation(bitmap).SetMetadata(std::move(exifMap));
}
- default:
+
+ auto bitmapPixelBuffer = bitmap.GetBuffer();
+ const int flags = 0;
+
+ int decodeResult = tjDecompress2(jpeg.get(), jpegBufferPtr, jpegBufferSize, reinterpret_cast<unsigned char*>(bitmapPixelBuffer), scaledPreXformWidth, 0, scaledPreXformHeight, pixelLibJpegType, flags);
+ if(decodeResult == -1 && IsJpegDecodingFailed())
{
- DALI_LOG_ERROR("Unsupported JPEG Orientation transformation: %x.\n", transform);
- break;
+ return false;
}
+ pixelBuffers.push_back(bitmap);
+
+ // Transform bitmap
+ result = TransformBitmap(scaledPreXformWidth, scaledPreXformHeight, transform, bitmapPixelBuffer, pixelFormat);
}
return result;
// Initialise a JPEG codec:
{
auto jpeg = MakeJpegCompressor();
- if(!jpeg)
+ if(DALI_UNLIKELY(!jpeg))
{
DALI_LOG_ERROR("JPEG Compressor init failed: %s\n", tjGetErrorStr());
return false;
unsigned long dstBufferSize = 0;
const int flags = 0;
- if(tjCompress2(jpeg.get(),
- const_cast<unsigned char*>(pixelBuffer),
- width,
- 0,
- height,
- jpegPixelFormat,
- SetPointer(dstBuffer),
- &dstBufferSize,
- TJSAMP_444,
- quality,
- flags))
+ if(DALI_UNLIKELY(tjCompress2(jpeg.get(),
+ const_cast<unsigned char*>(pixelBuffer),
+ width,
+ 0,
+ height,
+ jpegPixelFormat,
+ SetPointer(dstBuffer),
+ &dstBufferSize,
+ TJSAMP_444,
+ quality,
+ flags)))
{
DALI_LOG_ERROR("JPEG Compression failed: %s\n", tjGetErrorStr());
return false;
int numFactors = 0;
tjscalingfactor* factors = tjGetScalingFactors(&numFactors);
- if(factors == NULL)
+ if(DALI_UNLIKELY(factors == NULL))
{
DALI_LOG_WARNING("TurboJpeg tjGetScalingFactors error!\n");
success = false;
auto exifData = MakeNullExifData();
unsigned char dataBuffer[1024];
- if(fseek(fp, 0, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking to start of file\n");
}
unsigned int headerHeight;
success = LoadJpegHeader(fp, headerWidth, headerHeight);
- if(success)
+ if(DALI_LIKELY(success))
{
auto transform = JpegTransform::NONE;
#define DALI_TIZEN_PLATFORM_LOADER_JPEG_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
bool LoadBitmapFromJpeg(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap);
/**
+ * Loads the image planes from an JPEG file. This function checks the header first
+ * and if it is not a JPEG file, then it returns straight away.
+ * @param[in] input Information about the input image (including file pointer)
+ * @param[out] pixelBuffers The buffer list where the each plane will be stored
+ * @return true if file decoded successfully, false otherwise
+ * @note If the image file doesn't support to load planes, this method returns one RGB bitmap image.
+ */
+bool LoadPlanesFromJpeg(const Dali::ImageLoader::Input& input, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers);
+
+/**
* Loads the header of a JPEG file and fills in the width and height appropriately.
* If the width and height are set on entry, it will set the width and height
* to the closest scaled size (exactly as will be loaded by LoadBitmapFromJpeg with the same
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
const unsigned int readLength = sizeof(KtxFileHeader);
// Load the information directly into our structure
- if(fread(&header, 1, readLength, filePointer) != readLength)
+ if(DALI_UNLIKELY(fread(&header, 1, readLength, filePointer) != readLength))
{
return false;
}
bool LoadKtxHeader(FILE* const fp, unsigned int& width, unsigned int& height, KtxFileHeader& fileHeader)
{
// Pull the bytes of the file header in as a block:
- if(!ReadHeader(fp, fileHeader))
+ if(DALI_UNLIKELY(!ReadHeader(fp, fileHeader)))
{
return false;
}
width = fileHeader.pixelWidth;
height = fileHeader.pixelHeight;
- if(width > MAX_TEXTURE_DIMENSION || height > MAX_TEXTURE_DIMENSION)
+ if(DALI_UNLIKELY(width > MAX_TEXTURE_DIMENSION || height > MAX_TEXTURE_DIMENSION))
{
return false;
}
static_assert(sizeof(uint32_t) == 4);
FILE* const fp = input.file;
- if(fp == NULL)
+ if(DALI_UNLIKELY(fp == NULL))
{
DALI_LOG_ERROR("Null file handle passed to KTX compressed bitmap file loader.\n");
return false;
// Load the header info
unsigned int width, height;
- if(!LoadKtxHeader(fp, width, height, fileHeader))
+ if(DALI_UNLIKELY(!LoadKtxHeader(fp, width, height, fileHeader)))
{
return false;
}
// Skip the key-values:
const long int imageSizeOffset = sizeof(KtxFileHeader) + fileHeader.bytesOfKeyValueData;
- if(fseek(fp, imageSizeOffset, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, imageSizeOffset, SEEK_SET)))
{
DALI_LOG_ERROR("Seek past key/vals in KTX compressed bitmap file failed.\n");
return false;
// Load the size of the image data:
uint32_t imageByteCount = 0;
- if(fread(&imageByteCount, 1, 4, fp) != 4)
+ if(DALI_UNLIKELY(fread(&imageByteCount, 1, 4, fp) != 4))
{
DALI_LOG_ERROR("Read of image size failed.\n");
return false;
}
// Sanity-check the image size:
- if(imageByteCount > MAX_IMAGE_DATA_SIZE ||
- // A compressed texture should certainly be less than 2 bytes per texel:
- imageByteCount > width * height * 2)
+ if(DALI_UNLIKELY(imageByteCount > MAX_IMAGE_DATA_SIZE ||
+ // A compressed texture should certainly be less than 2 bytes per texel:
+ imageByteCount > width * height * 2))
{
DALI_LOG_ERROR("KTX file with too-large image-data field.\n");
return false;
Pixel::Format pixelFormat;
const bool pixelFormatKnown = ConvertPixelFormat(fileHeader.glInternalFormat, pixelFormat);
- if(!pixelFormatKnown)
+ if(DALI_UNLIKELY(!pixelFormatKnown))
{
DALI_LOG_ERROR("No internal pixel format supported for KTX file pixel format.\n");
return false;
pixels = bitmap.GetBuffer();
}
- if(!pixels)
+ if(DALI_UNLIKELY(!pixels))
{
DALI_LOG_ERROR("Unable to reserve a pixel buffer to load the requested bitmap into.\n");
return false;
}
const size_t bytesRead = fread(pixels, 1, imageByteCount, fp);
- if(bytesRead != imageByteCount)
+ if(DALI_UNLIKELY(bytesRead != imageByteCount))
{
DALI_LOG_ERROR("Read of image pixel data failed.\n");
return false;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// Check header to see if it is a PNG file
size_t size = fread(header, 1, 8, fp);
- if(size != 8)
+ if(DALI_UNLIKELY(size != 8))
{
DALI_LOG_ERROR("fread failed\n");
return false;
}
- if(png_sig_cmp(header, 0, 8))
+ if(DALI_UNLIKELY(png_sig_cmp(header, 0, 8)))
{
DALI_LOG_ERROR("png_sig_cmp failed\n");
return false;
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
- if(!png)
+ if(DALI_UNLIKELY(!png))
{
DALI_LOG_ERROR("Can't create PNG read structure\n");
return false;
}
info = png_create_info_struct(png);
- if(!info)
+ if(DALI_UNLIKELY(!info))
{
DALI_LOG_ERROR("png_create_info_struct failed\n");
return false;
png_set_expand(png);
- if(setjmp(png_jmpbuf(png)))
+ if(DALI_UNLIKELY(setjmp(png_jmpbuf(png))))
{
DALI_LOG_ERROR("error during png_init_io\n");
return false;
bool valid = false;
// Load info from the header
- if(!LoadPngHeader(input.file, width, height, png, info))
+ if(DALI_UNLIKELY(!LoadPngHeader(input.file, width, height, png, info)))
{
return false;
}
}
}
- if(!valid)
+ if(DALI_UNLIKELY(!valid))
{
DALI_LOG_ERROR("Unsupported png format\n");
return false;
png_read_update_info(png, info);
- if(setjmp(png_jmpbuf(png)))
+ if(DALI_UNLIKELY(setjmp(png_jmpbuf(png))))
{
DALI_LOG_ERROR("error during png_read_image\n");
return false;
break;
}
}
-
- // decode the whole image into bitmap buffer
- auto pixels = (bitmap = Dali::Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat)).GetBuffer();
-
- DALI_ASSERT_DEBUG(pixels);
rows = reinterpret_cast<png_bytep*>(malloc(sizeof(png_bytep) * height));
- if(!rows)
+ if(DALI_UNLIKELY(!rows))
{
DALI_LOG_ERROR("malloc is failed\n");
return false;
}
+ // decode the whole image into bitmap buffer
+ auto pixels = (bitmap = Dali::Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat)).GetBuffer();
+
+ DALI_ASSERT_DEBUG(pixels);
+
for(y = 0; y < height; y++)
{
rows[y] = pixels + y * stride;
if(encoded_img)
{
const Vector<unsigned char>::SizeType bufferSize = encoded_img->Count();
- encoded_img->Resize(bufferSize + length); //< Can throw OOM.
+ encoded_img->ResizeUninitialized(bufferSize + length); //< Can throw OOM.
unsigned char* const bufferBack = encoded_img->Begin() + bufferSize;
memcpy(bufferBack, data, length);
}
Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_LOADER_WBMP");
#endif
+// TODO : We need to determine it in dali-common.h or something else. Currently, we set this value in code level.
+#define DALI_BYTE_ORDER_BIG_ENDIAN 0
+
#define IMG_MAX_SIZE 65536
#define IMG_TOO_BIG(w, h) \
((1ULL << (29)) - 2048))
//extract multiple bytes integer , and saved in *data
-int extractMultiByteInteger(unsigned int* data, void* map, size_t length, size_t* position)
+int extractMultiByteInteger(unsigned int* data, const std::uint8_t* const& map, size_t length, size_t* position)
{
// the header field contains an image type indentifier of multi-byte length(TypeField), an octet of general header info(FixHeaderField)
//, a multi-byte width field(Width) and a multi-byte height field(Height) and so on.
// for general width and height, if(buf & 0x80) == 0, then the next byte does not need to fetch again
// first step, readBufCount = 1 , read int(4 bytes) to buf, if buf & 0x80 !=0, the buf need to continue to fetch
// second step, readBufCount = 2, read next( 4 bytes) to buf, if buf & 0x80 == 0, then assigned the buf to target
- if((readBufCount++) == 4)
+ if(DALI_UNLIKELY((readBufCount++) == 4))
{
return -1;
}
- if(*position > length)
+ if(DALI_UNLIKELY(*position > length))
{
return -1;
}
- buf = reinterpret_cast<unsigned char*>(map)[(*position)++];
+ buf = map[(*position)++];
targetMultiByteInteger = (targetMultiByteInteger << 7) | (buf & 0x7f);
if((buf & 0x80) == 0)
return 0;
}
+// Calculate 4bit integer into 4byte integer
+constexpr std::uint32_t Calculate4BitTo4Byte(const std::uint8_t& input)
+{
+ std::uint32_t output = 0;
+#if DALI_BYTE_ORDER_BIG_ENDIAN
+ output |= static_cast<std::uint32_t>(input & 0x08) << 21;
+ output |= static_cast<std::uint32_t>(input & 0x04) << 14;
+ output |= static_cast<std::uint32_t>(input & 0x02) << 7;
+ output |= static_cast<std::uint32_t>(input & 0x01);
+#else
+ output |= static_cast<std::uint32_t>(input & 0x08) >> 3;
+ output |= static_cast<std::uint32_t>(input & 0x04) << 6;
+ output |= static_cast<std::uint32_t>(input & 0x02) << 15;
+ output |= static_cast<std::uint32_t>(input & 0x01) << 24;
+#endif
+ return output * 0xff;
+}
+
+/**
+ * @brief Calculation result bit-->byte table in compile.
+ * Required memory = 16 * 4byte = 64byte
+ */
+// clang-format off
+constexpr std::uint32_t cachedCalculation4BitTo4ByteTable[16] = {
+ Calculate4BitTo4Byte(0x00), Calculate4BitTo4Byte(0x01), Calculate4BitTo4Byte(0x02), Calculate4BitTo4Byte(0x03),
+ Calculate4BitTo4Byte(0x04), Calculate4BitTo4Byte(0x05), Calculate4BitTo4Byte(0x06), Calculate4BitTo4Byte(0x07),
+ Calculate4BitTo4Byte(0x08), Calculate4BitTo4Byte(0x09), Calculate4BitTo4Byte(0x0a), Calculate4BitTo4Byte(0x0b),
+ Calculate4BitTo4Byte(0x0c), Calculate4BitTo4Byte(0x0d), Calculate4BitTo4Byte(0x0e), Calculate4BitTo4Byte(0x0f)};
+// clang-format on
} // end unnamed namespace
bool LoadBitmapFromWbmp(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
{
FILE* const fp = input.file;
- if(fp == NULL)
+ if(DALI_UNLIKELY(fp == NULL))
{
DALI_LOG_ERROR("Error loading bitmap\n");
return false;
}
Dali::Vector<unsigned char> map;
- Dali::Vector<unsigned char> surface; //unsigned int
size_t position = 0;
- unsigned int w, h;
- unsigned int type;
- unsigned int line_length;
- unsigned char* line = NULL;
- unsigned int cur = 0, x, y;
+ std::uint32_t w, h;
+ std::uint32_t type;
+ std::uint32_t lineByteLength;
- if(fseek(fp, 0, SEEK_END))
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END)))
{
DALI_LOG_ERROR("Error seeking WBMP data\n");
return false;
fsize = static_cast<unsigned int>(positionIndicator);
}
- if(0u == fsize)
+ if(DALI_UNLIKELY(0u == fsize))
{
DALI_LOG_ERROR("Error: filesize is 0!\n");
return false;
}
- if(fseek(fp, 0, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking WBMP data\n");
return false;
}
- if(fsize <= 4)
+ if(DALI_UNLIKELY(fsize <= 4))
{
DALI_LOG_ERROR("Error: WBMP Raw Data Not Found!\n");
return false;
}
- if(fsize > 4096 * 4096 * 4)
+ if(DALI_UNLIKELY(fsize > 4096 * 4096 * 4))
{
DALI_LOG_ERROR("Error: WBMP size is too large!\n");
return false;
}
map.ResizeUninitialized(fsize);
- if(fread(&map[0], 1, fsize, fp) != fsize)
+ if(DALI_UNLIKELY(fread(&map[0], 1, fsize, fp) != fsize))
{
DALI_LOG_WARNING("image file read opeation error!\n");
return false;
}
- if(extractMultiByteInteger(&type, &map[0], fsize, &position) < 0)
+ const std::uint8_t* const inputBufferPtr = &map[0];
+
+ if(DALI_UNLIKELY(extractMultiByteInteger(&type, inputBufferPtr, fsize, &position) < 0))
{
return false;
}
position++; /* skipping one byte */
- if(extractMultiByteInteger(&w, &map[0], fsize, &position) < 0)
+ if(DALI_UNLIKELY(extractMultiByteInteger(&w, inputBufferPtr, fsize, &position) < 0))
{
return false;
}
- if(extractMultiByteInteger(&h, &map[0], fsize, &position) < 0)
+ if(DALI_UNLIKELY(extractMultiByteInteger(&h, inputBufferPtr, fsize, &position) < 0))
{
return false;
}
- if(type != 0)
+ if(DALI_UNLIKELY(type != 0))
{
DALI_LOG_ERROR("Unknown Format!\n");
return false;
}
- if((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE))
+ if(DALI_UNLIKELY((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE)))
{
return false;
}
- surface.ResizeUninitialized(w * h); //(w * h * 4);
- memset(&surface[0], 0, w * h); // w * h * 4
+ lineByteLength = (w + 7) >> 3;
+ // fsize was wrong! Load failed.
+ if(DALI_UNLIKELY(position + h * lineByteLength > fsize))
+ {
+ DALI_LOG_ERROR("Pixel infomation is bigger than file size! (%u + %u * %u > %u)\n", static_cast<std::uint32_t>(position), h, lineByteLength, fsize);
+ return false;
+ }
- line_length = (w + 7) >> 3;
- for(y = 0; y < h; y++)
+ // w >= 1 and h >= 1. So we can assume that outputPixels is not null.
+ auto outputPixels = (bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::L8)).GetBuffer();
+
+ /**
+ * @code
+ * std::uint8_t* line = NULL;
+ * std::uint32_t cur = 0, x, y;
+ * for(y = 0; y < h; y++)
+ * {
+ * line = &map[0] + position;
+ * position += lineByteLength;
+ * for(x = 0; x < w; x++)
+ * {
+ * int idx = x >> 3;
+ * int offset = 1 << (0x07 - (x & 0x07));
+ * if(line[idx] & offset)
+ * {
+ * outputPixels[cur] = 0xff; //0xffffffff;
+ * }
+ * else
+ * {
+ * outputPixels[cur] = 0x00; //0xff000000;
+ * }
+ * cur++;
+ * }
+ * }
+ * @endcode
+ */
+
+ const std::uint8_t* inputPixels = inputBufferPtr + position;
+ const std::uint32_t lineBitLengthWithoutPadding = (w >> 3) << 3;
+
+ for(std::uint32_t y = 0; y < h; ++y)
{
- if(position + line_length > fsize)
- {
- return false;
- }
- line = &map[0] + position;
- position += line_length;
- for(x = 0; x < w; x++)
+ std::uint32_t x = 0;
+ if((reinterpret_cast<std::ptrdiff_t>(outputPixels) & (sizeof(std::uint32_t) - 1)) == 0)
{
- int idx = x >> 3;
- int offset = 1 << (0x07 - (x & 0x07));
- if(line[idx] & offset)
+ for(; x < lineBitLengthWithoutPadding; x += 8)
{
- surface[cur] = 0xff; //0xffffffff;
+ // memset whole 8 bits
+ // outputPixels filled 4 bytes in one operation.
+ // cachedCalculation4BitTo4ByteTable calculated in compile-time.
+ *(reinterpret_cast<std::uint32_t*>(outputPixels + 0)) = cachedCalculation4BitTo4ByteTable[((*inputPixels) >> 4) & 0x0f];
+ *(reinterpret_cast<std::uint32_t*>(outputPixels + 4)) = cachedCalculation4BitTo4ByteTable[(*inputPixels) & 0x0f];
+ outputPixels += 8;
+ ++inputPixels;
}
- else
+ }
+ {
+ // memset linePadding bits naive.
+ for(; x < w; ++x)
{
- surface[cur] = 0x00; //0xff000000;
+ const std::uint8_t offset = (0x07 - (x & 0x07));
+ *outputPixels = ((*inputPixels) >> offset) & 1 ? 0xff : 0x00;
+ ++outputPixels;
+ if(offset == 0)
+ {
+ ++inputPixels;
+ }
}
- cur++;
}
}
- auto pixels = (bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::L8)).GetBuffer();
-
- memcpy(pixels, &surface[0], w * h); //w * h * 4
return true;
}
bool LoadWbmpHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
{
FILE* const fp = input.file;
- if(fp == NULL)
+ if(DALI_UNLIKELY(fp == NULL))
{
DALI_LOG_ERROR("Error loading bitmap\n");
return false;
unsigned int w, h;
unsigned int type;
- if(fseek(fp, 0, SEEK_END))
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END)))
{
DALI_LOG_ERROR("Error seeking WBMP data\n");
return false;
fsize = static_cast<unsigned int>(positionIndicator);
}
- if(0u == fsize)
+ if(DALI_UNLIKELY(0u == fsize))
{
return false;
}
- if(fseek(fp, 0, SEEK_SET))
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
{
DALI_LOG_ERROR("Error seeking WBMP data\n");
return false;
}
- if(fsize <= 4)
+ if(DALI_UNLIKELY(fsize <= 4))
{
DALI_LOG_ERROR("Error: WBMP Raw Data Not Found!\n");
return false;
headerSize = std::min(headerSize, fsize);
map.ResizeUninitialized(headerSize);
- if(fread(&map[0], 1, headerSize, fp) != headerSize)
+ if(DALI_UNLIKELY(fread(&map[0], 1, headerSize, fp) != headerSize))
{
DALI_LOG_WARNING("image file read opeation error!\n");
return false;
}
- if(extractMultiByteInteger(&type, &map[0], headerSize, &position) < 0)
+ const std::uint8_t* const inputBufferPtr = &map[0];
+
+ if(DALI_UNLIKELY(extractMultiByteInteger(&type, inputBufferPtr, headerSize, &position) < 0))
{
DALI_LOG_ERROR("Error: unable to read type!\n");
return false;
}
position++; /* skipping one byte */
- if(type != 0)
+ if(DALI_UNLIKELY(type != 0))
{
DALI_LOG_ERROR("Error: unknown format!\n");
return false;
}
- if(extractMultiByteInteger(&w, &map[0], headerSize, &position) < 0)
+ if(DALI_UNLIKELY(extractMultiByteInteger(&w, inputBufferPtr, headerSize, &position) < 0))
{
DALI_LOG_ERROR("Error: can not read width!\n");
return false;
}
- if(extractMultiByteInteger(&h, &map[0], headerSize, &position) < 0)
+ if(DALI_UNLIKELY(extractMultiByteInteger(&h, inputBufferPtr, headerSize, &position) < 0))
{
DALI_LOG_ERROR("Error: can not read height!\n");
return false;
}
- if((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE))
+ if(DALI_UNLIKELY((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE)))
{
DALI_LOG_ERROR("Error: file size is not supported!\n");
return false;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
*/
+// HEADER
#include <dali/internal/imaging/common/loader-webp.h>
-// EXTERNAL INCLUDES
-#ifdef DALI_WEBP_AVAILABLE
-#include <webp/decode.h>
-#include <webp/demux.h>
-
-#if WEBP_DEMUX_ABI_VERSION > 0x0101
-#define DALI_ANIMATED_WEBP_ENABLED 1
-#endif
-#endif
+// INTERNAL INCLUDES
#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
-#include <dali/integration-api/debug.h>
-#include <cstring>
-#include <memory>
-
-typedef unsigned char WebPByteType;
+#include <dali/internal/imaging/common/webp-loading.h>
namespace Dali
{
namespace TizenPlatform
{
-#ifdef DALI_ANIMATED_WEBP_ENABLED
-bool ReadWebPInformation(FILE* const fp, WebPData& webPData)
-{
- if(fp == NULL)
- {
- return false;
- }
-
- if(fseek(fp, 0, SEEK_END) <= -1)
- {
- return false;
- }
- WebPDataInit(&webPData);
- webPData.size = ftell(fp);
-
- if((!fseek(fp, 0, SEEK_SET)))
- {
- unsigned char* WebPDataBuffer;
- WebPDataBuffer = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * webPData.size));
- webPData.size = fread(WebPDataBuffer, sizeof(WebPByteType), webPData.size, fp);
- webPData.bytes = WebPDataBuffer;
- }
- else
- {
- return false;
- }
- return true;
-}
-
-void ReleaseResource(WebPData& webPData, WebPAnimDecoder* webPAnimDecoder)
+namespace
{
- free((void*)webPData.bytes);
- webPData.bytes = nullptr;
- WebPDataInit(&webPData);
- if(webPAnimDecoder)
- {
- WebPAnimDecoderDelete(webPAnimDecoder);
- }
+constexpr uint32_t FIRST_FRAME_INDEX = 0u;
}
-#endif
-
bool LoadWebpHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
{
- FILE* const fp = input.file;
- if(fp == NULL)
- {
- return false;
- }
-
- if(fseek(fp, 0, SEEK_END) <= -1)
- {
- return false;
- }
-
- // If the image is non-animated webp
-#ifdef DALI_WEBP_AVAILABLE
- size_t webPSize = ftell(fp);
- if((!fseek(fp, 0, SEEK_SET)))
- {
- std::vector<uint8_t> encodedImage;
- encodedImage.resize(webPSize, 0);
- size_t readCount = fread(&encodedImage[0], sizeof(uint8_t), encodedImage.size(), fp);
- if(readCount != encodedImage.size())
- {
- return false;
- }
- int32_t imageWidth, imageHeight;
- if(WebPGetInfo(&encodedImage[0], encodedImage.size(), &imageWidth, &imageHeight))
- {
- width = static_cast<uint32_t>(imageWidth);
- height = static_cast<uint32_t>(imageHeight);
- return true;
- }
- }
-#endif
-
- // If the image is animated webp
-#ifdef DALI_ANIMATED_WEBP_ENABLED
- WebPData webPData;
- WebPAnimDecoder* webPAnimDecoder = nullptr;
- WebPAnimInfo webPAnimInfo;
- if(ReadWebPInformation(fp, webPData))
+ FILE* const fp = input.file;
+ Dali::AnimatedImageLoading webPLoading = Dali::AnimatedImageLoading(Dali::Internal::Adaptor::WebPLoading::New(fp).Get());
+ if(webPLoading)
{
- WebPAnimDecoderOptions webPAnimDecoderOptions;
- WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
- webPAnimDecoderOptions.color_mode = MODE_RGBA;
- webPAnimDecoder = WebPAnimDecoderNew(&webPData, &webPAnimDecoderOptions);
- if(webPAnimDecoder != nullptr)
+ ImageDimensions imageSize = webPLoading.GetImageSize();
+ if(webPLoading.HasLoadingSucceeded())
{
- WebPAnimDecoderGetInfo(webPAnimDecoder, &webPAnimInfo);
- width = webPAnimInfo.canvas_width;
- height = webPAnimInfo.canvas_height;
- ReleaseResource(webPData, webPAnimDecoder);
+ width = imageSize.GetWidth();
+ height = imageSize.GetHeight();
return true;
}
}
- ReleaseResource(webPData, webPAnimDecoder);
-#endif
- DALI_LOG_ERROR("WebP file open failed.\n");
return false;
}
bool LoadBitmapFromWebp(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
{
- FILE* const fp = input.file;
- if(fp == NULL)
- {
- return false;
- }
-
- if(fseek(fp, 0, SEEK_END) <= -1)
- {
- return false;
- }
-
- // If the image is non-animated webp
-#ifdef DALI_WEBP_AVAILABLE
- size_t webPSize = ftell(fp);
- if((!fseek(fp, 0, SEEK_SET)))
- {
- std::vector<uint8_t> encodedImage;
- encodedImage.resize(webPSize, 0);
- size_t readCount = fread(&encodedImage[0], sizeof(uint8_t), encodedImage.size(), fp);
- if(readCount != encodedImage.size())
- {
- DALI_LOG_ERROR("WebP image loading failed.\n");
- return false;
- }
-
- int32_t width, height;
- if(!WebPGetInfo(&encodedImage[0], encodedImage.size(), &width, &height))
- {
- DALI_LOG_ERROR("Cannot retrieve WebP image size information.\n");
- return false;
- }
-
- WebPBitstreamFeatures features;
- if(VP8_STATUS_NOT_ENOUGH_DATA == WebPGetFeatures(&encodedImage[0], encodedImage.size(), &features))
- {
- DALI_LOG_ERROR("Cannot retrieve WebP image features.\n");
- return false;
- }
-
- uint32_t channelNumber = (features.has_alpha) ? 4 : 3;
- Pixel::Format pixelFormat = (channelNumber == 4) ? Pixel::RGBA8888 : Pixel::RGB888;
- bitmap = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
- uint8_t* frameBuffer = nullptr;
- if(channelNumber == 4)
- {
- frameBuffer = WebPDecodeRGBA(&encodedImage[0], encodedImage.size(), &width, &height);
- }
- else
- {
- frameBuffer = WebPDecodeRGB(&encodedImage[0], encodedImage.size(), &width, &height);
- }
-
- if(frameBuffer != nullptr)
- {
- const int32_t bufferSize = width * height * sizeof(uint8_t) * channelNumber;
- memcpy(bitmap.GetBuffer(), frameBuffer, bufferSize);
- free((void*)frameBuffer);
- return true;
- }
- }
-#endif
-
- // If the image is animated webp
-#ifdef DALI_ANIMATED_WEBP_ENABLED
- WebPData webPData;
- WebPAnimDecoder* webPAnimDecoder = nullptr;
- WebPAnimInfo webPAnimInfo;
- if(ReadWebPInformation(fp, webPData))
+ FILE* const fp = input.file;
+ Dali::AnimatedImageLoading webPLoading = Dali::AnimatedImageLoading(Dali::Internal::Adaptor::WebPLoading::New(fp).Get());
+ if(webPLoading)
{
- WebPAnimDecoderOptions webPAnimDecoderOptions;
- WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
- webPAnimDecoderOptions.color_mode = MODE_RGBA;
- webPAnimDecoder = WebPAnimDecoderNew(&webPData, &webPAnimDecoderOptions);
- if(webPAnimDecoder != nullptr)
+ Dali::Devel::PixelBuffer pixelBuffer = webPLoading.LoadFrame(FIRST_FRAME_INDEX);
+ if(pixelBuffer)
{
- uint8_t* frameBuffer;
- int timestamp;
- WebPAnimDecoderGetInfo(webPAnimDecoder, &webPAnimInfo);
- WebPAnimDecoderReset(webPAnimDecoder);
- WebPAnimDecoderGetNext(webPAnimDecoder, &frameBuffer, ×tamp);
-
- bitmap = Dali::Devel::PixelBuffer::New(webPAnimInfo.canvas_width, webPAnimInfo.canvas_height, Dali::Pixel::RGBA8888);
- const int32_t bufferSize = webPAnimInfo.canvas_width * webPAnimInfo.canvas_height * sizeof(uint32_t);
- memcpy(bitmap.GetBuffer(), frameBuffer, bufferSize);
- ReleaseResource(webPData, webPAnimDecoder);
+ bitmap = pixelBuffer;
return true;
}
}
- ReleaseResource(webPData, webPAnimDecoder);
-#endif
-
- DALI_LOG_ERROR("WebP image loading failed.\n");
return false;
}
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
unsigned int bufferSize,
unsigned int width,
unsigned int height,
+ unsigned int stride,
Dali::Pixel::Format pixelFormat)
: mMetadata(),
mBuffer(buffer),
mBufferSize(bufferSize),
mWidth(width),
mHeight(height),
+ mStride(stride ? stride : width),
mPixelFormat(pixelFormat),
mPreMultiplied(false)
{
{
buffer = static_cast<unsigned char*>(malloc(bufferSize));
}
- return new PixelBuffer(buffer, bufferSize, width, height, pixelFormat);
+ return new PixelBuffer(buffer, bufferSize, width, height, width, pixelFormat);
}
PixelBufferPtr PixelBuffer::New(unsigned char* buffer,
unsigned int bufferSize,
unsigned int width,
unsigned int height,
+ unsigned int stride,
Dali::Pixel::Format pixelFormat)
{
- return new PixelBuffer(buffer, bufferSize, width, height, pixelFormat);
+ return new PixelBuffer(buffer, bufferSize, width, height, stride, pixelFormat);
}
Dali::PixelData PixelBuffer::Convert(PixelBuffer& pixelBuffer)
pixelBuffer.mBufferSize,
pixelBuffer.mWidth,
pixelBuffer.mHeight,
+ pixelBuffer.mStride,
pixelBuffer.mPixelFormat,
Dali::PixelData::FREE);
pixelBuffer.mBuffer = NULL;
pixelBuffer.mWidth = 0;
pixelBuffer.mHeight = 0;
pixelBuffer.mBufferSize = 0;
+ pixelBuffer.mStride = 0;
return pixelData;
}
return mHeight;
}
+uint32_t PixelBuffer::GetStride() const
+{
+ return mStride;
+}
+
Dali::Pixel::Format PixelBuffer::GetPixelFormat() const
{
return mPixelFormat;
memcpy(destBuffer, mBuffer, mBufferSize);
}
- Dali::PixelData pixelData = Dali::PixelData::New(destBuffer, mBufferSize, mWidth, mHeight, mPixelFormat, Dali::PixelData::FREE);
+ Dali::PixelData pixelData = Dali::PixelData::New(destBuffer, mBufferSize, mWidth, mHeight, mStride, mPixelFormat, Dali::PixelData::FREE);
return pixelData;
}
mBufferSize = pixelBuffer.mBufferSize;
mWidth = pixelBuffer.mWidth;
mHeight = pixelBuffer.mHeight;
+ mStride = pixelBuffer.mStride;
mPixelFormat = pixelBuffer.mPixelFormat;
}
Platform::RotateByShear(mBuffer,
mWidth,
mHeight,
+ mStride,
pixelSize,
radians,
pixelsOut,
mBuffer = pixelsOut;
pixelsOut = nullptr;
mBufferSize = mWidth * mHeight * pixelSize;
+ mStride = mWidth; // The buffer is tightly packed.
}
return success;
{
PixelBufferPtr outBuffer = PixelBuffer::New(cropDimensions.GetWidth(), cropDimensions.GetHeight(), inBuffer.GetPixelFormat());
int bytesPerPixel = Pixel::GetBytesPerPixel(inBuffer.mPixelFormat);
- int srcStride = inBuffer.mWidth * bytesPerPixel;
- int destStride = cropDimensions.GetWidth() * bytesPerPixel;
+ int srcStride = inBuffer.mStride * bytesPerPixel;
+ int destStride = cropDimensions.GetWidth() * bytesPerPixel; // The destination buffer is tightly packed
// Clamp crop to right edge
if(x + cropDimensions.GetWidth() > inBuffer.mWidth)
inBuffer.mPixelFormat == Pixel::RGBA8888 ||
inBuffer.mPixelFormat == Pixel::BGRA8888)
{
- Dali::Internal::Platform::Resample(inBuffer.mBuffer, inDimensions, outBuffer->GetBuffer(), outDimensions, filterType, bytesPerPixel, hasAlpha);
+ Dali::Internal::Platform::Resample(inBuffer.mBuffer, inDimensions, inBuffer.mStride, outBuffer->GetBuffer(), outDimensions, filterType, bytesPerPixel, hasAlpha);
}
else
{
// must be skipped in such case
if(Pixel::GetBytesPerPixel(mPixelFormat) && Pixel::HasAlpha(mPixelFormat))
{
- unsigned char* pixel = mBuffer;
- const unsigned int bufferSize = mWidth * mHeight;
+ unsigned char* pixel = mBuffer;
+ const unsigned int strideBytes = mStride * bytesPerPixel;
+ const unsigned int widthBytes = mWidth * bytesPerPixel;
- for(unsigned int i = 0; i < bufferSize; ++i)
+ // Collect all valid channel list before lookup whole buffer
+ std::vector<Channel> validChannelList;
+ for(const Channel& channel : {Adaptor::RED, Adaptor::GREEN, Adaptor::BLUE, Adaptor::LUMINANCE})
{
- unsigned int alpha = ReadChannel(pixel, mPixelFormat, Adaptor::ALPHA);
+ if(HasChannel(mPixelFormat, channel))
{
- auto red = ReadChannel(pixel, mPixelFormat, Adaptor::RED);
- auto green = ReadChannel(pixel, mPixelFormat, Adaptor::GREEN);
- auto blue = ReadChannel(pixel, mPixelFormat, Adaptor::BLUE);
- auto luminance = ReadChannel(pixel, mPixelFormat, Adaptor::LUMINANCE);
- WriteChannel(pixel, mPixelFormat, Adaptor::RED, red * alpha / 255);
- WriteChannel(pixel, mPixelFormat, Adaptor::GREEN, green * alpha / 255);
- WriteChannel(pixel, mPixelFormat, Adaptor::BLUE, blue * alpha / 255);
- WriteChannel(pixel, mPixelFormat, Adaptor::LUMINANCE, luminance * alpha / 255);
+ validChannelList.emplace_back(channel);
}
- pixel += bytesPerPixel;
}
+
+ if(DALI_LIKELY(!validChannelList.empty()))
+ {
+ for(unsigned int y = 0; y < mHeight; y++)
+ {
+ for(unsigned int x = 0; x < widthBytes; x += bytesPerPixel)
+ {
+ unsigned int alpha = ReadChannel(&pixel[x], mPixelFormat, Adaptor::ALPHA);
+ if(alpha < 255)
+ {
+ // If alpha is 255, we don't need to change color. Skip current pixel
+ // But if alpha is not 255, we should change color.
+ if(alpha > 0)
+ {
+ for(const Channel& channel : validChannelList)
+ {
+ auto color = ReadChannel(&pixel[x], mPixelFormat, channel);
+ WriteChannel(&pixel[x], mPixelFormat, channel, Platform::MultiplyAndNormalizeColor(color, alpha));
+ }
+ }
+ else
+ {
+ // If alpha is 0, just set all pixel as zero.
+ memset(&pixel[x], 0, bytesPerPixel);
+ }
+ }
+ }
+ pixel += strideBytes;
+ }
+ }
+ mPreMultiplied = true;
}
- mPreMultiplied = true;
}
bool PixelBuffer::IsAlphaPreMultiplied() const
uint32_t PixelBuffer::GetBrightness() const
{
- uint32_t brightness = 0;
-
+ uint32_t brightness = 0;
uint32_t bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
- if(bytesPerPixel)
+
+ if(bytesPerPixel && mWidth && mHeight)
{
- unsigned char* pixel = mBuffer;
- const uint32_t bufferSize = mWidth * mHeight;
+ unsigned char* pixel = mBuffer;
+ const uint32_t strideBytes = mStride * bytesPerPixel;
+ const uint32_t widthBytes = mWidth * bytesPerPixel;
+ const uint32_t bufferSize = mWidth * mHeight;
- if(bufferSize) // avoid division by zero to calculate brightness
- {
- uint64_t red = 0;
- uint64_t green = 0;
- uint64_t blue = 0;
+ uint64_t red = 0;
+ uint64_t green = 0;
+ uint64_t blue = 0;
- for(uint32_t i = 0; i < bufferSize; ++i)
+ for(unsigned int y = 0; y < mHeight; y++)
+ {
+ for(unsigned int x = 0; x < widthBytes; x += bytesPerPixel)
{
- red += ReadChannel(pixel, mPixelFormat, Adaptor::RED);
- green += ReadChannel(pixel, mPixelFormat, Adaptor::GREEN);
- blue += ReadChannel(pixel, mPixelFormat, Adaptor::BLUE);
- pixel += bytesPerPixel;
+ red += ReadChannel(&pixel[x], mPixelFormat, Adaptor::RED);
+ green += ReadChannel(&pixel[x], mPixelFormat, Adaptor::GREEN);
+ blue += ReadChannel(&pixel[x], mPixelFormat, Adaptor::BLUE);
}
- // http://www.w3.org/TR/AERT#color-contrast
- brightness = (red * BRIGHTNESS_CONSTANT_R + green * BRIGHTNESS_CONSTANT_G + blue * BRIGHTNESS_CONSTANT_B) / (1000uLL * bufferSize);
+ pixel += strideBytes;
}
+
+ // http://www.w3.org/TR/AERT#color-contrast
+ brightness = (red * BRIGHTNESS_CONSTANT_R + green * BRIGHTNESS_CONSTANT_G + blue * BRIGHTNESS_CONSTANT_B) / (1000uLL * bufferSize);
}
return brightness;
#define DALI_INTERNAL_ADAPTOR_PIXEL_BUFFER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* @param [in] bufferSize The size of the buffer in bytes
* @param [in] width Buffer width in pixels
* @param [in] height Buffer height in pixels
+ * @param [in] stride Buffer stride in pixels, 0 means the buffer is tightly packed
* @param [in] pixelFormat The pixel format
- * @param [in] releaseFunction The function used to release the memory.
*/
static PixelBufferPtr New(unsigned char* buffer,
unsigned int bufferSize,
unsigned int width,
unsigned int height,
+ unsigned int stride,
Pixel::Format pixelFormat);
/**
* @param [in] bufferSize The size of the buffer in bytes
* @param [in] width Buffer width in pixels
* @param [in] height Buffer height in pixels
+ * @param [in] stride Buffer stride in pixels, 0 means the buffer is tightly packed
* @param [in] pixelFormat The pixel format
*/
PixelBuffer(unsigned char* buffer,
unsigned int bufferSize,
unsigned int width,
unsigned int height,
+ unsigned int stride,
Pixel::Format pixelFormat);
protected:
unsigned int GetHeight() const;
/**
+ * @brief Gets the stride of the buffer in pixels.
+ * @return The stride of the buffer in pixels. 0 means the buffer is tightly packed.
+ */
+ unsigned int GetStride() const;
+
+ /**
* Get the pixel format
* @return The pixel format
*/
private:
std::unique_ptr<Property::Map> mMetadata; ///< Metadata fields
- unsigned char* mBuffer; ///< The raw pixel data
- unsigned int mBufferSize; ///< Buffer sized in bytes
- unsigned int mWidth; ///< Buffer width in pixels
- unsigned int mHeight; ///< Buffer height in pixels
+ uint8_t* mBuffer; ///< The raw pixel data
+ uint32_t mBufferSize; ///< Buffer sized in bytes
+ uint32_t mWidth; ///< Buffer width in pixels
+ uint32_t mHeight; ///< Buffer height in pixels
+ uint32_t mStride; ///< Buffer stride in bytes, 0 means the buffer is tightly packed
Pixel::Format mPixelFormat; ///< Pixel format
bool mPreMultiplied; ///< PreMultiplied
};
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
namespace
{
-constexpr Channel ALPHA_CHANNEL_ONLY[] = {ALPHA};
-constexpr Channel LUMINANCE_CHANNEL_ONLY[] = {LUMINANCE};
-constexpr Channel LUMINANCE_ALPHA_CHANNELS[] = {LUMINANCE, ALPHA};
-constexpr Channel RGB_CHANNELS[] = {RED, GREEN, BLUE};
-constexpr Channel BGR_CHANNELS[] = {BLUE, GREEN, RED};
-constexpr Channel RGBA_CHANNELS[] = {RED, GREEN, BLUE, ALPHA};
-constexpr Channel BGRA_CHANNELS[] = {BLUE, GREEN, RED, ALPHA};
+// clang-format off
+/**
+ * @brief Pre-defined offset tables for each Channel.
+ * If invalid channel, return -1. else, return the offset bytes.
+ */
+// | LUMINANCE | RED | GREEN | BLUE | ALPHA | DEPTH | STENCIL |
+constexpr std::int8_t ALPHA_ONLY_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS] = { -1 , -1 , -1 , -1 , 0 , -1 , -1 };
+constexpr std::int8_t LUMINANCE_ONLY_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS] = { 0 , -1 , -1 , -1 , -1 , -1 , -1 };
+constexpr std::int8_t LUMINANCE_ALPHA_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS] = { 0 , -1 , -1 , -1 , 1 , -1 , -1 };
+constexpr std::int8_t RGB_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS] = { -1 , 0 , 1 , 2 , -1 , -1 , -1 };
+constexpr std::int8_t BGR_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS] = { -1 , 2 , 1 , 0 , -1 , -1 , -1 };
+constexpr std::int8_t RGBA_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS] = { -1 , 0 , 1 , 2 , 3 , -1 , -1 };
+constexpr std::int8_t BGRA_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS] = { -1 , 2 , 1 , 0 , 3 , -1 , -1 };
+// clang-format on
/**
* @brief Template to Read from a buffer with pixel formats that have one byte per channel.
* @tparam NumberOfChannels The number of channels to check
* @param pixelData The pixel data to retrieve the value from
* @param channel The channel we're after
- * @param channels The array of channels in the pixel format
+ * @param offsetTable The array of offset bytes for each channels in the pixel format
* @return The value of the required channel
*/
-template<size_t NumberOfChannels>
-unsigned int ReadChannel(unsigned char* pixelData, Channel channel, const Channel (&channels)[NumberOfChannels])
+unsigned int ReadChannelTable(unsigned char* pixelData, Channel channel, const std::int8_t (&offsetTable)[MAX_NUMBER_OF_CHANNELS])
{
- auto num = 0u;
auto retVal = 0u;
- for(auto current : channels)
+ if(offsetTable[channel] >= 0)
{
- if(channel == current)
- {
- retVal = static_cast<unsigned int>(*(pixelData + num));
- break;
- }
- ++num;
+ retVal = static_cast<unsigned int>(*(pixelData + offsetTable[channel]));
}
return retVal;
}
* @param pixelData The pixel data to write the value to
* @param channel The channel we're after
* @param channelValue The value of the channel to set
- * @param channels The array of channels in the pixel format
+ * @param offsetTable The array of offset bytes for each channels in the pixel format
*/
-template<size_t NumberOfChannels>
-void WriteChannel(unsigned char* pixelData, Channel channel, unsigned int channelValue, const Channel (&channels)[NumberOfChannels])
+void WriteChannelTable(unsigned char* pixelData, Channel channel, unsigned int channelValue, const std::int8_t (&offsetTable)[MAX_NUMBER_OF_CHANNELS])
{
- auto num = 0u;
- for(auto current : channels)
+ if(offsetTable[channel] >= 0)
{
- if(channel == current)
- {
- *(pixelData + num) = static_cast<unsigned char>(channelValue & 0xFF);
- break;
- }
- ++num;
+ *(pixelData + offsetTable[channel]) = static_cast<unsigned char>(channelValue & 0xFF);
}
}
DALI_LOG_ERROR("Pixel formats for compressed images are not compatible with simple channels.\n");
break;
}
+
+ case Dali::Pixel::CHROMINANCE_U:
+ case Dali::Pixel::CHROMINANCE_V:
+ {
+ DALI_LOG_ERROR("Pixel formats for chrominance are not compatible with simple channels.\n");
+ break;
+ }
}
return false;
{
case Dali::Pixel::A8:
{
- return ReadChannel(pixelData, channel, ALPHA_CHANNEL_ONLY);
+ return ReadChannelTable(pixelData, channel, ALPHA_ONLY_OFFSET_TABLE);
}
case Dali::Pixel::L8:
{
- return ReadChannel(pixelData, channel, LUMINANCE_CHANNEL_ONLY);
+ return ReadChannelTable(pixelData, channel, LUMINANCE_ONLY_OFFSET_TABLE);
}
case Dali::Pixel::LA88:
{
- return ReadChannel(pixelData, channel, LUMINANCE_ALPHA_CHANNELS);
+ return ReadChannelTable(pixelData, channel, LUMINANCE_ALPHA_OFFSET_TABLE);
}
case Dali::Pixel::RGB565:
{
case Dali::Pixel::RGB888:
case Dali::Pixel::RGB8888:
{
- return ReadChannel(pixelData, channel, RGB_CHANNELS);
+ return ReadChannelTable(pixelData, channel, RGB_OFFSET_TABLE);
}
case Dali::Pixel::BGR8888:
{
- return ReadChannel(pixelData, channel, BGR_CHANNELS);
+ return ReadChannelTable(pixelData, channel, BGR_OFFSET_TABLE);
}
case Dali::Pixel::RGBA8888:
{
- return ReadChannel(pixelData, channel, RGBA_CHANNELS);
+ return ReadChannelTable(pixelData, channel, RGBA_OFFSET_TABLE);
}
case Dali::Pixel::BGRA8888:
{
- return ReadChannel(pixelData, channel, BGRA_CHANNELS);
+ return ReadChannelTable(pixelData, channel, BGRA_OFFSET_TABLE);
}
case Dali::Pixel::RGBA4444:
{
case Dali::Pixel::A8:
{
- WriteChannel(pixelData, channel, channelValue, ALPHA_CHANNEL_ONLY);
+ WriteChannelTable(pixelData, channel, channelValue, ALPHA_ONLY_OFFSET_TABLE);
break;
}
case Dali::Pixel::L8:
{
- WriteChannel(pixelData, channel, channelValue, LUMINANCE_CHANNEL_ONLY);
+ WriteChannelTable(pixelData, channel, channelValue, LUMINANCE_ONLY_OFFSET_TABLE);
break;
}
case Dali::Pixel::LA88:
{
- WriteChannel(pixelData, channel, channelValue, LUMINANCE_ALPHA_CHANNELS);
+ WriteChannelTable(pixelData, channel, channelValue, LUMINANCE_ALPHA_OFFSET_TABLE);
break;
}
case Dali::Pixel::RGB565:
case Dali::Pixel::RGB888:
case Dali::Pixel::RGB8888:
{
- WriteChannel(pixelData, channel, channelValue, RGB_CHANNELS);
+ WriteChannelTable(pixelData, channel, channelValue, RGB_OFFSET_TABLE);
break;
}
case Dali::Pixel::BGR8888:
{
- WriteChannel(pixelData, channel, channelValue, BGR_CHANNELS);
+ WriteChannelTable(pixelData, channel, channelValue, BGR_OFFSET_TABLE);
break;
}
case Dali::Pixel::RGBA8888:
{
- WriteChannel(pixelData, channel, channelValue, RGBA_CHANNELS);
+ WriteChannelTable(pixelData, channel, channelValue, RGBA_OFFSET_TABLE);
break;
}
case Dali::Pixel::BGRA8888:
{
- WriteChannel(pixelData, channel, channelValue, BGRA_CHANNELS);
+ WriteChannelTable(pixelData, channel, channelValue, BGRA_OFFSET_TABLE);
break;
}
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// INTERNAL INCLUDES
#include <dali/devel-api/adaptor-framework/image-loading.h>
+#include <dali/internal/imaging/common/pixel-buffer-impl.h>
-typedef unsigned char WebPByteType;
+typedef uint8_t WebPByteType;
namespace Dali
{
Debug::Filter* gWebPLoadingLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GIF_LOADING");
#endif
-constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
+static constexpr int32_t INITIAL_INDEX = -1;
+static constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
} // namespace
{
public:
Impl(const std::string& url, bool isLocalResource)
- : mUrl(url),
+ : mFile(nullptr),
+ mUrl(url),
mFrameCount(1u),
mMutex(),
mBuffer(nullptr),
mBufferSize(0u),
mImageSize(),
- mLoadSucceeded(true)
+ mLoadSucceeded(false),
+ mIsAnimatedImage(false),
+ mIsLocalResource(isLocalResource)
{
+ }
+
+ Impl(FILE* const fp)
+ : mFile(fp),
+ mUrl(),
+ mFrameCount(1u),
+ mMutex(),
+ mBuffer(nullptr),
+ mBufferSize(0u),
+ mImageSize(),
+ mLoadSucceeded(false),
+ mIsAnimatedImage(false),
+ mIsLocalResource(true)
+ {
+ }
+
+ bool LoadWebPInformation()
+ {
+ // Block to do not load this file again.
+ Mutex::ScopedLock lock(mMutex);
+ if(DALI_UNLIKELY(mLoadSucceeded))
+ {
+ return mLoadSucceeded;
+ }
+
+#ifndef DALI_WEBP_AVAILABLE
+ // If the system doesn't support webp, loading will be failed.
+ mFrameCount = 0u;
+ mLoadSucceeded = false;
+ return mLoadSucceeded;
+#endif
+
// mFrameCount will be 1 if the input image is non-animated image or animated image with single frame.
- if(ReadWebPInformation(isLocalResource))
+ if(DALI_LIKELY(ReadWebPInformation()))
{
-#ifdef DALI_ANIMATED_WEBP_ENABLED
+#ifdef DALI_WEBP_AVAILABLE
WebPDataInit(&mWebPData);
mWebPData.size = mBufferSize;
mWebPData.bytes = mBuffer;
- WebPAnimDecoderOptions webPAnimDecoderOptions;
- WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
- webPAnimDecoderOptions.color_mode = MODE_RGBA;
- mWebPAnimDecoder = WebPAnimDecoderNew(&mWebPData, &webPAnimDecoderOptions);
- WebPAnimDecoderGetInfo(mWebPAnimDecoder, &mWebPAnimInfo);
- mTimeStamp.assign(mWebPAnimInfo.frame_count, 0);
- mFrameCount = mWebPAnimInfo.frame_count;
- mImageSize = ImageDimensions(mWebPAnimInfo.canvas_width, mWebPAnimInfo.canvas_height);
-#elif DALI_WEBP_AVAILABLE
- int32_t imageWidth, imageHeight;
- if(WebPGetInfo(mBuffer, mBufferSize, &imageWidth, &imageHeight))
+
+ WebPDemuxer* demuxer = WebPDemux(&mWebPData);
+ uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS);
+ if(flags & ANIMATION_FLAG)
{
- mImageSize = ImageDimensions(imageWidth, imageHeight);
+ mIsAnimatedImage = true;
+ }
+
+ if(!mIsAnimatedImage)
+ {
+ int32_t imageWidth, imageHeight;
+ if(WebPGetInfo(mBuffer, mBufferSize, &imageWidth, &imageHeight))
+ {
+ mImageSize = ImageDimensions(imageWidth, imageHeight);
+ }
}
#endif
-#ifndef DALI_WEBP_AVAILABLE
- // If the system doesn't support webp, loading will be failed.
- mFrameCount = 0u;
- mLoadSucceeded = false;
+#ifdef DALI_ANIMATED_WEBP_ENABLED
+ if(mIsAnimatedImage)
+ {
+ WebPAnimDecoderOptions webPAnimDecoderOptions;
+ WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
+ webPAnimDecoderOptions.color_mode = MODE_RGBA;
+ mWebPAnimDecoder = WebPAnimDecoderNew(&mWebPData, &webPAnimDecoderOptions);
+ WebPAnimDecoderGetInfo(mWebPAnimDecoder, &mWebPAnimInfo);
+ mTimeStamp.assign(mWebPAnimInfo.frame_count, 0);
+ mFrameCount = mWebPAnimInfo.frame_count;
+ mImageSize = ImageDimensions(mWebPAnimInfo.canvas_width, mWebPAnimInfo.canvas_height);
+ }
#endif
+ mLoadSucceeded = true;
}
else
{
mLoadSucceeded = false;
DALI_LOG_ERROR("Image loading failed for: \"%s\".\n", mUrl.c_str());
}
+
+ return mLoadSucceeded;
}
- bool ReadWebPInformation(bool isLocalResource)
+ bool ReadWebPInformation()
{
- FILE* fp = nullptr;
- if(isLocalResource)
+ mBufferSize = 0;
+
+ FILE* fp = mFile;
+ std::unique_ptr<Internal::Platform::FileReader> fileReader;
+ Dali::Vector<uint8_t> dataBuffer;
+ if(fp == nullptr)
{
- Internal::Platform::FileReader fileReader(mUrl);
- fp = fileReader.GetFile();
- if(fp == nullptr)
+ if(mIsLocalResource)
{
- return false;
+ fileReader = std::make_unique<Internal::Platform::FileReader>(mUrl);
}
-
- if(fseek(fp, 0, SEEK_END) <= -1)
+ else
{
- return false;
+ size_t dataSize;
+ if(DALI_LIKELY(TizenPlatform::Network::DownloadRemoteFileIntoMemory(mUrl, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE)))
+ {
+ mBufferSize = dataBuffer.Size();
+ if(DALI_LIKELY(mBufferSize > 0U))
+ {
+ // Open a file handle on the memory buffer:
+ fileReader = std::make_unique<Internal::Platform::FileReader>(dataBuffer, mBufferSize);
+ }
+ }
}
- mBufferSize = ftell(fp);
- if(!fseek(fp, 0, SEEK_SET))
+ if(fileReader)
{
- mBuffer = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * mBufferSize));
- mBufferSize = fread(mBuffer, sizeof(WebPByteType), mBufferSize, fp);
- return true;
+ fp = fileReader->GetFile();
}
}
- else
- {
- // remote file
- bool succeeded;
- Dali::Vector<uint8_t> dataBuffer;
- size_t dataSize;
- succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory(mUrl, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE);
- if(succeeded)
+ if(DALI_LIKELY(fp != nullptr))
+ {
+ if(mBufferSize == 0)
{
- mBufferSize = dataBuffer.Size();
- if(mBufferSize > 0U)
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END) <= -1))
{
- // Open a file handle on the memory buffer:
- Internal::Platform::FileReader fileReader(dataBuffer, mBufferSize);
- fp = fileReader.GetFile();
- if(fp != nullptr)
- {
- if(!fseek(fp, 0, SEEK_SET))
- {
- mBuffer = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * mBufferSize));
- mBufferSize = fread(mBuffer, sizeof(WebPByteType), mBufferSize, fp);
- return true;
- }
- }
+ return false;
}
+ mBufferSize = ftell(fp);
+ }
+
+ if(DALI_LIKELY(!fseek(fp, 0, SEEK_SET)))
+ {
+ mBuffer = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * mBufferSize));
+ mBufferSize = fread(mBuffer, sizeof(WebPByteType), mBufferSize, fp);
+ return true;
}
}
return false;
void ReleaseResource()
{
-#ifdef DALI_ANIMATED_WEBP_ENABLED
+#ifdef DALI_WEBP_AVAILABLE
if(&mWebPData != nullptr)
{
mWebPData.bytes = nullptr;
WebPDataInit(&mWebPData);
}
- if(mWebPAnimDecoder != nullptr)
+#endif
+#ifdef DALI_ANIMATED_WEBP_ENABLED
+ if(mIsAnimatedImage)
{
- WebPAnimDecoderDelete(mWebPAnimDecoder);
- mWebPAnimDecoder = nullptr;
+ if(mWebPAnimDecoder != nullptr)
+ {
+ WebPAnimDecoderDelete(mWebPAnimDecoder);
+ mWebPAnimDecoder = nullptr;
+ }
}
#endif
if(mBuffer != nullptr)
free((void*)mBuffer);
mBuffer = nullptr;
}
+
+ mLoadSucceeded = false;
}
// Moveable but not copyable
ReleaseResource();
}
+ FILE* mFile;
std::string mUrl;
std::vector<uint32_t> mTimeStamp;
- uint32_t mLoadingFrame{0};
+ int32_t mLatestLoadedFrame{INITIAL_INDEX};
uint32_t mFrameCount;
Mutex mMutex;
// For the case the system doesn't support DALI_ANIMATED_WEBP_ENABLED
uint32_t mBufferSize;
ImageDimensions mImageSize;
bool mLoadSucceeded;
+ bool mIsAnimatedImage;
+ bool mIsLocalResource;
+
+#ifdef DALI_WEBP_AVAILABLE
+ WebPData mWebPData{0};
+#endif
#ifdef DALI_ANIMATED_WEBP_ENABLED
- WebPData mWebPData{0};
- WebPAnimDecoder* mWebPAnimDecoder{nullptr};
- WebPAnimInfo mWebPAnimInfo{0};
+ WebPAnimDecoder* mWebPAnimDecoder{nullptr};
+ WebPAnimInfo mWebPAnimInfo{0};
+ Dali::Devel::PixelBuffer mPreLoadedFrame{};
#endif
};
return AnimatedImageLoadingPtr(new WebPLoading(url, isLocalResource));
}
+AnimatedImageLoadingPtr WebPLoading::New(FILE* const fp)
+{
+#ifndef DALI_ANIMATED_WEBP_ENABLED
+ DALI_LOG_ERROR("The system does not support Animated WebP format.\n");
+#endif
+ return AnimatedImageLoadingPtr(new WebPLoading(fp));
+}
+
WebPLoading::WebPLoading(const std::string& url, bool isLocalResource)
: mImpl(new WebPLoading::Impl(url, isLocalResource))
{
}
-WebPLoading::~WebPLoading()
+WebPLoading::WebPLoading(FILE* const fp)
+: mImpl(new WebPLoading::Impl(fp))
{
- delete mImpl;
}
-bool WebPLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData)
+WebPLoading::~WebPLoading()
{
- for(int i = 0; i < count; ++i)
- {
- Dali::Devel::PixelBuffer pixelBuffer = LoadFrame((frameStartIndex + i) % mImpl->mFrameCount);
- Dali::PixelData imageData = Devel::PixelBuffer::Convert(pixelBuffer);
- pixelData.push_back(imageData);
- }
- if(pixelData.size() != static_cast<uint32_t>(count))
- {
- return false;
- }
- return true;
+ delete mImpl;
}
Dali::Devel::PixelBuffer WebPLoading::LoadFrame(uint32_t frameIndex)
{
Dali::Devel::PixelBuffer pixelBuffer;
+ // If WebP file is still not loaded, Load the information.
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ if(DALI_UNLIKELY(!mImpl->LoadWebPInformation()))
+ {
+ mImpl->ReleaseResource();
+ return pixelBuffer;
+ }
+ }
+
// WebPDecodeRGBA is faster than to use demux API for loading non-animated image.
// If frame count is 1, use WebPDecodeRGBA api.
#ifdef DALI_WEBP_AVAILABLE
- if(mImpl->mFrameCount == 1)
+ if(!mImpl->mIsAnimatedImage)
{
int32_t width, height;
- if(!WebPGetInfo(mImpl->mBuffer, mImpl->mBufferSize, &width, &height))
+ if(DALI_LIKELY(WebPGetInfo(mImpl->mBuffer, mImpl->mBufferSize, &width, &height)))
{
- return pixelBuffer;
- }
+ WebPBitstreamFeatures features;
+ if(DALI_LIKELY(VP8_STATUS_NOT_ENOUGH_DATA != WebPGetFeatures(mImpl->mBuffer, mImpl->mBufferSize, &features)))
+ {
+ uint32_t channelNumber = (features.has_alpha) ? 4 : 3;
+ uint8_t* frameBuffer = nullptr;
+ if(channelNumber == 4)
+ {
+ frameBuffer = WebPDecodeRGBA(mImpl->mBuffer, mImpl->mBufferSize, &width, &height);
+ }
+ else
+ {
+ frameBuffer = WebPDecodeRGB(mImpl->mBuffer, mImpl->mBufferSize, &width, &height);
+ }
- WebPBitstreamFeatures features;
- if(VP8_STATUS_NOT_ENOUGH_DATA == WebPGetFeatures(mImpl->mBuffer, mImpl->mBufferSize, &features))
- {
- return pixelBuffer;
+ if(frameBuffer != nullptr)
+ {
+ Pixel::Format pixelFormat = (channelNumber == 4) ? Pixel::RGBA8888 : Pixel::RGB888;
+ int32_t bufferSize = width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat);
+ Internal::Adaptor::PixelBufferPtr internal =
+ Internal::Adaptor::PixelBuffer::New(frameBuffer, bufferSize, width, height, width, pixelFormat);
+ pixelBuffer = Devel::PixelBuffer(internal.Get());
+ }
+ }
}
+ // The single frame resource should be released after loading.
+ mImpl->ReleaseResource();
+ }
+#endif
- uint32_t channelNumber = (features.has_alpha) ? 4 : 3;
- Pixel::Format pixelFormat = (channelNumber == 4) ? Pixel::RGBA8888 : Pixel::RGB888;
- pixelBuffer = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
- uint8_t* frameBuffer = nullptr;
- if(channelNumber == 4)
+#ifdef DALI_ANIMATED_WEBP_ENABLED
+ if(mImpl->mIsAnimatedImage && mImpl->mBuffer != nullptr)
+ {
+ Mutex::ScopedLock lock(mImpl->mMutex);
+ if(DALI_LIKELY(frameIndex < mImpl->mWebPAnimInfo.frame_count && mImpl->mLoadSucceeded))
{
- frameBuffer = WebPDecodeRGBA(mImpl->mBuffer, mImpl->mBufferSize, &width, &height);
+ DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
+
+ if(mImpl->mPreLoadedFrame && mImpl->mLatestLoadedFrame == static_cast<int32_t>(frameIndex))
+ {
+ pixelBuffer = mImpl->mPreLoadedFrame;
+ }
+ else
+ {
+ pixelBuffer = DecodeFrame(frameIndex);
+ }
+ mImpl->mPreLoadedFrame.Reset();
+
+ // If time stamp of next frame is unknown, load a frame more to know it.
+ if(frameIndex + 1 < mImpl->mWebPAnimInfo.frame_count && mImpl->mTimeStamp[frameIndex + 1] == 0u)
+ {
+ mImpl->mPreLoadedFrame = DecodeFrame(frameIndex + 1);
+ }
}
else
{
- frameBuffer = WebPDecodeRGB(mImpl->mBuffer, mImpl->mBufferSize, &width, &height);
+ mImpl->ReleaseResource();
}
-
- if(frameBuffer != nullptr)
- {
- const int32_t imageBufferSize = width * height * sizeof(uint8_t) * channelNumber;
- memcpy(pixelBuffer.GetBuffer(), frameBuffer, imageBufferSize);
- free((void*)frameBuffer);
- }
- mImpl->ReleaseResource();
- return pixelBuffer;
}
#endif
+ return pixelBuffer;
+}
+Dali::Devel::PixelBuffer WebPLoading::DecodeFrame(uint32_t frameIndex)
+{
+ Dali::Devel::PixelBuffer pixelBuffer;
#ifdef DALI_ANIMATED_WEBP_ENABLED
- Mutex::ScopedLock lock(mImpl->mMutex);
- if(frameIndex >= mImpl->mWebPAnimInfo.frame_count || !mImpl->mLoadSucceeded)
- {
- return pixelBuffer;
- }
-
- DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
-
- if(mImpl->mLoadingFrame > frameIndex)
+ if(mImpl->mLatestLoadedFrame >= static_cast<int32_t>(frameIndex))
{
- mImpl->mLoadingFrame = 0;
+ mImpl->mLatestLoadedFrame = INITIAL_INDEX;
WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
}
- for(; mImpl->mLoadingFrame < frameIndex; ++mImpl->mLoadingFrame)
+ uint8_t* frameBuffer = nullptr;
+ int32_t timestamp = 0u;
+ for(; mImpl->mLatestLoadedFrame < static_cast<int32_t>(frameIndex);)
{
- uint8_t* frameBuffer;
- int timestamp;
WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp);
- mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
+ mImpl->mTimeStamp[++mImpl->mLatestLoadedFrame] = timestamp;
}
- const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t);
- uint8_t* frameBuffer;
- int timestamp;
- WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp);
-
- pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888);
- memcpy(pixelBuffer.GetBuffer(), frameBuffer, bufferSize);
- mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
-
- mImpl->mLoadingFrame++;
- if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count)
+ if(frameBuffer != nullptr)
{
- mImpl->mLoadingFrame = 0;
- WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
+ const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t);
+ pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888);
+ memcpy(pixelBuffer.GetBuffer(), frameBuffer, bufferSize);
}
+
#endif
return pixelBuffer;
}
ImageDimensions WebPLoading::GetImageSize() const
{
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadWebPInformation();
+ }
return mImpl->mImageSize;
}
uint32_t WebPLoading::GetImageCount() const
{
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadWebPInformation();
+ }
return mImpl->mFrameCount;
}
uint32_t WebPLoading::GetFrameInterval(uint32_t frameIndex) const
{
- // If frameIndex is above the value of ImageCount or current frame is not loading yet, return 0u.
- if(frameIndex >= GetImageCount() || (frameIndex > 0 && mImpl->mTimeStamp[frameIndex - 1] > mImpl->mTimeStamp[frameIndex]))
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ DALI_LOG_ERROR("WebP file is still not loaded, this frame interval could not be correct value.\n");
+ }
+ if(frameIndex >= GetImageCount())
{
+ DALI_LOG_ERROR("Input frameIndex exceeded frame count of the WebP.");
return 0u;
}
else
{
- if(frameIndex > 0)
+ int32_t interval = 0u;
+ if(GetImageCount() == 1u)
+ {
+ return 0u;
+ }
+ else if(frameIndex + 1 == GetImageCount())
+ {
+ // For the interval between last frame and first frame, use last interval again.
+ interval = mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1];
+ }
+ else
+ {
+ interval = mImpl->mTimeStamp[frameIndex + 1] - mImpl->mTimeStamp[frameIndex];
+ }
+
+ if(DALI_UNLIKELY(interval < 0))
{
- return mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1];
+ DALI_LOG_ERROR("This interval value is not correct, because the frame still hasn't ever been decoded.");
+ return 0u;
}
- return mImpl->mTimeStamp[frameIndex];
+ return static_cast<uint32_t>(interval);
}
}
static AnimatedImageLoadingPtr New(const std::string& url, bool isLocalResource);
/**
+ * Create a WebPLoading with the given url and resourceType.
+ * @param[in] fp The file pointer to be load.
+ * @return A newly created WebPLoading.
+ */
+ static AnimatedImageLoadingPtr New(FILE* const fp);
+
+ /**
* @brief Constructor
*
* Construct a Loader with the given URL
WebPLoading(const std::string& url, bool isLocalResource);
/**
- * @brief Destructor
+ * @brief Constructor
+ *
+ * Construct a Loader with the given URL
+ * @param[in] fp The file pointer to be load.
*/
- ~WebPLoading() override;
+ WebPLoading(FILE* const fp);
/**
- * @brief Load the next N Frames of the webp.
- *
- * @note This function will load the entire webp into memory if not already loaded.
- * @param[in] frameStartIndex The frame counter to start from. Will usually be the next frame
- * after the previous invocation of this method, or 0 to start.
- * @param[in] count The number of frames to load
- * @param[out] pixelData The vector in which to return the frame data
- * @return True if the frame data was successfully loaded
+ * @brief Destructor
*/
- bool LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData) override;
+ ~WebPLoading() override;
/**
* @brief Load the next Frame of the animated image.
* @param[in] frameIndex The frame counter to load. Will usually be the next frame.
* @return Dali::Devel::PixelBuffer The loaded PixelBuffer. If loading is fail, return empty handle.
*/
-
Dali::Devel::PixelBuffer LoadFrame(uint32_t frameIndex) override;
/**
*
* @note The frame is needed to be loaded before this function is called.
*
- * @return The time interval of the frame(microsecond).
+ * @return The time interval between frameIndex and frameIndex + 1(microsecond).
*/
uint32_t GetFrameInterval(uint32_t frameIndex) const override;
bool HasLoadingSucceeded() const override;
private:
+ /**
+ * @brief Decode Frame of the animated image.
+ *
+ * @param[in] frameIndex The frame counter to load. Will usually be the next frame.
+ * @return Dali::Devel::PixelBuffer The loaded PixelBuffer. If loading is fail, return empty handle.
+ */
+ Dali::Devel::PixelBuffer DecodeFrame(uint32_t frameIndex);
+
+private:
struct Impl;
Impl* mImpl;
};
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
#define TBM_SURFACE_QUEUE_SIZE 3
-const char* SAMPLER_TYPE = "samplerExternalOES";
+const char* SAMPLER_TYPE = "samplerExternalOES";
// clang-format off
int FORMATS_BLENDING_REQUIRED[] = {
switch(colorFormat)
{
- case Dali::NativeImageSourceQueue::ColorFormat::RGBA8888:
+ case Dali::NativeImageSourceQueue::ColorFormat::RGBA8888: // TODO : Implement me after other codes fixed.
+ case Dali::NativeImageSourceQueue::ColorFormat::BGRA8888:
{
tbmFormat = TBM_FORMAT_ARGB8888;
mBlendingRequired = true;
break;
}
- case Dali::NativeImageSourceQueue::ColorFormat::RGBX8888:
+ case Dali::NativeImageSourceQueue::ColorFormat::RGBX8888: // TODO : Implement me after other codes fixed.
+ case Dali::NativeImageSourceQueue::ColorFormat::BGRX8888:
{
tbmFormat = TBM_FORMAT_XRGB8888;
mBlendingRequired = false;
break;
}
- case Dali::NativeImageSourceQueue::ColorFormat::RGB888:
+ case Dali::NativeImageSourceQueue::ColorFormat::RGB888: // TODO : Implement me after other codes fixed.
+ case Dali::NativeImageSourceQueue::ColorFormat::BGR888:
{
tbmFormat = TBM_FORMAT_RGB888;
mBlendingRequired = false;
#define DALI_INTERNAL_INPUT_COMMON_INPUT_METHOD_CONTEXT_IMPL_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
}
+ /**
+ * @copydoc Dali::InputMethodContext::SendSelectionSet()
+ */
+ virtual void SendSelectionSet(void* data, ImfContext* imfContext, void* eventInfo)
+ {
+ }
+
// Cursor related
/**
* @copydoc Dali::InputMethodContext::NotifyCursorPosition()
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextGeneric::SendCommitContent\n");
}
+void InputMethodContextGeneric::SendSelectionSet(void* data, ImfContext* imfContext, void* eventInfo)
+{
+ DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextGeneric::SendSelectionSet\n");
+}
+
void InputMethodContextGeneric::NotifyCursorPosition()
{
DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextGeneric::NotifyCursorPosition\n");
#define __DALI_INTERNAL_INPUT_METHOD_CONTEXT_IMPL_GENERIC_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
void SendCommitContent(void* data, ImfContext* imfContext, void* eventInfo) override;
+ /**
+ * @copydoc Dali::InputMethodContext::SendSelectionSet()
+ */
+ void SendSelectionSet(void* data, ImfContext* imfContext, void* eventInfo) override;
+
// Cursor related
/**
* @copydoc Dali::InputMethodContext::NotifyCursorPosition()
#pragma once
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
}
+ /**
+ * @copydoc Dali::InputMethodContext::SendSelectionSet()
+ */
+ void SendSelectionSet(void* data, ImfContext* imfContext, void* eventInfo) override
+ {
+ }
+
// Cursor related
/**
* @copydoc Dali::InputMethodContext::NotifyCursorPosition()
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
}
+/**
+ * Called when the input method sends a selection set.
+ */
+void SelectionSet(void* data, Ecore_IMF_Context* imfContext, void* eventInfo)
+{
+ if(data)
+ {
+ InputMethodContextEcoreWl* inputMethodContext = static_cast<InputMethodContextEcoreWl*>(data);
+ inputMethodContext->SendSelectionSet(data, imfContext, eventInfo);
+ }
+}
+
int GetWindowIdFromActor(Dali::Actor actor)
{
int windowId = kUninitializedWindowId;
ecore_imf_context_event_callback_add(mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding, this);
ecore_imf_context_event_callback_add(mIMFContext, ECORE_IMF_CALLBACK_PRIVATE_COMMAND_SEND, PrivateCommand, this);
ecore_imf_context_event_callback_add(mIMFContext, ECORE_IMF_CALLBACK_COMMIT_CONTENT, CommitContent, this);
+ ecore_imf_context_event_callback_add(mIMFContext, ECORE_IMF_CALLBACK_SELECTION_SET, SelectionSet, this);
ecore_imf_context_input_panel_event_callback_add(mIMFContext, ECORE_IMF_INPUT_PANEL_STATE_EVENT, InputPanelStateChangeCallback, this);
ecore_imf_context_input_panel_event_callback_add(mIMFContext, ECORE_IMF_INPUT_PANEL_LANGUAGE_EVENT, InputPanelLanguageChangeCallback, this);
ecore_imf_context_event_callback_del(mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding);
ecore_imf_context_event_callback_del(mIMFContext, ECORE_IMF_CALLBACK_PRIVATE_COMMAND_SEND, PrivateCommand);
ecore_imf_context_event_callback_del(mIMFContext, ECORE_IMF_CALLBACK_COMMIT_CONTENT, CommitContent);
+ ecore_imf_context_event_callback_del(mIMFContext, ECORE_IMF_CALLBACK_SELECTION_SET, SelectionSet);
ecore_imf_context_input_panel_event_callback_del(mIMFContext, ECORE_IMF_INPUT_PANEL_STATE_EVENT, InputPanelStateChangeCallback);
ecore_imf_context_input_panel_event_callback_del(mIMFContext, ECORE_IMF_INPUT_PANEL_LANGUAGE_EVENT, InputPanelLanguageChangeCallback);
}
}
+/**
+ * Called when the input method selection set.
+ */
+void InputMethodContextEcoreWl::SendSelectionSet(void* data, ImfContext* imfContext, void* eventInfo)
+{
+ DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextEcoreWl::SendCommitContent\n");
+
+ if(Dali::Adaptor::IsAvailable())
+ {
+ Ecore_IMF_Event_Selection* selection = static_cast<Ecore_IMF_Event_Selection*>(eventInfo);
+ if(selection)
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextEcoreWl::SendSelectionSet selection start index : %d, end index : %d\n", selection->start, selection->end);
+ Dali::InputMethodContext::EventData imfData(Dali::InputMethodContext::SELECTION_SET, selection->start, selection->end);
+ Dali::InputMethodContext handle(this);
+ mEventSignal.Emit(handle, imfData);
+ }
+ }
+}
+
void InputMethodContextEcoreWl::NotifyCursorPosition()
{
DALI_LOG_INFO(gLogFilter, Debug::General, "InputMethodContextEcoreWl::NotifyCursorPosition\n");
#define DALI_INTERNAL_INPUT_METHOD_CONTEXT_IMPL_ECORE_WL_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
void SendCommitContent(void* data, ImfContext* imfContext, void* eventInfo) override;
+ /**
+ * @copydoc Dali::InputMethodContext::SendSelectionSet()
+ */
+ void SendSelectionSet(void* data, ImfContext* imfContext, void* eventInfo) override;
+
// Cursor related
/**
* @copydoc Dali::InputMethodContext::NotifyCursorPosition()
#define DALI_INTERNAL_INPUT_METHOD_CONTEXT_IMPL_X_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
}
+ /**
+ * @copydoc Dali::InputMethodContext::SendSelectionSet()
+ */
+ void SendSelectionSet(void* data, ImfContext* imfContext, void* eventInfo) override
+ {
+ }
+
// Cursor related
/**
* @copydoc Dali::InputMethodContext::NotifyCursorPosition()
#define DALI_INTERNAL_INPUT_METHOD_CONTEXT_IMPL_WIN_H\r
\r
/*\r
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.\r
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.\r
*\r
* Licensed under the Apache License, Version 2.0 (the "License");\r
* you may not use this file except in compliance with the License.\r
{\r
}\r
\r
+ /**\r
+ * @copydoc Dali::InputMethodContext::SendSelectionSet()\r
+ */\r
+ void SendSelectionSet(void* data, ImfContext* imfContext, void* eventInfo) override\r
+ {\r
+ }\r
+\r
// Cursor related\r
/**\r
* @copydoc Dali::InputMethodContext::NotifyCursorPosition()\r
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// EXTERNAL INCLUDES
#include <dali/integration-api/debug.h>
-#include <dali/internal/adaptor/common/adaptor-impl.h>
#include <dali/public-api/dali-core.h>
#include <stdio.h>
#include <iomanip>
#include <sstream>
+// INTERNAL INCLUDES
+#include <dali/internal/adaptor/common/adaptor-impl.h>
+#include <dali/internal/network/common/network-service-impl.h>
+
using Dali::Matrix;
using Dali::Matrix3;
using Dali::Property;
sendData->SendData(json.c_str(), json.length(), clientId);
}
+void SetCustomCommand(const std::string& message)
+{
+ if(Adaptor::IsAvailable())
+ {
+ Internal::Adaptor::NetworkServicePtr networkService = Internal::Adaptor::NetworkService::Get();
+ if(networkService)
+ {
+ networkService->EmitCustomCommandReceivedSignal(message);
+ }
+ }
+}
+
} // namespace Automation
} // namespace Adaptor
#define DALI_INTERNAL_ADAPTOR_AUTOMATION_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
void DumpScene(unsigned int clientId, ClientSendDataInterface* sendData);
+
+/**
+ * @brief Sets a custom command.
+ * No ClientSendDataInterface required, as no response is sent back
+ * @param[in] message custom message
+ */
+void SetCustomCommand(const std::string& message);
+
+
} // namespace Automation
} // namespace Adaptor
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// EXTERNAL INCLUDES
#include <stdio.h>
+#include <iostream>
#include <string>
// INTERNAL INCLUDES
{
UNKNOWN_COMMAND,
SET_PROPERTY,
+ CUSTOM_COMMAND,
DUMP_SCENE
};
void AssignSetPropertyCommand(std::string setPropertyCommand)
{
- mCommandId = SET_PROPERTY;
- mPropertyCommand = setPropertyCommand;
+ mCommandId = SET_PROPERTY;
+ mCommandString = setPropertyCommand;
}
+
void AssignDumpSceneCommand()
{
mCommandId = DUMP_SCENE;
}
+ void AssignCustomCommand(std::string&& customCommand)
+ {
+ mCommandId = CUSTOM_COMMAND;
+ mCommandString = std::move(customCommand);
+ }
+
void RunCallback()
{
switch(mCommandId)
{
case SET_PROPERTY:
{
- Automation::SetProperty(mPropertyCommand);
+ Automation::SetProperty(mCommandString);
break;
}
case DUMP_SCENE:
Automation::DumpScene(mClientId, &mSendDataInterface);
break;
}
+ case CUSTOM_COMMAND:
+ {
+ Automation::SetCustomCommand(mCommandString);
+ break;
+ }
default:
{
DALI_ASSERT_DEBUG(0 && "Unknown command");
}
private:
- std::string mPropertyCommand; ///< property command
+ std::string mCommandString; ///< command string for property or custom command
ClientSendDataInterface& mSendDataInterface; ///< Abstract client send data interface
CommandId mCommandId; ///< command id
const unsigned int mClientId; ///< client id
};
+/**
+ * @brief Helper to ensure the AutomationCallback method we want is called in the main thread
+ */
+template<typename T>
+void TriggerOnMainThread(unsigned int clientId, ClientSendDataInterface& sendDataInterface, T&& lambda)
+{
+ // this needs to be run on the main thread, use the trigger event....
+ AutomationCallback* callback = new AutomationCallback(clientId, sendDataInterface);
+ lambda(callback);
+
+ // create a trigger event that automatically deletes itself after the callback has run in the main thread
+ TriggerEventInterface* interface = TriggerEventFactory::CreateTriggerEvent(callback, TriggerEventInterface::DELETE_AFTER_TRIGGER);
+
+ // asynchronous call, the call back will be run sometime later on the main thread
+ interface->Trigger();
+}
+
} // unnamed namespace
NetworkPerformanceClient::NetworkPerformanceClient(pthread_t* thread,
case PerformanceProtocol::DUMP_SCENE_GRAPH:
{
- // this needs to be run on the main thread, use the trigger event....
- AutomationCallback* callback = new AutomationCallback(mClientId, mSendDataInterface);
- callback->AssignDumpSceneCommand();
-
- // create a trigger event that automatically deletes itself after the callback has run in the main thread
- TriggerEventInterface* interface = TriggerEventFactory::CreateTriggerEvent(callback, TriggerEventInterface::DELETE_AFTER_TRIGGER);
-
- // asynchronous call, the call back will be run sometime later on the main thread
- interface->Trigger();
+ TriggerOnMainThread(mClientId, mSendDataInterface, [&](AutomationCallback* callback){callback->AssignDumpSceneCommand();});
break;
}
case PerformanceProtocol::SET_PROPERTIES:
{
- // this needs to be run on the main thread, use the trigger event....
- AutomationCallback* callback = new AutomationCallback(mClientId, mSendDataInterface);
- callback->AssignSetPropertyCommand(stringParam);
-
- // create a trigger event that automatically deletes itself after the callback has run in the main thread
- TriggerEventInterface* interface = TriggerEventFactory::CreateTriggerEvent(callback, TriggerEventInterface::DELETE_AFTER_TRIGGER);
+ TriggerOnMainThread(mClientId, mSendDataInterface, [&](AutomationCallback* callback){callback->AssignSetPropertyCommand(stringParam);});
+ response = "Completed";
+ break;
+ }
- // asynchronous call, the call back will be run sometime later on the main thread
- interface->Trigger();
+ case PerformanceProtocol::CUSTOM_COMMAND:
+ {
+ TriggerOnMainThread(mClientId, mSendDataInterface, [&](AutomationCallback* callback){callback->AssignCustomCommand(std::move(stringParam));});
+ response = "Completed";
break;
}
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{ENABLE_TIME_MARKER_BIT_MASK, "set_marker", UNSIGNED_INT},
{DUMP_SCENE_GRAPH, "dump_scene", NO_PARAMS },
{SET_PROPERTIES, "set_properties", STRING },
+ {CUSTOM_COMMAND, "custom_command", STRING },
{UNKNOWN_COMMAND, "unknown", NO_PARAMS }
};
// clang-format on
GREEN " set_properties " PARAM "|ActorIndex;Property;Value|" NORMAL ", e.g: \n"
GREEN " set_properties " PARAM "|178;Size;[ 144.0, 144.0, 144.0 ]|178;Color;[ 1.0, 1,0, 1.0 ]|\n"
"\n"
+ GREEN " custom_command " NORMAL " - A custom command for an application. Format:\n\n"
+ GREEN " custom_command " PARAM "ANY_STRING" NORMAL "\n"
+ "\n"
GREEN " dump_scene" NORMAL " - dump the current scene in json format\n";
// clang-format off
} // un-named namespace
#define DALI_INTERNAL_ADAPTOR_NETWORK_PERFORMANCE_PROTOCOL_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
ENABLE_TIME_MARKER_BIT_MASK = 4, ///< bit mask of time markers to enable
SET_PROPERTIES = 5, ///< set property
DUMP_SCENE_GRAPH = 6, ///< dump the scene graph
+ CUSTOM_COMMAND = 7, ///< custom command for the application
UNKNOWN_COMMAND = 4096
};
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/internal/network/common/network-service-impl.h>
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/common/singleton-service.h>
+
+namespace Dali::Internal::Adaptor
+{
+NetworkServicePtr NetworkService::Get()
+{
+ NetworkServicePtr networkService;
+
+ Dali::SingletonService service(SingletonService::Get());
+ if(service)
+ {
+ // Check whether the singleton is already created
+ Dali::BaseHandle handle = service.GetSingleton(typeid(NetworkService));
+ if(handle)
+ {
+ // If so, downcast the handle
+ networkService = NetworkServicePtr(dynamic_cast<NetworkService*>(handle.GetObjectPtr()));
+ }
+ else
+ {
+ // Create a singleon instance
+ networkService = NetworkServicePtr(new NetworkService());
+ service.Register(typeid(NetworkService), BaseHandle(networkService.Get()));
+ }
+ }
+
+ return networkService;
+}
+
+void NetworkService::EmitCustomCommandReceivedSignal(const std::string& message)
+{
+ if(!mCustomCommandReceivedSignal.Empty())
+ {
+ mCustomCommandReceivedSignal.Emit(message);
+ }
+}
+
+} // namespace Dali::Internal::Adaptor
--- /dev/null
+#ifndef DALI_INTERNAL_NETWORK_SERVICE_H
+#define DALI_INTERNAL_NETWORK_SERVICE_H
+
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/object/base-object.h>
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/application-devel.h>
+
+namespace Dali
+{
+namespace Internal::Adaptor
+{
+
+class NetworkService;
+using NetworkServicePtr = IntrusivePtr<NetworkService>;
+
+/**
+ * Provides methods to interface with the network services offered by DALi.
+ *
+ * This is only available when the adaptor is built with with -DENABLE_NETWORK_LOGGING=ON
+ * and when running, DALI_NETWORK_CONTROL=1 must also be set.
+ */
+class NetworkService : public Dali::BaseObject
+{
+public:
+ NetworkService() = default; ///< Constructor
+ ~NetworkService() override = default; ///< Default Destructor
+ NetworkService(const NetworkService&) = delete; ///< Deleted copy constructor
+ NetworkService(NetworkService&&) = delete; ///< Deleted move constructor
+ NetworkService& operator=(NetworkService&) = delete; ///< Deleted copy assignment operator
+ NetworkService& operator=(NetworkService&&) = delete; ///< Deleted move assignment operator
+
+ /**
+ * @brief Retrieve the NetworkService Singleton.
+ * @return An intrusive pointer to the NetworkService
+ */
+ static NetworkServicePtr Get();
+
+ /**
+ * @brief This signal will be triggered when a custom command is received.
+ * @return The signal when a custom command is received
+ */
+ DevelApplication::CustomCommandReceivedSignalType& CustomCommandReceivedSignal()
+ {
+ return mCustomCommandReceivedSignal;
+ }
+
+ /**
+ * @brief Emit the Custom Command Received Signal
+ */
+ void EmitCustomCommandReceivedSignal(const std::string& message);
+
+private:
+ DevelApplication::CustomCommandReceivedSignalType mCustomCommandReceivedSignal;
+
+}; // class NetworkService
+
+} // namespace Internal::Adaptor
+
+} // namespace Dali
+
+#endif // DALI_INTERNAL_NETWORK_SERVICE_H
# module: network, backend: common
SET( adaptor_network_common_src_files
- ${adaptor_network_dir}/common/socket-factory.cpp
+ ${adaptor_network_dir}/common/network-service-impl.cpp
+ ${adaptor_network_dir}/common/socket-factory.cpp
${adaptor_network_dir}/common/socket-impl.cpp
)
DALI_LOG_WARNING("Unable to read to UpdateEvent File descriptor\n");
}
+ // Save value to prevent duplicate deletion
+ TriggerEventInterface::Options options = mOptions;
+
// Call the connected callback
CallbackBase::Execute(*mCallback);
//check if we should delete ourselves after the trigger
- if(mOptions == TriggerEventInterface::DELETE_AFTER_TRIGGER)
+ if(options == TriggerEventInterface::DELETE_AFTER_TRIGGER)
{
delete this;
}
* Set content information to widget framework
*/
virtual void SetContentInfo(const std::string& contentInfo) = 0;
+
+ /**
+ * Check Widget is using key
+ */
+ virtual bool IsKeyEventUsing() const = 0;
+
+ /**
+ * Set the flag that widget is using keyEvent
+ */
+ virtual void SetUsingKeyEvent(bool flag) = 0;
+
+ /**
+ * Set the Information of widget
+ */
+ virtual void SetInformation(Dali::Window window, const std::string& widgetId) = 0;
+
+ /**
+ * Get the window
+ */
+ virtual Dali::Window GetWindow() const = 0;
+
+ /**
+ * Get the widget id
+ */
+ virtual std::string GetWidgetId() const = 0;
};
} // namespace Adaptor
#include <dali/internal/system/tizen-wayland/widget-controller-tizen.h>
#include <dali/public-api/adaptor-framework/widget-impl.h>
#include <dali/public-api/adaptor-framework/widget.h>
+#include <dali/devel-api/events/key-event-devel.h>
// EXTERNAL INCLUDES
#include <bundle.h>
{
namespace
{
+/**
+ * This Api is called when widget viewer send keyEvent.
+ * In this API, widget framework create a new keyEvent, find the proper widget and send this event.
+ * Finally widget framework receive feedback from widget.
+ */
+#ifdef OVER_TIZEN_VERSION_7
+bool OnKeyEventCallback(const char *id, screen_connector_event_type_e eventType, int keyCode, const char *keyName, long long cls, long long subcls, const char* identifier, long long timestamp, void *userData)
+{
+ Dali::Internal::Adaptor::WidgetApplicationTizen* application = static_cast<Dali::Internal::Adaptor::WidgetApplicationTizen*>(userData);
+
+ // Create new key for widget
+ Dali::KeyEvent::State state = Dali::KeyEvent::DOWN;
+ if(eventType == SCREEN_CONNECTOR_EVENT_TYPE_KEY_DOWN)
+ {
+ state = Dali::KeyEvent::DOWN;
+ }
+ else if(eventType == SCREEN_CONNECTOR_EVENT_TYPE_KEY_UP)
+ {
+ state = Dali::KeyEvent::UP;
+ }
+
+ bool consumed = true;
+ std::string keyEventName = std::string(keyName);
+ Dali::KeyEvent event = Dali::DevelKeyEvent::New(keyEventName, "", "", keyCode, 0, timestamp, state, "", "", Device::Class::NONE, Device::Subclass::NONE);
+
+ if(application)
+ {
+ std::string widgetId = std::string(id);
+ widget_base_instance_h instanceHandle = application->GetWidgetInstanceFromWidgetId(widgetId);
+ if(instanceHandle)
+ {
+ consumed = application->FeedKeyEvent(instanceHandle, event);
+ }
+ }
+
+ return consumed;
+}
+#endif
+
int OnInstanceInit(widget_base_instance_h instanceHandle, bundle* content, int w, int h, void* classData)
{
char* id;
Dali::WidgetApplication::CreateWidgetFunction createFunction = pair.second;
Dali::Widget widgetInstance = createFunction(pair.first);
- application->AddWidget(instanceHandle, widgetInstance, window);
Dali::Internal::Adaptor::Widget::Impl* widgetImpl = new Dali::Internal::Adaptor::WidgetImplTizen(instanceHandle);
Internal::Adaptor::GetImplementation(widgetInstance).SetImpl(widgetImpl);
+ application->AddWidget(instanceHandle, widgetInstance, window, std::string(id));
+
std::string encodedContentString = "";
if(bundle_get_count(content))
Internal::Adaptor::GetImplementation(widgetInstance).OnCreate(encodedContentString, window);
+ // connect keyEvent for widget
+#ifdef OVER_TIZEN_VERSION_7
+ application->ConnectKeyEvent(window);
+#endif
+
return 0;
}
// Get Dali::Widget instance.
Dali::Widget widgetInstance = application->GetWidget(instanceHandle);
- Dali::Window window = application->GetWindowFromWidget(instanceHandle);
+ Dali::Window window = application->GetWindowFromWidget(widgetInstance);
window.SetSize(Dali::Window::WindowSize(w, h));
Internal::Adaptor::GetImplementation(widgetInstance).OnResize(window);
}
WidgetApplicationTizen::WidgetApplicationTizen(int* argc, char** argv[], const std::string& stylesheet)
-: WidgetApplication(argc, argv, stylesheet)
+: WidgetApplication(argc, argv, stylesheet),
+ mConnectedKeyEvent(false),
+ mReceivedKeyEvent(false)
{
}
return CreateWidgetFunctionPair("", NULL);
}
-void WidgetApplicationTizen::AddWidget(widget_base_instance_h widgetBaseInstance, Dali::Widget widget, Dali::Window window)
+void WidgetApplicationTizen::AddWidget(widget_base_instance_h widgetBaseInstance, Dali::Widget widget, Dali::Window window, const std::string& widgetId)
{
mWidgetInstanceContainer.push_back(WidgetInstancePair(widgetBaseInstance, widget));
- mWindowInstanceContainer.push_back(WindowInstancePair(widgetBaseInstance, window));
+ Internal::Adaptor::GetImplementation(widget).SetInformation(window, widgetId);
}
Dali::Widget WidgetApplicationTizen::GetWidget(widget_base_instance_h widgetBaseInstance) const
{
mWidgetInstanceContainer.erase(widgetInstance);
}
+}
- // Delete WindowInstance
- auto windowInstance = std::find_if(mWindowInstanceContainer.begin(),
- mWindowInstanceContainer.end(),
- [widgetBaseInstance](WindowInstancePair pair) { return (pair.first == widgetBaseInstance); });
-
- if(windowInstance != mWindowInstanceContainer.end())
+Dali::Window WidgetApplicationTizen::GetWindowFromWidget(Dali::Widget widgetInstance) const
+{
+ if(widgetInstance)
{
- mWindowInstanceContainer.erase(windowInstance);
+ return Internal::Adaptor::GetImplementation(widgetInstance).GetWindow();
}
+
+ return Dali::Window();
}
-Dali::Window WidgetApplicationTizen::GetWindowFromWidget(widget_base_instance_h widgetBaseInstance) const
+widget_base_instance_h WidgetApplicationTizen::GetWidgetInstanceFromWidgetId(std::string& widgetId) const
{
- for(auto&& iter : mWindowInstanceContainer)
+ for(auto&& iter : mWidgetInstanceContainer)
{
- if((iter).first == widgetBaseInstance)
+ if(widgetId == Internal::Adaptor::GetImplementation((iter).second).GetWidgetId())
{
- Dali::Window ret = (iter).second;
- return ret;
+ return (iter).first;
}
}
- return Dali::Window();
+
+ return nullptr;
}
int WidgetApplicationTizen::GetWidgetCount()
return mWidgetInstanceContainer.size();
}
+void WidgetApplicationTizen::ConnectKeyEvent(Dali::Window window)
+{
+ if(!mConnectedKeyEvent)
+ {
+#ifdef OVER_TIZEN_VERSION_7
+ screen_connector_provider_set_key_event_cb(OnKeyEventCallback, this);
+#endif
+ mConnectedKeyEvent = true;
+ }
+ window.KeyEventSignal().Connect(this, &WidgetApplicationTizen::OnWindowKeyEvent);
+}
+
+void WidgetApplicationTizen::OnWindowKeyEvent(const Dali::KeyEvent& event)
+{
+ //If Widget Application consume key event, this api is not called.
+ mReceivedKeyEvent = true;
+}
+
+bool WidgetApplicationTizen::FeedKeyEvent(widget_base_instance_h instanceHandle, const Dali::KeyEvent& keyEvent)
+{
+ bool consumed = true;
+
+ // Check if application consume key event
+ Dali::Widget widgetInstance = GetWidget(instanceHandle);
+ if(widgetInstance)
+ {
+ Dali::Window window = GetWindowFromWidget(widgetInstance);
+
+ // Reset the state of key received
+ mReceivedKeyEvent = false;
+
+ // Feed the keyEvent to widget window
+ DevelWindow::FeedKeyEvent(window, keyEvent);
+
+ // if the application is not using a key event, verify that the window in the widget has received a key event.
+ if(Internal::Adaptor::GetImplementation(widgetInstance).IsKeyEventUsing() == false)
+ {
+ // if the window has received a key event, widget need to consume its key event
+ consumed = (mReceivedKeyEvent) ? false : true;
+ }
+ }
+
+ return consumed;
+}
+
void WidgetApplicationTizen::OnInit()
{
WidgetApplication::OnInit();
// EXTERNAL INCLUDES
#include <widget_base.h>
+#include <screen_connector_provider.h>
// INTERNAL INCLUDES
#include <dali/devel-api/adaptor-framework/window-devel.h>
/**
* Implementation of the WidgetApplication class.
*/
-class WidgetApplicationTizen : public WidgetApplication
+class WidgetApplicationTizen : public WidgetApplication, public ConnectionTracker
{
public:
typedef std::pair<const std::string, Dali::WidgetApplication::CreateWidgetFunction> CreateWidgetFunctionPair;
/**
* Add widget_base_instance_h - Widget instance pair to container.
*/
- void AddWidget(widget_base_instance_h widgetBaseInstance, Dali::Widget widget, Dali::Window window);
+ void AddWidget(widget_base_instance_h widgetBaseInstance, Dali::Widget widget, Dali::Window window, const std::string& widgetId);
/**
* Find and get Widget instance in container by widget_base_instance_h.
/**
* Find and get Window instance in container by widget_base_instance_h.
*/
- Dali::Window GetWindowFromWidget(widget_base_instance_h widgetBaseInstance) const;
+ Dali::Window GetWindowFromWidget(Dali::Widget widget) const;
/**
+ * Find and get widget_base_instance in container by widget id.
+ */
+ widget_base_instance_h GetWidgetInstanceFromWidgetId(std::string& widgetId) const;
+ /**
* Get the number of created widget.
*/
int32_t GetWidgetCount();
+ /**
+ * @brief connect the keyEvent for window
+ *
+ * @param[in] window window for connecting keyEvent
+ */
+ void ConnectKeyEvent(Dali::Window window);
+
+ /**
+ * @brief Callback for widget window
+ *
+ * If Widget Application consume key event, this api is not called.
+ *
+ * @param[in] event The key event.
+ */
+ void OnWindowKeyEvent(const Dali::KeyEvent& event);
+
+ /**
+ * @brief Feed keyEvent to Widget.
+ *
+ * @param[in] instanceHandle the handle of widget instance
+ * @param[in] keyEvent The key event for widget
+ * @return True if widget consume keyEvent, false otherwise.
+ */
+ bool FeedKeyEvent(widget_base_instance_h instanceHandle, const Dali::KeyEvent& keyEvent);
+
protected:
/**
* Private Constructor
CreateWidgetFunctionContainer mCreateWidgetFunctionContainer;
WidgetInstanceContainer mWidgetInstanceContainer;
- typedef std::pair<widget_base_instance_h, Dali::Window> WindowInstancePair;
- typedef std::vector<WindowInstancePair> WindowInstanceContainer;
- WindowInstanceContainer mWindowInstanceContainer;
+ bool mConnectedKeyEvent; // Check if keyEvent is connected
+ bool mReceivedKeyEvent; // Check if application receive keyEvent
};
} // namespace Adaptor
{
WidgetImplTizen::WidgetImplTizen(widget_base_instance_h instanceHandle)
: Widget::Impl(),
- mInstanceHandle(instanceHandle)
+ mInstanceHandle(instanceHandle),
+ mWindow(),
+ mWidgetId(),
+ mUsingKeyEvent(false)
{
}
bundle_free(contentBundle);
}
+bool WidgetImplTizen::IsKeyEventUsing() const
+{
+ return mUsingKeyEvent;
+}
+
+void WidgetImplTizen::SetUsingKeyEvent(bool flag)
+{
+ mUsingKeyEvent = flag;
+}
+
+void WidgetImplTizen::SetInformation(Dali::Window window, const std::string& widgetId)
+{
+ mWindow = window;
+ mWidgetId = widgetId;
+}
+
+Dali::Window WidgetImplTizen::GetWindow() const
+{
+ return mWindow;
+}
+
+std::string WidgetImplTizen::GetWidgetId() const
+{
+ return mWidgetId;
+}
+
} // namespace Adaptor
} // namespace Internal
*/
void SetContentInfo(const std::string& contentInfo) override;
+ /**
+ * Check Widget is using key
+ */
+ bool IsKeyEventUsing() const override;
+
+ /**
+ * Set the flag that widget is using keyEvent
+ */
+ void SetUsingKeyEvent(bool flag) override;
+
+ /**
+ * Set the Information of widget
+ */
+ void SetInformation(Dali::Window window, const std::string& widgetId) override;
+
+ /**
+ * Get the window
+ */
+ Dali::Window GetWindow() const override;
+
+ /**
+ * Get the widget id
+ */
+ std::string GetWidgetId() const override;
+
private:
widget_base_instance_h mInstanceHandle;
+ Dali::Window mWindow;
+ std::string mWidgetId;
+ bool mUsingKeyEvent;
};
} // namespace Adaptor
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
}
+bool WidgetImplUbuntu::IsKeyEventUsing() const
+{
+ return false;
+}
+
+void WidgetImplUbuntu::SetUsingKeyEvent(bool flag)
+{
+}
+
+void WidgetImplUbuntu::SetInformation(Dali::Window window, const std::string& widgetId)
+{
+}
+
+Dali::Window WidgetImplUbuntu::GetWindow() const
+{
+ return Dali::Window();
+}
+
+std::string WidgetImplUbuntu::GetWidgetId() const
+{
+ return std::string();
+}
+
} // namespace Adaptor
} // namespace Internal
* Set content information to widget framework
*/
void SetContentInfo(const std::string& contentInfo) override;
+
+ /**
+ * Check Widget is using key
+ */
+ bool IsKeyEventUsing() const override;
+
+ /**
+ * Set the flag that widget is using keyEvent
+ */
+ void SetUsingKeyEvent(bool flag) override;
+
+ /**
+ * Set the Information of widget
+ */
+ void SetInformation(Dali::Window window, const std::string& widgetId) override;
+
+ /**
+ * Get the window
+ */
+ Dali::Window GetWindow() const override;
+
+ /**
+ * Get the widget id
+ */
+ std::string GetWidgetId() const override;
};
} // namespace Adaptor
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
}
+bool WidgetImplWin::IsKeyEventUsing() const
+{
+ return false;
+}
+
+void WidgetImplWin::SetUsingKeyEvent(bool flag)
+{
+}
+
+void WidgetImplWin::SetInformation(Dali::Window window, const std::string& widgetId)
+{
+}
+
+Dali::Window WidgetImplWin::GetWindow() const
+{
+ return Dali::Window();
+}
+
+std::string WidgetImplWin::GetWidgetId() const
+{
+ return std::string();
+}
+
} // namespace Adaptor
} // namespace Internal
* Set content information to widget framework
*/
void SetContentInfo(const std::string& contentInfo) override;
+
+ /**
+ * Check Widget is using key
+ */
+ bool IsKeyEventUsing() const override;
+
+ /**
+ * Set the flag that widget is using keyEvent
+ */
+ void SetUsingKeyEvent(bool flag) override;
+
+ /**
+ * Set the Information of widget
+ */
+ void SetInformation(Dali::Window window, const std::string& widgetId) override;
+
+ /**
+ * Get the window
+ */
+ Dali::Window GetWindow() const override;
+
+ /**
+ * Get the widget id
+ */
+ std::string GetWidgetId() const override;
};
} // namespace Adaptor
${adaptor_text_dir}/text-abstraction/plugin/font-client-utils.cpp
${adaptor_text_dir}/text-abstraction/plugin/font-client-plugin-impl.cpp
${adaptor_text_dir}/text-abstraction/plugin/font-face-cache-item.cpp
+ ${adaptor_text_dir}/text-abstraction/plugin/font-face-glyph-cache-manager.cpp
+ ${adaptor_text_dir}/text-abstraction/plugin/harfbuzz-proxy-font.cpp
)
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// Retrieve the image
TextAbstraction::FontClient::GlyphBufferData data;
- std::unique_ptr<GlyphBuffer> glyphBufferPtr(new GlyphBuffer(data, GlyphBuffer::DELETE));
if(isEmoji)
{
data.width = parameters.glyphs[run.glyphIndex].width;
unsigned int heightOut = data.height;
const unsigned int pixelSize = Pixel::GetBytesPerPixel(data.format);
+ // If we need to decompress, create new memory and replace ownership.
+ if(data.compressionType != TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION)
+ {
+ uint8_t* newBuffer = (uint8_t*)malloc(widthOut * heightOut * pixelSize);
+ if(DALI_LIKELY(newBuffer != nullptr))
+ {
+ TextAbstraction::FontClient::GlyphBufferData::Decompress(data, newBuffer);
+ if(data.isBufferOwned)
+ {
+ // Release previous buffer
+ free(data.buffer);
+ }
+ data.isBufferOwned = true;
+ data.buffer = newBuffer;
+ data.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+ }
+ }
+
Dali::Internal::Platform::RotateByShear(data.buffer,
data.width,
data.height,
+ data.width,
pixelSize,
radians,
pixelsOut,
heightOut);
if(nullptr != pixelsOut)
{
- delete[] data.buffer;
- data.buffer = pixelsOut;
- glyphBufferPtr.get()->type = GlyphBuffer::FREE;
- data.width = widthOut;
- data.height = heightOut;
+ if(data.isBufferOwned)
+ {
+ free(data.buffer);
+ }
+ data.isBufferOwned = true;
+ data.compressionType = Dali::TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+ data.buffer = pixelsOut;
+ data.width = widthOut;
+ data.height = heightOut;
}
glyphX = centerX - 0.5 * static_cast<double>(data.width);
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return mPlugin->AddCustomFontDirectory(path);
}
+HarfBuzzFontHandle FontClient::GetHarfBuzzFont(FontId fontId)
+{
+ CreatePlugin();
+
+ return mPlugin->GetHarfBuzzFont(fontId);
+}
+
void FontClient::CreatePlugin()
{
if(!mPlugin)
#define DALI_INTERNAL_TEXT_ABSTRACTION_FONT_CLIENT_IMPL_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
namespace Internal
{
+using HarfBuzzFontHandle = void*; ///< @note We don't want to make other class include harfbuzz header. So we will keep harfbuzz font data as HarfBuzzFontHandle.
+
/**
* Implementation of the FontClient
*/
*/
~FontClient();
+public: // API for Dali::TextAbstraction::FontClient used.
/**
* @copydoc Dali::TextAbstraction::FontClient::Get()
*/
uint32_t GetNumberOfPointsPerOneUnitOfPointSize() const;
/**
+ * @copydoc Dali::TextAbstraction::FontClient::AddCustomFontDirectory()
+ */
+ bool AddCustomFontDirectory(const FontPath& path);
+
+public: // API for Dali::TextAbstraction::Internal::FontClient used.
+ /**
* @brief Retrieves the pointer to the FreeType Font Face for the given @p fontId.
*
* @param[in] fontId The font id.
FontDescription::Type GetFontType(FontId fontId);
/**
- * @copydoc Dali::TextAbstraction::FontClient::AddCustomFontDirectory()
+ * @brief Get the harfbuzz font data of font.
+ *
+ * @param fontId The font id.
+ * @return The harfbuzz font data, or nullptr if failed.
*/
- bool AddCustomFontDirectory(const FontPath& path);
+ HarfBuzzFontHandle GetHarfBuzzFont(FontId fontId);
private:
/**
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
metrics.underlineThickness = font.underlineThickness;
}
-bool BitmapFontCacheItem::GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertical, bool horizontal) const
+bool BitmapFontCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVertical, bool horizontal) const
{
bool success(false);
unsigned int index = 0u;
for(auto& item : font.glyphs)
{
- if(item.utf32 == glyph.index)
+ if(item.utf32 == glyphInfo.index)
{
Devel::PixelBuffer& pixelBuffer = const_cast<Devel::PixelBuffer&>(pixelBuffers[index]);
if(!pixelBuffer)
pixelBuffer = LoadImageFromFile(item.url);
}
- glyph.width = static_cast<float>(pixelBuffer.GetWidth());
- glyph.height = static_cast<float>(pixelBuffer.GetHeight());
- glyph.xBearing = 0.f;
- glyph.yBearing = glyph.height + item.descender;
- glyph.advance = glyph.width;
- glyph.scaleFactor = 1.f;
- success = true;
+ glyphInfo.width = static_cast<float>(pixelBuffer.GetWidth());
+ glyphInfo.height = static_cast<float>(pixelBuffer.GetHeight());
+ glyphInfo.xBearing = 0.f;
+ glyphInfo.yBearing = glyphInfo.height + item.descender;
+ glyphInfo.advance = glyphInfo.width;
+ glyphInfo.scaleFactor = 1.f;
+ success = true;
break;
}
++index;
data.isColorBitmap = font.isColorFont;
- ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer());
-
- // Sets the pixel format.
- data.format = pixelBuffer.GetPixelFormat();
+ ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer(), pixelBuffer.GetPixelFormat());
break;
}
++index;
#define DALI_INTERNAL_TEXT_ABSTRACTION_BITMAP_FONT_CACHE_ITEM_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
BitmapFontCacheItem(const BitmapFont& bitmapFont, FontId fontId);
/**
+ * Destructor
+ */
+ ~BitmapFontCacheItem() = default;
+
+ /**
* @copydoc FontCacheItemInterface::GetFontMetrics()
*/
void GetFontMetrics(FontMetrics& metrics, unsigned int dpiVertical) const override;
/**
* @copydoc FontCacheItemInterface::GetGlyphMetrics()
*/
- bool GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertical, bool horizontal) const override;
+ bool GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVertical, bool horizontal) const override;
/**
* @copydoc FontCacheItemInterface::CreateBitmap()
}
/**
+ * @copydoc FontCacheItemInterface::GetHarfBuzzFont()
+ */
+ HarfBuzzFontHandle GetHarfBuzzFont(const uint32_t& horizontalDpi, const uint32_t& verticalDpi) override
+ {
+ return nullptr;
+ }
+
+ /**
* @copydoc FontCacheItemInterface::HasItalicStyle()
*/
bool HasItalicStyle() const override
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Devel::PixelBuffer pixelBuffer = pixelBufferCache[pixelBufferId - 1u].pixelBuffer;
if(pixelBuffer)
{
- ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer());
-
- // Sets the pixel format.
- data.format = pixelBuffer.GetPixelFormat();
+ ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer(), pixelBuffer.GetPixelFormat());
}
}
else
{
+ data.isBufferOwned = true;
+ data.compressionType = Dali::TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+
// Creates the output buffer
- const unsigned int bufferSize = data.width * data.height * 4u;
- data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
+ const uint32_t bufferSize = data.width * data.height * 4u;
+ data.buffer = (uint8_t*)malloc(bufferSize); // @note The caller is responsible for deallocating the bitmap data using free.
memset(data.buffer, 0u, bufferSize);
#define DALI_TEST_ABSTRACTION_INTERNAL_FONT_CACHE_ITEM_INTERFACE_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
+// INTERNAL INCLUDES
#include <dali/devel-api/text-abstraction/font-client.h>
#include <dali/devel-api/text-abstraction/font-metrics.h>
#include <dali/devel-api/text-abstraction/glyph-info.h>
+#include <dali/internal/text/text-abstraction/font-client-impl.h> // for HarfBuzzFontHandle
+// EXTERNAL INCLUDES
#include <ft2build.h>
#include FT_FREETYPE_H
*
* @param[in,out] glyph The glyph to fill
*/
- virtual bool GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertical, bool horizontal) const = 0;
+ virtual bool GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVertical, bool horizontal) const = 0;
/**
* Create a bitmap for the given glyph
virtual FT_Face GetTypeface() const = 0;
/**
+ * Get the harfbuzz font struct for this font.
+ *
+ * @param[in] horizontalDpi Horizontal DPI for this harfbuzz font.
+ * @param[in] verticalDpi Vertical DPI for this harfbuzz font.
+ * @return the harfbuzz font data, or nullptr if failed.
+ */
+ virtual HarfBuzzFontHandle GetHarfBuzzFont(const uint32_t& horizontalDpi, const uint32_t& verticalDpi) = 0;
+
+ /**
* @return true if this font has an italic style
*/
virtual bool HasItalicStyle() const = 0;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
}
-FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem(FontDescriptionId validatedFontId,
- PointSize26Dot6 requestedPointSize,
- FontId fontId)
-: validatedFontId(validatedFontId),
- requestedPointSize(requestedPointSize),
- fontId(fontId)
+FontClient::Plugin::FontDescriptionSizeCacheKey::FontDescriptionSizeCacheKey(FontDescriptionId fontDescriptionId,
+ PointSize26Dot6 requestedPointSize)
+: fontDescriptionId(fontDescriptionId),
+ requestedPointSize(requestedPointSize)
{
}
mVectorFontCache(nullptr),
mEllipsisCache(),
mEmbeddedItemCache(),
+ mLatestFoundFontDescription(),
+ mLatestFoundFontDescriptionId(0u),
+ mLatestFoundCacheKey(0, 0),
+ mLatestFoundCacheIndex(0u),
mDefaultFontDescriptionCached(false),
mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS)
DestroyCharacterSets(mCharacterSetCache);
ClearCharacterSetFromFontFaceCache();
+ // Clear FontFaceCache here. Due to we sould deallocate FT_Faces before done freetype library
+ mFontFaceCache.clear();
+
#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
delete mVectorFontCache;
#endif
mCharacterSetCache.Clear();
mFontDescriptionSizeCache.clear();
+ mFontDescriptionSizeCache.rehash(0); // Note : unordered_map.clear() didn't deallocate memory
mEllipsisCache.Clear();
mPixelBufferCache.clear();
mEmbeddedItemCache.Clear();
mBitmapFontCache.clear();
+ mLatestFoundFontDescription.family.clear();
+ mLatestFoundCacheKey = FontDescriptionSizeCacheKey(0, 0);
+
mDefaultFontDescriptionCached = false;
}
FcDefaultSubstitute(matchPattern);
FcCharSet* characterSet = nullptr;
- MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
+ bool matched = MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
+
+ // Caching the default font description
+ if(matched)
+ {
+ // Copy default font description info.
+ // Due to the type changed, we need to make some temperal font description.
+ FontDescription tempFontDescription = mDefaultFontDescription;
+
+ // Add the path to the cache.
+ tempFontDescription.type = FontDescription::FACE_FONT;
+ mFontDescriptionCache.push_back(tempFontDescription);
+
+ // Set the index to the vector of paths to font file names.
+ const FontDescriptionId fontDescriptionId = mFontDescriptionCache.size();
+
+ FONT_LOG_DESCRIPTION(tempFontDescription, "default platform font");
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " default font fontDescriptionId : %d\n", fontDescriptionId);
+
+ // Cache the index and the matched font's description.
+ FontDescriptionCacheItem item(tempFontDescription,
+ fontDescriptionId);
+
+ mValidatedFontCache.push_back(std::move(item));
+ }
+ else
+ {
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " default font validation failed for font [%s]\n", mDefaultFontDescription.family.c_str());
+ }
+
// Decrease the reference counter of the character set as it's not stored.
+ // Note. the cached default font description will increase reference counter by
+ // mFontDescriptionCache. So we can decrease reference counter here.
FcCharSetDestroy(characterSet);
// Destroys the pattern created.
{
for(const auto& item : mFontDescriptionSizeCache)
{
- if(item.fontId == fontIdCacheItem.id)
+ if(item.second == fontIdCacheItem.index)
{
- fontDescription = *(mFontDescriptionCache.begin() + item.validatedFontId - 1u);
+ fontDescription = *(mFontDescriptionCache.begin() + item.first.fontDescriptionId - 1u);
FONT_LOG_DESCRIPTION(fontDescription, "");
return;
case FontDescription::BITMAP_FONT:
{
fontDescription.type = FontDescription::BITMAP_FONT;
- fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
+ fontDescription.family = mBitmapFontCache[fontIdCacheItem.index].font.name;
break;
}
default:
const FontCacheItemInterface* FontClient::Plugin::GetCachedFontItem(FontId id) const
{
- const FontId index = id - 1u;
+ const FontCacheIndex index = id - 1u;
if((id > 0u) && (index < mFontIdCache.Count()))
{
const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
{
case FontDescription::FACE_FONT:
{
- return &mFontFaceCache[fontIdCacheItem.id];
+ return &mFontFaceCache[fontIdCacheItem.index];
}
case FontDescription::BITMAP_FONT:
{
- return &mBitmapFontCache[fontIdCacheItem.id];
+ return &mBitmapFontCache[fontIdCacheItem.index];
}
default:
{
if((fontId > 0) &&
(fontId - 1u < mFontIdCache.Count()))
{
- const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
+ const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].index];
foundColor = item.mHasColorTables;
}
{
DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
FONT_LOG_DESCRIPTION(fontDescription, "");
+
+ // Special case when font Description don't have family information.
+ // In this case, we just use default description family and path.
+ const FontDescription& realFontDescription = fontDescription.family.empty() ? FontDescription(mDefaultFontDescription.path,
+ mDefaultFontDescription.family,
+ fontDescription.width,
+ fontDescription.weight,
+ fontDescription.slant,
+ fontDescription.type)
+ : fontDescription;
+
+ FONT_LOG_DESCRIPTION(realFontDescription, "");
DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
// This method uses three vectors which caches:
FontId fontId = 0u;
// Check first if the font description matches with a previously loaded bitmap font.
- if(FindBitmapFont(fontDescription.family, fontId))
+ if(FindBitmapFont(realFontDescription.family, fontId))
{
return fontId;
}
// Check if the font's description have been validated before.
- FontDescriptionId validatedFontId = 0u;
+ FontDescriptionId fontDescriptionId = 0u;
- if(!FindValidatedFont(fontDescription,
- validatedFontId))
+ if(!FindValidatedFont(realFontDescription,
+ fontDescriptionId))
{
// Use font config to validate the font's description.
- ValidateFont(fontDescription,
- validatedFontId);
+ ValidateFont(realFontDescription,
+ fontDescriptionId);
}
- FontId fontFaceId = 0u;
- // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
- if(!FindFont(validatedFontId, requestedPointSize, fontFaceId))
+ FontCacheIndex fontCacheIndex = 0u;
+ // Check if exists a pair 'fontDescriptionId, requestedPointSize' in the cache.
+ if(!FindFont(fontDescriptionId, requestedPointSize, fontCacheIndex))
{
// Retrieve the font file name path.
- const FontDescription& description = *(mFontDescriptionCache.begin() + validatedFontId - 1u);
+ const FontDescription& description = *(mFontDescriptionCache.begin() + fontDescriptionId - 1u);
// Retrieve the font id. Do not cache the description as it has been already cached.
fontId = GetFontId(description.path,
faceIndex,
false);
- fontFaceId = mFontIdCache[fontId - 1u].id;
- mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(mCharacterSetCache[validatedFontId - 1u]);
+ fontCacheIndex = mFontIdCache[fontId - 1u].index;
+ mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(mCharacterSetCache[fontDescriptionId - 1u]);
- // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
- mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
- requestedPointSize,
- fontFaceId));
+ // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
+ mFontDescriptionSizeCache.emplace(FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontCacheIndex);
}
else
{
- fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
+ fontId = mFontFaceCache[fontCacheIndex].mFontId + 1u;
}
DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
BitmapFontCacheItem bitmapFontCacheItem(bitmapFont, mFontIdCache.Count());
FontIdCacheItem fontIdCacheItem;
- fontIdCacheItem.type = FontDescription::BITMAP_FONT;
- fontIdCacheItem.id = mBitmapFontCache.size();
+ fontIdCacheItem.type = FontDescription::BITMAP_FONT;
+ fontIdCacheItem.index = mBitmapFontCache.size();
mBitmapFontCache.push_back(std::move(bitmapFontCacheItem));
mFontIdCache.PushBack(fontIdCacheItem);
}
void FontClient::Plugin::ValidateFont(const FontDescription& fontDescription,
- FontDescriptionId& validatedFontId)
+ FontDescriptionId& fontDescriptionId)
{
DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
FONT_LOG_DESCRIPTION(fontDescription, "");
mFontDescriptionCache.push_back(description);
// Set the index to the vector of paths to font file names.
- validatedFontId = mFontDescriptionCache.size();
+ fontDescriptionId = mFontDescriptionCache.size();
FONT_LOG_DESCRIPTION(description, "matched");
- DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId);
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fontDescriptionId : %d\n", fontDescriptionId);
// The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
mCharacterSetCache.PushBack(characterSet);
// Cache the index and the matched font's description.
FontDescriptionCacheItem item(description,
- validatedFontId);
+ fontDescriptionId);
mValidatedFontCache.push_back(std::move(item));
{
// Cache the given font's description if it's different than the matched.
FontDescriptionCacheItem item(fontDescription,
- validatedFontId);
+ fontDescriptionId);
mValidatedFontCache.push_back(std::move(item));
}
if((fontId > 0u) &&
(fontId - 1u) < mFontIdCache.Count())
{
- FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
+ FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].index];
if(!font.mVectorFontId)
{
data.width,
data.height,
data.format,
- PixelData::DELETE_ARRAY);
+ PixelData::FREE);
}
void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
if((fontId > 0u) &&
(fontId - 1u < mFontIdCache.Count()))
{
- const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
- FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
+ const FontCacheIndex fontFaceId = mFontIdCache[fontId - 1u].index;
+ FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
if(!font.mVectorFontId)
{
false);
// Set the character index to access the glyph inside the font.
- item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].id].mFreeTypeFace,
+ item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].index].mFreeTypeFace,
ELLIPSIS_CHARACTER);
GetBitmapMetrics(&item.glyph, 1u, true);
return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
}
+HarfBuzzFontHandle FontClient::Plugin::GetHarfBuzzFont(FontId fontId)
+{
+ FontCacheItemInterface* fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
+ if(fontCacheItem != nullptr)
+ {
+ return fontCacheItem->GetHarfBuzzFont(mDpiHorizontal, mDpiVertical); // May cache
+ }
+ return nullptr;
+}
+
GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
{
EmbeddedItem embeddedItem;
fontIdCacheItem.type = FontDescription::FACE_FONT;
// Set the index to the FreeType font face cache.
- fontIdCacheItem.id = mFontFaceCache.size();
- fontFaceId = fontIdCacheItem.id + 1u;
+ fontIdCacheItem.index = mFontFaceCache.size();
+ fontFaceId = fontIdCacheItem.index + 1u;
// Cache the items.
- mFontFaceCache.push_back(fontFaceCacheItem);
+ mFontFaceCache.emplace_back(std::move(fontFaceCacheItem));
mFontIdCache.PushBack(fontIdCacheItem);
// Set the font id to be returned.
fontIdCacheItem.type = FontDescription::FACE_FONT;
// Set the index to the FreeType font face cache.
- fontIdCacheItem.id = mFontFaceCache.size();
- fontFaceId = fontIdCacheItem.id + 1u;
+ fontIdCacheItem.index = mFontFaceCache.size();
+ fontFaceId = fontIdCacheItem.index + 1u;
// Cache the items.
- mFontFaceCache.push_back(fontFaceCacheItem);
+ mFontFaceCache.emplace_back(std::move(fontFaceCacheItem));
mFontIdCache.PushBack(fontIdCacheItem);
// Set the font id to be returned.
}
bool FontClient::Plugin::FindValidatedFont(const FontDescription& fontDescription,
- FontDescriptionId& validatedFontId)
+ FontDescriptionId& fontDescriptionId)
{
DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
FONT_LOG_DESCRIPTION(fontDescription, "");
DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size());
- validatedFontId = 0u;
+ fontDescriptionId = 0u;
+
+ // Fast cut if inputed family is empty.
+ if(DALI_UNLIKELY(fontDescription.family.empty()))
+ {
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font description not found / fontDescription.family is empty!\n");
+ return false;
+ }
+
+ // Heuristic optimize code : Compare with latest found item.
+ if((fontDescription.width == mLatestFoundFontDescription.width) &&
+ (fontDescription.weight == mLatestFoundFontDescription.weight) &&
+ (fontDescription.slant == mLatestFoundFontDescription.slant) &&
+ (fontDescription.family == mLatestFoundFontDescription.family))
+ {
+ fontDescriptionId = mLatestFoundFontDescriptionId;
+
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font description same as latest, id : %d\n", fontDescriptionId);
+ return true;
+ }
for(const auto& item : mValidatedFontCache)
{
- if(!fontDescription.family.empty() &&
- (fontDescription.family == item.fontDescription.family) &&
- (fontDescription.width == item.fontDescription.width) &&
+ if((fontDescription.width == item.fontDescription.width) &&
(fontDescription.weight == item.fontDescription.weight) &&
- (fontDescription.slant == item.fontDescription.slant))
+ (fontDescription.slant == item.fontDescription.slant) &&
+ (fontDescription.family == item.fontDescription.family))
{
- validatedFontId = item.index;
+ fontDescriptionId = item.index;
+
+ mLatestFoundFontDescription = fontDescription;
+ mLatestFoundFontDescriptionId = fontDescriptionId;
- DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId);
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font description found, id : %d\n", fontDescriptionId);
return true;
}
}
- DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font not found\n");
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font description not found\n");
return false;
}
return false;
}
-bool FontClient::Plugin::FindFont(FontDescriptionId validatedFontId,
+bool FontClient::Plugin::FindFont(FontDescriptionId fontDescriptionId,
PointSize26Dot6 requestedPointSize,
- FontId& fontId)
+ FontCacheIndex& fontCacheIndex)
{
DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
- DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId);
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fontDescriptionId : %d\n", fontDescriptionId);
DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
- fontId = 0u;
+ fontCacheIndex = 0u;
- for(const auto& item : mFontDescriptionSizeCache)
+ const FontDescriptionSizeCacheKey key(fontDescriptionId, requestedPointSize);
+
+ // Heuristic optimize code : Compare with latest found item.
+ if(key == mLatestFoundCacheKey)
{
- if((validatedFontId == item.validatedFontId) &&
- (requestedPointSize == item.requestedPointSize))
- {
- fontId = item.fontId;
+ fontCacheIndex = mLatestFoundCacheIndex;
- DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font found, id : %d\n", fontId);
- return true;
- }
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font same as latest, index of font cache : %d\n", fontCacheIndex);
+ return true;
+ }
+
+ const auto& iter = mFontDescriptionSizeCache.find(key);
+ if(iter != mFontDescriptionSizeCache.cend())
+ {
+ fontCacheIndex = iter->second;
+
+ mLatestFoundCacheKey = key;
+ mLatestFoundCacheIndex = fontCacheIndex;
+
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font found, index of font cache : %d\n", fontCacheIndex);
+ return true;
}
DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font not found.\n");
description.weight = FontWeight::BOLD;
}
- FontDescriptionId validatedFontId = 0u;
+ FontDescriptionId fontDescriptionId = 0u;
if(!FindValidatedFont(description,
- validatedFontId))
+ fontDescriptionId))
{
FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
mFontDescriptionCache.push_back(description);
// Set the index to the vector of paths to font file names.
- validatedFontId = mFontDescriptionCache.size();
+ fontDescriptionId = mFontDescriptionCache.size();
// Increase the reference counter and add the character set to the cache.
mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
// Cache the index and the font's description.
mValidatedFontCache.push_back(std::move(FontDescriptionCacheItem(std::move(description),
- validatedFontId)));
+ fontDescriptionId)));
- // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
- mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
- requestedPointSize,
- fontFaceId));
+ // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
+ mFontDescriptionSizeCache.emplace(FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontFaceId);
}
}
#define DALI_INTERNAL_TEXT_ABSTRACTION_FONT_CLIENT_PLUGIN_IMPL_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#endif
// EXTERNAL INCLUDES
+#include <unordered_map>
+
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
*/
struct FontClient::Plugin
{
+private:
+ /// Redefine FontId name to specifiy the value's usage
+ using FontCacheIndex = FontId;
+
+ /**
+ * @brief Index of FontCache container.
+ */
struct FontIdCacheItem
{
- FontDescription::Type type; ///< The type of font.
- FontId id; ///< Index to the cache of fonts for the specified type.
+ FontDescription::Type type; ///< The type of font.
+ FontCacheIndex index; ///< Index to the cache of fonts for the specified type. Face or Bitmap
};
/**
};
/**
- * @brief Caches the font id of the pair font point size and the index to the vector of font descriptions of validated fonts.
+ * @brief Pair of FontDescriptionId and PointSize. It will be used to find cached validate font.
*/
- struct FontDescriptionSizeCacheItem
+ struct FontDescriptionSizeCacheKey
{
- FontDescriptionSizeCacheItem(FontDescriptionId validatedFontId,
- PointSize26Dot6 requestedPointSize,
- FontId fontId);
+ FontDescriptionSizeCacheKey(FontDescriptionId fontDescriptionId,
+ PointSize26Dot6 requestedPointSize);
- FontDescriptionId validatedFontId; ///< Index to the vector with font descriptions.
+ FontDescriptionId fontDescriptionId; ///< Index to the vector with font descriptions.
PointSize26Dot6 requestedPointSize; ///< The font point size.
- FontId fontId; ///< The font identifier.
+
+ bool operator==(FontDescriptionSizeCacheKey const& rhs) const noexcept
+ {
+ return fontDescriptionId == rhs.fontDescriptionId && requestedPointSize == rhs.requestedPointSize;
+ }
+ };
+
+ /**
+ * @brief Custom hash functions for FontDescriptionSizeCacheKey.
+ */
+ struct FontDescriptionSizeCacheKeyHash
+ {
+ std::size_t operator()(FontDescriptionSizeCacheKey const& key) const noexcept
+ {
+ return key.fontDescriptionId ^ key.requestedPointSize;
+ }
};
+ /**
+ * @brief Caches the font id of the pair font point size and the index to the vector of font descriptions of validated fonts.
+ */
+ using FontDescriptionSizeCacheContainer = std::unordered_map<FontDescriptionSizeCacheKey, FontCacheIndex, FontDescriptionSizeCacheKeyHash>;
+
struct EllipsisItem
{
PointSize26Dot6 requestedPointSize;
GlyphInfo glyph;
};
+public:
/**
* Constructor.
*
*/
bool AddCustomFontDirectory(const FontPath& path);
+ /**
+ * @copydoc Dali::TextAbstraction::FontClient::GetHarfBuzzFont()
+ */
+ HarfBuzzFontHandle GetHarfBuzzFont(FontId fontId);
+
private:
/**
* @brief Caches the fonts present in the platform.
/**
* @brief Finds in the cache a pair 'validated font identifier and font point size'.
- * If there is one it writes the font identifier in the param @p fontId.
+ * If there is one it writes the font identifier in the param @p fontCacheIndex.
*
* @param[in] validatedFontId Index to the vector with font descriptions.
* @param[in] requestedPointSize The font point size.
- * @param[out] fontId The font identifier.
+ * @param[out] fontCacheIndex The index of font cache identifier.
*
* @return @e true if the pair is found.
*/
bool FindFont(FontDescriptionId validatedFontId,
PointSize26Dot6 requestedPointSize,
- FontId& fontId);
+ FontCacheIndex& fontCacheIndex);
/**
* @brief Finds in the cache a bitmap font with the @p bitmapFont family name.
std::vector<FallbackCacheItem> mFallbackCache; ///< Cached fallback font lists.
- Vector<FontIdCacheItem> mFontIdCache;
- std::vector<FontFaceCacheItem> mFontFaceCache; ///< Caches the FreeType face and font metrics of the triplet 'path to the font file name, font point size and face index'.
- std::vector<FontDescriptionCacheItem> mValidatedFontCache; ///< Caches indices to the vector of font descriptions for a given font.
- FontList mFontDescriptionCache; ///< Caches font descriptions for the validated font.
- CharacterSetList mCharacterSetCache; ///< Caches character set lists for the validated font.
- std::vector<FontDescriptionSizeCacheItem> mFontDescriptionSizeCache; ///< Caches font identifiers for the pairs of font point size and the index to the vector with font descriptions of the validated fonts.
+ Vector<FontIdCacheItem> mFontIdCache; ///< Caches from FontId to FontCacheIndex.
+ std::vector<FontFaceCacheItem> mFontFaceCache; ///< Caches the FreeType face and font metrics of the triplet 'path to the font file name, font point size and face index'.
+ std::vector<FontDescriptionCacheItem> mValidatedFontCache; ///< Caches indices to the vector of font descriptions for a given font.
+ FontList mFontDescriptionCache; ///< Caches font descriptions for the validated font.
+ CharacterSetList mCharacterSetCache; ///< Caches character set lists for the validated font.
+
+ FontDescriptionSizeCacheContainer mFontDescriptionSizeCache; ///< Caches font identifiers for the pairs of font point size and the index to the vector with font descriptions of the validated fonts.
VectorFontCache* mVectorFontCache; ///< Separate cache for vector data blobs etc.
Vector<EmbeddedItem> mEmbeddedItemCache; ///< Cache embedded items.
std::vector<BitmapFontCacheItem> mBitmapFontCache; ///< Stores bitmap fonts.
+ FontDescription mLatestFoundFontDescription; ///< Latest found font description and id in FindValidatedFont()
+ FontDescriptionId mLatestFoundFontDescriptionId;
+
+ FontDescriptionSizeCacheKey mLatestFoundCacheKey; ///< Latest found font description and id in FindFont()
+ FontCacheIndex mLatestFoundCacheIndex;
+
bool mDefaultFontDescriptionCached : 1; ///< Whether the default font is cached or not
bool mIsAtlasLimitationEnabled : 1; ///< Whether the validation on maximum atlas block size, then reduce block size to fit into it is enabled or not.
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* @param[in] srcHeight The height of the bitmap.
* @param[in] srcBuffer The buffer of the bitmap.
*/
-void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer)
+void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer, const Pixel::Format srcFormat)
{
// Set the input dimensions.
const ImageDimensions inputDimensions(srcWidth, srcHeight);
data.height = (data.height == 0) ? srcHeight : data.height;
const ImageDimensions desiredDimensions(data.width, data.height);
+ data.format = srcFormat;
+
+ // Note we don't compress here
+ data.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+
+ const uint32_t bytePerPixel = Dali::Pixel::GetBytesPerPixel(srcFormat);
+
// Creates the output buffer
- const unsigned int bufferSize = data.width * data.height * 4u;
- data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
+ const uint32_t bufferSize = data.width * data.height * bytePerPixel;
if(inputDimensions == desiredDimensions)
{
// There isn't downscaling.
- memcpy(data.buffer, srcBuffer, bufferSize);
+ data.isBufferOwned = false;
+ data.buffer = const_cast<uint8_t*>(srcBuffer);
}
else
{
- Dali::Internal::Platform::LanczosSample4BPP(srcBuffer,
- inputDimensions,
- data.buffer,
- desiredDimensions);
+ data.isBufferOwned = true;
+ data.buffer = (uint8_t*)malloc(bufferSize); // @note The caller is responsible for deallocating the bitmap data using free.
+ Dali::Internal::Platform::LanczosSample(srcBuffer,
+ inputDimensions,
+ srcWidth,
+ srcFormat,
+ data.buffer,
+ desiredDimensions);
}
}
* @brief Copy the FreeType bitmap to the given buffer.
*
* @param[out] data The bitmap data.
- * @param[in] srcBitmap The FreeType bitmap.
+ * @param[in,out] srcBitmap The FreeType bitmap.
* @param[in] isShearRequired Whether the bitmap needs a shear transform (for software italics).
+ * @param[in] moveBuffer Whether the bitmap buffer move. True if just copy buffer pointer. False if we use memcpy. (Default is false.)
+ * @note If you set moveBuffer=true, the bitmap's buffer moved frome srcBitmap to data. So srcBitmap buffer changed as nullptr.
*/
-void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired)
+void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap& srcBitmap, bool isShearRequired, bool moveBuffer)
{
+ data.buffer = nullptr;
if(srcBitmap.width * srcBitmap.rows > 0)
{
switch(srcBitmap.pixel_mode)
unsigned int width = srcBitmap.width;
unsigned height = srcBitmap.rows;
- std::unique_ptr<uint8_t, void (*)(void*)> pixelsOutPtr(nullptr, free);
+ uint8_t* releaseRequiredPixelPtr = nullptr;
if(isShearRequired)
{
Dali::Internal::Platform::HorizontalShear(pixelsIn,
width,
height,
+ width,
1u,
-TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
pixelsOut,
widthOut,
heightOut);
- width = widthOut;
- height = heightOut;
- pixelsIn = pixelsOut;
- pixelsOutPtr.reset(pixelsOut);
+ if(DALI_LIKELY(pixelsOut))
+ {
+ width = widthOut;
+ height = heightOut;
+
+ if(moveBuffer)
+ {
+ releaseRequiredPixelPtr = pixelsIn;
+ }
+ else
+ {
+ releaseRequiredPixelPtr = pixelsOut;
+ }
+
+ // Change input buffer ptr.
+ pixelsIn = pixelsOut;
+ }
+ else
+ {
+ DALI_LOG_ERROR("ERROR! software italic slant failed!\n");
+ }
}
- const unsigned int bufferSize = width * height;
- data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
- data.width = width;
- data.height = height;
- data.format = Pixel::L8; // Sets the pixel format.
- memcpy(data.buffer, pixelsIn, bufferSize);
+ data.width = width;
+ data.height = height;
+ data.format = Pixel::L8; // Sets the pixel format.
+
+ // Note we don't compress here
+ data.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+
+ if(moveBuffer)
+ {
+ data.isBufferOwned = true;
+ data.buffer = pixelsIn;
+
+ // Happy trick for copyless convert bitmap!
+ srcBitmap.buffer = nullptr;
+ }
+ else
+ {
+ data.isBufferOwned = false;
+ data.buffer = pixelsIn;
+ }
+
+ if(releaseRequiredPixelPtr)
+ {
+ free(releaseRequiredPixelPtr);
+ }
}
break;
}
{
if(srcBitmap.pitch == static_cast<int>(srcBitmap.width << 2u))
{
- ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer);
-
- // Sets the pixel format.
- data.format = Pixel::BGRA8888;
+ // Color glyph doesn't support copyless convert bitmap. Just memcpy
+ ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer, Pixel::BGRA8888);
}
break;
}
#define DALI_TEST_ABSTRACTION_INTERNAL_FONT_CLIENT_UTILS_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data,
unsigned int srcWidth,
unsigned int srcHeight,
- const unsigned char* const srcBuffer);
+ const unsigned char* const srcBuffer,
+ const Pixel::Format srcFormat);
void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data,
- FT_Bitmap srcBitmap,
- bool isShearRequired);
+ FT_Bitmap& srcBitmap,
+ bool isShearRequired,
+ bool moveBuffer = false);
/**
* @brief Creates a font family pattern used to match fonts.
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
+// EXTERNAL HEADERS
#include <dali/integration-api/debug.h>
+
+// INTERNAL HEADERS
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
#include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
#include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
namespace Dali::TextAbstraction::Internal
{
+namespace
+{
const float FROM_266 = 1.0f / 64.0f;
const float POINTS_PER_INCH = 72.f;
+/**
+ * @brief Maximum rate of bitmap glyph resize.
+ * If scale factor is bigger than this value, we will not cache resized glyph.
+ * Else, resize bitmap glyph itself and cache it.
+ */
+constexpr float MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE = 1.5f;
+
+/**
+ * @brief Maximum size of glyph cache per each font face.
+ */
+constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX = 128;
+constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 3u;
+
+constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
+
+/**
+ * @brief Get maximum size of glyph cache size from environment.
+ * If not settuped, default as 128.
+ * @note This value fixed when we call it first time.
+ * @return The max size of glyph cache.
+ */
+inline const size_t GetMaxNumberOfGlyphCache()
+{
+ using Dali::EnvironmentVariable::GetEnvironmentVariable;
+ static auto numberString = GetEnvironmentVariable(MAX_NUMBER_OF_GLYPH_CACHE_ENV);
+ static auto number = numberString ? std::strtoul(numberString, nullptr, 10) : DEFAULT_GLYPH_CACHE_MAX;
+ return (number < MINIMUM_SIZE_OF_GLYPH_CACHE_MAX) ? MINIMUM_SIZE_OF_GLYPH_CACHE_MAX : number;
+}
+
+/**
+ * @brief Behavior about cache the rendered glyph cache.
+ */
+constexpr bool DEFAULT_ENABLE_CACHE_RENDERED_GLYPH = true;
+constexpr auto ENABLE_CACHE_RENDERED_GLYPH_ENV = "DALI_ENABLE_CACHE_RENDERED_GLYPH";
+
+/**
+ * @brief Get whether we allow to cache rendered glyph from environment.
+ * If not settuped, default as true.
+ * @note This value fixed when we call it first time.
+ * @return True if we allow to cache rendered glyph.
+ */
+inline const bool EnableCacheRenderedGlyph()
+{
+ using Dali::EnvironmentVariable::GetEnvironmentVariable;
+ static auto numberString = GetEnvironmentVariable(ENABLE_CACHE_RENDERED_GLYPH_ENV);
+ static auto number = numberString ? (std::strtoul(numberString, nullptr, 10) ? true : false) : DEFAULT_ENABLE_CACHE_RENDERED_GLYPH;
+ return number;
+}
+
+/**
+ * @brief Policy about compress the cached rendered glyph.
+ * It will be used only if CacheRenderedGlyph is enabled
+ */
+constexpr auto DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY =
+#if !(defined(DALI_PROFILE_UBUNTU) || defined(ANDROID) || defined(WIN32) || defined(__APPLE__))
+ GlyphCacheManager::CompressionPolicyType::MEMORY; // If tizen target
+#else
+ GlyphCacheManager::CompressionPolicyType::SPEED; // If not tizen target
+#endif
+constexpr auto RENDERED_GLYPH_COMPRESS_POLICY_ENV = "DALI_RENDERED_GLYPH_COMPRESS_POLICY";
+
+/**
+ * @brief Get whether we allow to cache rendered glyph from environment.
+ * If not settuped, default value used, as defined above.
+ * @note This value fixed when we call it first time.
+ * @return SPEED if value start with 's' or 'S'. MEMORY if value start with 'm' or 'M'. otherwise, use default
+ */
+inline const GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPolicy()
+{
+ using Dali::EnvironmentVariable::GetEnvironmentVariable;
+ static auto policyString = GetEnvironmentVariable(RENDERED_GLYPH_COMPRESS_POLICY_ENV);
+
+ static auto policy = policyString ? policyString[0] == 's' || policyString[0] == 'S' ? GlyphCacheManager::CompressionPolicyType::SPEED
+ : policyString[0] == 'm' || policyString[0] == 'M' ? GlyphCacheManager::CompressionPolicyType::MEMORY
+ : DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY
+ : DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY;
+ return policy;
+}
+} // namespace
+
FontFaceCacheItem::FontFaceCacheItem(FT_Library& freeTypeLibrary,
FT_Face ftFace,
const FontPath& path,
const FontMetrics& metrics)
: mFreeTypeLibrary(freeTypeLibrary),
mFreeTypeFace(ftFace),
+ mGlyphCacheManager(new GlyphCacheManager(mFreeTypeFace, GetMaxNumberOfGlyphCache())),
+ mHarfBuzzProxyFont(),
mPath(path),
mRequestedPointSize(requestedPointSize),
mFaceIndex(face),
bool hasColorTables)
: mFreeTypeLibrary(freeTypeLibrary),
mFreeTypeFace(ftFace),
+ mGlyphCacheManager(new GlyphCacheManager(mFreeTypeFace, GetMaxNumberOfGlyphCache())),
+ mHarfBuzzProxyFont(),
mPath(path),
mRequestedPointSize(requestedPointSize),
mFaceIndex(face),
{
}
+// Move constructor. font client plugin container may call this.
+// Note that we make nullptr of some reference sensitive values here.
+FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs)
+: mFreeTypeLibrary(rhs.mFreeTypeLibrary)
+{
+ mFreeTypeFace = rhs.mFreeTypeFace;
+ mGlyphCacheManager = std::move(rhs.mGlyphCacheManager);
+ mHarfBuzzProxyFont = std::move(rhs.mHarfBuzzProxyFont);
+ mPath = std::move(rhs.mPath);
+ mRequestedPointSize = rhs.mRequestedPointSize;
+ mFaceIndex = rhs.mFaceIndex;
+ mMetrics = rhs.mMetrics;
+ mCharacterSet = rhs.mCharacterSet;
+ mFixedSizeIndex = rhs.mFixedSizeIndex;
+ mFixedWidthPixels = rhs.mFixedWidthPixels;
+ mFixedHeightPixels = rhs.mFixedWidthPixels;
+ mVectorFontId = rhs.mVectorFontId;
+ mFontId = rhs.mFontId;
+ mIsFixedSizeBitmap = rhs.mIsFixedSizeBitmap;
+ mHasColorTables = rhs.mHasColorTables;
+
+ rhs.mFreeTypeFace = nullptr;
+}
+
+FontFaceCacheItem::~FontFaceCacheItem()
+{
+ // delete glyph cache manager before free face.
+ if(mGlyphCacheManager)
+ {
+ mGlyphCacheManager.reset();
+ }
+
+ if(mHarfBuzzProxyFont)
+ {
+ mHarfBuzzProxyFont.reset();
+ }
+
+ // Free face.
+ if(mFreeTypeFace)
+ {
+ FT_Done_Face(mFreeTypeFace);
+ }
+}
+
void FontFaceCacheItem::GetFontMetrics(FontMetrics& metrics, unsigned int dpiVertical) const
{
metrics = mMetrics;
}
}
-bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertical, bool horizontal) const
+bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVertical, bool horizontal) const
{
bool success(true);
- FT_Face ftFace = mFreeTypeFace;
+ GlyphCacheManager::GlyphCacheData glyphData;
+ FT_Error error;
#ifdef FREETYPE_BITMAP_SUPPORT
// Check to see if we should be loading a Fixed Size bitmap?
if(mIsFixedSizeBitmap)
{
- FT_Select_Size(ftFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
- int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR);
+ FT_Select_Size(mFreeTypeFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
+ mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, glyphData, error);
+
if(FT_Err_Ok == error)
{
- glyph.width = mFixedWidthPixels;
- glyph.height = mFixedHeightPixels;
- glyph.advance = mFixedWidthPixels;
- glyph.xBearing = 0.0f;
+ glyphInfo.width = mFixedWidthPixels;
+ glyphInfo.height = mFixedHeightPixels;
+ glyphInfo.advance = mFixedWidthPixels;
+ glyphInfo.xBearing = 0.0f;
+
+ const auto& metrics = glyphData.mGlyphMetrics;
if(horizontal)
{
- glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
+ glyphInfo.yBearing += static_cast<float>(metrics.horiBearingY) * FROM_266;
}
else
{
- glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
+ glyphInfo.yBearing += static_cast<float>(metrics.vertBearingY) * FROM_266;
}
// Adjust the metrics if the fixed-size font should be down-scaled
if(desiredFixedSize > 0.f)
{
const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
- glyph.width = round(glyph.width * scaleFactor);
- glyph.height = round(glyph.height * scaleFactor);
- glyph.advance = round(glyph.advance * scaleFactor);
- glyph.xBearing = round(glyph.xBearing * scaleFactor);
- glyph.yBearing = round(glyph.yBearing * scaleFactor);
+ glyphInfo.width = round(glyphInfo.width * scaleFactor);
+ glyphInfo.height = round(glyphInfo.height * scaleFactor);
+ glyphInfo.advance = round(glyphInfo.advance * scaleFactor);
+ glyphInfo.xBearing = round(glyphInfo.xBearing * scaleFactor);
+ glyphInfo.yBearing = round(glyphInfo.yBearing * scaleFactor);
+
+ glyphInfo.scaleFactor = scaleFactor;
- glyph.scaleFactor = scaleFactor;
+ if(scaleFactor < MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE)
+ {
+ // Resize bitmap glyph and cache it due to the performance issue.
+ // If scaleFactor is too big, cached bitmap may hold too big memory.
+ // So, we only hold small enough case.
+
+ // TODO : If dpiVertical value changed, this resize feature will be break down.
+ // Otherwise, this glyph will be resized only one times.
+ mGlyphCacheManager->ResizeBitmapGlyph(glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, static_cast<uint32_t>(glyphInfo.width), static_cast<uint32_t>(glyphInfo.height));
+ }
}
}
else
// FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
// i.e. with the SNum-3R font.
// @todo: add an option to use the FT_LOAD_DEFAULT if required?
- int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT);
+ mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphData, error);
// Keep the width of the glyph before doing the software emboldening.
// It will be used to calculate a scale factor to be applied to the
// advance as Harfbuzz doesn't apply any SW emboldening to calculate
// the advance of the glyph.
- const float width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
if(FT_Err_Ok == error)
{
- const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD);
- if(isEmboldeningRequired)
- {
- // Does the software bold.
- FT_GlyphSlot_Embolden(ftFace->glyph);
- }
+ const auto& metrics = glyphData.mGlyphMetrics;
- glyph.width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
- glyph.height = static_cast<float>(ftFace->glyph->metrics.height) * FROM_266;
+ glyphInfo.width = static_cast<float>(metrics.width) * FROM_266;
+ glyphInfo.height = static_cast<float>(metrics.height) * FROM_266;
if(horizontal)
{
- glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingX) * FROM_266;
- glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
+ glyphInfo.xBearing += static_cast<float>(metrics.horiBearingX) * FROM_266;
+ glyphInfo.yBearing += static_cast<float>(metrics.horiBearingY) * FROM_266;
}
else
{
- glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingX) * FROM_266;
- glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
+ glyphInfo.xBearing += static_cast<float>(metrics.vertBearingX) * FROM_266;
+ glyphInfo.yBearing += static_cast<float>(metrics.vertBearingY) * FROM_266;
}
- if(isEmboldeningRequired && !Dali::EqualsZero(width))
+ const bool isEmboldeningRequired = glyphInfo.isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD);
+ if(isEmboldeningRequired)
{
- // If the glyph is emboldened by software, the advance is multiplied by a
- // scale factor to make it slightly bigger.
- glyph.advance *= (glyph.width / width);
+ // Get dummy glyph data without embolden.
+ GlyphCacheManager::GlyphCacheData dummyData;
+ if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyData, error))
+ {
+ // If the glyph is emboldened by software, the advance is multiplied by a
+ // scale factor to make it slightly bigger.
+ const float width = static_cast<float>(dummyData.mGlyphMetrics.width) * FROM_266;
+ if(!EqualsZero(width))
+ {
+ glyphInfo.advance *= (glyphInfo.width / width);
+ }
+ }
}
// Use the bounding box of the bitmap to correct the metrics.
// For some fonts i.e the SNum-3R the metrics need to be corrected,
// otherwise the glyphs 'dance' up and down depending on the
// font's point size.
-
- FT_Glyph ftGlyph;
- error = FT_Get_Glyph(ftFace->glyph, &ftGlyph);
+ FT_Glyph glyph = glyphData.mGlyph;
FT_BBox bbox;
- FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
+ FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
- const float descender = glyph.height - glyph.yBearing;
- glyph.height = (bbox.yMax - bbox.yMin) * FROM_266;
- glyph.yBearing = glyph.height - round(descender);
-
- // Created FT_Glyph object must be released with FT_Done_Glyph
- FT_Done_Glyph(ftGlyph);
+ const float descender = glyphInfo.height - glyphInfo.yBearing;
+ glyphInfo.height = (bbox.yMax - bbox.yMin) * FROM_266;
+ glyphInfo.yBearing = glyphInfo.height - round(descender);
}
else
{
* @brief Create a bitmap representation of a glyph from a face font
*
* @param[in] glyphIndex The index of a glyph within the specified font.
- * @param[in] isItalicRequired Whether the glyph requires italic style.
- * @param[in] isBoldRequired Whether the glyph requires bold style.
* @param[out] data The bitmap data.
* @param[in] outlineWidth The width of the glyph outline in pixels.
+ * @param[in] isItalicRequired Whether the glyph requires italic style.
+ * @param[in] isBoldRequired Whether the glyph requires bold style.
*/
void FontFaceCacheItem::CreateBitmap(
GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth, bool isItalicRequired, bool isBoldRequired) const
{
- FT_Face ftFace = mFreeTypeFace;
- FT_Error error;
+ GlyphCacheManager::GlyphCacheData glyphData;
+ FT_Error error;
+ FT_Int32 loadFlag;
// For the software italics.
bool isShearRequired = false;
// Check to see if this is fixed size bitmap
if(mIsFixedSizeBitmap)
{
- error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
+ loadFlag = FT_LOAD_COLOR;
}
else
#endif
// FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
// i.e. with the SNum-3R font.
// @todo: add an option to use the FT_LOAD_DEFAULT if required?
- error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT);
+ loadFlag = FT_LOAD_NO_AUTOHINT;
}
+ mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, loadFlag, isBoldRequired, glyphData, error);
+
if(FT_Err_Ok == error)
{
- if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD))
- {
- // Does the software bold.
- FT_GlyphSlot_Embolden(ftFace->glyph);
- }
-
- if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC))
+ if(isItalicRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_ITALIC))
{
// Will do the software italic.
isShearRequired = true;
}
- FT_Glyph glyph;
- error = FT_Get_Glyph(ftFace->glyph, &glyph);
-
- // Convert to bitmap if necessary
- if(FT_Err_Ok == error)
+ if(!glyphData.mIsBitmap)
{
- if(glyph->format != FT_GLYPH_FORMAT_BITMAP)
- {
- int offsetX = 0, offsetY = 0;
- bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
+ // Convert to bitmap if necessary
+ FT_Glyph glyph = glyphData.mGlyph;
+
+ DALI_ASSERT_ALWAYS(glyph->format != FT_GLYPH_FORMAT_BITMAP && "Something wrong with cashing. Some bitmap glyph cached failed.");
- // Create a bitmap for the outline
- if(isOutlineGlyph)
+ int offsetX = 0, offsetY = 0;
+ bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
+ bool isStrokeGlyphSuccess = false;
+
+ // Create a bitmap for the outline
+ if(isOutlineGlyph)
+ {
+ // Retrieve the horizontal and vertical distance from the current pen position to the
+ // left and top border of the glyph bitmap for a normal glyph before applying the outline.
+ if(FT_Err_Ok == error)
{
- // Retrieve the horizontal and vertical distance from the current pen position to the
- // left and top border of the glyph bitmap for a normal glyph before applying the outline.
+ // Copy new glyph, and keep original cached glyph.
+ error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0);
if(FT_Err_Ok == error)
{
- FT_Glyph normalGlyph;
- error = FT_Get_Glyph(ftFace->glyph, &normalGlyph);
-
- error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
- if(FT_Err_Ok == error)
- {
- FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(normalGlyph);
+ FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
- offsetX = bitmapGlyph->left;
- offsetY = bitmapGlyph->top;
- }
+ offsetX = bitmapGlyph->left;
+ offsetY = bitmapGlyph->top;
- // Created FT_Glyph object must be released with FT_Done_Glyph
- FT_Done_Glyph(normalGlyph);
+ // Copied FT_Glyph object must be released with FT_Done_Glyph
+ FT_Done_Glyph(glyph);
}
- // Now apply the outline
+ // Replace as original glyph
+ glyph = glyphData.mGlyph;
+ }
+
+ // Now apply the outline
- // Set up a stroker
- FT_Stroker stroker;
- error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
+ // Set up a stroker
+ FT_Stroker stroker;
+ error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
+
+ if(FT_Err_Ok == error)
+ {
+ // Copy glyph pointer for release memory.
+ FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
+ error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 0);
if(FT_Err_Ok == error)
{
- FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
- error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
-
- if(FT_Err_Ok == error)
- {
- FT_Stroker_Done(stroker);
- }
- else
- {
- DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
- }
+ FT_Stroker_Done(stroker);
+ isStrokeGlyphSuccess = true;
}
else
{
- DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
+ DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
}
}
+ else
+ {
+ DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
+ }
+ }
+
+ const bool ableUseCachedRenderedGlyph = EnableCacheRenderedGlyph() && !isOutlineGlyph && !isShearRequired;
- error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
+ // If we cache rendered glyph, and if we can use it, use cached thing first.
+ if(ableUseCachedRenderedGlyph && glyphData.mRenderedBuffer)
+ {
+ data.buffer = glyphData.mRenderedBuffer->buffer;
+ data.width = glyphData.mRenderedBuffer->width;
+ data.height = glyphData.mRenderedBuffer->height;
+ data.format = glyphData.mRenderedBuffer->format;
+ data.compressionType = glyphData.mRenderedBuffer->compressionType;
+ data.isBufferOwned = false;
+ }
+ else
+ {
+ // Copy new glyph, and keep original cached glyph.
+ // If we already copy new glyph by stroke, just re-use that.
+ error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, isStrokeGlyphSuccess);
if(FT_Err_Ok == error)
{
FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
}
- ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
+ // If we can cache this bitmapGlyph, store it.
+ // Note : We will call this API once per each glyph.
+ if(ableUseCachedRenderedGlyph)
+ {
+ mGlyphCacheManager->CacheRenderedGlyphBuffer(glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
+
+ GlyphCacheManager::GlyphCacheData dummyData;
+ mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, loadFlag, isBoldRequired, dummyData, error);
+
+ if(DALI_LIKELY(FT_Err_Ok == error && dummyData.mRenderedBuffer))
+ {
+ data.buffer = dummyData.mRenderedBuffer->buffer;
+ data.width = dummyData.mRenderedBuffer->width;
+ data.height = dummyData.mRenderedBuffer->height;
+ data.format = dummyData.mRenderedBuffer->format;
+ data.compressionType = dummyData.mRenderedBuffer->compressionType;
+ data.isBufferOwned = false;
+ }
+ else
+ {
+ // Something problem during cache or get rendered glyph buffer.
+ // Move bitmap buffer into data.buffer
+ ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
+ }
+ }
+ else
+ {
+ // Move bitmap buffer into data.buffer
+ ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
+ }
+
+ // Copied FT_Glyph object must be released with FT_Done_Glyph
+ FT_Done_Glyph(glyph);
}
else
{
DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
}
}
- else
- {
- ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired);
- }
-
- data.isColorEmoji = mIsFixedSizeBitmap;
-
- // Created FT_Glyph object must be released with FT_Done_Glyph
- FT_Done_Glyph(glyph);
}
+ else
+ {
+ ConvertBitmap(data, *glyphData.mBitmap, isShearRequired);
+ }
+
+ data.isColorEmoji = mIsFixedSizeBitmap;
}
else
{
// Check to see if this is fixed size bitmap
if(mHasColorTables)
{
- error = FT_Load_Glyph(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR);
+ GlyphCacheManager::GlyphCacheData dummyData;
+ mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_COLOR, false, dummyData, error);
}
#endif
return FT_Err_Ok == error;
return FT_Face_GetCharVariantIndex(mFreeTypeFace, character, variantSelector);
}
+HarfBuzzFontHandle FontFaceCacheItem::GetHarfBuzzFont(const uint32_t& horizontalDpi, const uint32_t& verticalDpi)
+{
+ // Create new harfbuzz font only first time or DPI changed.
+ if(DALI_UNLIKELY(!mHarfBuzzProxyFont || mHarfBuzzProxyFont->mHorizontalDpi != horizontalDpi || mHarfBuzzProxyFont->mVerticalDpi != verticalDpi))
+ {
+ mHarfBuzzProxyFont.reset(new HarfBuzzProxyFont(mFreeTypeFace, mRequestedPointSize, horizontalDpi, verticalDpi, mGlyphCacheManager.get()));
+ }
+ return mHarfBuzzProxyFont->GetHarfBuzzFont();
+}
+
} // namespace Dali::TextAbstraction::Internal
#define DALI_TEST_ABSTRACTION_INTERNAL_FONT_FACE_CACHE_ITEM_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
// INTERNAL INCLUDES
-
#include <dali/internal/text/text-abstraction/plugin/font-cache-item-interface.h>
+#include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
+#include <dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h>
// EXTERNAL INCLUDES
#include <fontconfig/fontconfig.h>
+#include <memory> // for std::unique_ptr
// EXTERNAL INCLUDES
#include <ft2build.h>
float fixedHeight,
bool hasColorTables);
+ FontFaceCacheItem(const FontFaceCacheItem& rhs) = delete; // Do not use copy construct
+ FontFaceCacheItem(FontFaceCacheItem&& rhs);
+
+ ~FontFaceCacheItem();
+
/**
* @copydoc FontCacheItemInterface::GetFontMetrics()
*/
/**
* @copydoc FontCacheItemInterface::GetGlyphMetrics()
*/
- bool GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertical, bool horizontal) const override;
+ bool GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVertical, bool horizontal) const override;
/**
* @copydoc FontCacheItemInterface::CreateBitmap()
}
/**
+ * @copydoc FontCacheItemInterface::GetHarfBuzzFont()
+ */
+ HarfBuzzFontHandle GetHarfBuzzFont(const uint32_t& horizontalDpi, const uint32_t& verticalDpi) override;
+
+ /**
* @copydoc FontCacheItemInterface::HasItalicStyle()
*/
bool HasItalicStyle() const override
return (0u != (mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC));
}
- FT_Library& mFreeTypeLibrary; ///< A handle to a FreeType library instance.
- FT_Face mFreeTypeFace; ///< The FreeType face.
+public:
+ FT_Library& mFreeTypeLibrary; ///< A handle to a FreeType library instance.
+ FT_Face mFreeTypeFace; ///< The FreeType face.
+
+ std::unique_ptr<GlyphCacheManager> mGlyphCacheManager; ///< The glyph cache manager. It will cache this face's glyphs.
+ std::unique_ptr<HarfBuzzProxyFont> mHarfBuzzProxyFont; ///< The harfbuzz font. It will store harfbuzz relate data.
+
FontPath mPath; ///< The path to the font file name.
PointSize26Dot6 mRequestedPointSize; ///< The font point size.
FaceIndex mFaceIndex; ///< The face index.
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
+
+// INTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include <dali/internal/imaging/common/image-operations.h>
+#include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
+
+// EXTERNAL INCLUDES
+#include FT_BITMAP_H
+
+#if defined(DEBUG_ENABLED)
+extern Dali::Integration::Log::Filter* gFontClientLogFilter;
+#endif
+
+namespace Dali::TextAbstraction::Internal
+{
+namespace
+{
+constexpr uint32_t THRESHOLD_WIDTH_FOR_RLE4_COMPRESSION = 8; // The smallest width of glyph that we use RLE4 method.
+} // namespace
+
+GlyphCacheManager::GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyphCache)
+: mFreeTypeFace(ftFace),
+ mGlyphCacheMaxSize(maxNumberOfGlyphCache),
+ mLRUGlyphCache(mGlyphCacheMaxSize)
+{
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager Create with maximum size : %d\n", static_cast<int>(mGlyphCacheMaxSize));
+}
+
+GlyphCacheManager::~GlyphCacheManager()
+{
+ while(!mLRUGlyphCache.IsEmpty())
+ {
+ auto removedData = mLRUGlyphCache.Pop();
+
+ // Release Glyph data resource
+ removedData.ReleaseGlyphData();
+ }
+ mLRUGlyphCache.Clear();
+}
+
+bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ GlyphCacheData& glyphData,
+ FT_Error& error)
+{
+ // Append some error value here instead of FT_Err_Ok.
+ error = static_cast<FT_Error>(-1);
+
+ const GlyphCacheKey key = GlyphCacheKey(index, flag, isBoldRequired);
+ auto iter = mLRUGlyphCache.Find(key);
+
+ if(iter == mLRUGlyphCache.End())
+ {
+ // If cache size is full, remove oldest glyph.
+ if(mLRUGlyphCache.IsFull())
+ {
+ auto removedData = mLRUGlyphCache.Pop();
+
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Remove oldest cache for glyph : %p\n", removedData.mGlyph);
+
+ // Release Glyph data resource
+ removedData.ReleaseGlyphData();
+ }
+
+ const bool loadSuccess = LoadGlyphDataFromIndex(index, flag, isBoldRequired, glyphData, error);
+ if(loadSuccess)
+ {
+ // Copy and cached data.
+ mLRUGlyphCache.Push(key, glyphData);
+
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Create cache for index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
+ }
+
+ return loadSuccess;
+ }
+ else
+ {
+ error = FT_Err_Ok;
+
+ // We already notify that we use this glyph. And now, copy cached data.
+ glyphData = iter->element;
+
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Find cache for index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
+ return true;
+ }
+}
+
+bool GlyphCacheManager::LoadGlyphDataFromIndex(
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ GlyphCacheData& glyphData,
+ FT_Error& error)
+{
+ error = FT_Load_Glyph(mFreeTypeFace, index, flag);
+ if(FT_Err_Ok == error)
+ {
+ glyphData.mStyleFlags = mFreeTypeFace->style_flags;
+
+ const bool isEmboldeningRequired = isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD);
+ if(isEmboldeningRequired)
+ {
+ // Does the software bold.
+ FT_GlyphSlot_Embolden(mFreeTypeFace->glyph);
+ }
+
+ glyphData.mGlyphMetrics = mFreeTypeFace->glyph->metrics;
+ glyphData.mIsBitmap = false;
+ // Load glyph
+ error = FT_Get_Glyph(mFreeTypeFace->glyph, &glyphData.mGlyph);
+
+ if(glyphData.mGlyph->format == FT_GLYPH_FORMAT_BITMAP)
+ {
+ // Copy original glyph infomation. Due to we use union, we should keep original handle.
+ FT_Glyph bitmapGlyph = glyphData.mGlyph;
+
+ // Copy rendered bitmap
+ // TODO : Is there any way to keep bitmap buffer without copy?
+ glyphData.mBitmap = new FT_Bitmap();
+ *glyphData.mBitmap = mFreeTypeFace->glyph->bitmap;
+
+ // New allocate buffer
+ size_t bufferSize = 0;
+ switch(glyphData.mBitmap->pixel_mode)
+ {
+ case FT_PIXEL_MODE_GRAY:
+ {
+ if(glyphData.mBitmap->pitch == static_cast<int>(glyphData.mBitmap->width))
+ {
+ bufferSize = static_cast<size_t>(glyphData.mBitmap->width) * static_cast<size_t>(glyphData.mBitmap->rows);
+ }
+ break;
+ }
+#ifdef FREETYPE_BITMAP_SUPPORT
+ case FT_PIXEL_MODE_BGRA:
+ {
+ if(glyphData.mBitmap->pitch == static_cast<int>(glyphData.mBitmap->width << 2u))
+ {
+ bufferSize = (static_cast<size_t>(glyphData.mBitmap->width) * static_cast<size_t>(glyphData.mBitmap->rows)) << 2u;
+ }
+ break;
+ }
+#endif
+ default:
+ {
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GlyphCacheManager::LoadGlyphDataFromIndex. FontClient Unable to create Bitmap of this PixelType\n");
+ break;
+ }
+ }
+
+ if(bufferSize > 0)
+ {
+ glyphData.mIsBitmap = true;
+ glyphData.mBitmap->buffer = (uint8_t*)malloc(bufferSize * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
+ memcpy(glyphData.mBitmap->buffer, mFreeTypeFace->glyph->bitmap.buffer, bufferSize);
+ }
+ else
+ {
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GlyphCacheManager::LoadGlyphDataFromIndex. Bitmap glyph buffer size is zero\n");
+ delete glyphData.mBitmap;
+ glyphData.mBitmap = nullptr;
+ error = static_cast<FT_Error>(-1);
+ }
+
+ // Release glyph data.
+ FT_Done_Glyph(bitmapGlyph);
+ }
+
+ if(FT_Err_Ok == error)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void GlyphCacheManager::ResizeBitmapGlyph(
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ const uint32_t desiredWidth,
+ const uint32_t desiredHeight)
+{
+ if(desiredWidth * desiredHeight <= 0)
+ {
+ // Skip this API if desired size is zero
+ return;
+ }
+ FT_Error error;
+ GlyphCacheData originGlyphData;
+ if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error))
+ {
+ if(DALI_LIKELY(originGlyphData.mIsBitmap && originGlyphData.mBitmap))
+ {
+ const bool requiredResize = (originGlyphData.mBitmap->rows != desiredHeight) || (originGlyphData.mBitmap->width != desiredWidth);
+ if(requiredResize)
+ {
+ // originalGlyphData is copy data. For change cached information, we should access as iterator.
+ const GlyphCacheKey key = GlyphCacheKey(index, flag, isBoldRequired);
+ auto iter = mLRUGlyphCache.Find(key);
+
+ GlyphCacheData& destinationGlpyhData = iter->element;
+
+ const ImageDimensions inputDimensions(destinationGlpyhData.mBitmap->width, destinationGlpyhData.mBitmap->rows);
+ const ImageDimensions desiredDimensions(desiredWidth, desiredHeight);
+
+ uint8_t* desiredBuffer = nullptr;
+
+ switch(destinationGlpyhData.mBitmap->pixel_mode)
+ {
+ case FT_PIXEL_MODE_GRAY:
+ {
+ if(destinationGlpyhData.mBitmap->pitch == static_cast<int>(destinationGlpyhData.mBitmap->width))
+ {
+ desiredBuffer = (uint8_t*)malloc(desiredWidth * desiredHeight * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
+ // Resize bitmap here.
+ Dali::Internal::Platform::LanczosSample1BPP(destinationGlpyhData.mBitmap->buffer,
+ inputDimensions,
+ destinationGlpyhData.mBitmap->width,
+ desiredBuffer,
+ desiredDimensions);
+ }
+ break;
+ }
+#ifdef FREETYPE_BITMAP_SUPPORT
+ case FT_PIXEL_MODE_BGRA:
+ {
+ if(destinationGlpyhData.mBitmap->pitch == static_cast<int>(destinationGlpyhData.mBitmap->width << 2u))
+ {
+ desiredBuffer = (uint8_t*)malloc((desiredWidth * desiredHeight * sizeof(uint8_t)) << 2u); // @note The caller is responsible for deallocating the bitmap data using free.
+ // Resize bitmap here.
+ Dali::Internal::Platform::LanczosSample4BPP(destinationGlpyhData.mBitmap->buffer,
+ inputDimensions,
+ destinationGlpyhData.mBitmap->width,
+ desiredBuffer,
+ desiredDimensions);
+ }
+ break;
+ }
+#endif
+ default:
+ {
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GlyphCacheManager::ResizeBitmapGlyph. FontClient Unable to create Bitmap of this PixelType\n");
+ break;
+ }
+ }
+
+ if(desiredBuffer)
+ {
+ // Success to resize bitmap glyph.
+ // Release origin bitmap buffer.
+ free(destinationGlpyhData.mBitmap->buffer);
+
+ // Replace as desired buffer and size.
+ destinationGlpyhData.mBitmap->buffer = desiredBuffer;
+ destinationGlpyhData.mBitmap->width = desiredWidth;
+ destinationGlpyhData.mBitmap->rows = desiredHeight;
+ switch(destinationGlpyhData.mBitmap->pixel_mode)
+ {
+ case FT_PIXEL_MODE_GRAY:
+ {
+ destinationGlpyhData.mBitmap->pitch = desiredWidth;
+ break;
+ }
+#ifdef FREETYPE_BITMAP_SUPPORT
+ case FT_PIXEL_MODE_BGRA:
+ {
+ destinationGlpyhData.mBitmap->pitch = desiredWidth << 2u;
+ break;
+ }
+#endif
+ }
+ }
+ }
+ }
+ }
+}
+
+void GlyphCacheManager::CacheRenderedGlyphBuffer(
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ const FT_Bitmap& srcBitmap,
+ const CompressionPolicyType policy)
+{
+ if(srcBitmap.width * srcBitmap.rows <= 0)
+ {
+ // Skip this API if rendered bitmap size is zero
+ return;
+ }
+ FT_Error error;
+ GlyphCacheData originGlyphData;
+ if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error))
+ {
+ if(DALI_LIKELY(!originGlyphData.mIsBitmap && originGlyphData.mRenderedBuffer == nullptr))
+ {
+ // originalGlyphData is copy data. For change cached information, we should access as iterator.
+ const GlyphCacheKey key = GlyphCacheKey(index, flag, isBoldRequired);
+ auto iter = mLRUGlyphCache.Find(key);
+
+ GlyphCacheData& destinationGlpyhData = iter->element;
+
+ destinationGlpyhData.mRenderedBuffer = new TextAbstraction::FontClient::GlyphBufferData();
+ if(DALI_UNLIKELY(!destinationGlpyhData.mRenderedBuffer))
+ {
+ DALI_LOG_ERROR("Allocate GlyphBufferData failed\n");
+ return;
+ }
+
+ TextAbstraction::FontClient::GlyphBufferData& renderBuffer = *destinationGlpyhData.mRenderedBuffer;
+
+ // Set basic informations.
+ renderBuffer.width = srcBitmap.width;
+ renderBuffer.height = srcBitmap.rows;
+
+ switch(srcBitmap.pixel_mode)
+ {
+ case FT_PIXEL_MODE_GRAY:
+ {
+ renderBuffer.format = Pixel::L8;
+
+ if(policy == CompressionPolicyType::SPEED)
+ {
+ // If policy is SPEED, we will not compress bitmap.
+ renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+ }
+ else
+ {
+ // If small enough glyph, compress as BPP4 method.
+ if(srcBitmap.width < THRESHOLD_WIDTH_FOR_RLE4_COMPRESSION)
+ {
+ renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4;
+ }
+ else
+ {
+ renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4;
+ }
+ }
+
+ const auto compressedBufferSize = TextAbstraction::FontClient::GlyphBufferData::Compress(srcBitmap.buffer, renderBuffer);
+ if(DALI_UNLIKELY(compressedBufferSize == 0u))
+ {
+ DALI_ASSERT_DEBUG(0 == "Compress failed at FT_PIXEL_MODE_GRAY");
+ DALI_LOG_ERROR("Compress failed. Ignore cache\n");
+ delete destinationGlpyhData.mRenderedBuffer;
+ destinationGlpyhData.mRenderedBuffer = nullptr;
+ return;
+ }
+ break;
+ }
+#ifdef FREETYPE_BITMAP_SUPPORT
+ case FT_PIXEL_MODE_BGRA:
+ {
+ // Copy buffer without compress
+ renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+ renderBuffer.format = Pixel::BGRA8888;
+
+ const auto compressedBufferSize = TextAbstraction::FontClient::GlyphBufferData::Compress(srcBitmap.buffer, renderBuffer);
+ if(DALI_UNLIKELY(compressedBufferSize == 0u))
+ {
+ DALI_ASSERT_DEBUG(0 == "Compress failed at FT_PIXEL_MODE_BGRA");
+ DALI_LOG_ERROR("Compress failed. Ignore cache\n");
+ delete destinationGlpyhData.mRenderedBuffer;
+ destinationGlpyhData.mRenderedBuffer = nullptr;
+ return;
+ }
+ break;
+ }
+#endif
+ default:
+ {
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GlyphCacheManager::CacheRenderedGlyphBuffer. FontClient Unable to create Bitmap of this PixelType\n");
+ delete destinationGlpyhData.mRenderedBuffer;
+ destinationGlpyhData.mRenderedBuffer = nullptr;
+ break;
+ }
+ }
+ }
+ }
+}
+
+void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData()
+{
+ if(mIsBitmap && mBitmap)
+ {
+ // Created FT_Bitmap object must be released with FT_Bitmap_Done
+ free(mBitmap->buffer); // This buffer created by malloc
+
+ delete mBitmap;
+ mBitmap = nullptr;
+ }
+ else if(mGlyph)
+ {
+ // Created FT_Glyph object must be released with FT_Done_Glyph
+ FT_Done_Glyph(mGlyph);
+ mGlyph = nullptr;
+ }
+
+ if(mRenderedBuffer)
+ {
+ delete mRenderedBuffer;
+ }
+}
+
+} // namespace Dali::TextAbstraction::Internal
--- /dev/null
+#ifndef DALI_TEST_ABSTRACTION_INTERNAL_FONT_FACE_GLYPH_CACHE_MANAGER_H
+#define DALI_TEST_ABSTRACTION_INTERNAL_FONT_FACE_GLYPH_CACHE_MANAGER_H
+
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/font-client.h> // For GlyphBufferData
+#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
+#include <dali/internal/text/text-abstraction/plugin/lru-cache-container.h>
+
+// EXTERNAL INCLUDES
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
+namespace Dali::TextAbstraction::Internal
+{
+/**
+ * @brief Helper class to load and cache some glyphs of FT_Face.
+ */
+class GlyphCacheManager
+{
+public:
+ // Constructor
+ GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyphCache);
+
+ // Destructor
+ ~GlyphCacheManager();
+
+ GlyphCacheManager(const GlyphCacheManager& rhs) = delete; // Do not use copy construct
+ GlyphCacheManager(GlyphCacheManager&& rhs) = delete; // Do not use move construct
+
+public:
+ // Public struct area.
+
+ /**
+ * @brief Result informations of glyph. It can be whether FT_Glyph or FT_Bitmap type.
+ * @note FontFaceCacheItem could use this.
+ */
+ struct GlyphCacheData
+ {
+ GlyphCacheData()
+ : mGlyph{nullptr}
+ {
+ }
+
+ union
+ {
+ FT_Glyph mGlyph;
+ FT_Bitmap* mBitmap;
+ };
+ FT_Glyph_Metrics_ mGlyphMetrics{}; // Get from FT_GlyphSlot
+ FT_Int32 mStyleFlags{0}; // Get from FT_Face
+ bool mIsBitmap{false};
+
+ TextAbstraction::FontClient::GlyphBufferData* mRenderedBuffer{nullptr}; // Rendered glyph buffer. Cached only if system allow to cache and we rendered it before. Otherwise, just nullptr
+
+ /**
+ * @brief Release the memory of loaded mGlyph / mBitmap.
+ */
+ void ReleaseGlyphData();
+ };
+
+ // Compression priority of rendered glyph buffer.
+ enum class CompressionPolicyType
+ {
+ SPEED = 0,
+ MEMORY = 1,
+ };
+
+public:
+ // Public API area.
+
+ /**
+ * @brief Load GlyphCacheData from face. The result will be cached.
+ *
+ * @param[in] index Index of glyph in this face.
+ * @param[in] flag Flag when we load the glyph.
+ * @param[in] isBoldRequired True if we require some software bold.
+ * @param[out] data Result of glyph load.
+ * @param[out] error Error code during load glyph.
+ * @return True if load successfully. False if something error occured.
+ */
+ bool GetGlyphCacheDataFromIndex(
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ GlyphCacheData& data,
+ FT_Error& error);
+
+ /**
+ * @brief Load GlyphCacheData from face. The result will not be cached.
+ * @note If we call this API, We should release GlyphCacheData manually.
+ *
+ * @param[in] index Index of glyph in this face.
+ * @param[in] flag Flag when we load the glyph.
+ * @param[in] isBoldRequired True if we require some software bold.
+ * @param[out] data Result of glyph load.
+ * @param[out] error Error code during load glyph.
+ * @return True if load successfully. False if something error occured.
+ */
+ bool LoadGlyphDataFromIndex(
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ GlyphCacheData& data,
+ FT_Error& error);
+
+ /**
+ * @brief Resize bitmap glyph. The result will change cached glyph bitmap information.
+ * If glyph is not bitmap glyph, nothing happened.
+ *
+ * @param[in] index Index of glyph in this face.
+ * @param[in] flag Flag when we load the glyph.
+ * @param[in] isBoldRequired True if we require some software bold.
+ * @param[in] desiredWidth Desired width of bitmap.
+ * @param[in] desiredHeight Desired height of bitmap.
+ */
+ void ResizeBitmapGlyph(
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ const uint32_t desiredWidth,
+ const uint32_t desiredHeight);
+
+ /**
+ * @brief Cache rendered glyph bitmap. The result will change cached glyph information.
+ * If glyph is not single color glyph, or we already cached buffer before, nothing happened.
+ *
+ * @param[in] index Index of glyph in this face.
+ * @param[in] flag Flag when we load the glyph.
+ * @param[in] isBoldRequired True if we require some software bold.
+ * @param[in] srcBitmap Rendered glyph bitmap.
+ * @param[in] policy Compress behavior policy.
+ */
+ void CacheRenderedGlyphBuffer(
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ const FT_Bitmap& srcBitmap,
+ const CompressionPolicyType policy);
+
+private:
+ // Private struct area.
+ /**
+ * @brief Key of cached glyph.
+ */
+ struct GlyphCacheKey
+ {
+ GlyphCacheKey()
+ : mIndex(0u),
+ mFlag(0),
+ mIsBoldRequired(false)
+ {
+ }
+
+ GlyphCacheKey(const GlyphIndex index, const FT_Int32 flag, const bool boldRequired)
+ : mIndex(index),
+ mFlag(flag),
+ mIsBoldRequired(boldRequired)
+ {
+ }
+ GlyphIndex mIndex;
+ FT_Int32 mFlag;
+ bool mIsBoldRequired : 1;
+
+ bool operator==(GlyphCacheKey const& rhs) const noexcept
+ {
+ return mIndex == rhs.mIndex && mFlag == rhs.mFlag && mIsBoldRequired == rhs.mIsBoldRequired;
+ }
+ };
+
+ /**
+ * @brief Hash function of GlyphCacheKey.
+ */
+ struct GlyphCacheKeyHash
+ {
+ std::size_t operator()(GlyphCacheKey const& key) const noexcept
+ {
+ return static_cast<std::size_t>(key.mIndex) ^ static_cast<std::size_t>(key.mFlag) ^ (static_cast<std::size_t>(key.mIsBoldRequired) << 29);
+ }
+ };
+
+private:
+ // Private member value area.
+ FT_Face mFreeTypeFace; ///< The FreeType face. Owned from font-face-cache-item
+
+ std::size_t mGlyphCacheMaxSize; ///< The maximum capacity of glyph cache.
+
+ using CacheContainer = LRUCacheContainer<GlyphCacheKey, GlyphCacheData, GlyphCacheKeyHash>;
+
+ CacheContainer mLRUGlyphCache; ///< LRU Cache container of glyph
+};
+
+} // namespace Dali::TextAbstraction::Internal
+
+#endif //DALI_TEST_ABSTRACTION_INTERNAL_FONT_FACE_GLYPH_CACHE_MANAGER_H
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// INTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include <dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h>
+
+// EXTERNAL INCLUDES
+#include FT_GLYPH_H
+#include <harfbuzz/hb-ft.h>
+#include <harfbuzz/hb.h>
+
+#if defined(DEBUG_ENABLED)
+extern Dali::Integration::Log::Filter* gFontClientLogFilter;
+#endif
+
+namespace Dali::TextAbstraction::Internal
+{
+/**
+ * @brief Helper class to create and destroy harfbuzz font, and hold data in harfbuzz callback
+ * It also cache informations what harfbuzz font need to be created.
+ */
+struct HarfBuzzProxyFont::Impl
+{
+ /**
+ * @brief Constructor.
+ *
+ * @param[in] freeTypeFace The FreeType face.
+ * @param[in] glyphCacheManager Glyph caching system for this harfbuzz font. It will be used as harfbuzz callback data.
+ */
+ Impl(FT_Face freeTypeFace, GlyphCacheManager* glyphCacheManager)
+ : mFreeTypeFace(freeTypeFace),
+ mGlyphCacheManager(glyphCacheManager),
+ mHarfBuzzFont(nullptr)
+ {
+ }
+
+ // Destructor
+ ~Impl()
+ {
+ if(mHarfBuzzFont)
+ {
+ // It will reduce reference of freetype face automatically.
+ hb_font_destroy(mHarfBuzzFont);
+ }
+ }
+
+public:
+ /**
+ * @brief Create new harfbuzz font.
+ *
+ * @param[in] requestedPointSize The requiested point size of font.
+ * @param[in] horizontalDpi Horizontal DPI.
+ * @param[in] verticalDpi Vertical DPI.
+ */
+ void CreateHarfBuzzFont(const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi);
+
+private:
+ /**
+ * @brief Register harfbuzz callback functions into current harfbuzz font.
+ */
+ void SetHarfBuzzFunctions();
+
+public:
+ FT_Face mFreeTypeFace; ///< The FreeType face. Owned from font-face-cache-item.
+ GlyphCacheManager* mGlyphCacheManager; ///< Glyph caching system for this harfbuzz font. Owned from font-face-cache-item.
+
+ hb_font_t* mHarfBuzzFont; ///< Harfbuzz font handle integrated with FT_Face.
+};
+
+HarfBuzzProxyFont::HarfBuzzProxyFont(FT_Face freeTypeFace, const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi, GlyphCacheManager* glyphCacheManager)
+: mHorizontalDpi(horizontalDpi),
+ mVerticalDpi(verticalDpi),
+ mImpl(new Impl(freeTypeFace, glyphCacheManager))
+{
+ mImpl->CreateHarfBuzzFont(requestedPointSize, mHorizontalDpi, mVerticalDpi);
+}
+
+HarfBuzzProxyFont::~HarfBuzzProxyFont()
+{
+ if(mImpl)
+ {
+ delete mImpl;
+ }
+}
+
+HarfBuzzFontHandle HarfBuzzProxyFont::GetHarfBuzzFont() const
+{
+ if(mImpl)
+ {
+ return static_cast<HarfBuzzFontHandle>(mImpl->mHarfBuzzFont);
+ }
+ return nullptr;
+}
+
+// Collection of harfbuzz custom callback functions.
+// Reference : https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-ft.cc
+namespace
+{
+/**
+ * @brief Get glyph informations by dali glyph cache system.
+ *
+ * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
+ * @param[in] glyphIndex Index of glyph.
+ * @param[out] glyphData The result of cached glyph data.
+ * @return True if we success to get some glyph data. False otherwise.
+ */
+static bool GetGlyphCacheData(void* font_data, const GlyphIndex& glyphIndex, GlyphCacheManager::GlyphCacheData& glyphData)
+{
+ HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
+
+ // Note : HarfBuzz used only FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING internally.
+ if(DALI_LIKELY(impl && impl->mGlyphCacheManager))
+ {
+ FT_Error error;
+ return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, glyphData, error);
+ }
+ return false;
+}
+
+/**
+ * @brief Calculate font extents value both in vertical and horizontal.
+ *
+ * @param[in] font Current harfbuzz font data.
+ * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
+ * @param[out] extents Extents value of font. (scale as 26.6)
+ * @param[in] user_data Registered user data.
+ * @return True if we success to get font extents. False otherwise.
+ */
+static hb_bool_t FontExtentsFunc(hb_font_t* font, void* font_data, hb_font_extents_t* extents, void* user_data)
+{
+ HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
+
+ if(DALI_LIKELY(impl && impl->mFreeTypeFace))
+ {
+ FT_Size_Metrics& ftMetrics = impl->mFreeTypeFace->size->metrics;
+
+ extents->ascender = ftMetrics.ascender;
+ extents->descender = ftMetrics.descender;
+ extents->line_gap = ftMetrics.height - (extents->ascender - extents->descender);
+
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief Convert from character into index of glyph.
+ *
+ * @param[in] font Current harfbuzz font data.
+ * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
+ * @param[in] character The value of character what we want to get index.
+ * @param[out] glyphIndex Index of glyph that current font face used.
+ * @param[in] user_data Registered user data.
+ * @return True if we success to convert.
+ */
+static hb_bool_t GlyphNormalIndexConvertFunc(hb_font_t* font, void* font_data, hb_codepoint_t character, hb_codepoint_t* glyphIndex, void* user_data)
+{
+ HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
+
+ if(DALI_LIKELY(impl && impl->mFreeTypeFace))
+ {
+ *glyphIndex = FT_Get_Char_Index(impl->mFreeTypeFace, character);
+ return *glyphIndex != 0;
+ }
+ return false;
+}
+
+/**
+ * @brief Convert from character and variant selector into index of glyph.
+ *
+ * @param[in] font Current harfbuzz font data.
+ * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
+ * @param[in] character The value of character what we want to get index.
+ * @param[in] variantSelector Variant selector.
+ * @param[out] glyphIndex Index of glyph that current font face used.
+ * @param[in] user_data Registered user data.
+ * @return True if we success to convert.
+ */
+static hb_bool_t GlyphVariantIndexConvertFunc(hb_font_t* font, void* font_data, hb_codepoint_t character, hb_codepoint_t variantSelector, hb_codepoint_t* glyphIndex, void* user_data)
+{
+ HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
+
+ if(DALI_LIKELY(impl && impl->mFreeTypeFace))
+ {
+ *glyphIndex = FT_Face_GetCharVariantIndex(impl->mFreeTypeFace, character, variantSelector);
+ return *glyphIndex != 0;
+ }
+ return false;
+}
+
+/**
+ * @brief Calculate glyph advance value in horizontal.
+ *
+ * @param[in] font Current harfbuzz font data.
+ * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
+ * @param[in] glyphIndex Index of glyph.
+ * @param[in] user_data Registered user data.
+ * @return Horizontal advance value of glyphIndex. (scale as 26.6)
+ */
+static hb_position_t GlyphHorizontalAdvanceFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, void* user_data)
+{
+ // Output data stored here.
+ GlyphCacheManager::GlyphCacheData glyphData;
+ if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphData))
+ {
+ // Note : It may return invalid value for fixed size bitmap glyph.
+ // But, Harfbuzz library also return Undefined advanced value if it is fixed size font.
+ // So we'll also ignore that case.
+ return static_cast<hb_position_t>(glyphData.mGlyphMetrics.horiAdvance);
+ }
+ return 0;
+}
+/**
+ * @brief Calculate glyph advance value in vertical.
+ *
+ * @param[in] font Current harfbuzz font data.
+ * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
+ * @param[in] glyphIndex Index of glyph.
+ * @param[in] user_data Registered user data.
+ * @return Vertical advance value of glyphIndex. (scale as 26.6)
+ */
+static hb_position_t GlyphVerticalAdvanceFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, void* user_data)
+{
+ // Output data stored here.
+ GlyphCacheManager::GlyphCacheData glyphData;
+ if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphData))
+ {
+ // Note : It may return invalid value for fixed size bitmap glyph.
+ // But, Harfbuzz library also return Undefined advanced value if it is fixed size font.
+ // So we'll also ignore that case.
+ return static_cast<hb_position_t>(glyphData.mGlyphMetrics.vertAdvance);
+ }
+ return 0;
+}
+
+/**
+ * @brief Calculate glyph origin position value in horizontal.
+ *
+ * @param[in] font Current harfbuzz font data.
+ * @param[in] font_data HarfBuzzProxyFont::Impl that register this callback as void* type.
+ * @param[in] glyphIndex Index of glyph.
+ * @param[out] x Origin position x (scale as 26.6)
+ * @param[out] y Origin position y (scale as 26.6)
+ * @param[in] user_data Registered user data.
+ * @return True if we get data successfully. False if some error occured.
+ */
+static hb_bool_t GlyphHorizontalOriginFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_position_t* x, hb_position_t* y, void* user_data)
+{
+ // Nothing to do
+ return true;
+}
+/**
+ * @brief Calculate glyph origin position value in vertical.
+ *
+ * @param[in] font Current harfbuzz font data.
+ * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
+ * @param[in] glyphIndex Index of glyph.
+ * @param[out] x Origin position x (scale as 26.6)
+ * @param[out] y Origin position y (scale as 26.6)
+ * @param[in] user_data Registered user data.
+ * @return True if we get data successfully. False if some error occured.
+ */
+static hb_bool_t GlyphVerticalOriginFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_position_t* x, hb_position_t* y, void* user_data)
+{
+ // Output data stored here.
+ GlyphCacheManager::GlyphCacheData glyphData;
+ if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphData))
+ {
+ *x = glyphData.mGlyphMetrics.horiBearingX - glyphData.mGlyphMetrics.vertBearingX;
+ *y = glyphData.mGlyphMetrics.horiBearingY + glyphData.mGlyphMetrics.vertBearingY;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief Calculate glyph kerning value in horizontal.
+ *
+ * @param[in] font Current harfbuzz font data.
+ * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
+ * @param[in] glyphIndex1 First index of glyph to get kerning.
+ * @param[in] glyphIndex2 Second index of glyph to get kerning.
+ * @param[in] user_data Registered user data.
+ * @return Horizontal kerning position. (scale as 26.6)
+ */
+static hb_position_t GlyphHorizontalKerningFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex1, hb_codepoint_t glyphIndex2, void* user_data)
+{
+ HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
+
+ if(DALI_LIKELY(impl && impl->mFreeTypeFace))
+ {
+ FT_Error error;
+ FT_Vector kerning;
+
+ error = FT_Get_Kerning(impl->mFreeTypeFace, glyphIndex1, glyphIndex2, FT_KERNING_UNSCALED, &kerning);
+ if(error == FT_Err_Ok)
+ {
+ return kerning.x;
+ }
+ }
+ return 0;
+}
+/**
+ * @brief Calculate glyph kerning value in vertical.
+ *
+ * @param[in] font Current harfbuzz font data.
+ * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
+ * @param[in] glyphIndex1 First index of glyph to get kerning.
+ * @param[in] glyphIndex2 Second index of glyph to get kerning.
+ * @param[in] user_data Registered user data.
+ * @return Vertical kerning position. (scale as 26.6)
+ */
+static hb_position_t GlyphVerticalKerningFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex1, hb_codepoint_t glyphIndex2, void* user_data)
+{
+ // FreeType doesn't support vertical kerning
+ return 0;
+}
+
+/**
+ * @brief Calculate glyph extents.
+ *
+ * @param[in] font Current harfbuzz font data.
+ * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
+ * @param[in] glyphIndex Index of glyph.
+ * @param[out] extents Extents value of glyph. (scale as 26.6)
+ * @param[in] user_data Registered user data.
+ * @return True if we get data successfully. False if some error occured.
+ */
+static hb_bool_t GlyphExtentsFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_glyph_extents_t* extents, void* user_data)
+{
+ // Output data stored here.
+ GlyphCacheManager::GlyphCacheData glyphData;
+ if(!GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphData))
+ {
+ extents->x_bearing = glyphData.mGlyphMetrics.horiBearingX;
+ extents->y_bearing = glyphData.mGlyphMetrics.horiBearingY;
+ extents->width = glyphData.mGlyphMetrics.width;
+ extents->height = glyphData.mGlyphMetrics.height;
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+void HarfBuzzProxyFont::Impl::CreateHarfBuzzFont(const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi)
+{
+ // Destroy previous hb_font_t if exist.
+ if(mHarfBuzzFont)
+ {
+ // It will reduce reference of freetype face automatically.
+ hb_font_destroy(mHarfBuzzFont);
+ mHarfBuzzFont = nullptr;
+ }
+
+ if(mFreeTypeFace)
+ {
+ // Before create hb_font_t, we must set FT_Char_Size
+ FT_Set_Char_Size(mFreeTypeFace,
+ 0u,
+ requestedPointSize,
+ horizontalDpi,
+ verticalDpi);
+
+ // Create font face with increase font face's reference.
+ mHarfBuzzFont = hb_ft_font_create_referenced(mFreeTypeFace);
+
+ SetHarfBuzzFunctions();
+
+ if(mHarfBuzzFont)
+ {
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::HarfBuzzManager::GetHarfBuzzFont. Create new harfbuzz font : %p freetype face : %p. Requested point size : %u, dpi : horizon %u vertial %u\n", mHarfBuzzFont, mFreeTypeFace, requestedPointSize, horizontalDpi, verticalDpi);
+ }
+ else
+ {
+ DALI_LOG_ERROR("ERROR! failed to create harfbuzz font.");
+ }
+ }
+ else
+ {
+ DALI_LOG_ERROR("ERROR! freetype face is null! something unknown problem occured.");
+ }
+}
+
+void HarfBuzzProxyFont::Impl::SetHarfBuzzFunctions()
+{
+ if(mHarfBuzzFont)
+ {
+ hb_font_funcs_t* customFunctions = hb_font_funcs_create();
+
+ if(customFunctions)
+ {
+ // Bind custom functions here
+ hb_font_funcs_set_font_h_extents_func(customFunctions, FontExtentsFunc, 0, 0);
+ hb_font_funcs_set_font_v_extents_func(customFunctions, FontExtentsFunc, 0, 0);
+
+ hb_font_funcs_set_nominal_glyph_func(customFunctions, GlyphNormalIndexConvertFunc, 0, 0);
+ hb_font_funcs_set_variation_glyph_func(customFunctions, GlyphVariantIndexConvertFunc, 0, 0);
+
+ hb_font_funcs_set_glyph_h_advance_func(customFunctions, GlyphHorizontalAdvanceFunc, 0, 0);
+ hb_font_funcs_set_glyph_v_advance_func(customFunctions, GlyphVerticalAdvanceFunc, 0, 0);
+ hb_font_funcs_set_glyph_extents_func(customFunctions, GlyphExtentsFunc, 0, 0);
+
+ hb_font_funcs_set_glyph_h_origin_func(customFunctions, GlyphHorizontalOriginFunc, 0, 0);
+ hb_font_funcs_set_glyph_v_origin_func(customFunctions, GlyphVerticalOriginFunc, 0, 0);
+ hb_font_funcs_set_glyph_h_kerning_func(customFunctions, GlyphHorizontalKerningFunc, 0, 0);
+ hb_font_funcs_set_glyph_v_kerning_func(customFunctions, GlyphVerticalKerningFunc, 0, 0);
+
+ // Set custom functions into our own harfbuzz font
+ hb_font_set_funcs(mHarfBuzzFont, customFunctions, this, 0);
+
+ // We must release functions type what we create.
+ hb_font_funcs_destroy(customFunctions);
+ }
+ else
+ {
+ DALI_LOG_ERROR("ERROR! Fail to create custom harfbuzz functions.");
+
+ // Something wrong while create harfbuzz font. Destory it.
+ // It will reduce reference of freetype face automatically.
+ hb_font_destroy(mHarfBuzzFont);
+ mHarfBuzzFont = nullptr;
+ }
+ }
+}
+
+} // namespace Dali::TextAbstraction::Internal
--- /dev/null
+#ifndef DALI_TEXT_ABSTRACTION_INTERNAL_HARFBUZZ_PROXY_FONT_H
+#define DALI_TEXT_ABSTRACTION_INTERNAL_HARFBUZZ_PROXY_FONT_H
+
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
+#include <dali/internal/text/text-abstraction/font-client-impl.h> // for HarfBuzzFontHandle
+#include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
+
+// EXTERNAL INCLUDES
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+namespace Dali::TextAbstraction::Internal
+{
+/**
+ * @brief Helper class to shape of FT_Face by harfbuzz library.
+ * @note Current class only be used for font face cache item.
+ */
+class HarfBuzzProxyFont
+{
+public:
+ /**
+ * @brief Constructor harfbuzz font data integrated with FreeType face and our font face cache item.
+ *
+ * @param[in] freeTypeFace The FreeType face.
+ * @param[in] requestedPointSize The requiested point size of font.
+ * @param[in] horizontalDpi Horizontal DPI.
+ * @param[in] verticalDpi Vertical DPI.
+ * @param[in] glyphCacheManager Glyph caching system for this harfbuzz font. It will be used as harfbuzz callback data.
+ */
+ HarfBuzzProxyFont(FT_Face freeTypeFace, const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi, GlyphCacheManager* glyphCacheManager);
+
+ // Destructor
+ ~HarfBuzzProxyFont();
+
+public:
+ // Public API area.
+
+ /**
+ * @brief Get the created harfbuzz font data integrated with FreeType face and our font face cache item.
+ *
+ * @return Created harfbuzz font data. or nullptr if there is something error.
+ */
+ HarfBuzzFontHandle GetHarfBuzzFont() const;
+
+private:
+ // Private API area.
+ HarfBuzzProxyFont() = delete; // Do not use default construct
+ HarfBuzzProxyFont(const HarfBuzzProxyFont& rhs) = delete; // Do not use copy construct
+ HarfBuzzProxyFont(HarfBuzzProxyFont&& rhs) = delete; // Do not use move construct
+public:
+ struct Impl; // Harfbuzz callback can access this struct.
+ uint32_t mHorizontalDpi; ///< Horizontal DPI.
+ uint32_t mVerticalDpi; ///< VerticalDPI.
+
+private:
+ // Private member value area.
+ Impl* mImpl;
+};
+
+} // namespace Dali::TextAbstraction::Internal
+
+#endif //DALI_TEXT_ABSTRACTION_INTERNAL_HARFBUZZ_PROXY_FONT_H
--- /dev/null
+#ifndef DALI_TEXT_ABSTRACTION_INTERNAL_LRU_CACHE_CONTAINER_H
+#define DALI_TEXT_ABSTRACTION_INTERNAL_LRU_CACHE_CONTAINER_H
+
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <limits> // for std::numeric_limits
+#include <unordered_map>
+
+namespace Dali::TextAbstraction::Internal
+{
+/**
+ * @brief Helper class to cache as LRU algirhtm.
+ * It implement as double-linked-list with header and footer.
+ *
+ * HEADER <-> n(LatestId) <-> n <-> ... <-> n(OldestId) <-> FOOTER <-> n(FreeId) <-> n <-> .. <-> n <-> HEADER
+ *
+ * @note This container cannot control the KeyType and ElementType construct and destruct timming.
+ *
+ * @todo Could we make "iteration" system here?
+ * @todo Could we move it into dali-core/devel-api?
+ *
+ * @tparam KeyType The type of the key that pairwise with element.
+ * @tparam ElementType The type of the data that the container holds
+ * @tparam KeyHash The custom hash funcion of KeyType. Default is std::hash
+ * @tparam KeyEqual The custom equal function of KeyType. Default is std::equal_to
+ */
+template<class KeyType, class ElementType, class KeyHash = std::hash<KeyType>, class KeyEqual = std::equal_to<KeyType>>
+class LRUCacheContainer
+{
+public:
+ // Constructor
+ LRUCacheContainer(std::size_t maxNumberOfCache = std::numeric_limits<std::size_t>::max())
+ : mCacheMaxSize(maxNumberOfCache)
+ {
+ }
+
+ // Destructor
+ ~LRUCacheContainer() = default;
+
+ LRUCacheContainer(const LRUCacheContainer& rhs) = default;
+ LRUCacheContainer(LRUCacheContainer&& rhs) = default;
+
+public:
+ // Public struct area.
+ using CacheId = std::size_t; ///< The id of cached element. It can be used until element poped out.
+ using CacheIdContainer = std::unordered_map<KeyType, CacheId, KeyHash, KeyEqual>;
+
+ /**
+ * @brief Special CacheId for header.
+ */
+ static constexpr CacheId CACHE_HEADER_ID = std::numeric_limits<std::size_t>::max();
+ /**
+ * @brief Special CacheId for footer.
+ */
+ static constexpr CacheId CACHE_FOOTER_ID = std::numeric_limits<std::size_t>::max() - 1u;
+
+ /**
+ * @brief Double linked CacheNode that this container used.
+ */
+ struct CacheNode
+ {
+ CacheNode() = default;
+ ~CacheNode() = default;
+
+ CacheId prev{CACHE_FOOTER_ID};
+ CacheId next{CACHE_HEADER_ID};
+ ElementType element;
+
+ using CacheIdIterator = typename CacheIdContainer::iterator;
+
+ CacheIdIterator cacheIdIterator; ///< Note : It only validate until mCacheId rehashing.
+ };
+
+ using iterator = typename std::vector<CacheNode>::iterator;
+
+public:
+ // Public API area.
+
+ /**
+ * @brief Push an element into the cache. It will be marked as recent
+ * If it is already existed key, it will replace element.
+ *
+ * @param[in] key The key to push
+ * @param[in] element The element to push
+ * @warning This method pop oldest elements if the user attempts to push
+ * more elements than the maximum size specified in the constructor
+ */
+ void Push(const KeyType& key, const ElementType& element)
+ {
+ const auto iter = mCacheId.find(key);
+
+ // If already exist key, just replace element, and return.
+ if(iter != mCacheId.end())
+ {
+ const CacheId id = iter->second;
+
+ // Mark as recently used.
+ InternalPop(id);
+ InternalInsertAfterHeader(id);
+
+ mData[id].element = element;
+ return;
+ }
+
+ if(DALI_UNLIKELY(IsFull()))
+ {
+ // Pop latest element automatically.
+ Pop();
+ }
+
+ if(DALI_UNLIKELY(mNumberOfElements == mData.size()))
+ {
+ InternalReserve(mNumberOfElements == 0 ? 1 : (mNumberOfElements << 1));
+ }
+
+ ++mNumberOfElements;
+
+ const CacheId id = mFreeId;
+
+ // Mark as recently used.
+ InternalPop(id);
+ InternalInsertAfterHeader(id);
+
+ // Copy element
+ mData[id].element = element;
+
+ // Store cache iterator.
+ mData[id].cacheIdIterator = mCacheId.emplace(key, id).first;
+ }
+
+ /**
+ * @brief Pops an element off the oldest used element.
+ * After pop, CacheId relative with this element cannot be used.
+ * Access by poped element's CacheId is Undefined Behavior.
+ *
+ * @return A copy of the element
+ * @warning This method asserts if the container is empty
+ */
+ ElementType Pop()
+ {
+ DALI_ASSERT_ALWAYS(!IsEmpty() && "Reading from empty container");
+
+ const CacheId id = mOldestId;
+ InternalPop(id);
+ InternalInsertAfterFooter(id);
+
+ --mNumberOfElements;
+
+ // Erase cache id.
+ mCacheId.erase(mData[id].cacheIdIterator);
+
+ return mData[id].element;
+ }
+
+ /**
+ * @brief Get an element by the key. It will be marked as recent
+ *
+ * @param[in] key The key of element
+ * @return A reference of the element
+ * @warning This method asserts if invalid key inputed
+ */
+ ElementType& Get(const KeyType& key)
+ {
+ const auto iter = mCacheId.find(key);
+ DALI_ASSERT_ALWAYS((iter != mCacheId.end()) && "Try to get invalid key");
+
+ const auto id = iter->second;
+
+ // Mark as recently used.
+ InternalPop(id);
+ InternalInsertAfterHeader(id);
+
+ return mData[id].element;
+ }
+
+ /**
+ * @brief Find an element by the key. It will be marked as recent
+ *
+ * @param[in] key The key of element
+ * @return A iterator of cache node. If key not exist, return End()
+ */
+ iterator Find(const KeyType& key)
+ {
+ if(mCacheId.find(key) == mCacheId.end())
+ {
+ return End();
+ }
+
+ const auto id = mCacheId[key];
+
+ // Mark as recently used.
+ InternalPop(id);
+ InternalInsertAfterHeader(id);
+
+ return Begin() + id;
+ }
+
+ /**
+ * @brief Clear all data
+ */
+ void Clear()
+ {
+ mCacheId.clear();
+ mCacheId.rehash(0);
+ mData.clear();
+ mData.shrink_to_fit();
+
+ mNumberOfElements = 0;
+ mLatestId = CACHE_FOOTER_ID;
+ mOldestId = CACHE_HEADER_ID;
+ mFreeId = CACHE_HEADER_ID;
+ }
+
+ /**
+ * @brief Predicate to determine if the container is empty
+ *
+ * @return true if the container is empty
+ */
+ bool IsEmpty() const
+ {
+ return mNumberOfElements == 0;
+ }
+
+ /**
+ * @brief Predicate to determine if the container is full
+ *
+ * @return true if the container is full
+ */
+ bool IsFull() const
+ {
+ return (mNumberOfElements == mCacheMaxSize);
+ }
+
+ iterator Begin()
+ {
+ return mData.begin();
+ }
+
+ iterator End()
+ {
+ return mData.end();
+ }
+
+ /**
+ * @brief Get a count of the elements in the container
+ *
+ * @return the number of elements in the container.
+ */
+ std::size_t Count() const
+ {
+ return mNumberOfElements;
+ }
+
+private:
+ // Private struct area.
+
+private:
+ // Private API area.
+
+ /**
+ * @brief Allocate cache memory as reserveSize.
+ * @note We assume that mFreeId is header.
+ *
+ * @param reserveSize Reserved size of cache.
+ */
+ void InternalReserve(std::size_t reserveSize) noexcept
+ {
+ // Increase mData capacity
+ if(reserveSize > mCacheMaxSize)
+ {
+ reserveSize = mCacheMaxSize;
+ }
+
+ CacheId newCreatedIdBegin = mData.size();
+ CacheId newCreatedIdEnd = reserveSize - 1;
+
+ // Make temporary array for re-validate iterator.
+ std::vector<KeyType> keyList(mData.size());
+ for(auto i = static_cast<std::size_t>(0); i < newCreatedIdBegin; ++i)
+ {
+ keyList[i] = mData[i].cacheIdIterator->first;
+ }
+
+ // Reserve data and cacheid capacity.
+ mData.resize(reserveSize);
+ mCacheId.rehash(reserveSize);
+
+ // Revalidate each iterator.
+ for(auto i = static_cast<std::size_t>(0); i < newCreatedIdBegin; ++i)
+ {
+ mData[i].cacheIdIterator = mCacheId.find(keyList[i]);
+ }
+
+ // Setup new created CacheNode's prev and next id.
+ for(auto i = newCreatedIdBegin;; ++i)
+ {
+ mData[i].prev = DALI_UNLIKELY(i == newCreatedIdBegin) ? CACHE_FOOTER_ID : i - 1;
+ mData[i].next = DALI_UNLIKELY(i == newCreatedIdEnd) ? CACHE_HEADER_ID : i + 1;
+ if(DALI_UNLIKELY(i == newCreatedIdEnd))
+ {
+ break;
+ }
+ }
+ mFreeId = newCreatedIdBegin;
+ }
+
+ /**
+ * @brief Temperary pop of node. After call this, we should call
+ * InternalInsertAfterHeader or InternalInsertAfterFooter
+ *
+ * @param id CacheId that removed temperary.
+ */
+ void InternalPop(const CacheId& id) noexcept
+ {
+ const CacheId prev = mData[id].prev;
+ const CacheId next = mData[id].next;
+
+ // Disconnect prev -> id. and connect prev -> next
+ if(prev == CACHE_HEADER_ID)
+ {
+ mLatestId = next;
+ }
+ else if(prev == CACHE_FOOTER_ID)
+ {
+ mFreeId = next;
+ }
+ else
+ {
+ mData[prev].next = next;
+ }
+
+ // Disconnect id <- next. and connect prev <- next
+ if(next == CACHE_HEADER_ID)
+ {
+ // Do nothing.
+ }
+ else if(next == CACHE_FOOTER_ID)
+ {
+ mOldestId = prev;
+ }
+ else
+ {
+ mData[next].prev = prev;
+ }
+ }
+
+ /**
+ * @brief Insert the node after the header. That mean, this id recently used.
+ *
+ * @param id CacheId that insert after header.
+ */
+ void InternalInsertAfterHeader(const CacheId& id) noexcept
+ {
+ const CacheId next = mLatestId;
+
+ // Connect Header -> id.
+ mLatestId = id;
+
+ // Connect id <- next
+ if(next == CACHE_FOOTER_ID)
+ {
+ mOldestId = id;
+ }
+ else
+ {
+ mData[next].prev = id;
+ }
+
+ // Connect Header <- id -> next
+ mData[id].prev = CACHE_HEADER_ID;
+ mData[id].next = next;
+ }
+
+ /**
+ * @brief Insert the node after the footer. That mean, this id become free.
+ *
+ * @param id CacheId that insert after footer.
+ */
+ void InternalInsertAfterFooter(const CacheId& id) noexcept
+ {
+ const CacheId next = mFreeId;
+
+ // Connect Footer -> id.
+ mFreeId = id;
+
+ // Connect id <- next
+ if(next == CACHE_HEADER_ID)
+ {
+ // Do nothing.
+ }
+ else
+ {
+ mData[next].prev = id;
+ }
+
+ // Connect Footer <- id -> next
+ mData[id].prev = CACHE_FOOTER_ID;
+ mData[id].next = next;
+ }
+
+private:
+ // Private member value area.
+ std::size_t mCacheMaxSize{0}; ///< The maximum capacity of cache.
+ std::size_t mNumberOfElements{0}; ///< The number of elements.
+
+ CacheId mLatestId{CACHE_FOOTER_ID}; ///< The recently used element id
+ CacheId mOldestId{CACHE_HEADER_ID}; ///< The oldest used element id
+ CacheId mFreeId{CACHE_HEADER_ID}; ///< The free element id that can be allocated.
+
+ std::unordered_map<KeyType, CacheId, KeyHash, KeyEqual> mCacheId{}; ///< LRU Cache id container
+ std::vector<CacheNode> mData{}; ///< The real data container.
+};
+
+} // namespace Dali::TextAbstraction::Internal
+
+#endif //DALI_TEXT_ABSTRACTION_INTERNAL_LRU_CACHE_CONTAINER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
case FontDescription::FACE_FONT:
{
+ // Get our harfbuzz font struct
+ hb_font_t* harfBuzzFont = reinterpret_cast<hb_font_t*>(fontClientImpl.GetHarfBuzzFont(fontId));
+ if(nullptr == harfBuzzFont)
+ {
+ // Nothing to do if the harfBuzzFont is null.
+ return 0u;
+ }
+
// Reserve some space to avoid reallocations.
const Length numberOfGlyphs = static_cast<Length>(1.3f * static_cast<float>(numberOfCharacters));
mIndices.Reserve(numberOfGlyphs);
mCharacterMap.Reserve(numberOfGlyphs);
mOffset.Reserve(2u * numberOfGlyphs);
- // Retrieve a FreeType font's face.
- FT_Face face = fontClientImpl.GetFreetypeFace(fontId);
- if(nullptr == face)
- {
- // Nothing to do if the face is null.
- return 0u;
- }
-
- unsigned int horizontalDpi = 0u;
- unsigned int verticalDpi = 0u;
- fontClient.GetDpi(horizontalDpi, verticalDpi);
-
- FT_Set_Char_Size(face,
- 0u,
- fontClient.GetPointSize(fontId),
- horizontalDpi,
- verticalDpi);
-
- /* Get our harfbuzz font struct */
- hb_font_t* harfBuzzFont;
- harfBuzzFont = hb_ft_font_create(face, NULL);
-
/* Create a buffer for harfbuzz to use */
hb_buffer_t* harfBuzzBuffer = hb_buffer_create();
/* Cleanup */
hb_buffer_destroy(harfBuzzBuffer);
- hb_font_destroy(harfBuzzFont);
break;
}
case FontDescription::BITMAP_FONT:
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void TraceManagerAndroid::LogContext(bool start, const char* tag)
{
- if(start)
+ if(traceManagerAndroid && traceManagerAndroid->mPerformanceInterface)
{
- unsigned short contextId = traceManagerAndroid->mPerformanceInterface->AddContext(tag);
- traceManagerAndroid->mPerformanceInterface->AddMarker(PerformanceInterface::START, contextId);
- }
- else
- {
- unsigned short contextId = traceManagerAndroid->mPerformanceInterface->AddContext(tag);
- traceManagerAndroid->mPerformanceInterface->AddMarker(PerformanceInterface::END, contextId);
+ if(start)
+ {
+ unsigned short contextId = traceManagerAndroid->mPerformanceInterface->AddContext(tag);
+ traceManagerAndroid->mPerformanceInterface->AddMarker(PerformanceInterface::START, contextId);
+ }
+ else
+ {
+ unsigned short contextId = traceManagerAndroid->mPerformanceInterface->AddContext(tag);
+ traceManagerAndroid->mPerformanceInterface->AddMarker(PerformanceInterface::END, contextId);
+ }
}
}
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void TraceManagerGeneric::LogContext(bool start, const char* tag)
{
- if(start)
+ if(traceManagerGeneric && traceManagerGeneric->mPerformanceInterface)
{
- unsigned short contextId = traceManagerGeneric->mPerformanceInterface->AddContext(tag);
- traceManagerGeneric->mPerformanceInterface->AddMarker(PerformanceInterface::START, contextId);
- }
- else
- {
- unsigned short contextId = traceManagerGeneric->mPerformanceInterface->AddContext(tag);
- traceManagerGeneric->mPerformanceInterface->AddMarker(PerformanceInterface::END, contextId);
+ if(start)
+ {
+ unsigned short contextId = traceManagerGeneric->mPerformanceInterface->AddContext(tag);
+ traceManagerGeneric->mPerformanceInterface->AddMarker(PerformanceInterface::START, contextId);
+ }
+ else
+ {
+ unsigned short contextId = traceManagerGeneric->mPerformanceInterface->AddContext(tag);
+ traceManagerGeneric->mPerformanceInterface->AddMarker(PerformanceInterface::END, contextId);
+ }
}
}
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Dali::TypeRegistration type(typeid(Dali::VectorImageRenderer), typeid(Dali::BaseHandle), Create);
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gVectorImageLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_IMAGE");
+#endif
+
} // unnamed namespace
VectorImageRendererPtr VectorImageRenderer::New()
VectorImageRenderer::VectorImageRenderer()
#ifdef THORVG_SUPPORT
-: mPicture(nullptr),
- mDefaultWidth(0),
- mDefaultHeight(0)
+: mPicture(nullptr)
#else
: mParsedImage(nullptr),
mRasterizer(nullptr)
mSwCanvas = tvg::SwCanvas::gen();
mSwCanvas->mempool(tvg::SwCanvas::MempoolPolicy::Individual);
- mSwCanvas->reserve(1); //has one picture
+ mSwCanvas->reserve(1); //has one picture
#else
mRasterizer = nsvgCreateRasterizer();
#endif
return false;
}
}
+ else
+ {
+ return true;
+ }
- tvg::Result ret = mPicture->load(reinterpret_cast<char*>(data.Begin()), data.Size(), false);
+ tvg::Result ret = mPicture->load(reinterpret_cast<char*>(data.Begin()), data.Size(), true);
if(ret != tvg::Result::Success)
{
- switch (ret)
+ switch(ret)
{
case tvg::Result::InvalidArguments:
{
float w, h;
mPicture->size(&w, &h);
- mDefaultWidth = static_cast<uint32_t>(w);
+ mDefaultWidth = static_cast<uint32_t>(w);
mDefaultHeight = static_cast<uint32_t>(h);
return true;
#else
+ if(mParsedImage)
+ {
+ return true;
+ }
+
mParsedImage = nsvgParse(reinterpret_cast<char*>(data.Begin()), UNITS, dpi);
if(!mParsedImage || !mParsedImage->shapes)
{
DALI_LOG_ERROR("VectorImageRenderer::Load: nsvgParse failed\n");
return false;
}
+
+ mDefaultWidth = mParsedImage->width;
+ mDefaultHeight = mParsedImage->height;
+
return true;
#endif
}
-bool VectorImageRenderer::Rasterize(Dali::Devel::PixelBuffer& buffer, float scale)
+bool VectorImageRenderer::IsLoaded() const
{
#ifdef THORVG_SUPPORT
+ return mPicture ? true : false;
+#else
+ return mParsedImage ? true : false;
+#endif
+}
+
+Dali::Devel::PixelBuffer VectorImageRenderer::Rasterize(uint32_t width, uint32_t height)
+{
+ if(width == 0)
+ {
+ if(mDefaultWidth == 0)
+ {
+ DALI_LOG_ERROR("Invalid size [%d, %d]\n", width, height);
+ return Devel::PixelBuffer();
+ }
+ else
+ {
+ width = mDefaultWidth;
+ }
+ }
+
+ if(height == 0)
+ {
+ if(mDefaultHeight == 0)
+ {
+ DALI_LOG_ERROR("Invalid size [%d, %d]\n", width, height);
+ return Devel::PixelBuffer();
+ }
+ else
+ {
+ height = mDefaultHeight;
+ }
+ }
+
+#ifdef THORVG_SUPPORT
if(!mSwCanvas || !mPicture)
{
DALI_LOG_ERROR("VectorImageRenderer::Rasterize: either Canvas[%p] or Picture[%p] is invalid [%p]\n", mSwCanvas.get(), mPicture, this);
- return false;
+ return Devel::PixelBuffer();
}
+ Devel::PixelBuffer pixelBuffer = Devel::PixelBuffer::New(width, height, Dali::Pixel::RGBA8888);
+
mSwCanvas->clear(false);
- auto pBuffer = buffer.GetBuffer();
+ auto pBuffer = pixelBuffer.GetBuffer();
if(!pBuffer)
{
DALI_LOG_ERROR("VectorImageRenderer::Rasterize: pixel buffer is null [%p]\n", this);
- return false;
+ return Devel::PixelBuffer();
}
- auto width = buffer.GetWidth();
- auto height = buffer.GetHeight();
-
mSwCanvas->target(reinterpret_cast<uint32_t*>(pBuffer), width, width, height, tvg::SwCanvas::ABGR8888);
- DALI_LOG_RELEASE_INFO("VectorImageRenderer::Rasterize: Buffer[%p] size[%d x %d]! [%p]\n", pBuffer, width, height, this);
+ DALI_LOG_INFO(gVectorImageLogFilter, Debug::Verbose, "Buffer[%p] size[%d x %d]! [%p]\n", pBuffer, width, height, this);
mPicture->size(width, height);
if(mSwCanvas->push(std::unique_ptr<tvg::Picture>(mPicture)) != tvg::Result::Success)
{
DALI_LOG_ERROR("VectorImageRenderer::Rasterize: Picture push fail [%p]\n", this);
- return false;
+ return Devel::PixelBuffer();
}
- if(mSwCanvas->draw() != tvg::Result::Success)
+ auto ret = mSwCanvas->draw();
+ if(ret != tvg::Result::Success)
{
- DALI_LOG_ERROR("VectorImageRenderer::Rasterize: Draw fail [%p]\n", this);
- return false;
+ DALI_LOG_ERROR("VectorImageRenderer::Rasterize: Draw fail %d [%p]\n", static_cast<int>(ret), this);
+ return Devel::PixelBuffer();
}
mSwCanvas->sync();
- return true;
+ return pixelBuffer;
#else
if(mParsedImage != nullptr)
{
- int stride = buffer.GetWidth() * Pixel::GetBytesPerPixel(buffer.GetPixelFormat());
- nsvgRasterize(mRasterizer, mParsedImage, 0.0f, 0.0f, scale, buffer.GetBuffer(), buffer.GetWidth(), buffer.GetHeight(), stride);
- return true;
+ Devel::PixelBuffer pixelBuffer = Devel::PixelBuffer::New(width, height, Dali::Pixel::RGBA8888);
+
+ float scaleX = static_cast<float>(width) / (mDefaultWidth > 0 ? static_cast<float>(mDefaultWidth) : 1.0f);
+ float scaleY = static_cast<float>(height) / (mDefaultHeight > 0 ? static_cast<float>(mDefaultHeight) : 1.0f);
+ float scale = scaleX < scaleY ? scaleX : scaleY;
+ int stride = pixelBuffer.GetWidth() * Pixel::GetBytesPerPixel(Dali::Pixel::RGBA8888);
+
+ nsvgRasterize(mRasterizer, mParsedImage, 0.0f, 0.0f, scale, pixelBuffer.GetBuffer(), width, height, stride);
+ return pixelBuffer;
}
- return false;
+ return Devel::PixelBuffer();
#endif
}
void VectorImageRenderer::GetDefaultSize(uint32_t& width, uint32_t& height) const
{
-#ifdef THORVG_SUPPORT
- width = mDefaultWidth;
+ width = mDefaultWidth;
height = mDefaultHeight;
-#else
- if(mParsedImage)
- {
- width = mParsedImage->width;
- height = mParsedImage->height;
- }
-#endif
}
} // namespace Adaptor
#define DALI_INTERNAL_VECTOR_IMAGE_RENDERER_IMPL_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
bool Load(const Vector<uint8_t>& data, float dpi);
/**
+ * @copydoc Dali::VectorImageRenderer::IsLoaded()
+ */
+ bool IsLoaded() const;
+
+ /**
* @copydoc Dali::VectorImageRenderer::Rasterize()
*/
- bool Rasterize(Dali::Devel::PixelBuffer& buffer, float scale);
+ Dali::Devel::PixelBuffer Rasterize(uint32_t width, uint32_t height);
/**
* @copydoc Dali::VectorImageRenderer::GetDefaultSize()
private:
#ifdef THORVG_SUPPORT
- std::unique_ptr< tvg::SwCanvas > mSwCanvas;
- tvg::Picture* mPicture; ///< The pointer to the picture
- uint32_t mDefaultWidth; ///< The width of the surface
- uint32_t mDefaultHeight; ///< The height of the surface
+ std::unique_ptr<tvg::SwCanvas> mSwCanvas{nullptr};
+ tvg::Picture* mPicture{nullptr}; ///< The pointer to the picture
#else
- NSVGimage* mParsedImage;
- NSVGrasterizer* mRasterizer;
+ NSVGimage* mParsedImage{nullptr};
+ NSVGrasterizer* mRasterizer{nullptr};
#endif
+ uint32_t mDefaultWidth{0}; ///< The default width of the file
+ uint32_t mDefaultHeight{0}; ///< The default height of the file
};
} // namespace Adaptor
mPlugin->ActivateAccessibility(activated);
}
+Accessibility::Address WebEngine::GetAccessibilityAddress()
+{
+ return mPlugin->GetAccessibilityAddress();
+}
+
bool WebEngine::SetVisibility(bool visible)
{
return mPlugin->SetVisibility(visible);
#include <dali/public-api/object/base-object.h>
// INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/accessibility.h>
#include <dali/devel-api/adaptor-framework/web-engine-plugin.h>
#include <dali/devel-api/adaptor-framework/web-engine.h>
void ActivateAccessibility(bool activated);
/**
+ * @copydoc Dali::WebEngine::GetAccessibilityAddress()
+ */
+ Accessibility::Address GetAccessibilityAddress();
+
+ /**
* @copydoc Dali::WebEngine::SetVisibility()
*/
bool SetVisibility(bool visible);
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return 0;
}
+std::string WindowBaseAndroid::GetNativeWindowResourceId()
+{
+ return std::string();
+}
+
EGLNativeWindowType WindowBaseAndroid::CreateEglWindow(int width, int height)
{
// from eglplatform.h header
return false;
}
+void WindowBaseAndroid::SetMaximumSize(Dali::Window::WindowSize size)
+{
+}
+
void WindowBaseAndroid::Minimize(bool minimize)
{
}
return false;
}
+void WindowBaseAndroid::SetMimimumSize(Dali::Window::WindowSize size)
+{
+}
+
void WindowBaseAndroid::SetAvailableAnlges(const std::vector<int>& angles)
{
}
#define DALI_INTERNAL_WINDOWSYSTEM_ANDROID_WINDOW_BASE_ANDROID_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
int GetNativeWindowId() override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::GetNativeWindowResourceId()
+ */
+ std::string GetNativeWindowResourceId() override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::CreateEglWindow()
*/
EGLNativeWindowType CreateEglWindow(int width, int height) override;
bool IsMaximized() const override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::SetMaximumSize()
+ */
+ void SetMaximumSize(Dali::Window::WindowSize size) override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::Minimize()
*/
void Minimize(bool minimize) override;
bool IsMinimized() const override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::SetMimimumSize()
+ */
+ void SetMimimumSize(Dali::Window::WindowSize size) override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::SetAvailableAnlges()
*/
void SetAvailableAnlges(const std::vector<int>& angles) override;
{
int angle; ///< one of 0, 90, 180, 270
int winResize; ///< true if the window should be resized
+ int x; ///< converted x coordinate
+ int y; ///< converted y coordinate
int width; ///< new window width
int height; ///< new window height
};
mSelectionDataSendSignal(),
mSelectionDataReceivedSignal(),
mStyleChangedSignal(),
- mAccessibilitySignal(),
mTransitionEffectEventSignal(),
mKeyboardRepeatSettingsChangedSignal(),
mUpdatePositionSizeSignal(),
return mStyleChangedSignal;
}
-WindowBase::AccessibilitySignalType& WindowBase::AccessibilitySignal()
-{
- return mAccessibilitySignal;
-}
-
WindowBase::TransitionEffectEventSignalType& WindowBase::TransitionEffectEventSignal()
{
return mTransitionEffectEventSignal;
#define DALI_INTERNAL_WINDOWSYSTEM_COMMON_WINDOW_BASE_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// Accessibility
typedef Signal<void(StyleChange::Type)> StyleSignalType;
- typedef Signal<void(const AccessibilityInfo&)> AccessibilitySignalType;
/**
* @brief Default constructor
virtual int GetNativeWindowId() = 0;
/**
+ * @brief Get the native window resource id assinged by window manager
+ * @return The native window resource id
+ */
+ virtual std::string GetNativeWindowResourceId() = 0;
+
+ /**
* @brief Create the egl window
*/
virtual EGLNativeWindowType CreateEglWindow(int width, int height) = 0;
virtual bool IsMaximized() const = 0;
/**
+ * @copydoc Dali::DevelWindow::SetMaximumSize()
+ */
+ virtual void SetMaximumSize(Dali::Window::WindowSize size) = 0;
+
+ /**
* @copydoc Dali::DevelWindow::Minimize()
*/
virtual void Minimize(bool minimize) = 0;
virtual bool IsMinimized() const = 0;
/**
+ * @copydoc Dali::DevelWindow::SetMimimumSize()
+ */
+ virtual void SetMimimumSize(Dali::Window::WindowSize size) = 0;
+
+ /**
* @copydoc Dali::Window::SetAvailableOrientations()
*/
virtual void SetAvailableAnlges(const std::vector<int>& angles) = 0;
StyleSignalType& StyleChangedSignal();
/**
- * @brief This signal is emitted when an accessibility event is received.
- */
- AccessibilitySignalType& AccessibilitySignal();
-
- /**
* @brief This signal is emitted when window's transition animation is started or ended.
*/
TransitionEffectEventSignalType& TransitionEffectEventSignal();
SelectionSignalType mSelectionDataSendSignal;
SelectionSignalType mSelectionDataReceivedSignal;
StyleSignalType mStyleChangedSignal;
- AccessibilitySignalType mAccessibilitySignal;
TransitionEffectEventSignalType mTransitionEffectEventSignal;
KeyboardRepeatSettingsChangedSignalType mKeyboardRepeatSettingsChangedSignal;
WindowRedrawRequestSignalType mWindowRedrawRequestSignal;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// EXTERNAL HEADERS
#include <dali/devel-api/adaptor-framework/orientation.h>
+#include <dali/devel-api/events/key-event-devel.h>
#include <dali/integration-api/core.h>
#include <dali/integration-api/events/touch-event-integ.h>
+#include <dali/integration-api/events/touch-integ.h>
#include <dali/public-api/actors/actor.h>
#include <dali/public-api/actors/camera-actor.h>
#include <dali/public-api/actors/layer.h>
Window::Window()
: mWindowSurface(nullptr),
mWindowBase(),
- mIsTransparent(false),
- mIsFocusAcceptable(true),
- mIconified(false),
- mOpaqueState(false),
- mWindowRotationAcknowledgement(false),
mParentWindow(NULL),
mPreferredAngle(static_cast<int>(WindowOrientation::NO_ORIENTATION_PREFERENCE)),
mRotationAngle(0),
mWindowWidth(0),
mWindowHeight(0),
- mOrientationMode(Internal::Adaptor::Window::OrientationMode::PORTRAIT),
mNativeWindowId(-1),
+ mOrientationMode(Internal::Adaptor::Window::OrientationMode::PORTRAIT),
mDeleteRequestSignal(),
mFocusChangeSignal(),
mResizeSignal(),
mVisibilityChangedSignal(),
mTransitionEffectEventSignal(),
mKeyboardRepeatSettingsChangedSignal(),
- mAuxiliaryMessageSignal()
+ mAuxiliaryMessageSignal(),
+ mLastKeyEvent(),
+ mLastTouchEvent(),
+ mIsTransparent(false),
+ mIsFocusAcceptable(true),
+ mIconified(false),
+ mOpaqueState(false),
+ mWindowRotationAcknowledgement(false),
+ mFocused(false)
{
}
Window::~Window()
{
- auto bridge = Accessibility::Bridge::GetCurrentBridge();
- auto rootLayer = mScene.GetRootLayer();
- auto accessible = Accessibility::Accessible::Get(rootLayer, true);
- bridge->RemoveTopLevelWindow(accessible);
+ if(mScene)
+ {
+ auto bridge = Accessibility::Bridge::GetCurrentBridge();
+ auto rootLayer = mScene.GetRootLayer();
+ auto accessible = Accessibility::Accessible::Get(rootLayer);
+ bridge->RemoveTopLevelWindow(accessible);
+ // Related to multi-window case. This is called for default window and non-default window, but it is effective for non-default window.
+ bridge->Emit(accessible, Accessibility::WindowEvent::DESTROY);
+ }
if(mAdaptor)
{
mEventHandler->AddObserver(*this);
// Add Window to bridge for ATSPI
- auto bridge = Accessibility::Bridge::GetCurrentBridge();
- auto rootLayer = mScene.GetRootLayer();
- auto accessible = Accessibility::Accessible::Get(rootLayer, true);
- bridge->AddTopLevelWindow(accessible);
+ auto bridge = Accessibility::Bridge::GetCurrentBridge();
+ if(bridge->IsUp())
+ {
+ auto rootLayer = mScene.GetRootLayer();
+ auto accessible = Accessibility::Accessible::Get(rootLayer);
+ bridge->AddTopLevelWindow(accessible);
+ }
bridge->EnabledSignal().Connect(this, &Window::OnAccessibilityEnabled);
bridge->DisabledSignal().Connect(this, &Window::OnAccessibilityDisabled);
return mWindowBase->IsMaximized();
}
+void Window::SetMaximumSize(Dali::Window::WindowSize size)
+{
+ mWindowBase->SetMaximumSize(size);
+}
+
void Window::Minimize(bool minimize)
{
mWindowBase->Minimize(minimize);
return mWindowBase->IsMinimized();
}
+void Window::SetMimimumSize(Dali::Window::WindowSize size)
+{
+ mWindowBase->SetMimimumSize(size);
+}
+
uint32_t Window::GetLayerCount() const
{
return mScene.GetLayerCount();
return mScene.GetRenderTaskList();
}
+std::string Window::GetNativeResourceId() const
+{
+ return mWindowBase->GetNativeWindowResourceId();
+}
+
void Window::AddAvailableOrientation(WindowOrientation orientation)
{
if(IsOrientationAvailable(orientation) == false)
mWindowBase->SetPositionSizeWithAngle(positionSize, angle);
}
+void Window::EmitAccessibilityHighlightSignal(bool highlight)
+{
+ Dali::Window handle(this);
+ mAccessibilityHighlightSignal.Emit(handle, highlight);
+}
+
void Window::SetAvailableAnlges(const std::vector<int>& angles)
{
if(angles.size() > 4)
{
Dali::Window handle(this);
mVisibilityChangedSignal.Emit(handle, true);
+ Dali::Accessibility::Bridge::GetCurrentBridge()->WindowShown(handle);
WindowVisibilityObserver* observer(mAdaptor);
observer->OnWindowShown();
{
Dali::Window handle(this);
mVisibilityChangedSignal.Emit(handle, false);
+ Dali::Accessibility::Bridge::GetCurrentBridge()->WindowHidden(handle);
WindowVisibilityObserver* observer(mAdaptor);
observer->OnWindowHidden();
{
Uint16Pair newSize(newRect.width, newRect.height);
+ mWindowWidth = newRect.width;
+ mWindowHeight = newRect.height;
+
SurfaceResized();
mAdaptor->SurfaceResizePrepare(mSurface.get(), newSize);
- DALI_LOG_RELEASE_INFO("Window (%p), WinId (%d), SetSize(): resize signal [%d x %d]\n", this, mNativeWindowId, newRect.width, newRect.height);
+ DALI_LOG_RELEASE_INFO("Window (%p), WinId (%d), current angle (%d), SetSize(): resize signal [%d x %d]\n", this, mNativeWindowId, mRotationAngle, newRect.width, newRect.height);
Dali::Window handle(this);
mResizeSignal.Emit(handle, newSize);
mSurface->SetFullSwapNextFrame();
- Dali::Accessibility::Accessible::Get(mScene.GetRootLayer(), true)->EmitBoundsChanged(Dali::Rect<>(oldRect.x, oldRect.y, size.GetWidth(), size.GetHeight()));
+ Dali::Accessibility::Accessible::Get(mScene.GetRootLayer())->EmitBoundsChanged(Dali::Rect<>(oldRect.x, oldRect.y, size.GetWidth(), size.GetHeight()));
}
Dali::Window::WindowSize Window::GetSize() const
mSurface->SetFullSwapNextFrame();
- Dali::Accessibility::Accessible::Get(mScene.GetRootLayer(), true)->EmitBoundsChanged(Dali::Rect<>(position.GetX(), position.GetY(), oldRect.width, oldRect.height));
+ Dali::Accessibility::Accessible::Get(mScene.GetRootLayer())->EmitBoundsChanged(Dali::Rect<>(position.GetX(), position.GetY(), oldRect.width, oldRect.height));
}
Dali::Window::WindowPosition Window::GetPosition() const
{
Uint16Pair newSize(newRect.width, newRect.height);
+ mWindowWidth = newRect.width;
+ mWindowHeight = newRect.height;
+
SurfaceResized();
mAdaptor->SurfaceResizePrepare(mSurface.get(), newSize);
- DALI_LOG_RELEASE_INFO("Window (%p), WinId (%d), SetPositionSize():resize signal [%d x %d]\n", this, mNativeWindowId, newRect.width, newRect.height);
+ DALI_LOG_RELEASE_INFO("Window (%p), WinId (%d), current angle (%d), SetPositionSize():resize signal [%d x %d]\n", this, mNativeWindowId, mRotationAngle, newRect.width, newRect.height);
Dali::Window handle(this);
mResizeSignal.Emit(handle, newSize);
mAdaptor->SurfaceResizeComplete(mSurface.get(), newSize);
mSurface->SetFullSwapNextFrame();
- Dali::Accessibility::Accessible::Get(mScene.GetRootLayer(), true)->EmitBoundsChanged(Dali::Rect<>(positionSize.x, positionSize.y, positionSize.width, positionSize.height));
+ Dali::Accessibility::Accessible::Get(mScene.GetRootLayer())->EmitBoundsChanged(Dali::Rect<>(positionSize.x, positionSize.y, positionSize.width, positionSize.height));
}
Dali::Layer Window::GetRootLayer() const
{
Dali::Window handle(this);
mVisibilityChangedSignal.Emit(handle, false);
+ Dali::Accessibility::Bridge::GetCurrentBridge()->WindowHidden(handle);
WindowVisibilityObserver* observer(mAdaptor);
observer->OnWindowHidden();
{
Dali::Window handle(this);
mVisibilityChangedSignal.Emit(handle, true);
+ Dali::Accessibility::Bridge::GetCurrentBridge()->WindowShown(handle);
WindowVisibilityObserver* observer(mAdaptor);
observer->OnWindowShown();
bridge->WindowUnfocused(handle);
}
}
+ mFocused = focusIn;
}
void Window::OnOutputTransformed()
mSurface->SetFullSwapNextFrame();
- Dali::Accessibility::Accessible::Get(mScene.GetRootLayer(), true)->EmitBoundsChanged(Dali::Rect<>(positionSize.x, positionSize.y, positionSize.width, positionSize.height));
+ Dali::Accessibility::Accessible::Get(mScene.GetRootLayer())->EmitBoundsChanged(Dali::Rect<>(positionSize.x, positionSize.y, positionSize.width, positionSize.height));
}
void Window::OnTouchPoint(Dali::Integration::Point& point, int timeStamp)
{
+ mLastTouchEvent = Dali::Integration::NewTouchEvent(timeStamp, point);
FeedTouchPoint(point, timeStamp);
}
void Window::OnKeyEvent(Dali::Integration::KeyEvent& keyEvent)
{
+ mLastKeyEvent = Dali::DevelKeyEvent::New(keyEvent.keyName, keyEvent.logicalKey, keyEvent.keyString, keyEvent.keyCode, keyEvent.keyModifier, keyEvent.time, static_cast<Dali::KeyEvent::State>(keyEvent.state), keyEvent.compose, keyEvent.deviceName, keyEvent.deviceClass, keyEvent.deviceSubclass);
FeedKeyEvent(keyEvent);
}
void Window::OnRotation(const RotationEvent& rotation)
{
+ PositionSize newPositionSize(rotation.x, rotation.y, rotation.width, rotation.height);
+
mRotationAngle = rotation.angle;
mWindowWidth = rotation.width;
mWindowHeight = rotation.height;
// Notify that the orientation is changed
mOrientation->OnOrientationChange(rotation);
- mWindowSurface->RequestRotation(mRotationAngle, mWindowWidth, mWindowHeight);
+ mWindowSurface->RequestRotation(mRotationAngle, newPositionSize);
int orientation = (mRotationAngle + mWindowBase->GetScreenRotationAngle()) % 360;
SurfaceRotated(mWindowWidth, mWindowHeight, orientation);
mAdaptor->SurfaceResizePrepare(mSurface.get(), Adaptor::SurfaceSize(mWindowWidth, mWindowHeight));
- DALI_LOG_RELEASE_INFO("Window (%p), WinId (%d), OnRotation(): resize signal emit [%d x %d]\n", this, mNativeWindowId, mWindowWidth, mWindowHeight);
+ DALI_LOG_RELEASE_INFO("Window (%p), WinId (%d), OnRotation(): x[%d], y[%d], resize signal emit [%d x %d]\n", this, mNativeWindowId, newPositionSize.x, newPositionSize.y, mWindowWidth, mWindowHeight);
// Emit signal
Dali::Window handle(this);
mResizeSignal.Emit(handle, Dali::Window::WindowSize(mWindowWidth, mWindowHeight));
{
auto bridge = Accessibility::Bridge::GetCurrentBridge();
auto rootLayer = mScene.GetRootLayer();
- auto accessible = Accessibility::Accessible::Get(rootLayer, true);
+ auto accessible = Accessibility::Accessible::Get(rootLayer);
bridge->AddTopLevelWindow(accessible);
+
+ if(!mVisible || mIconified)
+ {
+ return;
+ }
+
+ Dali::Window handle(this);
+ bridge->WindowShown(handle);
+
+ if(mFocused)
+ {
+ bridge->WindowFocused(handle);
+ }
}
void Window::OnAccessibilityDisabled()
{
auto bridge = Accessibility::Bridge::GetCurrentBridge();
auto rootLayer = mScene.GetRootLayer();
- auto accessible = Accessibility::Accessible::Get(rootLayer, true);
+ auto accessible = Accessibility::Accessible::Get(rootLayer);
bridge->RemoveTopLevelWindow(accessible);
}
Rect<int> Window::RecalculateRect(const Rect<int>& rect)
{
Rect<int> newRect;
- int screenWidth, screenHeight;
+ int screenWidth, screenHeight;
WindowSystem::GetScreenSize(screenWidth, screenHeight);
}
}
+bool Window::IsWindowRotating() const
+{
+ return mWindowSurface->IsWindowRotating();
+}
+
+const Dali::KeyEvent& Window::GetLastKeyEvent() const
+{
+ return mLastKeyEvent;
+}
+
+const Dali::TouchEvent& Window::GetLastTouchEvent() const
+{
+ return mLastTouchEvent;
+}
+
} // namespace Adaptor
} // namespace Internal
#define DALI_INTERNAL_WINDOWSYSTEM_COMMON_WINDOW_IMPL_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// EXTERNAL INCLUDES
#include <dali/public-api/actors/layer.h>
#include <dali/public-api/adaptor-framework/window-enumerations.h>
+#include <dali/public-api/events/touch-event.h>
#include <dali/public-api/object/base-object.h>
#include <dali/public-api/object/property-array.h>
#include <dali/public-api/object/ref-object.h>
typedef Dali::DevelWindow::TransitionEffectEventSignalType TransitionEffectEventSignalType;
typedef Dali::DevelWindow::KeyboardRepeatSettingsChangedSignalType KeyboardRepeatSettingsChangedSignalType;
typedef Dali::DevelWindow::AuxiliaryMessageSignalType AuxiliaryMessageSignalType;
+ typedef Dali::DevelWindow::AccessibilityHighlightSignalType AccessibilityHighlightSignalType;
typedef Signal<void()> SignalType;
/**
* @param[in] isTransparent Whether window is transparent
* @return A newly allocated Window
*/
- static Window* New(Any surface, const PositionSize& positionSize, const std::string& name, const std::string& className, Dali::WindowType type, bool isTransparent = false);
+ static Window* New(Any surface, const PositionSize& positionSize, const std::string& name, const std::string& className, Dali::WindowType type, bool isTransparent = false);
/**
* @copydoc Dali::Window::SetClass()
bool IsMaximized() const;
/**
+ * @copydoc Dali::DevelWindow::SetMaximumSize()
+ */
+ void SetMaximumSize(Dali::Window::WindowSize size);
+
+ /**
* @copydoc Dali::DevelWindow::Minimize()
*/
void Minimize(bool minimize);
bool IsMinimized() const;
/**
+ * @copydoc Dali::DevelWindow::SetMimimumSize()
+ */
+ void SetMimimumSize(Dali::Window::WindowSize size);
+
+ /**
* @copydoc Dali::Window::GetLayerCount()
*/
uint32_t GetLayerCount() const;
Dali::RenderTaskList GetRenderTaskList() const;
/**
+ * @brief Get window resource ID assigned by window manager
+ * @return The resource ID of the window
+ */
+ std::string GetNativeResourceId() const;
+
+ /**
* @copydoc Dali::Window::AddAvailableOrientation()
*/
void AddAvailableOrientation(WindowOrientation orientation);
*/
void SetPositionSizeWithOrientation(PositionSize positionSize, WindowOrientation orientation);
+ /**
+ * @brief Emit the accessibility highlight signal.
+ * The highlight indicates that it is an object to interact with the user regardless of focus.
+ * After setting the highlight on the object, you can do things that the object can do, such as
+ * giving or losing focus.
+ *
+ * @param[in] highlight If window needs to grab or clear highlight.
+ */
+ void EmitAccessibilityHighlightSignal(bool highlight);
+
public: // Dali::Internal::Adaptor::SceneHolder
/**
* @copydoc Dali::Internal::Adaptor::SceneHolder::GetNativeHandle
*/
void SendRotationCompletedAcknowledgement();
+ /**
+ * @copydoc Dali::DevelWindow::IsWindowRotating()
+ */
+ bool IsWindowRotating() const;
+
+ /**
+ * @copydoc Dali::DevelWindow::GetLastKeyEvent()
+ */
+ const Dali::KeyEvent& GetLastKeyEvent() const;
+
+ /**
+ * @copydoc Dali::DevelWindow::GetLastTouchEvent()
+ */
+ const Dali::TouchEvent& GetLastTouchEvent() const;
+
private:
/**
* @brief Enumeration for orietation mode.
return mAuxiliaryMessageSignal;
}
+ /**
+ * @copydoc Dali::DevelWindow::AccessibilityHighlightSignal()
+ */
+ AccessibilityHighlightSignalType& AccessibilityHighlightSignal()
+ {
+ return mAccessibilityHighlightSignal;
+ }
+
private:
WindowRenderSurface* mWindowSurface; ///< The window rendering surface
WindowBase* mWindowBase;
std::string mName;
std::string mClassName;
- bool mIsTransparent : 1;
- bool mIsFocusAcceptable : 1;
- bool mIconified : 1;
- bool mOpaqueState : 1;
- bool mWindowRotationAcknowledgement : 1;
Dali::Window mParentWindow;
OrientationPtr mOrientation;
std::vector<int> mAvailableAngles;
int mPreferredAngle;
- int mRotationAngle; ///< The angle of the rotation
- int mWindowWidth; ///< The width of the window
- int mWindowHeight; ///< The height of the window
-
- EventHandlerPtr mEventHandler; ///< The window events handler
+ int mRotationAngle; ///< The angle of the rotation
+ int mWindowWidth; ///< The width of the window
+ int mWindowHeight; ///< The height of the window
+ int mNativeWindowId; ///< The Native Window Id
- OrientationMode mOrientationMode; ///< The physical screen mode is portrait or landscape
-
- int mNativeWindowId; ///< The Native Window Id
+ EventHandlerPtr mEventHandler; ///< The window events handler
+ OrientationMode mOrientationMode; ///< The physical screen mode is portrait or landscape
// Signals
SignalType mDeleteRequestSignal;
TransitionEffectEventSignalType mTransitionEffectEventSignal;
KeyboardRepeatSettingsChangedSignalType mKeyboardRepeatSettingsChangedSignal;
AuxiliaryMessageSignalType mAuxiliaryMessageSignal;
+ AccessibilityHighlightSignalType mAccessibilityHighlightSignal;
+
+ Dali::KeyEvent mLastKeyEvent;
+ Dali::TouchEvent mLastTouchEvent;
+
+ bool mIsTransparent : 1;
+ bool mIsFocusAcceptable : 1;
+ bool mIconified : 1;
+ bool mOpaqueState : 1;
+ bool mWindowRotationAcknowledgement : 1;
+ bool mFocused : 1;
};
} // namespace Adaptor
mWindowBase->SetTransparency(transparent);
}
-void WindowRenderSurface::RequestRotation(int angle, int width, int height)
+void WindowRenderSurface::RequestRotation(int angle, PositionSize positionSize)
{
if(!mPostRenderTrigger)
{
TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER));
}
- mPositionSize.width = width;
- mPositionSize.height = height;
+ mPositionSize = positionSize;
mWindowRotationAngle = angle;
mWindowRotationFinished = false;
mWindowBase->SetWindowRotationAngle(mWindowRotationAngle);
- DALI_LOG_INFO(gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::Rotate: angle = %d screen rotation = %d\n", mWindowRotationAngle, mScreenRotationAngle);
+ DALI_LOG_RELEASE_INFO("angle = %d screen rotation = %d, flag = %d\n", mWindowRotationAngle, mScreenRotationAngle, mWindowRotationFinished);
}
WindowBase* WindowRenderSurface::GetWindowBase()
mWindowBase->ResizeEglWindow(positionSize);
mResizeFinished = true;
- DALI_LOG_RELEASE_INFO("WindowRenderSurface::PreRender: Set resize, x: %d, y: %d, w: %d, h:%d\n", positionSize.x, positionSize.y, positionSize.width, positionSize.height);
+ DALI_LOG_RELEASE_INFO("WindowRenderSurface::PreRender: Set resize, totalAngle: %d, x: %d, y: %d, w: %d, h:%d\n", totalAngle, positionSize.x, positionSize.y, positionSize.width, positionSize.height);
}
SetFullSwapNextFrame();
}
}
+bool WindowRenderSurface::IsWindowRotating() const
+{
+ return !(mWindowRotationFinished);
+}
+
void WindowRenderSurface::ProcessPostRender()
{
if(!mWindowRotationFinished)
{
mWindowBase->WindowRotationCompleted(mWindowRotationAngle, mPositionSize.width, mPositionSize.height);
- DALI_LOG_RELEASE_INFO("WindowRenderSurface::ProcessPostRender: Rotation Done\n");
mWindowRotationFinished = true;
+ DALI_LOG_RELEASE_INFO("WindowRenderSurface::ProcessPostRender: Rotation Done, flag = %d\n", mWindowRotationFinished);
}
if(mIsImeWindowSurface)
/**
* Request surface rotation
* @param[in] angle A new angle of the surface
- * @param[in] width A new width of the surface
- * @param[in] height A new height of the surface
+ * @param[in] positionSize A new position and size of the surface
*/
- void RequestRotation(int angle, int width, int height);
+ void RequestRotation(int angle, PositionSize positionSize);
/**
* @brief Gets the window base object
void UpdatePositionSize(Dali::PositionSize positionSize);
/**
+ * @brief Query whether window is rotating or not.
+ *
+ * @return true if window is rotating, false otherwise.
+ */
+ bool IsWindowRotating() const;
+
+ /**
* @brief This signal is emitted when the output is transformed.
*/
OutputSignalType& OutputTransformedSignal();
#pragma once
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
int GetNativeWindowId() override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::GetNativeWindowResourceId()
+ */
+ std::string GetNativeWindowResourceId() override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::CreateEglWindow()
*/
EGLNativeWindowType CreateEglWindow(int width, int height) override;
bool IsMaximized() const override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::SetMaximumSize()
+ */
+ void SetMaximumSize(Dali::Window::WindowSize size) override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::Minimize()
*/
void Minimize(bool minimize) override;
bool IsMinimized() const override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::SetMimimumSize()
+ */
+ void SetMimimumSize(Dali::Window::WindowSize size) override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::SetAvailableAnlges()
*/
void SetAvailableAnlges(const std::vector<int>& angles) override;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return mImpl->mWindow.windowNumber;
}
+std::string WindowBaseCocoa::GetNativeWindowResourceId()
+{
+ return std::string();
+}
+
EGLNativeWindowType WindowBaseCocoa::CreateEglWindow(int width, int height)
{
// XXX: this method is called from a secondary thread, but
return false;
}
+void WindowBaseCocoa::SetMaximumSize(Dali::Window::WindowSize size)
+{
+}
+
void WindowBaseCocoa::Minimize(bool minimize)
{
}
return false;
}
+void WindowBaseCocoa::SetMimimumSize(Dali::Window::WindowSize size)
+{
+}
+
void WindowBaseCocoa::SetAvailableAnlges( const std::vector< int >& angles )
{
}
DisplayConnectionEcoreWl::DisplayConnectionEcoreWl()
: mDisplay(NULL),
mSurfaceType(RenderSurfaceInterface::WINDOW_RENDER_SURFACE),
- mGraphics(nullptr)
+ mGraphics(nullptr),
+ mBufMgr(nullptr)
{
}
EGLNativeDisplayType DisplayConnectionEcoreWl::GetNativeDisplay()
{
+ mBufMgr = tbm_bufmgr_init(-1); // -1 is meaningless. The parameter in this function is deprecated.
+ if(mBufMgr == nullptr)
+ {
+ DALI_LOG_ERROR("Fail to init tbm buf mgr\n");
+ return nullptr;
+ }
return reinterpret_cast<EGLNativeDisplayType>(tbm_dummy_display_create());
}
{
tbm_dummy_display_destroy(reinterpret_cast<tbm_dummy_display*>(mDisplay));
}
+
+ if(mBufMgr != nullptr)
+ {
+ tbm_bufmgr_deinit(mBufMgr);
+ }
}
} // namespace Adaptor
*
*/
+// EXTERNAL INCLUDES
+#include <tbm_bufmgr.h>
+
// INTERNAL INCLUDES
#include <dali/internal/window-system/common/display-connection-impl.h>
EGLNativeDisplayType mDisplay; ///< Wayland-display for rendering
Dali::RenderSurfaceInterface::Type mSurfaceType; ///< The surface type
GraphicsInterface* mGraphics; ///< The graphics interface
+ tbm_bufmgr mBufMgr; ///< For creating tbm_dummy_display
};
} // namespace Adaptor
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
}
-/////////////////////////////////////////////////////////////////////////////////////////////////
-// ElDBus Accessibility Callbacks
-/////////////////////////////////////////////////////////////////////////////////////////////////
-
-#ifdef DALI_ELDBUS_AVAILABLE
-// Callback for Ecore ElDBus accessibility events.
-static void EcoreElDBusAccessibilityNotification(void* context, const Eldbus_Message* message)
-{
- WindowBaseEcoreWl* windowBase = static_cast<WindowBaseEcoreWl*>(context);
- if(windowBase)
- {
- windowBase->OnEcoreElDBusAccessibilityNotification(context, message);
- }
-}
-#endif // DALI_ELDBUS_AVAILABLE
-
static void RegistryGlobalCallback(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version)
{
WindowBaseEcoreWl* windowBase = static_cast<WindowBaseEcoreWl*>(data);
mWindowRotationAngle(0),
mScreenRotationAngle(0),
mSupportedPreProtation(0)
-#ifdef DALI_ELDBUS_AVAILABLE
- ,
- mSystemConnection(NULL)
-#endif
{
Initialize(positionSize, surface, isTransparent);
}
WindowBaseEcoreWl::~WindowBaseEcoreWl()
{
-#ifdef DALI_ELDBUS_AVAILABLE
- // Close down ElDBus connections.
- if(mSystemConnection)
- {
- eldbus_connection_unref(mSystemConnection);
- }
-#endif // DALI_ELDBUS_AVAILABLE
-
vconf_ignore_key_changed(VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_SIZE, VconfNotifyFontSizeChanged);
vconf_ignore_key_changed(DALI_VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_NAME, VconfNotifyFontNameChanged);
vconf_notify_key_changed(DALI_VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_NAME, VconfNotifyFontNameChanged, this);
vconf_notify_key_changed(VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_SIZE, VconfNotifyFontSizeChanged, this);
- InitializeEcoreElDBus();
-
mDisplay = ecore_wl_display_get();
if(mDisplay)
mStyleChangedSignal.Emit(StyleChange::DEFAULT_FONT_SIZE_CHANGE);
}
-void WindowBaseEcoreWl::OnEcoreElDBusAccessibilityNotification(void* context, const Eldbus_Message* message)
-{
-#ifdef DALI_ELDBUS_AVAILABLE
- AccessibilityInfo info;
-
- // The string defines the arg-list's respective types.
- if(!eldbus_message_arguments_get(message, "iiiiiiu", &info.gestureValue, &info.startX, &info.startY, &info.endX, &info.endY, &info.state, &info.eventTime))
- {
- DALI_LOG_ERROR("OnEcoreElDBusAccessibilityNotification: Error getting arguments\n");
- }
-
- mAccessibilitySignal.Emit(info);
-#endif
-}
-
void WindowBaseEcoreWl::RegistryGlobalCallback(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version)
{
if(strcmp(interface, tizen_policy_interface.name) == 0)
return ecore_wl_window_id_get(mEcoreWindow);
}
+std::string WindowBaseEcoreWl::GetNativeWindowResourceId()
+{
+ return std::string();
+}
+
EGLNativeWindowType WindowBaseEcoreWl::CreateEglWindow(int width, int height)
{
mEglWindow = wl_egl_window_create(mWlSurface, width, height);
return false;
}
+void WindowBaseEcoreWl::SetMaximumSize(Dali::Window::WindowSize size)
+{
+}
+
void WindowBaseEcoreWl::Minimize(bool minimize)
{
}
return false;
}
+void WindowBaseEcoreWl::SetMimimumSize(Dali::Window::WindowSize size)
+{
+}
+
void WindowBaseEcoreWl::SetAvailableAnlges(const std::vector<int>& angles)
{
int rotations[4] = {0};
ecore_wl_window_alpha_set(mEcoreWindow, transparent);
}
-void WindowBaseEcoreWl::InitializeEcoreElDBus()
-{
-#ifdef DALI_ELDBUS_AVAILABLE
- Eldbus_Object* object;
- Eldbus_Proxy* manager;
-
- if(!(mSystemConnection = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM)))
- {
- DALI_LOG_ERROR("Unable to get system bus\n");
- }
-
- object = eldbus_object_get(mSystemConnection, BUS, PATH);
- if(!object)
- {
- DALI_LOG_ERROR("Getting object failed\n");
- return;
- }
-
- manager = eldbus_proxy_get(object, INTERFACE);
- if(!manager)
- {
- DALI_LOG_ERROR("Getting proxy failed\n");
- return;
- }
-
- if(!eldbus_proxy_signal_handler_add(manager, "GestureDetected", EcoreElDBusAccessibilityNotification, this))
- {
- DALI_LOG_ERROR("No signal handler returned\n");
- }
-#endif
-}
-
void WindowBaseEcoreWl::CreateWindow(PositionSize positionSize)
{
mEcoreWindow = ecore_wl_window_new(0, positionSize.x, positionSize.y, positionSize.width, positionSize.height, ECORE_WL_WINDOW_BUFFER_TYPE_EGL_WINDOW);
#define DALI_INTERNAL_WINDOWSYSTEM_TIZENWAYLAND_WINDOW_BASE_ECORE_WL_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <tizen-extension-client-protocol.h>
#include <wayland-egl.h>
-#ifdef DALI_ELDBUS_AVAILABLE
-#include <Eldbus.h>
-#endif
-
namespace Dali
{
namespace Internal
*/
void OnFontSizeChanged();
-#ifdef DALI_ELDBUS_AVAILABLE
- /**
- * @brief Called when Ecore ElDBus accessibility event is received.
- */
- void OnEcoreElDBusAccessibilityNotification(void* context, const Eldbus_Message* message);
-#endif
-
/**
* @brief RegistryGlobalCallback
*/
int GetNativeWindowId() override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::GetNativeWindowResourceId()
+ */
+ std::string GetNativeWindowResourceId() override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::CreateEglWindow()
*/
EGLNativeWindowType CreateEglWindow(int width, int height) override;
bool IsMaximized() const override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::SetMaximumSize()
+ */
+ void SetMaximumSize(Dali::Window::WindowSize size) override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::Minimize()
*/
void Minimize(bool minimize) override;
bool IsMinimized() const override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::SetMimimumSize()
+ */
+ void SetMimimumSize(Dali::Window::WindowSize size) override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::SetAvailableAnlges()
*/
void SetAvailableAnlges(const std::vector<int>& angles) override;
void Initialize(PositionSize positionSize, Any surface, bool isTransparent);
/**
- * Initialize Ecore ElDBus
- */
- void InitializeEcoreElDBus();
-
- /**
* @brief Create window
*/
void CreateWindow(PositionSize positionSize);
int mWindowRotationAngle;
int mScreenRotationAngle;
int mSupportedPreProtation;
-#ifdef DALI_ELDBUS_AVAILABLE
- Eldbus_Connection* mSystemConnection;
-#endif // DALI_ELDBUS_AVAILABLE
};
} // namespace Adaptor
const char* DALI_VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_NAME = "db/setting/accessibility/font_name"; // It will be update at vconf-key.h and replaced.
-#ifdef DALI_ELDBUS_AVAILABLE
-// DBUS accessibility
-const char* BUS = "org.enlightenment.wm-screen-reader";
-const char* INTERFACE = "org.tizen.GestureNavigation";
-const char* PATH = "/org/tizen/GestureNavigation";
-#endif // DALI_ELDBUS_AVAILABLE
-
struct KeyCodeMap
{
xkb_keysym_t keySym;
}
/**
-* Called when the source window sends us about the selected content.
-* For example, when item is selected in the clipboard.
-*/
+ * Called when the source window sends us about the selected content.
+ * For example, when item is selected in the clipboard.
+ */
static Eina_Bool EcoreEventDataReceive(void* data, int type, void* event)
{
WindowBaseEcoreWl2* windowBase = static_cast<WindowBaseEcoreWl2*>(data);
static Eina_Bool EcoreEventSeatKeyboardRepeatChanged(void* data, int type, void* event)
{
- Ecore_Wl2_Event_Seat_Keyboard_Repeat_Changed* keyboardRepeat = static_cast<Ecore_Wl2_Event_Seat_Keyboard_Repeat_Changed*>(event);
- WindowBaseEcoreWl2* windowBase = static_cast<WindowBaseEcoreWl2*>(data);
- DALI_LOG_INFO(gWindowBaseLogFilter, Debug::General, "WindowBaseEcoreWl2::EcoreEventSeatKeyboardRepeatChanged, id[ %d ]\n", keyboardRepeat->id);
+ WindowBaseEcoreWl2* windowBase = static_cast<WindowBaseEcoreWl2*>(data);
+ DALI_LOG_INFO(gWindowBaseLogFilter, Debug::General, "WindowBaseEcoreWl2::EcoreEventSeatKeyboardRepeatChanged, id[ %d ]\n", static_cast<Ecore_Wl2_Event_Seat_Keyboard_Repeat_Changed*>(event)->id);
if(windowBase)
{
windowBase->OnKeyboardRepeatSettingsChanged();
static Eina_Bool EcoreEventWindowRedrawRequest(void* data, int type, void* event)
{
- Ecore_Wl2_Event_Window_Redraw_Request* windowRedrawRequest = static_cast<Ecore_Wl2_Event_Window_Redraw_Request*>(event);
- WindowBaseEcoreWl2* windowBase = static_cast<WindowBaseEcoreWl2*>(data);
- DALI_LOG_INFO(gWindowBaseLogFilter, Debug::General, "WindowBaseEcoreWl2::EcoreEventWindowRedrawRequest, window[ %d ]\n", windowRedrawRequest->win);
+ WindowBaseEcoreWl2* windowBase = static_cast<WindowBaseEcoreWl2*>(data);
+ DALI_LOG_INFO(gWindowBaseLogFilter, Debug::General, "WindowBaseEcoreWl2::EcoreEventWindowRedrawRequest, window[ %d ]\n", static_cast<Ecore_Wl2_Event_Window_Redraw_Request*>(event)->win);
if(windowBase)
{
windowBase->OnEcoreEventWindowRedrawRequest();
return ECORE_CALLBACK_RENEW;
}
-/////////////////////////////////////////////////////////////////////////////////////////////////
-// ElDBus Accessibility Callbacks
-/////////////////////////////////////////////////////////////////////////////////////////////////
-
-#ifdef DALI_ELDBUS_AVAILABLE
-// Callback for Ecore ElDBus accessibility events.
-static void EcoreElDBusAccessibilityNotification(void* context, const Eldbus_Message* message)
-{
- WindowBaseEcoreWl2* windowBase = static_cast<WindowBaseEcoreWl2*>(context);
- if(windowBase)
- {
- windowBase->OnEcoreElDBusAccessibilityNotification(context, message);
- }
-}
-#endif // DALI_ELDBUS_AVAILABLE
-
static void RegistryGlobalCallback(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version)
{
WindowBaseEcoreWl2* windowBase = static_cast<WindowBaseEcoreWl2*>(data);
mWindowRotationAngle(0),
mScreenRotationAngle(0),
mSupportedPreProtation(0),
+ mScreenWidth(0),
+ mScreenHeight(0),
mNotificationChangeState(0),
mScreenOffModeChangeState(0),
mBrightnessChangeState(0),
mVisible(true),
mOwnSurface(false),
mBrightnessChangeDone(true)
-#ifdef DALI_ELDBUS_AVAILABLE
- ,
- mSystemConnection(NULL)
-#endif
{
Initialize(positionSize, surface, isTransparent);
}
WindowBaseEcoreWl2::~WindowBaseEcoreWl2()
{
-#ifdef DALI_ELDBUS_AVAILABLE
- // Close down ElDBus connections.
- if(mSystemConnection)
- {
- eldbus_connection_unref(mSystemConnection);
- }
-#endif // DALI_ELDBUS_AVAILABLE
-
vconf_ignore_key_changed(VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_SIZE, VconfNotifyFontSizeChanged);
vconf_ignore_key_changed(DALI_VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_NAME, VconfNotifyFontNameChanged);
vconf_notify_key_changed(DALI_VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_NAME, VconfNotifyFontNameChanged, this);
vconf_notify_key_changed(VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_SIZE, VconfNotifyFontSizeChanged, this);
- InitializeEcoreElDBus();
-
Ecore_Wl2_Display* display = ecore_wl2_connected_display_get(NULL);
mDisplay = ecore_wl2_display_get(display);
if(ev->w == 0 || ev->h == 0)
{
- // Use previous client side window's size.
- if(mWindowRotationAngle == 90 || mWindowRotationAngle == 270)
- {
- ev->w = mWindowPositionSize.height;
- ev->h = mWindowPositionSize.width;
- }
- else
- {
- ev->w = mWindowPositionSize.width;
- ev->h = mWindowPositionSize.height;
- }
+ // When rotation event does not have the window width or height,
+ // previous DALi side window's size are used.
+ ev->w = mWindowPositionSize.width;
+ ev->h = mWindowPositionSize.height;
}
mWindowRotationAngle = ev->angle;
- if(ev->angle == 0 || ev->angle == 180)
- {
- rotationEvent.width = ev->w;
- rotationEvent.height = ev->h;
- }
- else
- {
- rotationEvent.width = ev->h;
- rotationEvent.height = ev->w;
- }
+ mWindowPositionSize.width = ev->w;
+ mWindowPositionSize.height = ev->h;
- mWindowPositionSize.width = rotationEvent.width;
- mWindowPositionSize.height = rotationEvent.height;
+ PositionSize newPositionSize = RecalculatePositionSizeToCurrentOrientation(mWindowPositionSize);
+
+ rotationEvent.x = newPositionSize.x;
+ rotationEvent.y = newPositionSize.y;
+ rotationEvent.width = newPositionSize.width;
+ rotationEvent.height = newPositionSize.height;
mRotationSignal.Emit(rotationEvent);
}
if(windowMoved || windowResized)
{
- Dali::PositionSize newPositionSize(ev->x, ev->y, newWidth, newHeight);
- mWindowPositionSize = newPositionSize;
- DALI_LOG_RELEASE_INFO("Update position & resize signal by server, x[%d] y[%d] w[%d] h[%d]\n", newPositionSize.x, newPositionSize.y, newPositionSize.width, newPositionSize.height);
+ mWindowPositionSize.x = ev->x;
+ mWindowPositionSize.y = ev->y;
+ mWindowPositionSize.width = newWidth;
+ mWindowPositionSize.height = newHeight;
+ DALI_LOG_RELEASE_INFO("Update position & resize signal by server, current angle [%d] x[%d] y[%d] w[%d] h[%d]\n", mWindowRotationAngle, mWindowPositionSize.x, mWindowPositionSize.y, mWindowPositionSize.width, mWindowPositionSize.height);
+
+ ecore_wl2_window_geometry_set(mEcoreWindow, mWindowPositionSize.x, mWindowPositionSize.y, mWindowPositionSize.width, mWindowPositionSize.height);
+
+ Dali::PositionSize newPositionSize = RecalculatePositionSizeToCurrentOrientation(mWindowPositionSize);
+ DALI_LOG_RELEASE_INFO("emit signal to update window's position and size, x[%d] y[%d] w[%d] h[%d]\n", newPositionSize.x, newPositionSize.y, newPositionSize.width, newPositionSize.height);
mUpdatePositionSizeSignal.Emit(newPositionSize);
}
mStyleChangedSignal.Emit(StyleChange::DEFAULT_FONT_SIZE_CHANGE);
}
-#ifdef DALI_ELDBUS_AVAILABLE
-void WindowBaseEcoreWl2::OnEcoreElDBusAccessibilityNotification(void* context, const Eldbus_Message* message)
-{
- AccessibilityInfo info;
-
- // The string defines the arg-list's respective types.
- if(!eldbus_message_arguments_get(message, "iiiiiiu", &info.gestureValue, &info.startX, &info.startY, &info.endX, &info.endY, &info.state, &info.eventTime))
- {
- DALI_LOG_ERROR("OnEcoreElDBusAccessibilityNotification: Error getting arguments\n");
- }
-
- mAccessibilitySignal.Emit(info);
-}
-#endif // DALI_ELDBUS_AVAILABLE
-
void WindowBaseEcoreWl2::OnTransitionEffectEvent(WindowEffectState state, WindowEffectType type)
{
mTransitionEffectEventSignal.Emit(state, type);
Ecore_Wl2_Event_Aux_Message* message = static_cast<Ecore_Wl2_Event_Aux_Message*>(event);
if(message)
{
- DALI_LOG_RELEASE_INFO("WindowBaseEcoreWl2::OnEcoreEventWindowAuxiliaryMessage, key:%s, value:%s \n", message->key, message->val);
+ DALI_LOG_INFO(gWindowBaseLogFilter, Debug::General, "WindowBaseEcoreWl2::OnEcoreEventWindowAuxiliaryMessage, key:%s, value:%s \n", message->key, message->val);
std::string key(message->key);
std::string value(message->val);
Dali::Property::Array options;
void* data;
EINA_LIST_FOREACH(message->options, l, data)
{
- DALI_LOG_RELEASE_INFO("WindowBaseEcoreWl2::OnEcoreEventWindowAuxiliaryMessage, option: %s\n", (char*)data);
+ DALI_LOG_INFO(gWindowBaseLogFilter, Debug::General, "WindowBaseEcoreWl2::OnEcoreEventWindowAuxiliaryMessage, option: %s\n", (char*)data);
std::string option(static_cast<char*>(data));
options.Add(option);
}
return ecore_wl2_window_id_get(mEcoreWindow);
}
+std::string WindowBaseEcoreWl2::GetNativeWindowResourceId()
+{
+#ifdef OVER_TIZEN_VERSION_7
+ return std::to_string(ecore_wl2_window_resource_id_get(mEcoreWindow));
+#else
+ return std::string();
+#endif
+}
+
EGLNativeWindowType WindowBaseEcoreWl2::CreateEglWindow(int width, int height)
{
int totalAngle = (mWindowRotationAngle + mScreenRotationAngle) % 360;
return false;
}
+PositionSize WindowBaseEcoreWl2::RecalculatePositionSizeToSystem(PositionSize positionSize)
+{
+ PositionSize newPositionSize;
+
+ if(mWindowRotationAngle == 90)
+ {
+ newPositionSize.x = positionSize.y;
+ newPositionSize.y = mScreenHeight - (positionSize.x + positionSize.width);
+ newPositionSize.width = positionSize.height;
+ newPositionSize.height = positionSize.width;
+ }
+ else if(mWindowRotationAngle == 180)
+ {
+ newPositionSize.x = mScreenWidth - (positionSize.x + positionSize.width);
+ newPositionSize.y = mScreenHeight - (positionSize.y + positionSize.height);
+ newPositionSize.width = positionSize.width;
+ newPositionSize.height = positionSize.height;
+ }
+ else if(mWindowRotationAngle == 270)
+ {
+ newPositionSize.x = mScreenWidth - (positionSize.y + positionSize.height);
+ newPositionSize.y = positionSize.x;
+ newPositionSize.width = positionSize.height;
+ newPositionSize.height = positionSize.width;
+ }
+ else
+ {
+ newPositionSize.x = positionSize.x;
+ newPositionSize.y = positionSize.y;
+ newPositionSize.width = positionSize.width;
+ newPositionSize.height = positionSize.height;
+ }
+
+ DALI_LOG_RELEASE_INFO("input coord x[%d], y[%d], w{%d], h[%d], screen w[%d], h[%d]\n", positionSize.x, positionSize.y, positionSize.width, positionSize.height, mScreenWidth, mScreenHeight);
+ DALI_LOG_RELEASE_INFO("recalc coord x[%d], y[%d], w{%d], h[%d]\n", newPositionSize.x, newPositionSize.y, newPositionSize.width, newPositionSize.height);
+
+ return newPositionSize;
+}
+
+PositionSize WindowBaseEcoreWl2::RecalculatePositionSizeToCurrentOrientation(PositionSize positionSize)
+{
+ PositionSize newPositionSize;
+
+ if(mWindowRotationAngle == 90)
+ {
+ newPositionSize.x = mScreenHeight - (positionSize.y + positionSize.height);
+ newPositionSize.y = positionSize.x;
+ newPositionSize.width = positionSize.height;
+ newPositionSize.height = positionSize.width;
+ }
+ else if(mWindowRotationAngle == 180)
+ {
+ newPositionSize.x = mScreenWidth - (positionSize.x + positionSize.width);
+ newPositionSize.y = mScreenHeight - (positionSize.y + positionSize.height);
+ newPositionSize.width = positionSize.width;
+ newPositionSize.height = positionSize.height;
+ }
+ else if(mWindowRotationAngle == 270)
+ {
+ newPositionSize.x = positionSize.y;
+ newPositionSize.y = mScreenWidth - (positionSize.x + positionSize.width);
+ newPositionSize.width = positionSize.height;
+ newPositionSize.height = positionSize.width;
+ }
+ else
+ {
+ newPositionSize.x = positionSize.x;
+ newPositionSize.y = positionSize.y;
+ newPositionSize.width = positionSize.width;
+ newPositionSize.height = positionSize.height;
+ }
+
+ DALI_LOG_RELEASE_INFO("input coord x[%d], y[%d], w{%d], h[%d], screen w[%d], h[%d]\n", positionSize.x, positionSize.y, positionSize.width, positionSize.height, mScreenWidth, mScreenHeight);
+ DALI_LOG_RELEASE_INFO("recalc by current orientation coord x[%d], y[%d], w{%d], h[%d]\n", newPositionSize.x, newPositionSize.y, newPositionSize.width, newPositionSize.height);
+
+ return newPositionSize;
+}
+
void WindowBaseEcoreWl2::Move(PositionSize positionSize)
{
- mWindowPositionSize = positionSize;
- ecore_wl2_window_position_set(mEcoreWindow, positionSize.x, positionSize.y);
+ PositionSize newPositionSize = RecalculatePositionSizeToSystem(positionSize);
+
+ mWindowPositionSize = newPositionSize;
+ DALI_LOG_RELEASE_INFO("ecore_wl2_window_position_set x[%d], y[%d]\n", newPositionSize.x, newPositionSize.y);
+ ecore_wl2_window_position_set(mEcoreWindow, newPositionSize.x, newPositionSize.y);
}
void WindowBaseEcoreWl2::Resize(PositionSize positionSize)
{
- mWindowPositionSize = positionSize;
- ecore_wl2_window_geometry_set(mEcoreWindow, positionSize.x, positionSize.y, positionSize.width, positionSize.height);
+ PositionSize newPositionSize = RecalculatePositionSizeToSystem(positionSize);
+
+ mWindowPositionSize = newPositionSize;
+ DALI_LOG_RELEASE_INFO("ecore_wl2_window_sync_geometry_set, x[%d], y[%d], w{%d], h[%d]\n", newPositionSize.x, newPositionSize.y, newPositionSize.width, newPositionSize.height);
+ ecore_wl2_window_sync_geometry_set(mEcoreWindow, ++mMoveResizeSerial, newPositionSize.x, newPositionSize.y, newPositionSize.width, newPositionSize.height);
}
void WindowBaseEcoreWl2::MoveResize(PositionSize positionSize)
{
- mWindowPositionSize = positionSize;
- ecore_wl2_window_sync_geometry_set(mEcoreWindow, ++mMoveResizeSerial, positionSize.x, positionSize.y, positionSize.width, positionSize.height);
+ PositionSize newPositionSize = RecalculatePositionSizeToSystem(positionSize);
+
+ mWindowPositionSize = newPositionSize;
+ DALI_LOG_RELEASE_INFO("ecore_wl2_window_sync_geometry_set, x[%d], y[%d], w{%d], h[%d]\n", newPositionSize.x, newPositionSize.y, newPositionSize.width, newPositionSize.height);
+ ecore_wl2_window_sync_geometry_set(mEcoreWindow, ++mMoveResizeSerial, newPositionSize.x, newPositionSize.y, newPositionSize.width, newPositionSize.height);
}
void WindowBaseEcoreWl2::SetClass(const std::string& name, const std::string& className)
return ecore_wl2_window_maximized_get(mEcoreWindow);
}
+void WindowBaseEcoreWl2::SetMaximumSize(Dali::Window::WindowSize size)
+{
+ DALI_LOG_RELEASE_INFO("ecore_wl2_window_maximum_size_set, width: %d, height: %d\n", size.GetWidth(), size.GetHeight());
+ ecore_wl2_window_maximum_size_set(mEcoreWindow, size.GetWidth(), size.GetHeight());
+}
+
void WindowBaseEcoreWl2::Minimize(bool minimize)
{
ecore_wl2_window_iconified_set(mEcoreWindow, minimize);
return ecore_wl2_window_iconified_get(mEcoreWindow);
}
+void WindowBaseEcoreWl2::SetMimimumSize(Dali::Window::WindowSize size)
+{
+ DALI_LOG_RELEASE_INFO("ecore_wl2_window_minimum_size_set, width: %d, height: %d\n", size.GetWidth(), size.GetHeight());
+ ecore_wl2_window_minimum_size_set(mEcoreWindow, size.GetWidth(), size.GetHeight());
+}
+
void WindowBaseEcoreWl2::SetAvailableAnlges(const std::vector<int>& angles)
{
int rotations[4] = {0};
for(std::size_t i = 0; i < angles.size(); ++i)
{
rotations[i] = static_cast<int>(angles[i]);
- DALI_LOG_RELEASE_INFO("%d ", rotations[i]);
+ DALI_LOG_INFO(gWindowBaseLogFilter, Debug::General, "%d ", rotations[i]);
}
ecore_wl2_window_available_rotations_set(mEcoreWindow, rotations, angles.size());
}
{
if(!mVisible)
{
- // Ecore-wl2 has the original window size
- // and he always sends the window rotation event with the swapped size.
- // So, to restore, dali should set the reswapped size(original window size) to ecore-wl2 for restoring.
- if(mWindowRotationAngle == 0 || mWindowRotationAngle == 180)
- {
- ecore_wl2_window_geometry_set(mEcoreWindow, mWindowPositionSize.x, mWindowPositionSize.y, mWindowPositionSize.width, mWindowPositionSize.height);
- }
- else
- {
- ecore_wl2_window_geometry_set(mEcoreWindow, mWindowPositionSize.x, mWindowPositionSize.y, mWindowPositionSize.height, mWindowPositionSize.width);
- }
+ ecore_wl2_window_geometry_set(mEcoreWindow, mWindowPositionSize.x, mWindowPositionSize.y, mWindowPositionSize.width, mWindowPositionSize.height);
}
mVisible = true;
ecore_wl2_window_alpha_set(mEcoreWindow, transparent);
}
-void WindowBaseEcoreWl2::InitializeEcoreElDBus()
-{
-#ifdef DALI_ELDBUS_AVAILABLE
- Eldbus_Object* object;
- Eldbus_Proxy* manager;
-
- if(!(mSystemConnection = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM)))
- {
- DALI_LOG_ERROR("Unable to get system bus\n");
- }
-
- object = eldbus_object_get(mSystemConnection, BUS, PATH);
- if(!object)
- {
- DALI_LOG_ERROR("Getting object failed\n");
- return;
- }
-
- manager = eldbus_proxy_get(object, INTERFACE);
- if(!manager)
- {
- DALI_LOG_ERROR("Getting proxy failed\n");
- return;
- }
-
- if(!eldbus_proxy_signal_handler_add(manager, "GestureDetected", EcoreElDBusAccessibilityNotification, this))
- {
- DALI_LOG_ERROR("No signal handler returned\n");
- }
-#endif
-}
-
void WindowBaseEcoreWl2::CreateWindow(PositionSize positionSize)
{
Ecore_Wl2_Display* display = ecore_wl2_display_connect(NULL);
// Set default type
ecore_wl2_window_type_set(mEcoreWindow, ECORE_WL2_WINDOW_TYPE_TOPLEVEL);
+
+ // Get Screen width, height
+ ecore_wl2_display_screen_size_get(display, &mScreenWidth, &mScreenHeight);
}
void WindowBaseEcoreWl2::SetParent(WindowBase* parentWinBase, bool belowParent)
#include <wayland-egl.h>
#include <xkbcommon/xkbcommon.h>
-#ifdef DALI_ELDBUS_AVAILABLE
-#include <Eldbus.h>
-#endif
-
namespace Dali
{
namespace Internal
*/
void OnEcoreEventWindowAuxiliaryMessage(void* event);
-#ifdef DALI_ELDBUS_AVAILABLE
- /**
- * @brief Called when Ecore ElDBus accessibility event is received.
- */
- void OnEcoreElDBusAccessibilityNotification(void* context, const Eldbus_Message* message);
-#endif
-
/**
* @brief Called when a keymap is changed.
*/
int GetNativeWindowId() override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::GetNativeWindowResourceId()
+ */
+ std::string GetNativeWindowResourceId() override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::CreateEglWindow()
*/
EGLNativeWindowType CreateEglWindow(int width, int height) override;
bool IsMaximized() const override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::SetMaximumSize()
+ */
+ void SetMaximumSize(Dali::Window::WindowSize size) override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::Minimize()
*/
void Minimize(bool minimize) override;
bool IsMinimized() const override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::SetMimimumSize()
+ */
+ void SetMimimumSize(Dali::Window::WindowSize size) override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::SetAvailableAnlges()
*/
void SetAvailableAnlges(const std::vector<int>& angles) override;
void Initialize(PositionSize positionSize, Any surface, bool isTransparent);
/**
- * Initialize Ecore ElDBus
+ * @brief Create window
*/
- void InitializeEcoreElDBus();
+ void CreateWindow(PositionSize positionSize);
/**
- * @brief Create window
+ * @brief Return the window's position and size to recalulate with the default system coordinates.
+ * It is used when window is moved or resized for native ecore wayland window system.
+ *
+ * @param[in] positionSize the window's current position and size with current oriented window's coordinates.
+ * @return the re-calculated window's position and size on the default system coordinates.
*/
- void CreateWindow(PositionSize positionSize);
+ PositionSize RecalculatePositionSizeToSystem(PositionSize positionSize);
+
+ /**
+ * @brief Return the window's position and size to recalulate with current oriented window's coordinates.
+ * It is used when window is moved or resized for dali and uppler layer framework.
+ *
+ * @param[in] positionSize the window's current position and size with the default system coordinates.
+ * @return the re-calculated window's position and size on current oriented window's coordinates.
+ */
+ PositionSize RecalculatePositionSizeToCurrentOrientation(PositionSize positionSize);
protected:
// Undefined
#ifdef OVER_TIZEN_VERSION_7
zwp_input_panel_v1* mWlInputPanel;
#else
- wl_input_panel* mWlInputPanel;
+ wl_input_panel* mWlInputPanel;
#endif
wl_output* mWlOutput;
#ifdef OVER_TIZEN_VERSION_7
std::vector<std::string> mSupportedAuxiliaryHints;
+ // It is based on the default system coordinates.
Dali::PositionSize mWindowPositionSize;
- AuxiliaryHints mAuxiliaryHints;
+
+ AuxiliaryHints mAuxiliaryHints;
WindowType mType;
int mNotificationLevel;
int mWindowRotationAngle;
int mScreenRotationAngle;
int mSupportedPreProtation;
+ int mScreenWidth;
+ int mScreenHeight;
uint32_t mNotificationChangeState;
uint32_t mScreenOffModeChangeState;
bool mVisible : 1;
bool mOwnSurface;
bool mBrightnessChangeDone;
-
-#ifdef DALI_ELDBUS_AVAILABLE
- Eldbus_Connection* mSystemConnection;
-#endif // DALI_ELDBUS_AVAILABLE
};
} // namespace Adaptor
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return mEcoreWindow;
}
+std::string WindowBaseEcoreX::GetNativeWindowResourceId()
+{
+ return std::string();
+}
+
EGLNativeWindowType WindowBaseEcoreX::CreateEglWindow(int width, int height)
{
// need to create X handle as in 64bit system ECore handle is 32 bit whereas EGLnative and XWindow are 64 bit
return false;
}
+void WindowBaseEcoreX::SetMaximumSize(Dali::Window::WindowSize size)
+{
+}
+
void WindowBaseEcoreX::Minimize(bool minimize)
{
}
return false;
}
+void WindowBaseEcoreX::SetMimimumSize(Dali::Window::WindowSize size)
+{
+}
+
void WindowBaseEcoreX::SetAvailableAnlges(const std::vector<int>& angles)
{
}
#define DALI_INTERNAL_WINDOWSYSTEM_ECOREX_WINDOW_BASE_ECORE_X_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
int GetNativeWindowId() override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::GetNativeWindowResourceId()
+ */
+ std::string GetNativeWindowResourceId() override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::CreateEglWindow()
*/
EGLNativeWindowType CreateEglWindow(int width, int height) override;
bool IsMaximized() const override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::SetMaximumSize()
+ */
+ void SetMaximumSize(Dali::Window::WindowSize size) override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::Minimize()
*/
void Minimize(bool minimize) override;
bool IsMinimized() const override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::SetMimimumSize()
+ */
+ void SetMimimumSize(Dali::Window::WindowSize size) override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::SetAvailableAnlges()
*/
void SetAvailableAnlges(const std::vector<int>& angles) override;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return mWin32Window;
}
+std::string WindowBaseWin::GetNativeWindowResourceId()
+{
+ return std::string();
+}
+
EGLNativeWindowType WindowBaseWin::CreateEglWindow(int width, int height)
{
return reinterpret_cast<EGLNativeWindowType>(mWin32Window);
return false;
}
+void WindowBaseWin::SetMaximumSize(Dali::Window::WindowSize size)
+{
+}
+
void WindowBaseWin::Minimize(bool minimize)
{
}
return false;
}
+void WindowBaseWin::SetMimimumSize(Dali::Window::WindowSize size)
+{
+}
+
void WindowBaseWin::SetAvailableAnlges(const std::vector<int>& angles)
{
}
#define DALI_INTERNAL_WINDOWSYSTEM_WINDOW_BASE_WIN_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
int GetNativeWindowId() override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::GetNativeWindowResourceId()
+ */
+ std:string GetNativeWindowResourceId() override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::CreateEglWindow()
*/
EGLNativeWindowType CreateEglWindow(int width, int height) override;
bool IsMaximized() const override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::SetMaximumSize()
+ */
+ void SetMaximumSize(Dali::Window::WindowSize size) override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::Minimize()
*/
void Minimize(bool minimize) override;
bool IsMinimized() const override;
/**
+ * @copydoc Dali::Internal::Adaptor::WindowBase::SetMimimumSize()
+ */
+ void SetMimimumSize(Dali::Window::WindowSize size) override;
+
+ /**
* @copydoc Dali::Internal::Adaptor::WindowBase::SetAvailableAnlges()
*/
void SetAvailableAnlges(const std::vector<int>& angles) override;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return GetImplementation(*this).GetRawBuffer();
}
+const std::size_t EncodedImageBuffer::GetHash() const
+{
+ return GetImplementation(*this).GetHash();
+}
+
EncodedImageBuffer& EncodedImageBuffer::operator=(const EncodedImageBuffer& handle)
{
BaseHandle::operator=(handle);
#ifndef DALI_ENCODED_IMAGE_BUFFER_H
#define DALI_ENCODED_IMAGE_BUFFER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
// EXTERNAL INCLUDES
-#include <dali/public-api/object/base-handle.h>
#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/object/base-handle.h>
// INTERNAL INCLUDES
#include <dali/public-api/dali-adaptor-common.h>
*/
const RawBufferType& GetRawBuffer() const;
+ /**
+ * @brief Get the hash value of raw buffer
+ *
+ * @return A hash value of raw buffer.
+ */
+ const std::size_t GetHash() const;
+
public: // Not intended for developer use
explicit DALI_INTERNAL EncodedImageBuffer(Internal::EncodedImageBuffer* impl);
};
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
WidgetApplication WidgetApplication::New(int* argc, char** argv[], const std::string& stylesheet)
{
+ Internal::Adaptor::ApplicationPtr preInitializedApplication = Internal::Adaptor::Application::GetPreInitializedApplication();
+ if(preInitializedApplication)
+ {
+ // WidgetApplication can't use pre-initialized application. So reset it.
+ preInitializedApplication.Reset();
+ }
+
Internal::Adaptor::WidgetApplicationPtr internal = Internal::Adaptor::WidgetApplication::New(argc, argv, stylesheet);
return WidgetApplication(internal.Get());
}
}
}
+bool Widget::IsKeyEventUsing() const
+{
+ if(mImpl != nullptr)
+ {
+ return mImpl->IsKeyEventUsing();
+ }
+
+ //if mImpl is null, return default value
+ return false;
+}
+
+void Widget::SetUsingKeyEvent(bool flag)
+{
+ if(mImpl != nullptr)
+ {
+ mImpl->SetUsingKeyEvent(flag);
+ }
+}
+
void Widget::SetImpl(Impl* impl)
{
mImpl = impl;
}
+
+void Widget::SetInformation(Dali::Window window, const std::string& widgetId)
+{
+ if(mImpl != nullptr)
+ {
+ mImpl->SetInformation(window, widgetId);
+ }
+}
+
+Dali::Window Widget::GetWindow() const
+{
+ if(mImpl != nullptr)
+ {
+ return mImpl->GetWindow();
+ }
+
+ return Dali::Window();
+}
+
+std::string Widget::GetWidgetId() const
+{
+ if(mImpl != nullptr)
+ {
+ return mImpl->GetWidgetId();
+ }
+
+ return std::string();
+}
+
Internal::Adaptor::Widget& GetImplementation(Dali::Widget& widget)
{
DALI_ASSERT_ALWAYS(widget && "widget handle is empty");
*/
void SetContentInfo(const std::string& contentInfo);
+ /**
+ * @brief Check Widget is using key
+ *
+ * widget can set flag to SetUsingKeyEvent
+ *
+ * @return True if Widget is using key
+ */
+ bool IsKeyEventUsing() const;
+
+ /**
+ * @brief Set the flag that widget is using keyEvent
+ *
+ * @param[in] flag The flag to set.
+ */
+ void SetUsingKeyEvent(bool flag);
+
protected:
/**
* @brief WidgetImpl constructor
*/
void SetImpl(Impl* impl);
+ /**
+ * @brief Set the Information of widget
+ *
+ * @param window The window to set the information of widget
+ * @param widgetId The Id of widget
+ */
+ void SetInformation(Dali::Window window, const std::string& widgetId);
+
+ /**
+ * @brief Get the Window of widget
+ *
+ * @return the window of widget
+ */
+ Dali::Window GetWindow() const;
+
+ /**
+ * @brief Get the id of widget
+ *
+ * @return the id of widget
+ */
+ std::string GetWidgetId() const;
+
private:
Impl* mImpl;
{
const unsigned int ADAPTOR_MAJOR_VERSION = 2;
const unsigned int ADAPTOR_MINOR_VERSION = 1;
-const unsigned int ADAPTOR_MICRO_VERSION = 10;
+const unsigned int ADAPTOR_MICRO_VERSION = 29;
const char* const ADAPTOR_BUILD_DATE = __DATE__ " " __TIME__;
#ifdef DEBUG_ENABLED
Name: dali2-adaptor
Summary: The DALi Tizen Adaptor
-Version: 2.1.10
+Version: 2.1.29
Release: 1
Group: System/Libraries
License: Apache-2.0 and BSD-3-Clause and MIT
%if "%{?profile}" != "mobile" && "%{?profile}" != "tv" && "%{?profile}" != "ivi" && "%{?profile}" != "common"
BuildRequires: pkgconfig(capi-appfw-watch-application)
BuildRequires: pkgconfig(appcore-watch)
-BuildRequires: pkgconfig(screen_connector_provider)
%endif
+BuildRequires: pkgconfig(screen_connector_provider)
BuildRequires: pkgconfig(gles20)
BuildRequires: pkgconfig(glesv2)
BuildRequires: pkgconfig(ttrace)
BuildRequires: pkgconfig(libtbm)
# for the adaptor
-BuildRequires: pkgconfig(appcore-ui)
+BuildRequires: pkgconfig(app-core-ui-cpp)
BuildRequires: pkgconfig(appcore-widget-base)
BuildRequires: pkgconfig(bundle)
BuildRequires: pkgconfig(capi-appfw-app-common)
CFLAGS+=" -DWAYLAND"
CXXFLAGS+=" -DWAYLAND"
-cmake_flags=" -DENABLE_WAYLAND=ON -DENABLE_ATSPI=ON"
+cmake_flags=" -DENABLE_WAYLAND=ON -DENABLE_ATSPI=ON -DENABLE_TRACE=ON"
# Use this conditional when Tizen version is 5.x or greater
%if 0%{?tizen_version_major} >= 5
cmake_flags+=" -DCMAKE_BUILD_TYPE=Debug"
%endif
-%if 0%{?enable_trace}
-cmake_flags+=" -DENABLE_TRACE=ON"
-%endif
-
%if 0%{?enable_logging}
cmake_flags+=" -DENABLE_NETWORK_LOGGING=ON"
%endif