diff options
author | Brad Linden <brad@lindenlab.com> | 2024-05-23 11:31:19 -0700 |
---|---|---|
committer | Brad Linden <brad@lindenlab.com> | 2024-05-23 11:31:19 -0700 |
commit | a1f49564d670a2c41bfa25c833bba2564b9b7f48 (patch) | |
tree | 1d205e51bc37621916a17d459ad83782fe41f975 /indra/llimage | |
parent | 6af5db09faf5ea33a2d4c47b64e76f42edae178a (diff) | |
parent | 6377610f6587989c126b00f490dfc8d527a1c2ce (diff) |
Merge remote-tracking branch 'origin/DRTVWR-600-maint-A' into brad/merge-maint-a-to-dev
Diffstat (limited to 'indra/llimage')
24 files changed, 6264 insertions, 6239 deletions
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 625dcf7410..863a28996d 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llimage.cpp * @brief Base class for images. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -54,40 +54,40 @@ #define _UNROL_GEN_TPL_comma_1 BOOST_PP_COMMA() //.................................................................................. #define _UNROL_GEN_TPL_ARGS_macro(z,n,seq) \ - BOOST_PP_CAT(_UNROL_GEN_TPL_arg_, BOOST_PP_MOD(n, 2))(BOOST_PP_SEQ_ELEM(n, seq)) BOOST_PP_CAT(_UNROL_GEN_TPL_comma_, BOOST_PP_AND(BOOST_PP_MOD(n, 2), BOOST_PP_NOT_EQUAL(BOOST_PP_INC(n), BOOST_PP_SEQ_SIZE(seq)))) + BOOST_PP_CAT(_UNROL_GEN_TPL_arg_, BOOST_PP_MOD(n, 2))(BOOST_PP_SEQ_ELEM(n, seq)) BOOST_PP_CAT(_UNROL_GEN_TPL_comma_, BOOST_PP_AND(BOOST_PP_MOD(n, 2), BOOST_PP_NOT_EQUAL(BOOST_PP_INC(n), BOOST_PP_SEQ_SIZE(seq)))) #define _UNROL_GEN_TPL_ARGS(seq) \ - BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(seq), _UNROL_GEN_TPL_ARGS_macro, seq) + BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(seq), _UNROL_GEN_TPL_ARGS_macro, seq) //.................................................................................. #define _UNROL_GEN_TPL_TYPE_ARGS_macro(z,n,seq) \ - BOOST_PP_SEQ_ELEM(n, seq) BOOST_PP_CAT(_UNROL_GEN_TPL_comma_, BOOST_PP_AND(BOOST_PP_MOD(n, 2), BOOST_PP_NOT_EQUAL(BOOST_PP_INC(n), BOOST_PP_SEQ_SIZE(seq)))) + BOOST_PP_SEQ_ELEM(n, seq) BOOST_PP_CAT(_UNROL_GEN_TPL_comma_, BOOST_PP_AND(BOOST_PP_MOD(n, 2), BOOST_PP_NOT_EQUAL(BOOST_PP_INC(n), BOOST_PP_SEQ_SIZE(seq)))) #define _UNROL_GEN_TPL_TYPE_ARGS(seq) \ - BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(seq), _UNROL_GEN_TPL_TYPE_ARGS_macro, seq) + BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(seq), _UNROL_GEN_TPL_TYPE_ARGS_macro, seq) //.................................................................................. #define _UNROLL_GEN_TPL_foreach_ee(z, n, seq) \ - executor<n>(_UNROL_GEN_TPL_ARGS(seq)); + executor<n>(_UNROL_GEN_TPL_ARGS(seq)); #define _UNROLL_GEN_TPL(name, args_seq, operation, spec) \ - template<> struct name<spec> { \ - private: \ - template<S32 _idx> inline void executor(_UNROL_GEN_TPL_TYPE_ARGS(args_seq)) { \ - BOOST_PP_SEQ_ENUM(operation) ; \ - } \ - public: \ - inline void operator()(_UNROL_GEN_TPL_TYPE_ARGS(args_seq)) { \ - BOOST_PP_REPEAT(spec, _UNROLL_GEN_TPL_foreach_ee, args_seq) \ - } \ + template<> struct name<spec> { \ + private: \ + template<S32 _idx> inline void executor(_UNROL_GEN_TPL_TYPE_ARGS(args_seq)) { \ + BOOST_PP_SEQ_ENUM(operation) ; \ + } \ + public: \ + inline void operator()(_UNROL_GEN_TPL_TYPE_ARGS(args_seq)) { \ + BOOST_PP_REPEAT(spec, _UNROLL_GEN_TPL_foreach_ee, args_seq) \ + } \ }; //.................................................................................. #define _UNROLL_GEN_TPL_foreach_seq_macro(r, data, elem) \ - _UNROLL_GEN_TPL(BOOST_PP_SEQ_ELEM(0, data), BOOST_PP_SEQ_ELEM(1, data), BOOST_PP_SEQ_ELEM(2, data), elem) + _UNROLL_GEN_TPL(BOOST_PP_SEQ_ELEM(0, data), BOOST_PP_SEQ_ELEM(1, data), BOOST_PP_SEQ_ELEM(2, data), elem) #define UNROLL_GEN_TPL(name, args_seq, operation, spec_seq) \ - /*general specialization - should not be implemented!*/ \ - template<U8> struct name { inline void operator()(_UNROL_GEN_TPL_TYPE_ARGS(args_seq)) { /*static_assert(!"Should not be instantiated.");*/ } }; \ - BOOST_PP_SEQ_FOR_EACH(_UNROLL_GEN_TPL_foreach_seq_macro, (name)(args_seq)(operation), spec_seq) + /*general specialization - should not be implemented!*/ \ + template<U8> struct name { inline void operator()(_UNROL_GEN_TPL_TYPE_ARGS(args_seq)) { /*static_assert(!"Should not be instantiated.");*/ } }; \ + BOOST_PP_SEQ_FOR_EACH(_UNROLL_GEN_TPL_foreach_seq_macro, (name)(args_seq)(operation), spec_seq) //.................................................................................. //.................................................................................. @@ -123,460 +123,460 @@ UNROLL_GEN_TPL(uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff, (U8 *&)(dptr)(S3 template<U8 ch> -struct scale_info +struct scale_info { public: - std::vector<S32> xpoints; - std::vector<const U8*> ystrides; - std::vector<S32> xapoints, yapoints; - S32 xup_yup; + std::vector<S32> xpoints; + std::vector<const U8*> ystrides; + std::vector<S32> xapoints, yapoints; + S32 xup_yup; public: - //unrolling loop types declaration - typedef uroll_zeroze_cx_comp<ch> uroll_zeroze_cx_comp_t; - typedef uroll_comp_rshftasgn_constval<ch> uroll_comp_rshftasgn_constval_t; - typedef uroll_comp_asgn_cx_rshft_cval_all_mul_val<ch> uroll_comp_asgn_cx_rshft_cval_all_mul_val_t; - typedef uroll_comp_plusasgn_cx_rshft_cval_all_mul_val<ch> uroll_comp_plusasgn_cx_rshft_cval_all_mul_val_t; - typedef uroll_inp_plusasgn_pix_mul_val<ch> uroll_inp_plusasgn_pix_mul_val_t; - typedef uroll_inp_asgn_pix_mul_val<ch> uroll_inp_asgn_pix_mul_val_t; - typedef uroll_comp_asgn_cx_mul_apoint_plus_comp_mul_inv_apoint_allshifted_16_r<ch> uroll_comp_asgn_cx_mul_apoint_plus_comp_mul_inv_apoint_allshifted_16_r_t; - typedef uroll_comp_asgn_comp_plus_pix_mul_apoint_allshifted_8_r<ch> uroll_comp_asgn_comp_plus_pix_mul_apoint_allshifted_8_r_t; - typedef uroll_comp_asgn_comp_mul_inv_apoint_plus_cx_mul_apoint_allshifted_12_r<ch> uroll_comp_asgn_comp_mul_inv_apoint_plus_cx_mul_apoint_allshifted_12_r_t; - typedef uroll_uref_dptr_inc_asgn_comp_and_ff<ch> uroll_uref_dptr_inc_asgn_comp_and_ff_t; - typedef uroll_uref_dptr_inc_asgn_sptr_apoint_plus_idx_alland_ff<ch> uroll_uref_dptr_inc_asgn_sptr_apoint_plus_idx_alland_ff_t; - typedef uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff<ch> uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff_t; + //unrolling loop types declaration + typedef uroll_zeroze_cx_comp<ch> uroll_zeroze_cx_comp_t; + typedef uroll_comp_rshftasgn_constval<ch> uroll_comp_rshftasgn_constval_t; + typedef uroll_comp_asgn_cx_rshft_cval_all_mul_val<ch> uroll_comp_asgn_cx_rshft_cval_all_mul_val_t; + typedef uroll_comp_plusasgn_cx_rshft_cval_all_mul_val<ch> uroll_comp_plusasgn_cx_rshft_cval_all_mul_val_t; + typedef uroll_inp_plusasgn_pix_mul_val<ch> uroll_inp_plusasgn_pix_mul_val_t; + typedef uroll_inp_asgn_pix_mul_val<ch> uroll_inp_asgn_pix_mul_val_t; + typedef uroll_comp_asgn_cx_mul_apoint_plus_comp_mul_inv_apoint_allshifted_16_r<ch> uroll_comp_asgn_cx_mul_apoint_plus_comp_mul_inv_apoint_allshifted_16_r_t; + typedef uroll_comp_asgn_comp_plus_pix_mul_apoint_allshifted_8_r<ch> uroll_comp_asgn_comp_plus_pix_mul_apoint_allshifted_8_r_t; + typedef uroll_comp_asgn_comp_mul_inv_apoint_plus_cx_mul_apoint_allshifted_12_r<ch> uroll_comp_asgn_comp_mul_inv_apoint_plus_cx_mul_apoint_allshifted_12_r_t; + typedef uroll_uref_dptr_inc_asgn_comp_and_ff<ch> uroll_uref_dptr_inc_asgn_comp_and_ff_t; + typedef uroll_uref_dptr_inc_asgn_sptr_apoint_plus_idx_alland_ff<ch> uroll_uref_dptr_inc_asgn_sptr_apoint_plus_idx_alland_ff_t; + typedef uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff<ch> uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff_t; public: - scale_info(const U8 *src, U32 srcW, U32 srcH, U32 dstW, U32 dstH, U32 srcStride) - : xup_yup((dstW >= srcW) + ((dstH >= srcH) << 1)) - { - calc_x_points(srcW, dstW); - calc_y_strides(src, srcStride, srcH, dstH); - calc_aa_points(srcW, dstW, xup_yup&1, xapoints); - calc_aa_points(srcH, dstH, xup_yup&2, yapoints); - } + scale_info(const U8 *src, U32 srcW, U32 srcH, U32 dstW, U32 dstH, U32 srcStride) + : xup_yup((dstW >= srcW) + ((dstH >= srcH) << 1)) + { + calc_x_points(srcW, dstW); + calc_y_strides(src, srcStride, srcH, dstH); + calc_aa_points(srcW, dstW, xup_yup&1, xapoints); + calc_aa_points(srcH, dstH, xup_yup&2, yapoints); + } private: - //........................................................................................... - void calc_x_points(U32 srcW, U32 dstW) - { - xpoints.resize(dstW+1); - - S32 val = dstW >= srcW ? 0x8000 * srcW / dstW - 0x8000 : 0; - S32 inc = (srcW << 16) / dstW; - - for(U32 i = 0, j = 0; i < dstW; ++i, ++j, val += inc) - { - xpoints[j] = llmax(0, val >> 16); - } - } - //........................................................................................... - void calc_y_strides(const U8 *src, U32 srcStride, U32 srcH, U32 dstH) - { - ystrides.resize(dstH+1); - - S32 val = dstH >= srcH ? 0x8000 * srcH / dstH - 0x8000 : 0; - S32 inc = (srcH << 16) / dstH; - - for(U32 i = 0, j = 0; i < dstH; ++i, ++j, val += inc) - { - ystrides[j] = src + llmax(0, val >> 16) * srcStride; - } - } - //........................................................................................... - void calc_aa_points(U32 srcSz, U32 dstSz, bool scale_up, std::vector<S32> &vp) - { - vp.resize(dstSz); - - if(scale_up) - { - S32 val = 0x8000 * srcSz / dstSz - 0x8000; - S32 inc = (srcSz << 16) / dstSz; - U32 pos; - - for(U32 i = 0, j = 0; i < dstSz; ++i, ++j, val += inc) - { - pos = val >> 16; - - if (pos >= (srcSz - 1)) - vp[j] = 0; - else - vp[j] = (val >> 8) - ((val >> 8) & 0xffffff00); - } - } - else - { - S32 inc = (srcSz << 16) / dstSz; - S32 Cp = ((dstSz << 14) / srcSz) + 1; - S32 ap; - - for(U32 i = 0, j = 0, val = 0; i < dstSz; ++i, ++j, val += inc) - { - ap = ((0x100 - ((val >> 8) & 0xff)) * Cp) >> 8; - vp[j] = ap | (Cp << 16); - } - } - } + //........................................................................................... + void calc_x_points(U32 srcW, U32 dstW) + { + xpoints.resize(dstW+1); + + S32 val = dstW >= srcW ? 0x8000 * srcW / dstW - 0x8000 : 0; + S32 inc = (srcW << 16) / dstW; + + for(U32 i = 0, j = 0; i < dstW; ++i, ++j, val += inc) + { + xpoints[j] = llmax(0, val >> 16); + } + } + //........................................................................................... + void calc_y_strides(const U8 *src, U32 srcStride, U32 srcH, U32 dstH) + { + ystrides.resize(dstH+1); + + S32 val = dstH >= srcH ? 0x8000 * srcH / dstH - 0x8000 : 0; + S32 inc = (srcH << 16) / dstH; + + for(U32 i = 0, j = 0; i < dstH; ++i, ++j, val += inc) + { + ystrides[j] = src + llmax(0, val >> 16) * srcStride; + } + } + //........................................................................................... + void calc_aa_points(U32 srcSz, U32 dstSz, bool scale_up, std::vector<S32> &vp) + { + vp.resize(dstSz); + + if(scale_up) + { + S32 val = 0x8000 * srcSz / dstSz - 0x8000; + S32 inc = (srcSz << 16) / dstSz; + U32 pos; + + for(U32 i = 0, j = 0; i < dstSz; ++i, ++j, val += inc) + { + pos = val >> 16; + + if (pos >= (srcSz - 1)) + vp[j] = 0; + else + vp[j] = (val >> 8) - ((val >> 8) & 0xffffff00); + } + } + else + { + S32 inc = (srcSz << 16) / dstSz; + S32 Cp = ((dstSz << 14) / srcSz) + 1; + S32 ap; + + for(U32 i = 0, j = 0, val = 0; i < dstSz; ++i, ++j, val += inc) + { + ap = ((0x100 - ((val >> 8) & 0xff)) * Cp) >> 8; + vp[j] = ap | (Cp << 16); + } + } + } }; template<U8 ch> inline void bilinear_scale( - const U8 *src, U32 srcW, U32 srcH, U32 srcStride - , U8 *dst, U32 dstW, U32 dstH, U32 dstStride - ) -{ - typedef scale_info<ch> scale_info_t; - - scale_info_t info(src, srcW, srcH, dstW, dstH, srcStride); - - const U8 *sptr; - U8 *dptr; - U32 x, y; - const U8 *pix; - - S32 cx[ch], comp[ch]; - - - if(3 == info.xup_yup) - { //scale x/y - up - for(y = 0; y < dstH; ++y) - { - dptr = dst + (y * dstStride); - sptr = info.ystrides[y]; - - if(0 < info.yapoints[y]) - { - for(x = 0; x < dstW; ++x) - { - //for(c = 0; c < ch; ++c) cx[c] = comp[c] = 0; - typename scale_info_t::uroll_zeroze_cx_comp_t()(cx, comp); - - if(0 < info.xapoints[x]) - { - pix = info.ystrides[y] + info.xpoints[x] * ch; - - //for(c = 0; c < ch; ++c) comp[c] = pix[c] * (256 - info.xapoints[x]); - typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(comp, pix, 256 - info.xapoints[x]); - - pix += ch; - - //for(c = 0; c < ch; ++c) comp[c] += pix[c] * info.xapoints[x]; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(comp, pix, info.xapoints[x]); - - pix += srcStride; - - //for(c = 0; c < ch; ++c) cx[c] = pix[c] * info.xapoints[x]; - typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(cx, pix, info.xapoints[x]); - - pix -= ch; - - //for(c = 0; c < ch; ++c) { - // cx[c] += pix[c] * (256 - info.xapoints[x]); - // comp[c] = ((cx[c] * info.yapoints[y]) + (comp[c] * (256 - info.yapoints[y]))) >> 16; - // *dptr++ = comp[c]&0xff; - //} - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, 256 - info.xapoints[x]); - typename scale_info_t::uroll_comp_asgn_cx_mul_apoint_plus_comp_mul_inv_apoint_allshifted_16_r_t()(comp, cx, info.yapoints[y]); - typename scale_info_t::uroll_uref_dptr_inc_asgn_comp_and_ff_t()(dptr, comp); - } - else - { - pix = info.ystrides[y] + info.xpoints[x] * ch; - - //for(c = 0; c < ch; ++c) comp[c] = pix[c] * (256 - info.yapoints[y]); - typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(comp, pix, 256-info.yapoints[y]); - - pix += srcStride; - - //for(c = 0; c < ch; ++c) { - // comp[c] = (comp[c] + pix[c] * info.yapoints[y]) >> 8; - // *dptr++ = comp[c]&0xff; - //} - typename scale_info_t::uroll_comp_asgn_comp_plus_pix_mul_apoint_allshifted_8_r_t()(comp, pix, info.yapoints[y]); - typename scale_info_t::uroll_uref_dptr_inc_asgn_comp_and_ff_t()(dptr, comp); - } - } - } - else - { - for(x = 0; x < dstW; ++x) - { - if(0 < info.xapoints[x]) - { - pix = info.ystrides[y] + info.xpoints[x] * ch; - - //for(c = 0; c < ch; ++c) { - // comp[c] = pix[c] * (256 - info.xapoints[x]); - // comp[c] = (comp[c] + pix[c] * info.xapoints[x]) >> 8; - // *dptr++ = comp[c]&0xff; - //} - typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(comp, pix, 256 - info.xapoints[x]); - typename scale_info_t::uroll_comp_asgn_comp_plus_pix_mul_apoint_allshifted_8_r_t()(comp, pix, info.xapoints[x]); - typename scale_info_t::uroll_uref_dptr_inc_asgn_comp_and_ff_t()(dptr, comp); - } - else - { - //for(c = 0; c < ch; ++c) *dptr++ = (sptr[info.xpoints[x]*ch + c])&0xff; - typename scale_info_t::uroll_uref_dptr_inc_asgn_sptr_apoint_plus_idx_alland_ff_t()(dptr, sptr, info.xpoints[x]*ch); - } - } - } - } - } - else if(info.xup_yup == 1) - { //scaling down vertically - S32 Cy, j; - S32 yap; - - for(y = 0; y < dstH; y++) - { - Cy = info.yapoints[y] >> 16; - yap = info.yapoints[y] & 0xffff; - - dptr = dst + (y * dstStride); - - for(x = 0; x < dstW; x++) - { - pix = info.ystrides[y] + info.xpoints[x] * ch; - - //for(c = 0; c < ch; ++c) comp[c] = pix[c] * yap; - typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(comp, pix, yap); - - pix += srcStride; - - for(j = (1 << 14) - yap; j > Cy; j -= Cy, pix += srcStride) - { - //for(c = 0; c < ch; ++c) comp[c] += pix[c] * Cy; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(comp, pix, Cy); - } - - if(j > 0) - { - //for(c = 0; c < ch; ++c) comp[c] += pix[c] * j; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(comp, pix, j); - } - - if(info.xapoints[x] > 0) - { - pix = info.ystrides[y] + info.xpoints[x]*ch + ch; - //for(c = 0; c < ch; ++c) cx[c] = pix[c] * yap; - typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(cx, pix, yap); - - pix += srcStride; - for(j = (1 << 14) - yap; j > Cy; j -= Cy) - { - //for(c = 0; c < ch; ++c) cx[c] += pix[c] * Cy; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, Cy); - pix += srcStride; - } - - if(j > 0) - { - //for(c = 0; c < ch; ++c) cx[c] += pix[c] * j; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, j); - } - - //for(c = 0; c < ch; ++c) comp[c] = ((comp[c]*(256 - info.xapoints[x])) + ((cx[c] * info.xapoints[x]))) >> 12; - typename scale_info_t::uroll_comp_asgn_comp_mul_inv_apoint_plus_cx_mul_apoint_allshifted_12_r_t()(comp, info.xapoints[x], cx); - } - else - { - //for(c = 0; c < ch; ++c) comp[c] >>= 4; - typename scale_info_t::uroll_comp_rshftasgn_constval_t()(comp, 4); - } - - //for(c = 0; c < ch; ++c) *dptr++ = (comp[c]>>10)&0xff; - typename scale_info_t::uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff_t()(dptr, comp, 10); - } - } - } - else if(info.xup_yup == 2) - { // scaling down horizontally - S32 Cx, j; - S32 xap; - - for(y = 0; y < dstH; y++) - { - dptr = dst + (y * dstStride); - - for(x = 0; x < dstW; x++) - { - Cx = info.xapoints[x] >> 16; - xap = info.xapoints[x] & 0xffff; - - pix = info.ystrides[y] + info.xpoints[x] * ch; - - //for(c = 0; c < ch; ++c) comp[c] = pix[c] * xap; - typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(comp, pix, xap); - - pix+=ch; - for(j = (1 << 14) - xap; j > Cx; j -= Cx) - { - //for(c = 0; c < ch; ++c) comp[c] += pix[c] * Cx; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(comp, pix, Cx); - pix+=ch; - } - - if(j > 0) - { - //for(c = 0; c < ch; ++c) comp[c] += pix[c] * j; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(comp, pix, j); - } - - if(info.yapoints[y] > 0) - { - pix = info.ystrides[y] + info.xpoints[x]*ch + srcStride; - //for(c = 0; c < ch; ++c) cx[c] = pix[c] * xap; - typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(cx, pix, xap); - - pix+=ch; - for(j = (1 << 14) - xap; j > Cx; j -= Cx) - { - //for(c = 0; c < ch; ++c) cx[c] += pix[c] * Cx; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, Cx); - pix+=ch; - } - - if(j > 0) - { - //for(c = 0; c < ch; ++c) cx[c] += pix[c] * j; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, j); - } - - //for(c = 0; c < ch; ++c) comp[c] = ((comp[c] * (256 - info.yapoints[y])) + ((cx[c] * info.yapoints[y]))) >> 12; - typename scale_info_t::uroll_comp_asgn_comp_mul_inv_apoint_plus_cx_mul_apoint_allshifted_12_r_t()(comp, info.yapoints[y], cx); - } - else - { - //for(c = 0; c < ch; ++c) comp[c] >>= 4; - typename scale_info_t::uroll_comp_rshftasgn_constval_t()(comp, 4); - } - - //for(c = 0; c < ch; ++c) *dptr++ = (comp[c]>>10)&0xff; - typename scale_info_t::uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff_t()(dptr, comp, 10); - } - } - } - else - { //scale x/y - down - S32 Cx, Cy, i, j; - S32 xap, yap; - - for(y = 0; y < dstH; y++) - { - Cy = info.yapoints[y] >> 16; - yap = info.yapoints[y] & 0xffff; - - dptr = dst + (y * dstStride); - for(x = 0; x < dstW; x++) - { - Cx = info.xapoints[x] >> 16; - xap = info.xapoints[x] & 0xffff; - - sptr = info.ystrides[y] + info.xpoints[x] * ch; - pix = sptr; - sptr += srcStride; - - //for(c = 0; c < ch; ++c) cx[c] = pix[c] * xap; - typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(cx, pix, xap); - - pix+=ch; - for(i = (1 << 14) - xap; i > Cx; i -= Cx) - { - //for(c = 0; c < ch; ++c) cx[c] += pix[c] * Cx; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, Cx); - pix+=ch; - } - - if(i > 0) - { - //for(c = 0; c < ch; ++c) cx[c] += pix[c] * i; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, i); - } - - //for(c = 0; c < ch; ++c) comp[c] = (cx[c] >> 5) * yap; - typename scale_info_t::uroll_comp_asgn_cx_rshft_cval_all_mul_val_t()(comp, cx, 5, yap); - - for(j = (1 << 14) - yap; j > Cy; j -= Cy) - { - pix = sptr; - sptr += srcStride; - - //for(c = 0; c < ch; ++c) cx[c] = pix[c] * xap; - typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(cx, pix, xap); - - pix+=ch; - for(i = (1 << 14) - xap; i > Cx; i -= Cx) - { - //for(c = 0; c < ch; ++c) cx[c] += pix[c] * Cx; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, Cx); - pix+=ch; - } - - if(i > 0) - { - //for(c = 0; c < ch; ++c) cx[c] += pix[c] * i; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, i); - } - - //for(c = 0; c < ch; ++c) comp[c] += (cx[c] >> 5) * Cy; - typename scale_info_t::uroll_comp_plusasgn_cx_rshft_cval_all_mul_val_t()(comp, cx, 5, Cy); - } - - if(j > 0) - { - pix = sptr; - sptr += srcStride; - - //for(c = 0; c < ch; ++c) cx[c] = pix[c] * xap; - typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(cx, pix, xap); - - pix+=ch; - for(i = (1 << 14) - xap; i > Cx; i -= Cx) - { - //for(c = 0; c < ch; ++c) cx[c] += pix[c] * Cx; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, Cx); - pix+=ch; - } - - if(i > 0) - { - //for(c = 0; c < ch; ++c) cx[c] += pix[c] * i; - typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, i); - } - - //for(c = 0; c < ch; ++c) comp[c] += (cx[c] >> 5) * j; - typename scale_info_t::uroll_comp_plusasgn_cx_rshft_cval_all_mul_val_t()(comp, cx, 5, j); - } - - //for(c = 0; c < ch; ++c) *dptr++ = (comp[c]>>23)&0xff; - typename scale_info_t::uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff_t()(dptr, comp, 23); - } - } - } //else + const U8 *src, U32 srcW, U32 srcH, U32 srcStride + , U8 *dst, U32 dstW, U32 dstH, U32 dstStride + ) +{ + typedef scale_info<ch> scale_info_t; + + scale_info_t info(src, srcW, srcH, dstW, dstH, srcStride); + + const U8 *sptr; + U8 *dptr; + U32 x, y; + const U8 *pix; + + S32 cx[ch], comp[ch]; + + + if(3 == info.xup_yup) + { //scale x/y - up + for(y = 0; y < dstH; ++y) + { + dptr = dst + (y * dstStride); + sptr = info.ystrides[y]; + + if(0 < info.yapoints[y]) + { + for(x = 0; x < dstW; ++x) + { + //for(c = 0; c < ch; ++c) cx[c] = comp[c] = 0; + typename scale_info_t::uroll_zeroze_cx_comp_t()(cx, comp); + + if(0 < info.xapoints[x]) + { + pix = info.ystrides[y] + info.xpoints[x] * ch; + + //for(c = 0; c < ch; ++c) comp[c] = pix[c] * (256 - info.xapoints[x]); + typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(comp, pix, 256 - info.xapoints[x]); + + pix += ch; + + //for(c = 0; c < ch; ++c) comp[c] += pix[c] * info.xapoints[x]; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(comp, pix, info.xapoints[x]); + + pix += srcStride; + + //for(c = 0; c < ch; ++c) cx[c] = pix[c] * info.xapoints[x]; + typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(cx, pix, info.xapoints[x]); + + pix -= ch; + + //for(c = 0; c < ch; ++c) { + // cx[c] += pix[c] * (256 - info.xapoints[x]); + // comp[c] = ((cx[c] * info.yapoints[y]) + (comp[c] * (256 - info.yapoints[y]))) >> 16; + // *dptr++ = comp[c]&0xff; + //} + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, 256 - info.xapoints[x]); + typename scale_info_t::uroll_comp_asgn_cx_mul_apoint_plus_comp_mul_inv_apoint_allshifted_16_r_t()(comp, cx, info.yapoints[y]); + typename scale_info_t::uroll_uref_dptr_inc_asgn_comp_and_ff_t()(dptr, comp); + } + else + { + pix = info.ystrides[y] + info.xpoints[x] * ch; + + //for(c = 0; c < ch; ++c) comp[c] = pix[c] * (256 - info.yapoints[y]); + typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(comp, pix, 256-info.yapoints[y]); + + pix += srcStride; + + //for(c = 0; c < ch; ++c) { + // comp[c] = (comp[c] + pix[c] * info.yapoints[y]) >> 8; + // *dptr++ = comp[c]&0xff; + //} + typename scale_info_t::uroll_comp_asgn_comp_plus_pix_mul_apoint_allshifted_8_r_t()(comp, pix, info.yapoints[y]); + typename scale_info_t::uroll_uref_dptr_inc_asgn_comp_and_ff_t()(dptr, comp); + } + } + } + else + { + for(x = 0; x < dstW; ++x) + { + if(0 < info.xapoints[x]) + { + pix = info.ystrides[y] + info.xpoints[x] * ch; + + //for(c = 0; c < ch; ++c) { + // comp[c] = pix[c] * (256 - info.xapoints[x]); + // comp[c] = (comp[c] + pix[c] * info.xapoints[x]) >> 8; + // *dptr++ = comp[c]&0xff; + //} + typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(comp, pix, 256 - info.xapoints[x]); + typename scale_info_t::uroll_comp_asgn_comp_plus_pix_mul_apoint_allshifted_8_r_t()(comp, pix, info.xapoints[x]); + typename scale_info_t::uroll_uref_dptr_inc_asgn_comp_and_ff_t()(dptr, comp); + } + else + { + //for(c = 0; c < ch; ++c) *dptr++ = (sptr[info.xpoints[x]*ch + c])&0xff; + typename scale_info_t::uroll_uref_dptr_inc_asgn_sptr_apoint_plus_idx_alland_ff_t()(dptr, sptr, info.xpoints[x]*ch); + } + } + } + } + } + else if(info.xup_yup == 1) + { //scaling down vertically + S32 Cy, j; + S32 yap; + + for(y = 0; y < dstH; y++) + { + Cy = info.yapoints[y] >> 16; + yap = info.yapoints[y] & 0xffff; + + dptr = dst + (y * dstStride); + + for(x = 0; x < dstW; x++) + { + pix = info.ystrides[y] + info.xpoints[x] * ch; + + //for(c = 0; c < ch; ++c) comp[c] = pix[c] * yap; + typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(comp, pix, yap); + + pix += srcStride; + + for(j = (1 << 14) - yap; j > Cy; j -= Cy, pix += srcStride) + { + //for(c = 0; c < ch; ++c) comp[c] += pix[c] * Cy; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(comp, pix, Cy); + } + + if(j > 0) + { + //for(c = 0; c < ch; ++c) comp[c] += pix[c] * j; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(comp, pix, j); + } + + if(info.xapoints[x] > 0) + { + pix = info.ystrides[y] + info.xpoints[x]*ch + ch; + //for(c = 0; c < ch; ++c) cx[c] = pix[c] * yap; + typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(cx, pix, yap); + + pix += srcStride; + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + //for(c = 0; c < ch; ++c) cx[c] += pix[c] * Cy; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, Cy); + pix += srcStride; + } + + if(j > 0) + { + //for(c = 0; c < ch; ++c) cx[c] += pix[c] * j; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, j); + } + + //for(c = 0; c < ch; ++c) comp[c] = ((comp[c]*(256 - info.xapoints[x])) + ((cx[c] * info.xapoints[x]))) >> 12; + typename scale_info_t::uroll_comp_asgn_comp_mul_inv_apoint_plus_cx_mul_apoint_allshifted_12_r_t()(comp, info.xapoints[x], cx); + } + else + { + //for(c = 0; c < ch; ++c) comp[c] >>= 4; + typename scale_info_t::uroll_comp_rshftasgn_constval_t()(comp, 4); + } + + //for(c = 0; c < ch; ++c) *dptr++ = (comp[c]>>10)&0xff; + typename scale_info_t::uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff_t()(dptr, comp, 10); + } + } + } + else if(info.xup_yup == 2) + { // scaling down horizontally + S32 Cx, j; + S32 xap; + + for(y = 0; y < dstH; y++) + { + dptr = dst + (y * dstStride); + + for(x = 0; x < dstW; x++) + { + Cx = info.xapoints[x] >> 16; + xap = info.xapoints[x] & 0xffff; + + pix = info.ystrides[y] + info.xpoints[x] * ch; + + //for(c = 0; c < ch; ++c) comp[c] = pix[c] * xap; + typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(comp, pix, xap); + + pix+=ch; + for(j = (1 << 14) - xap; j > Cx; j -= Cx) + { + //for(c = 0; c < ch; ++c) comp[c] += pix[c] * Cx; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(comp, pix, Cx); + pix+=ch; + } + + if(j > 0) + { + //for(c = 0; c < ch; ++c) comp[c] += pix[c] * j; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(comp, pix, j); + } + + if(info.yapoints[y] > 0) + { + pix = info.ystrides[y] + info.xpoints[x]*ch + srcStride; + //for(c = 0; c < ch; ++c) cx[c] = pix[c] * xap; + typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(cx, pix, xap); + + pix+=ch; + for(j = (1 << 14) - xap; j > Cx; j -= Cx) + { + //for(c = 0; c < ch; ++c) cx[c] += pix[c] * Cx; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, Cx); + pix+=ch; + } + + if(j > 0) + { + //for(c = 0; c < ch; ++c) cx[c] += pix[c] * j; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, j); + } + + //for(c = 0; c < ch; ++c) comp[c] = ((comp[c] * (256 - info.yapoints[y])) + ((cx[c] * info.yapoints[y]))) >> 12; + typename scale_info_t::uroll_comp_asgn_comp_mul_inv_apoint_plus_cx_mul_apoint_allshifted_12_r_t()(comp, info.yapoints[y], cx); + } + else + { + //for(c = 0; c < ch; ++c) comp[c] >>= 4; + typename scale_info_t::uroll_comp_rshftasgn_constval_t()(comp, 4); + } + + //for(c = 0; c < ch; ++c) *dptr++ = (comp[c]>>10)&0xff; + typename scale_info_t::uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff_t()(dptr, comp, 10); + } + } + } + else + { //scale x/y - down + S32 Cx, Cy, i, j; + S32 xap, yap; + + for(y = 0; y < dstH; y++) + { + Cy = info.yapoints[y] >> 16; + yap = info.yapoints[y] & 0xffff; + + dptr = dst + (y * dstStride); + for(x = 0; x < dstW; x++) + { + Cx = info.xapoints[x] >> 16; + xap = info.xapoints[x] & 0xffff; + + sptr = info.ystrides[y] + info.xpoints[x] * ch; + pix = sptr; + sptr += srcStride; + + //for(c = 0; c < ch; ++c) cx[c] = pix[c] * xap; + typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(cx, pix, xap); + + pix+=ch; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + //for(c = 0; c < ch; ++c) cx[c] += pix[c] * Cx; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, Cx); + pix+=ch; + } + + if(i > 0) + { + //for(c = 0; c < ch; ++c) cx[c] += pix[c] * i; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, i); + } + + //for(c = 0; c < ch; ++c) comp[c] = (cx[c] >> 5) * yap; + typename scale_info_t::uroll_comp_asgn_cx_rshft_cval_all_mul_val_t()(comp, cx, 5, yap); + + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + pix = sptr; + sptr += srcStride; + + //for(c = 0; c < ch; ++c) cx[c] = pix[c] * xap; + typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(cx, pix, xap); + + pix+=ch; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + //for(c = 0; c < ch; ++c) cx[c] += pix[c] * Cx; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, Cx); + pix+=ch; + } + + if(i > 0) + { + //for(c = 0; c < ch; ++c) cx[c] += pix[c] * i; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, i); + } + + //for(c = 0; c < ch; ++c) comp[c] += (cx[c] >> 5) * Cy; + typename scale_info_t::uroll_comp_plusasgn_cx_rshft_cval_all_mul_val_t()(comp, cx, 5, Cy); + } + + if(j > 0) + { + pix = sptr; + sptr += srcStride; + + //for(c = 0; c < ch; ++c) cx[c] = pix[c] * xap; + typename scale_info_t::uroll_inp_asgn_pix_mul_val_t()(cx, pix, xap); + + pix+=ch; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + //for(c = 0; c < ch; ++c) cx[c] += pix[c] * Cx; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, Cx); + pix+=ch; + } + + if(i > 0) + { + //for(c = 0; c < ch; ++c) cx[c] += pix[c] * i; + typename scale_info_t::uroll_inp_plusasgn_pix_mul_val_t()(cx, pix, i); + } + + //for(c = 0; c < ch; ++c) comp[c] += (cx[c] >> 5) * j; + typename scale_info_t::uroll_comp_plusasgn_cx_rshft_cval_all_mul_val_t()(comp, cx, 5, j); + } + + //for(c = 0; c < ch; ++c) *dptr++ = (comp[c]>>23)&0xff; + typename scale_info_t::uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff_t()(dptr, comp, 23); + } + } + } //else } //wrapper static void bilinear_scale(const U8 *src, U32 srcW, U32 srcH, U32 srcCh, U32 srcStride, U8 *dst, U32 dstW, U32 dstH, U32 dstCh, U32 dstStride) { - llassert(srcCh == dstCh); + llassert(srcCh == dstCh); - switch(srcCh) - { - case 1: - bilinear_scale<1>(src, srcW, srcH, srcStride, dst, dstW, dstH, dstStride); - break; - case 3: - bilinear_scale<3>(src, srcW, srcH, srcStride, dst, dstW, dstH, dstStride); - break; - case 4: - bilinear_scale<4>(src, srcW, srcH, srcStride, dst, dstW, dstH, dstStride); - break; - default: - llassert(!"Implement if need"); - break; - } + switch(srcCh) + { + case 1: + bilinear_scale<1>(src, srcW, srcH, srcStride, dst, dstW, dstH, dstStride); + break; + case 3: + bilinear_scale<3>(src, srcW, srcH, srcStride, dst, dstW, dstH, dstStride); + break; + case 4: + bilinear_scale<4>(src, srcW, srcH, srcStride, dst, dstW, dstH, dstStride); + break; + default: + llassert(!"Implement if need"); + break; + } } @@ -585,37 +585,33 @@ static void bilinear_scale(const U8 *src, U32 srcW, U32 srcH, U32 srcCh, U32 src //--------------------------------------------------------------------------- //static -std::string LLImage::sLastErrorMessage; -LLMutex* LLImage::sMutex = NULL; +thread_local std::string LLImage::sLastThreadErrorMessage; bool LLImage::sUseNewByteRange = false; S32 LLImage::sMinimalReverseByteRangePercent = 75; //static void LLImage::initClass(bool use_new_byte_range, S32 minimal_reverse_byte_range_percent) { - sUseNewByteRange = use_new_byte_range; + sUseNewByteRange = use_new_byte_range; sMinimalReverseByteRangePercent = minimal_reverse_byte_range_percent; - sMutex = new LLMutex(); } //static void LLImage::cleanupClass() { - delete sMutex; - sMutex = NULL; } //static -const std::string& LLImage::getLastError() +const std::string& LLImage::getLastThreadError() { - static const std::string noerr("No Error"); - return sLastErrorMessage.empty() ? noerr : sLastErrorMessage; + static const std::string noerr("No Error"); + return sLastThreadErrorMessage.empty() ? noerr : sLastThreadErrorMessage; } //static void LLImage::setLastError(const std::string& message) { - sLastErrorMessage = message; + sLastThreadErrorMessage = message; } //--------------------------------------------------------------------------- @@ -623,175 +619,175 @@ void LLImage::setLastError(const std::string& message) //--------------------------------------------------------------------------- LLImageBase::LLImageBase() -: mData(NULL), - mDataSize(0), - mWidth(0), - mHeight(0), - mComponents(0), - mBadBufferAllocation(false), - mAllowOverSize(false) +: mData(NULL), + mDataSize(0), + mWidth(0), + mHeight(0), + mComponents(0), + mBadBufferAllocation(false), + mAllowOverSize(false) {} // virtual LLImageBase::~LLImageBase() { - deleteData(); // virtual + deleteData(); // virtual } // virtual void LLImageBase::dump() { - LL_INFOS() << "LLImageBase mComponents " << mComponents - << " mData " << mData - << " mDataSize " << mDataSize - << " mWidth " << mWidth - << " mHeight " << mHeight - << LL_ENDL; + LL_INFOS() << "LLImageBase mComponents " << mComponents + << " mData " << mData + << " mDataSize " << mDataSize + << " mWidth " << mWidth + << " mHeight " << mHeight + << LL_ENDL; } // virtual void LLImageBase::sanityCheck() { - if (mWidth > MAX_IMAGE_SIZE - || mHeight > MAX_IMAGE_SIZE - || mDataSize > (S32)MAX_IMAGE_DATA_SIZE - || mComponents > (S8)MAX_IMAGE_COMPONENTS - ) - { - LL_ERRS() << "Failed LLImageBase::sanityCheck " - << "width " << mWidth - << "height " << mHeight - << "datasize " << mDataSize - << "components " << mComponents - << "data " << mData - << LL_ENDL; - } + if (mWidth > MAX_IMAGE_SIZE + || mHeight > MAX_IMAGE_SIZE + || mDataSize > (S32)MAX_IMAGE_DATA_SIZE + || mComponents > (S8)MAX_IMAGE_COMPONENTS + ) + { + LL_ERRS() << "Failed LLImageBase::sanityCheck " + << "width " << mWidth + << "height " << mHeight + << "datasize " << mDataSize + << "components " << mComponents + << "data " << mData + << LL_ENDL; + } } // virtual void LLImageBase::deleteData() { - ll_aligned_free_16(mData); - mDataSize = 0; - mData = NULL; + ll_aligned_free_16(mData); + mDataSize = 0; + mData = NULL; } // virtual U8* LLImageBase::allocateData(S32 size) { - //make this function thread-safe. - static const U32 MAX_BUFFER_SIZE = 4096 * 4096 * 16; //256 MB - mBadBufferAllocation = false; - - if (size < 0) - { - size = mWidth * mHeight * mComponents; - if (size <= 0) - { - LL_WARNS() << llformat("LLImageBase::allocateData called with bad dimensions: %dx%dx%d",mWidth,mHeight,(S32)mComponents) << LL_ENDL; - mBadBufferAllocation = true; - } - } - - if (!mBadBufferAllocation && (size < 1 || size > MAX_BUFFER_SIZE)) - { - LL_INFOS() << "width: " << mWidth << " height: " << mHeight << " components: " << mComponents << LL_ENDL ; - if(mAllowOverSize) - { - LL_INFOS() << "Oversize: " << size << LL_ENDL ; - } - else - { - LL_WARNS() << "LLImageBase::allocateData: bad size: " << size << LL_ENDL; - mBadBufferAllocation = true; - } - } - - if (!mBadBufferAllocation && (!mData || size != mDataSize)) - { - deleteData(); // virtual - mData = (U8*)ll_aligned_malloc_16(size); - if (!mData) - { - LL_WARNS() << "Failed to allocate image data size [" << size << "]" << LL_ENDL; - mBadBufferAllocation = true; - } - } - - if (mBadBufferAllocation) - { - size = 0; - mWidth = mHeight = 0; - if (mData) - { - deleteData(); // virtual - mData = NULL; - } - } - mDataSize = size; - - return mData; + //make this function thread-safe. + static const U32 MAX_BUFFER_SIZE = 4096 * 4096 * 16; //256 MB + mBadBufferAllocation = false; + + if (size < 0) + { + size = mWidth * mHeight * mComponents; + if (size <= 0) + { + LL_WARNS() << llformat("LLImageBase::allocateData called with bad dimensions: %dx%dx%d",mWidth,mHeight,(S32)mComponents) << LL_ENDL; + mBadBufferAllocation = true; + } + } + + if (!mBadBufferAllocation && (size < 1 || size > MAX_BUFFER_SIZE)) + { + LL_INFOS() << "width: " << mWidth << " height: " << mHeight << " components: " << mComponents << LL_ENDL ; + if(mAllowOverSize) + { + LL_INFOS() << "Oversize: " << size << LL_ENDL ; + } + else + { + LL_WARNS() << "LLImageBase::allocateData: bad size: " << size << LL_ENDL; + mBadBufferAllocation = true; + } + } + + if (!mBadBufferAllocation && (!mData || size != mDataSize)) + { + deleteData(); // virtual + mData = (U8*)ll_aligned_malloc_16(size); + if (!mData) + { + LL_WARNS() << "Failed to allocate image data size [" << size << "]" << LL_ENDL; + mBadBufferAllocation = true; + } + } + + if (mBadBufferAllocation) + { + size = 0; + mWidth = mHeight = 0; + if (mData) + { + deleteData(); // virtual + mData = NULL; + } + } + mDataSize = size; + + return mData; } // virtual U8* LLImageBase::reallocateData(S32 size) { - U8 *new_datap = (U8*)ll_aligned_malloc_16(size); - if (!new_datap) - { - LL_WARNS() << "Out of memory in LLImageBase::reallocateData" << LL_ENDL; - return 0; - } - if (mData) - { - S32 bytes = llmin(mDataSize, size); - memcpy(new_datap, mData, bytes); /* Flawfinder: ignore */ - ll_aligned_free_16(mData) ; - } - mData = new_datap; - mDataSize = size; - mBadBufferAllocation = false; - return mData; + U8 *new_datap = (U8*)ll_aligned_malloc_16(size); + if (!new_datap) + { + LL_WARNS() << "Out of memory in LLImageBase::reallocateData" << LL_ENDL; + return 0; + } + if (mData) + { + S32 bytes = llmin(mDataSize, size); + memcpy(new_datap, mData, bytes); /* Flawfinder: ignore */ + ll_aligned_free_16(mData) ; + } + mData = new_datap; + mDataSize = size; + mBadBufferAllocation = false; + return mData; } const U8* LLImageBase::getData() const -{ - if(mBadBufferAllocation) - { - LL_WARNS() << "Bad memory allocation for the image buffer!" << LL_ENDL ; - return NULL; - } - - return mData; +{ + if(mBadBufferAllocation) + { + LL_WARNS() << "Bad memory allocation for the image buffer!" << LL_ENDL ; + return NULL; + } + + return mData; } // read only U8* LLImageBase::getData() -{ - if(mBadBufferAllocation) - { - LL_WARNS() << "Bad memory allocation for the image buffer!" << LL_ENDL; - return NULL; - } +{ + if(mBadBufferAllocation) + { + LL_WARNS() << "Bad memory allocation for the image buffer!" << LL_ENDL; + return NULL; + } - return mData; + return mData; } bool LLImageBase::isBufferInvalid() const { - return mBadBufferAllocation || mData == NULL; + return mBadBufferAllocation || mData == NULL; } void LLImageBase::setSize(S32 width, S32 height, S32 ncomponents) { - mWidth = width; - mHeight = height; - mComponents = ncomponents; + mWidth = width; + mHeight = height; + mComponents = ncomponents; } U8* LLImageBase::allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 size) { - setSize(width, height, ncomponents); - return allocateData(size); // virtual + setSize(width, height, ncomponents); + return allocateData(size); // virtual } //--------------------------------------------------------------------------- @@ -801,17 +797,17 @@ U8* LLImageBase::allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 si S32 LLImageRaw::sRawImageCount = 0; LLImageRaw::LLImageRaw() - : LLImageBase() + : LLImageBase() { - ++sRawImageCount; + ++sRawImageCount; } LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components) - : LLImageBase() + : LLImageBase() { - //llassert( S32(width) * S32(height) * S32(components) <= MAX_IMAGE_DATA_SIZE ); - allocateDataSize(width, height, components); - ++sRawImageCount; + //llassert( S32(width) * S32(height) * S32(components) <= MAX_IMAGE_DATA_SIZE ); + allocateDataSize(width, height, components); + ++sRawImageCount; } LLImageRaw::LLImageRaw(const U8* data, U16 width, U16 height, S8 components) @@ -824,49 +820,49 @@ LLImageRaw::LLImageRaw(const U8* data, U16 width, U16 height, S8 components) } LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy) - : LLImageBase() + : LLImageBase() { - if(no_copy) - { - setDataAndSize(data, width, height, components); - } - else if(allocateDataSize(width, height, components)) - { - memcpy(getData(), data, width*height*components); - } - ++sRawImageCount; + if(no_copy) + { + setDataAndSize(data, width, height, components); + } + else if(allocateDataSize(width, height, components)) + { + memcpy(getData(), data, width*height*components); + } + ++sRawImageCount; } //LLImageRaw::LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only) -// : LLImageBase() +// : LLImageBase() //{ -// createFromFile(filename, j2c_lowest_mip_only); +// createFromFile(filename, j2c_lowest_mip_only); //} LLImageRaw::~LLImageRaw() { - // NOTE: ~LLimageBase() call to deleteData() calls LLImageBase::deleteData() - // NOT LLImageRaw::deleteData() - deleteData(); - --sRawImageCount; + // NOTE: ~LLimageBase() call to deleteData() calls LLImageBase::deleteData() + // NOT LLImageRaw::deleteData() + deleteData(); + --sRawImageCount; } // virtual U8* LLImageRaw::allocateData(S32 size) { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - U8* res = LLImageBase::allocateData(size); - return res; + U8* res = LLImageBase::allocateData(size); + return res; } // virtual U8* LLImageRaw::reallocateData(S32 size) { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - U8* res = LLImageBase::reallocateData(size); - return res; + U8* res = LLImageBase::reallocateData(size); + return res; } void LLImageRaw::releaseData() @@ -880,131 +876,131 @@ void LLImageRaw::releaseData() // virtual void LLImageRaw::deleteData() { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - LLImageBase::deleteData(); + LLImageBase::deleteData(); } -void LLImageRaw::setDataAndSize(U8 *data, S32 width, S32 height, S8 components) +void LLImageRaw::setDataAndSize(U8 *data, S32 width, S32 height, S8 components) { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - if(data == getData()) - { - return ; - } + if(data == getData()) + { + return ; + } - deleteData(); + deleteData(); - LLImageBase::setSize(width, height, components) ; - LLImageBase::setDataAndSize(data, width * height * components) ; + LLImageBase::setSize(width, height, components) ; + LLImageBase::setDataAndSize(data, width * height * components) ; } bool LLImageRaw::resize(U16 width, U16 height, S8 components) { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - if ((getWidth() == width) && (getHeight() == height) && (getComponents() == components) && !isBufferInvalid()) - { - return true; - } - // Reallocate the data buffer. - deleteData(); + if ((getWidth() == width) && (getHeight() == height) && (getComponents() == components) && !isBufferInvalid()) + { + return true; + } + // Reallocate the data buffer. + deleteData(); - allocateDataSize(width,height,components); + allocateDataSize(width,height,components); - return !isBufferInvalid(); + return !isBufferInvalid(); } bool LLImageRaw::setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height, - const U8 *data, U32 stride, bool reverse_y) + const U8 *data, U32 stride, bool reverse_y) { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - if (!getData()) - { - return false; - } - if (!data) - { - return false; - } + if (!getData()) + { + return false; + } + if (!data) + { + return false; + } - // Should do some simple bounds checking + // Should do some simple bounds checking - U32 i; - for (i = 0; i < height; i++) - { - const U32 row = reverse_y ? height - 1 - i : i; - const U32 from_offset = row * ((stride == 0) ? width*getComponents() : stride); - const U32 to_offset = (y_pos + i)*getWidth() + x_pos; - memcpy(getData() + to_offset*getComponents(), /* Flawfinder: ignore */ - data + from_offset, getComponents()*width); - } + U32 i; + for (i = 0; i < height; i++) + { + const U32 row = reverse_y ? height - 1 - i : i; + const U32 from_offset = row * ((stride == 0) ? width*getComponents() : stride); + const U32 to_offset = (y_pos + i)*getWidth() + x_pos; + memcpy(getData() + to_offset*getComponents(), /* Flawfinder: ignore */ + data + from_offset, getComponents()*width); + } - return true; + return true; } void LLImageRaw::clear(U8 r, U8 g, U8 b, U8 a) { - llassert( getComponents() <= 4 ); - - LLImageDataLock lock(this); - - // This is fairly bogus, but it'll do for now. - if (isBufferInvalid()) - { - LL_WARNS() << "Invalid image buffer" << LL_ENDL; - return; - } - - U8 *pos = getData(); - U32 x, y; - for (x = 0; x < getWidth(); x++) - { - for (y = 0; y < getHeight(); y++) - { - *pos = r; - pos++; - if (getComponents() == 1) - { - continue; - } - *pos = g; - pos++; - if (getComponents() == 2) - { - continue; - } - *pos = b; - pos++; - if (getComponents() == 3) - { - continue; - } - *pos = a; - pos++; - } - } + llassert( getComponents() <= 4 ); + + LLImageDataLock lock(this); + + // This is fairly bogus, but it'll do for now. + if (isBufferInvalid()) + { + LL_WARNS() << "Invalid image buffer" << LL_ENDL; + return; + } + + U8 *pos = getData(); + U32 x, y; + for (x = 0; x < getWidth(); x++) + { + for (y = 0; y < getHeight(); y++) + { + *pos = r; + pos++; + if (getComponents() == 1) + { + continue; + } + *pos = g; + pos++; + if (getComponents() == 2) + { + continue; + } + *pos = b; + pos++; + if (getComponents() == 3) + { + continue; + } + *pos = a; + pos++; + } + } } // Reverses the order of the rows in the image void LLImageRaw::verticalFlip() { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - S32 row_bytes = getWidth() * getComponents(); - llassert(row_bytes > 0); - std::vector<U8> line_buffer(row_bytes); - S32 mid_row = getHeight() / 2; - for( S32 row = 0; row < mid_row; row++ ) - { - U8* row_a_data = getData() + row * row_bytes; - U8* row_b_data = getData() + (getHeight() - 1 - row) * row_bytes; - memcpy( &line_buffer[0], row_a_data, row_bytes ); - memcpy( row_a_data, row_b_data, row_bytes ); - memcpy( row_b_data, &line_buffer[0], row_bytes ); - } + S32 row_bytes = getWidth() * getComponents(); + llassert(row_bytes > 0); + std::vector<U8> line_buffer(row_bytes); + S32 mid_row = getHeight() / 2; + for( S32 row = 0; row < mid_row; row++ ) + { + U8* row_a_data = getData() + row * row_bytes; + U8* row_b_data = getData() + (getHeight() - 1 - row) * row_bytes; + memcpy( &line_buffer[0], row_a_data, row_bytes ); + memcpy( row_a_data, row_b_data, row_bytes ); + memcpy( row_b_data, &line_buffer[0], row_bytes ); + } } @@ -1099,275 +1095,275 @@ bool LLImageRaw::makeAlpha() void LLImageRaw::expandToPowerOfTwo(S32 max_dim, bool scale_image) { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - // Find new sizes - S32 new_width = expandDimToPowerOfTwo(getWidth(), max_dim); - S32 new_height = expandDimToPowerOfTwo(getHeight(), max_dim); + // Find new sizes + S32 new_width = expandDimToPowerOfTwo(getWidth(), max_dim); + S32 new_height = expandDimToPowerOfTwo(getHeight(), max_dim); - scale( new_width, new_height, scale_image ); + scale( new_width, new_height, scale_image ); } void LLImageRaw::contractToPowerOfTwo(S32 max_dim, bool scale_image) { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - // Find new sizes - S32 new_width = contractDimToPowerOfTwo(getWidth(), MIN_IMAGE_SIZE); - S32 new_height = contractDimToPowerOfTwo(getHeight(), MIN_IMAGE_SIZE); + // Find new sizes + S32 new_width = contractDimToPowerOfTwo(getWidth(), MIN_IMAGE_SIZE); + S32 new_height = contractDimToPowerOfTwo(getHeight(), MIN_IMAGE_SIZE); - scale( new_width, new_height, scale_image ); + scale( new_width, new_height, scale_image ); } // static S32 LLImageRaw::biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim) { - // Strong bias towards rounding down (to save bandwidth) - // No bias would mean THRESHOLD == 1.5f; - const F32 THRESHOLD = 1.75f; - - // Find new sizes - S32 larger_dim = max_dim; // 2^n >= curr_dim - S32 smaller_dim = max_dim; // 2^(n-1) <= curr_dim - while( (smaller_dim > curr_dim) && (smaller_dim > MIN_IMAGE_SIZE) ) - { - larger_dim = smaller_dim; - smaller_dim >>= 1; - } - return ( ((F32)curr_dim / (F32)smaller_dim) > THRESHOLD ) ? larger_dim : smaller_dim; + // Strong bias towards rounding down (to save bandwidth) + // No bias would mean THRESHOLD == 1.5f; + const F32 THRESHOLD = 1.75f; + + // Find new sizes + S32 larger_dim = max_dim; // 2^n >= curr_dim + S32 smaller_dim = max_dim; // 2^(n-1) <= curr_dim + while( (smaller_dim > curr_dim) && (smaller_dim > MIN_IMAGE_SIZE) ) + { + larger_dim = smaller_dim; + smaller_dim >>= 1; + } + return ( ((F32)curr_dim / (F32)smaller_dim) > THRESHOLD ) ? larger_dim : smaller_dim; } // static S32 LLImageRaw::expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim) { - S32 new_dim = MIN_IMAGE_SIZE; - while( (new_dim < curr_dim) && (new_dim < max_dim) ) - { - new_dim <<= 1; - } + S32 new_dim = MIN_IMAGE_SIZE; + while( (new_dim < curr_dim) && (new_dim < max_dim) ) + { + new_dim <<= 1; + } return new_dim; } // static S32 LLImageRaw::contractDimToPowerOfTwo(S32 curr_dim, S32 min_dim) { - S32 new_dim = MAX_IMAGE_SIZE; - while( (new_dim > curr_dim) && (new_dim > min_dim) ) - { - new_dim >>= 1; - } + S32 new_dim = MAX_IMAGE_SIZE; + while( (new_dim > curr_dim) && (new_dim > min_dim) ) + { + new_dim >>= 1; + } return new_dim; } void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim) { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - // Find new sizes - S32 new_width = biasedDimToPowerOfTwo(getWidth(),max_dim); - S32 new_height = biasedDimToPowerOfTwo(getHeight(),max_dim); + // Find new sizes + S32 new_width = biasedDimToPowerOfTwo(getWidth(),max_dim); + S32 new_height = biasedDimToPowerOfTwo(getHeight(),max_dim); - scale( new_width, new_height ); + scale( new_width, new_height ); } // static // Calculates (U8)(255*(a/255.f)*(b/255.f) + 0.5f). Thanks, Jim Blinn! inline U8 LLImageRaw::fastFractionalMult( U8 a, U8 b ) { - U32 i = a * b + 128; - return U8((i + (i>>8)) >> 8); + U32 i = a * b + 128; + return U8((i + (i>>8)) >> 8); } void LLImageRaw::composite( const LLImageRaw* src ) { - LLImageRaw* dst = this; // Just for clarity. - - LLImageDataSharedLock lockIn(src); - LLImageDataLock lockOut(this); - - if (!validateSrcAndDst("LLImageRaw::composite", src, dst)) - { - return; - } - - llassert((3 == src->getComponents()) || (4 == src->getComponents())); - llassert(3 == dst->getComponents()); - - if( 3 == dst->getComponents() ) - { - if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ) - { - // No scaling needed - if( 3 == src->getComponents() ) - { - copyUnscaled( src ); // alpha is one so just copy the data. - } - else - { - compositeUnscaled4onto3( src ); - } - } - else - { - if( 3 == src->getComponents() ) - { - copyScaled( src ); // alpha is one so just copy the data. - } - else - { - compositeScaled4onto3( src ); - } - } - } + LLImageRaw* dst = this; // Just for clarity. + + LLImageDataSharedLock lockIn(src); + LLImageDataLock lockOut(this); + + if (!validateSrcAndDst("LLImageRaw::composite", src, dst)) + { + return; + } + + llassert((3 == src->getComponents()) || (4 == src->getComponents())); + llassert(3 == dst->getComponents()); + + if( 3 == dst->getComponents() ) + { + if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ) + { + // No scaling needed + if( 3 == src->getComponents() ) + { + copyUnscaled( src ); // alpha is one so just copy the data. + } + else + { + compositeUnscaled4onto3( src ); + } + } + else + { + if( 3 == src->getComponents() ) + { + copyScaled( src ); // alpha is one so just copy the data. + } + else + { + compositeScaled4onto3( src ); + } + } + } } // Src and dst can be any size. Src has 4 components. Dst has 3 components. void LLImageRaw::compositeScaled4onto3(const LLImageRaw* src) { - LL_INFOS() << "compositeScaled4onto3" << LL_ENDL; + LL_INFOS() << "compositeScaled4onto3" << LL_ENDL; - LLImageRaw* dst = this; // Just for clarity. + LLImageRaw* dst = this; // Just for clarity. - LLImageDataLock lock(this); + LLImageDataLock lock(this); - llassert( (4 == src->getComponents()) && (3 == dst->getComponents()) ); + llassert( (4 == src->getComponents()) && (3 == dst->getComponents()) ); - S32 temp_data_size = src->getWidth() * dst->getHeight() * src->getComponents(); - llassert_always(temp_data_size > 0); - std::vector<U8> temp_buffer(temp_data_size); + S32 temp_data_size = src->getWidth() * dst->getHeight() * src->getComponents(); + llassert_always(temp_data_size > 0); + std::vector<U8> temp_buffer(temp_data_size); - // Vertical: scale but no composite - for( S32 col = 0; col < src->getWidth(); col++ ) - { - copyLineScaled( src->getData() + (src->getComponents() * col), &temp_buffer[0] + (src->getComponents() * col), src->getHeight(), dst->getHeight(), src->getWidth(), src->getWidth() ); - } + // Vertical: scale but no composite + for( S32 col = 0; col < src->getWidth(); col++ ) + { + copyLineScaled( src->getData() + (src->getComponents() * col), &temp_buffer[0] + (src->getComponents() * col), src->getHeight(), dst->getHeight(), src->getWidth(), src->getWidth() ); + } - // Horizontal: scale and composite - for( S32 row = 0; row < dst->getHeight(); row++ ) - { - compositeRowScaled4onto3( &temp_buffer[0] + (src->getComponents() * src->getWidth() * row), dst->getData() + (dst->getComponents() * dst->getWidth() * row), src->getWidth(), dst->getWidth() ); - } + // Horizontal: scale and composite + for( S32 row = 0; row < dst->getHeight(); row++ ) + { + compositeRowScaled4onto3( &temp_buffer[0] + (src->getComponents() * src->getWidth() * row), dst->getData() + (dst->getComponents() * dst->getWidth() * row), src->getWidth(), dst->getWidth() ); + } } // Src and dst are same size. Src has 4 components. Dst has 3 components. void LLImageRaw::compositeUnscaled4onto3( const LLImageRaw* src ) { - LLImageRaw* dst = this; // Just for clarity. + LLImageRaw* dst = this; // Just for clarity. - LLImageDataLock lock(this); + LLImageDataLock lock(this); - llassert( (3 == src->getComponents()) || (4 == src->getComponents()) ); - llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ); + llassert( (3 == src->getComponents()) || (4 == src->getComponents()) ); + llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ); - const U8* src_data = src->getData(); - U8* dst_data = dst->getData(); - S32 pixels = getWidth() * getHeight(); - while( pixels-- ) - { - U8 alpha = src_data[3]; - if( alpha ) - { - if( 255 == alpha ) - { - dst_data[0] = src_data[0]; - dst_data[1] = src_data[1]; - dst_data[2] = src_data[2]; - } - else - { + const U8* src_data = src->getData(); + U8* dst_data = dst->getData(); + S32 pixels = getWidth() * getHeight(); + while( pixels-- ) + { + U8 alpha = src_data[3]; + if( alpha ) + { + if( 255 == alpha ) + { + dst_data[0] = src_data[0]; + dst_data[1] = src_data[1]; + dst_data[2] = src_data[2]; + } + else + { - U8 transparency = 255 - alpha; - dst_data[0] = fastFractionalMult( dst_data[0], transparency ) + fastFractionalMult( src_data[0], alpha ); - dst_data[1] = fastFractionalMult( dst_data[1], transparency ) + fastFractionalMult( src_data[1], alpha ); - dst_data[2] = fastFractionalMult( dst_data[2], transparency ) + fastFractionalMult( src_data[2], alpha ); - } - } + U8 transparency = 255 - alpha; + dst_data[0] = fastFractionalMult( dst_data[0], transparency ) + fastFractionalMult( src_data[0], alpha ); + dst_data[1] = fastFractionalMult( dst_data[1], transparency ) + fastFractionalMult( src_data[1], alpha ); + dst_data[2] = fastFractionalMult( dst_data[2], transparency ) + fastFractionalMult( src_data[2], alpha ); + } + } - src_data += 4; - dst_data += 3; - } + src_data += 4; + dst_data += 3; + } } void LLImageRaw::copyUnscaledAlphaMask( const LLImageRaw* src, const LLColor4U& fill) { - LLImageRaw* dst = this; // Just for clarity. + LLImageRaw* dst = this; // Just for clarity. - LLImageDataSharedLock lockIn(src); - LLImageDataLock lockOut(this); + LLImageDataSharedLock lockIn(src); + LLImageDataLock lockOut(this); - if (!validateSrcAndDst("LLImageRaw::copyUnscaledAlphaMask", src, dst)) - { - return; - } + if (!validateSrcAndDst("LLImageRaw::copyUnscaledAlphaMask", src, dst)) + { + return; + } - llassert( 1 == src->getComponents() ); - llassert( 4 == dst->getComponents() ); - llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ); + llassert( 1 == src->getComponents() ); + llassert( 4 == dst->getComponents() ); + llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ); - S32 pixels = getWidth() * getHeight(); - const U8* src_data = src->getData(); - U8* dst_data = dst->getData(); - for ( S32 i = 0; i < pixels; i++ ) - { - dst_data[0] = fill.mV[0]; - dst_data[1] = fill.mV[1]; - dst_data[2] = fill.mV[2]; - dst_data[3] = src_data[0]; - src_data += 1; - dst_data += 4; - } + S32 pixels = getWidth() * getHeight(); + const U8* src_data = src->getData(); + U8* dst_data = dst->getData(); + for ( S32 i = 0; i < pixels; i++ ) + { + dst_data[0] = fill.mV[0]; + dst_data[1] = fill.mV[1]; + dst_data[2] = fill.mV[2]; + dst_data[3] = src_data[0]; + src_data += 1; + dst_data += 4; + } } // Fill the buffer with a constant color void LLImageRaw::fill( const LLColor4U& color ) { - LLImageDataLock lock(this); - - if (isBufferInvalid()) - { - LL_WARNS() << "Invalid image buffer" << LL_ENDL; - return; - } - - S32 pixels = getWidth() * getHeight(); - if( 4 == getComponents() ) - { - U32* data = (U32*) getData(); - U32 rgbaColor = color.asRGBA(); - for( S32 i = 0; i < pixels; i++ ) - { - data[ i ] = rgbaColor; - } - } - else - if( 3 == getComponents() ) - { - U8* data = getData(); - for( S32 i = 0; i < pixels; i++ ) - { - data[0] = color.mV[0]; - data[1] = color.mV[1]; - data[2] = color.mV[2]; - data += 3; - } - } + LLImageDataLock lock(this); + + if (isBufferInvalid()) + { + LL_WARNS() << "Invalid image buffer" << LL_ENDL; + return; + } + + S32 pixels = getWidth() * getHeight(); + if( 4 == getComponents() ) + { + U32* data = (U32*) getData(); + U32 rgbaColor = color.asRGBA(); + for( S32 i = 0; i < pixels; i++ ) + { + data[ i ] = rgbaColor; + } + } + else + if( 3 == getComponents() ) + { + U8* data = getData(); + for( S32 i = 0; i < pixels; i++ ) + { + data[0] = color.mV[0]; + data[1] = color.mV[1]; + data[2] = color.mV[2]; + data += 3; + } + } } void LLImageRaw::tint( const LLColor3& color ) { - llassert( (3 == getComponents()) || (4 == getComponents()) ); - if (isBufferInvalid()) - { - LL_WARNS() << "Invalid image buffer" << LL_ENDL; - return; - } + llassert( (3 == getComponents()) || (4 == getComponents()) ); + if (isBufferInvalid()) + { + LL_WARNS() << "Invalid image buffer" << LL_ENDL; + return; + } - S32 pixels = getWidth() * getHeight(); + S32 pixels = getWidth() * getHeight(); const S32 components = getComponents(); U8* data = getData(); for( S32 i = 0; i < pixels; i++ ) @@ -1384,203 +1380,203 @@ void LLImageRaw::tint( const LLColor3& color ) LLPointer<LLImageRaw> LLImageRaw::duplicate() { - if(getNumRefs() < 2) - { - return this; //nobody else refences to this image, no need to duplicate. - } + if(getNumRefs() < 2) + { + return this; //nobody else refences to this image, no need to duplicate. + } - LLImageDataSharedLock lock(this); + LLImageDataSharedLock lock(this); - //make a duplicate - LLPointer<LLImageRaw> dup = new LLImageRaw(getData(), getWidth(), getHeight(), getComponents()); - return dup; + //make a duplicate + LLPointer<LLImageRaw> dup = new LLImageRaw(getData(), getWidth(), getHeight(), getComponents()); + return dup; } // Src and dst can be any size. Src and dst can each have 3 or 4 components. void LLImageRaw::copy(const LLImageRaw* src) { - LLImageRaw* dst = this; // Just for clarity. - - LLImageDataSharedLock lockIn(src); - LLImageDataLock lockOut(this); - - if (!validateSrcAndDst("LLImageRaw::copy", src, dst)) - { - return; - } - - if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ) - { - // No scaling needed - if( src->getComponents() == dst->getComponents() ) - { - copyUnscaled( src ); - } - else - if( 3 == src->getComponents() ) - { - copyUnscaled3onto4( src ); - } - else - { - // 4 == src->getComponents() - copyUnscaled4onto3( src ); - } - } - else - { - // Scaling needed - // No scaling needed - if( src->getComponents() == dst->getComponents() ) - { - copyScaled( src ); - } - else - if( 3 == src->getComponents() ) - { - copyScaled3onto4( src ); - } - else - { - // 4 == src->getComponents() - copyScaled4onto3( src ); - } - } + LLImageRaw* dst = this; // Just for clarity. + + LLImageDataSharedLock lockIn(src); + LLImageDataLock lockOut(this); + + if (!validateSrcAndDst("LLImageRaw::copy", src, dst)) + { + return; + } + + if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ) + { + // No scaling needed + if( src->getComponents() == dst->getComponents() ) + { + copyUnscaled( src ); + } + else + if( 3 == src->getComponents() ) + { + copyUnscaled3onto4( src ); + } + else + { + // 4 == src->getComponents() + copyUnscaled4onto3( src ); + } + } + else + { + // Scaling needed + // No scaling needed + if( src->getComponents() == dst->getComponents() ) + { + copyScaled( src ); + } + else + if( 3 == src->getComponents() ) + { + copyScaled3onto4( src ); + } + else + { + // 4 == src->getComponents() + copyScaled4onto3( src ); + } + } } // Src and dst are same size. Src and dst have same number of components. void LLImageRaw::copyUnscaled(const LLImageRaw* src) { - LLImageRaw* dst = this; // Just for clarity. + LLImageRaw* dst = this; // Just for clarity. - LLImageDataLock lock(this); + LLImageDataLock lock(this); - llassert( (1 == src->getComponents()) || (3 == src->getComponents()) || (4 == src->getComponents()) ); - llassert( src->getComponents() == dst->getComponents() ); - llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ); + llassert( (1 == src->getComponents()) || (3 == src->getComponents()) || (4 == src->getComponents()) ); + llassert( src->getComponents() == dst->getComponents() ); + llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ); - memcpy( dst->getData(), src->getData(), getWidth() * getHeight() * getComponents() ); /* Flawfinder: ignore */ + memcpy( dst->getData(), src->getData(), getWidth() * getHeight() * getComponents() ); /* Flawfinder: ignore */ } // Src and dst can be any size. Src has 3 components. Dst has 4 components. void LLImageRaw::copyScaled3onto4(const LLImageRaw* src) { - llassert( (3 == src->getComponents()) && (4 == getComponents()) ); + llassert( (3 == src->getComponents()) && (4 == getComponents()) ); - // Slow, but simple. Optimize later if needed. - LLImageRaw temp( src->getWidth(), src->getHeight(), 4); - temp.copyUnscaled3onto4( src ); - copyScaled( &temp ); + // Slow, but simple. Optimize later if needed. + LLImageRaw temp( src->getWidth(), src->getHeight(), 4); + temp.copyUnscaled3onto4( src ); + copyScaled( &temp ); } // Src and dst can be any size. Src has 4 components. Dst has 3 components. void LLImageRaw::copyScaled4onto3(const LLImageRaw* src) { - llassert( (4 == src->getComponents()) && (3 == getComponents()) ); + llassert( (4 == src->getComponents()) && (3 == getComponents()) ); - // Slow, but simple. Optimize later if needed. - LLImageRaw temp( src->getWidth(), src->getHeight(), 3); - temp.copyUnscaled4onto3( src ); - copyScaled( &temp ); + // Slow, but simple. Optimize later if needed. + LLImageRaw temp( src->getWidth(), src->getHeight(), 3); + temp.copyUnscaled4onto3( src ); + copyScaled( &temp ); } // Src and dst are same size. Src has 4 components. Dst has 3 components. void LLImageRaw::copyUnscaled4onto3( const LLImageRaw* src ) { - LLImageRaw* dst = this; // Just for clarity. + LLImageRaw* dst = this; // Just for clarity. - LLImageDataLock lock(this); + LLImageDataLock lock(this); - llassert( (3 == dst->getComponents()) && (4 == src->getComponents()) ); - llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ); + llassert( (3 == dst->getComponents()) && (4 == src->getComponents()) ); + llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ); - S32 pixels = getWidth() * getHeight(); - const U8* src_data = src->getData(); - U8* dst_data = dst->getData(); - for( S32 i=0; i<pixels; i++ ) - { - dst_data[0] = src_data[0]; - dst_data[1] = src_data[1]; - dst_data[2] = src_data[2]; - src_data += 4; - dst_data += 3; - } + S32 pixels = getWidth() * getHeight(); + const U8* src_data = src->getData(); + U8* dst_data = dst->getData(); + for( S32 i=0; i<pixels; i++ ) + { + dst_data[0] = src_data[0]; + dst_data[1] = src_data[1]; + dst_data[2] = src_data[2]; + src_data += 4; + dst_data += 3; + } } // Src and dst are same size. Src has 3 components. Dst has 4 components. void LLImageRaw::copyUnscaled3onto4( const LLImageRaw* src ) { - LLImageRaw* dst = this; // Just for clarity. + LLImageRaw* dst = this; // Just for clarity. - LLImageDataLock lock(this); + LLImageDataLock lock(this); - llassert( 3 == src->getComponents() ); - llassert( 4 == dst->getComponents() ); - llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ); + llassert( 3 == src->getComponents() ); + llassert( 4 == dst->getComponents() ); + llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ); - S32 pixels = getWidth() * getHeight(); - const U8* src_data = src->getData(); - U8* dst_data = dst->getData(); - for( S32 i=0; i<pixels; i++ ) - { - dst_data[0] = src_data[0]; - dst_data[1] = src_data[1]; - dst_data[2] = src_data[2]; - dst_data[3] = 255; - src_data += 3; - dst_data += 4; - } + S32 pixels = getWidth() * getHeight(); + const U8* src_data = src->getData(); + U8* dst_data = dst->getData(); + for( S32 i=0; i<pixels; i++ ) + { + dst_data[0] = src_data[0]; + dst_data[1] = src_data[1]; + dst_data[2] = src_data[2]; + dst_data[3] = 255; + src_data += 3; + dst_data += 4; + } } // Src and dst can be any size. Src and dst have same number of components. void LLImageRaw::copyScaled( const LLImageRaw* src ) { - LLImageRaw* dst = this; // Just for clarity. + LLImageRaw* dst = this; // Just for clarity. - LLImageDataSharedLock lockIn(src); - LLImageDataLock lockOut(this); + LLImageDataSharedLock lockIn(src); + LLImageDataLock lockOut(this); - if (!validateSrcAndDst("LLImageRaw::copyScaled", src, dst)) - { - return; - } + if (!validateSrcAndDst("LLImageRaw::copyScaled", src, dst)) + { + return; + } - llassert_always( (1 == src->getComponents()) || (3 == src->getComponents()) || (4 == src->getComponents()) ); - llassert_always( src->getComponents() == dst->getComponents() ); + llassert_always( (1 == src->getComponents()) || (3 == src->getComponents()) || (4 == src->getComponents()) ); + llassert_always( src->getComponents() == dst->getComponents() ); - if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ) - { - memcpy( dst->getData(), src->getData(), getWidth() * getHeight() * getComponents() ); /* Flawfinder: ignore */ - return; - } + if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ) + { + memcpy( dst->getData(), src->getData(), getWidth() * getHeight() * getComponents() ); /* Flawfinder: ignore */ + return; + } - bilinear_scale( - src->getData(), src->getWidth(), src->getHeight(), src->getComponents(), src->getWidth()*src->getComponents() - , dst->getData(), dst->getWidth(), dst->getHeight(), dst->getComponents(), dst->getWidth()*dst->getComponents() - ); + bilinear_scale( + src->getData(), src->getWidth(), src->getHeight(), src->getComponents(), src->getWidth()*src->getComponents() + , dst->getData(), dst->getWidth(), dst->getHeight(), dst->getComponents(), dst->getWidth()*dst->getComponents() + ); - /* - S32 temp_data_size = src->getWidth() * dst->getHeight() * getComponents(); - llassert_always(temp_data_size > 0); - std::vector<U8> temp_buffer(temp_data_size); + /* + S32 temp_data_size = src->getWidth() * dst->getHeight() * getComponents(); + llassert_always(temp_data_size > 0); + std::vector<U8> temp_buffer(temp_data_size); - // Vertical - for( S32 col = 0; col < src->getWidth(); col++ ) - { - copyLineScaled( src->getData() + (getComponents() * col), &temp_buffer[0] + (getComponents() * col), src->getHeight(), dst->getHeight(), src->getWidth(), src->getWidth() ); - } + // Vertical + for( S32 col = 0; col < src->getWidth(); col++ ) + { + copyLineScaled( src->getData() + (getComponents() * col), &temp_buffer[0] + (getComponents() * col), src->getHeight(), dst->getHeight(), src->getWidth(), src->getWidth() ); + } - // Horizontal - for( S32 row = 0; row < dst->getHeight(); row++ ) - { - copyLineScaled( &temp_buffer[0] + (getComponents() * src->getWidth() * row), dst->getData() + (getComponents() * dst->getWidth() * row), src->getWidth(), dst->getWidth(), 1, 1 ); - } - */ + // Horizontal + for( S32 row = 0; row < dst->getHeight(); row++ ) + { + copyLineScaled( &temp_buffer[0] + (getComponents() * src->getWidth() * row), dst->getData() + (getComponents() * dst->getWidth() * row), src->getWidth(), dst->getWidth(), 1, 1 ); + } + */ } @@ -1595,79 +1591,79 @@ bool LLImageRaw::scale( S32 new_width, S32 new_height, bool scale_image_data ) return false; } - if (isBufferInvalid()) - { - LL_WARNS() << "Invalid image buffer" << LL_ENDL; - return false; - } + if (isBufferInvalid()) + { + LL_WARNS() << "Invalid image buffer" << LL_ENDL; + return false; + } + + S32 old_width = getWidth(); + S32 old_height = getHeight(); - S32 old_width = getWidth(); - S32 old_height = getHeight(); - - if( (old_width == new_width) && (old_height == new_height) ) - { - return true; // Nothing to do. - } + if( (old_width == new_width) && (old_height == new_height) ) + { + return true; // Nothing to do. + } - // Reallocate the data buffer. + // Reallocate the data buffer. - if (scale_image_data) - { - S32 new_data_size = new_width * new_height * components; + if (scale_image_data) + { + S32 new_data_size = new_width * new_height * components; - if (new_data_size > 0) + if (new_data_size > 0) { - U8 *new_data = (U8*)ll_aligned_malloc_16(new_data_size); - if(NULL == new_data) + U8 *new_data = (U8*)ll_aligned_malloc_16(new_data_size); + if(NULL == new_data) { - return false; + return false; } bilinear_scale(getData(), old_width, old_height, components, old_width*components, new_data, new_width, new_height, components, new_width*components); - setDataAndSize(new_data, new_width, new_height, components); - } - } - else try - { - // copy out existing image data - S32 temp_data_size = old_width * old_height * components; - std::vector<U8> temp_buffer(temp_data_size); - memcpy(&temp_buffer[0], getData(), temp_data_size); - - // allocate new image data, will delete old data - U8* new_buffer = allocateDataSize(new_width, new_height, components); + setDataAndSize(new_data, new_width, new_height, components); + } + } + else try + { + // copy out existing image data + S32 temp_data_size = old_width * old_height * components; + std::vector<U8> temp_buffer(temp_data_size); + memcpy(&temp_buffer[0], getData(), temp_data_size); + + // allocate new image data, will delete old data + U8* new_buffer = allocateDataSize(new_width, new_height, components); if (!new_buffer) { LL_WARNS() << "Failed to allocate new image data buffer" << LL_ENDL; return false; } - - for( S32 row = 0; row < new_height; row++ ) + + for( S32 row = 0; row < new_height; row++ ) { - if (row < old_height) + if (row < old_height) { - memcpy(new_buffer + (new_width * row * components), &temp_buffer[0] + (old_width * row * components), components * llmin(old_width, new_width)); - if (old_width < new_width) + memcpy(new_buffer + (new_width * row * components), &temp_buffer[0] + (old_width * row * components), components * llmin(old_width, new_width)); + if (old_width < new_width) { - // pad out rest of row with black - memset(new_buffer + (components * ((new_width * row) + old_width)), 0, components * (new_width - old_width)); + // pad out rest of row with black + memset(new_buffer + (components * ((new_width * row) + old_width)), 0, components * (new_width - old_width)); } } else { // pad remaining rows with black - memset(new_buffer + (new_width * row * components), 0, new_width * components); + memset(new_buffer + (new_width * row * components), 0, new_width * components); } } - } + } catch (std::bad_alloc&) // for temp_buffer { LL_WARNS() << "Failed to allocate temporary image buffer" << LL_ENDL; return false; } - return true ; + return true ; } LLPointer<LLImageRaw> LLImageRaw::scaled(S32 new_width, S32 new_height) @@ -1723,253 +1719,253 @@ LLPointer<LLImageRaw> LLImageRaw::scaled(S32 new_width, S32 new_height) void LLImageRaw::copyLineScaled( const U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step ) { - const S32 components = getComponents(); - llassert( components >= 1 && components <= 4 ); - - const F32 ratio = F32(in_pixel_len) / out_pixel_len; // ratio of old to new - const F32 norm_factor = 1.f / ratio; - - S32 goff = components >= 2 ? 1 : 0; - S32 boff = components >= 3 ? 2 : 0; - for( S32 x = 0; x < out_pixel_len; x++ ) - { - // Sample input pixels in range from sample0 to sample1. - // Avoid floating point accumulation error... don't just add ratio each time. JC - const F32 sample0 = x * ratio; - const F32 sample1 = (x+1) * ratio; - const S32 index0 = llfloor(sample0); // left integer (floor) - const S32 index1 = llfloor(sample1); // right integer (floor) - const F32 fract0 = 1.f - (sample0 - F32(index0)); // spill over on left - const F32 fract1 = sample1 - F32(index1); // spill-over on right - - if( index0 == index1 ) - { - // Interval is embedded in one input pixel - S32 t0 = x * out_pixel_step * components; - S32 t1 = index0 * in_pixel_step * components; - U8* outp = out + t0; - const U8* inp = in + t1; - for (S32 i = 0; i < components; ++i) - { - *outp = *inp; - ++outp; - ++inp; - } - } - else - { - // Left straddle - S32 t1 = index0 * in_pixel_step * components; - F32 r = in[t1 + 0] * fract0; - F32 g = in[t1 + goff] * fract0; - F32 b = in[t1 + boff] * fract0; - F32 a = 0; - if( components == 4) - { - a = in[t1 + 3] * fract0; - } - - // Central interval - if (components < 4) - { - for( S32 u = index0 + 1; u < index1; u++ ) - { - S32 t2 = u * in_pixel_step * components; - r += in[t2 + 0]; - g += in[t2 + goff]; - b += in[t2 + boff]; - } - } - else - { - for( S32 u = index0 + 1; u < index1; u++ ) - { - S32 t2 = u * in_pixel_step * components; - r += in[t2 + 0]; - g += in[t2 + 1]; - b += in[t2 + 2]; - a += in[t2 + 3]; - } - } - - // right straddle - // Watch out for reading off of end of input array. - if( fract1 && index1 < in_pixel_len ) - { - S32 t3 = index1 * in_pixel_step * components; - if (components < 4) - { - U8 in0 = in[t3 + 0]; - U8 in1 = in[t3 + goff]; - U8 in2 = in[t3 + boff]; - r += in0 * fract1; - g += in1 * fract1; - b += in2 * fract1; - } - else - { - U8 in0 = in[t3 + 0]; - U8 in1 = in[t3 + 1]; - U8 in2 = in[t3 + 2]; - U8 in3 = in[t3 + 3]; - r += in0 * fract1; - g += in1 * fract1; - b += in2 * fract1; - a += in3 * fract1; - } - } - - r *= norm_factor; - g *= norm_factor; - b *= norm_factor; - a *= norm_factor; // skip conditional - - S32 t4 = x * out_pixel_step * components; - out[t4 + 0] = U8(ll_round(r)); - if (components >= 2) - out[t4 + 1] = U8(ll_round(g)); - if (components >= 3) - out[t4 + 2] = U8(ll_round(b)); - if( components == 4) - out[t4 + 3] = U8(ll_round(a)); - } - } + const S32 components = getComponents(); + llassert( components >= 1 && components <= 4 ); + + const F32 ratio = F32(in_pixel_len) / out_pixel_len; // ratio of old to new + const F32 norm_factor = 1.f / ratio; + + S32 goff = components >= 2 ? 1 : 0; + S32 boff = components >= 3 ? 2 : 0; + for( S32 x = 0; x < out_pixel_len; x++ ) + { + // Sample input pixels in range from sample0 to sample1. + // Avoid floating point accumulation error... don't just add ratio each time. JC + const F32 sample0 = x * ratio; + const F32 sample1 = (x+1) * ratio; + const S32 index0 = llfloor(sample0); // left integer (floor) + const S32 index1 = llfloor(sample1); // right integer (floor) + const F32 fract0 = 1.f - (sample0 - F32(index0)); // spill over on left + const F32 fract1 = sample1 - F32(index1); // spill-over on right + + if( index0 == index1 ) + { + // Interval is embedded in one input pixel + S32 t0 = x * out_pixel_step * components; + S32 t1 = index0 * in_pixel_step * components; + U8* outp = out + t0; + const U8* inp = in + t1; + for (S32 i = 0; i < components; ++i) + { + *outp = *inp; + ++outp; + ++inp; + } + } + else + { + // Left straddle + S32 t1 = index0 * in_pixel_step * components; + F32 r = in[t1 + 0] * fract0; + F32 g = in[t1 + goff] * fract0; + F32 b = in[t1 + boff] * fract0; + F32 a = 0; + if( components == 4) + { + a = in[t1 + 3] * fract0; + } + + // Central interval + if (components < 4) + { + for( S32 u = index0 + 1; u < index1; u++ ) + { + S32 t2 = u * in_pixel_step * components; + r += in[t2 + 0]; + g += in[t2 + goff]; + b += in[t2 + boff]; + } + } + else + { + for( S32 u = index0 + 1; u < index1; u++ ) + { + S32 t2 = u * in_pixel_step * components; + r += in[t2 + 0]; + g += in[t2 + 1]; + b += in[t2 + 2]; + a += in[t2 + 3]; + } + } + + // right straddle + // Watch out for reading off of end of input array. + if( fract1 && index1 < in_pixel_len ) + { + S32 t3 = index1 * in_pixel_step * components; + if (components < 4) + { + U8 in0 = in[t3 + 0]; + U8 in1 = in[t3 + goff]; + U8 in2 = in[t3 + boff]; + r += in0 * fract1; + g += in1 * fract1; + b += in2 * fract1; + } + else + { + U8 in0 = in[t3 + 0]; + U8 in1 = in[t3 + 1]; + U8 in2 = in[t3 + 2]; + U8 in3 = in[t3 + 3]; + r += in0 * fract1; + g += in1 * fract1; + b += in2 * fract1; + a += in3 * fract1; + } + } + + r *= norm_factor; + g *= norm_factor; + b *= norm_factor; + a *= norm_factor; // skip conditional + + S32 t4 = x * out_pixel_step * components; + out[t4 + 0] = U8(ll_round(r)); + if (components >= 2) + out[t4 + 1] = U8(ll_round(g)); + if (components >= 3) + out[t4 + 2] = U8(ll_round(b)); + if( components == 4) + out[t4 + 3] = U8(ll_round(a)); + } + } } void LLImageRaw::compositeRowScaled4onto3( const U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len ) { - llassert( getComponents() == 3 ); - - const S32 IN_COMPONENTS = 4; - const S32 OUT_COMPONENTS = 3; - - const F32 ratio = F32(in_pixel_len) / out_pixel_len; // ratio of old to new - const F32 norm_factor = 1.f / ratio; - - for( S32 x = 0; x < out_pixel_len; x++ ) - { - // Sample input pixels in range from sample0 to sample1. - // Avoid floating point accumulation error... don't just add ratio each time. JC - const F32 sample0 = x * ratio; - const F32 sample1 = (x+1) * ratio; - const S32 index0 = S32(sample0); // left integer (floor) - const S32 index1 = S32(sample1); // right integer (floor) - const F32 fract0 = 1.f - (sample0 - F32(index0)); // spill over on left - const F32 fract1 = sample1 - F32(index1); // spill-over on right - - U8 in_scaled_r; - U8 in_scaled_g; - U8 in_scaled_b; - U8 in_scaled_a; - - if( index0 == index1 ) - { - // Interval is embedded in one input pixel - S32 t1 = index0 * IN_COMPONENTS; - in_scaled_r = in[t1 + 0]; - in_scaled_g = in[t1 + 0]; - in_scaled_b = in[t1 + 0]; - in_scaled_a = in[t1 + 0]; - } - else - { - // Left straddle - S32 t1 = index0 * IN_COMPONENTS; - F32 r = in[t1 + 0] * fract0; - F32 g = in[t1 + 1] * fract0; - F32 b = in[t1 + 2] * fract0; - F32 a = in[t1 + 3] * fract0; - - // Central interval - for( S32 u = index0 + 1; u < index1; u++ ) - { - S32 t2 = u * IN_COMPONENTS; - r += in[t2 + 0]; - g += in[t2 + 1]; - b += in[t2 + 2]; - a += in[t2 + 3]; - } - - // right straddle - // Watch out for reading off of end of input array. - if( fract1 && index1 < in_pixel_len ) - { - S32 t3 = index1 * IN_COMPONENTS; - r += in[t3 + 0] * fract1; - g += in[t3 + 1] * fract1; - b += in[t3 + 2] * fract1; - a += in[t3 + 3] * fract1; - } - - r *= norm_factor; - g *= norm_factor; - b *= norm_factor; - a *= norm_factor; - - in_scaled_r = U8(ll_round(r)); - in_scaled_g = U8(ll_round(g)); - in_scaled_b = U8(ll_round(b)); - in_scaled_a = U8(ll_round(a)); - } - - if( in_scaled_a ) - { - if( 255 == in_scaled_a ) - { - out[0] = in_scaled_r; - out[1] = in_scaled_g; - out[2] = in_scaled_b; - } - else - { - U8 transparency = 255 - in_scaled_a; - out[0] = fastFractionalMult( out[0], transparency ) + fastFractionalMult( in_scaled_r, in_scaled_a ); - out[1] = fastFractionalMult( out[1], transparency ) + fastFractionalMult( in_scaled_g, in_scaled_a ); - out[2] = fastFractionalMult( out[2], transparency ) + fastFractionalMult( in_scaled_b, in_scaled_a ); - } - } - out += OUT_COMPONENTS; - } + llassert( getComponents() == 3 ); + + const S32 IN_COMPONENTS = 4; + const S32 OUT_COMPONENTS = 3; + + const F32 ratio = F32(in_pixel_len) / out_pixel_len; // ratio of old to new + const F32 norm_factor = 1.f / ratio; + + for( S32 x = 0; x < out_pixel_len; x++ ) + { + // Sample input pixels in range from sample0 to sample1. + // Avoid floating point accumulation error... don't just add ratio each time. JC + const F32 sample0 = x * ratio; + const F32 sample1 = (x+1) * ratio; + const S32 index0 = S32(sample0); // left integer (floor) + const S32 index1 = S32(sample1); // right integer (floor) + const F32 fract0 = 1.f - (sample0 - F32(index0)); // spill over on left + const F32 fract1 = sample1 - F32(index1); // spill-over on right + + U8 in_scaled_r; + U8 in_scaled_g; + U8 in_scaled_b; + U8 in_scaled_a; + + if( index0 == index1 ) + { + // Interval is embedded in one input pixel + S32 t1 = index0 * IN_COMPONENTS; + in_scaled_r = in[t1 + 0]; + in_scaled_g = in[t1 + 0]; + in_scaled_b = in[t1 + 0]; + in_scaled_a = in[t1 + 0]; + } + else + { + // Left straddle + S32 t1 = index0 * IN_COMPONENTS; + F32 r = in[t1 + 0] * fract0; + F32 g = in[t1 + 1] * fract0; + F32 b = in[t1 + 2] * fract0; + F32 a = in[t1 + 3] * fract0; + + // Central interval + for( S32 u = index0 + 1; u < index1; u++ ) + { + S32 t2 = u * IN_COMPONENTS; + r += in[t2 + 0]; + g += in[t2 + 1]; + b += in[t2 + 2]; + a += in[t2 + 3]; + } + + // right straddle + // Watch out for reading off of end of input array. + if( fract1 && index1 < in_pixel_len ) + { + S32 t3 = index1 * IN_COMPONENTS; + r += in[t3 + 0] * fract1; + g += in[t3 + 1] * fract1; + b += in[t3 + 2] * fract1; + a += in[t3 + 3] * fract1; + } + + r *= norm_factor; + g *= norm_factor; + b *= norm_factor; + a *= norm_factor; + + in_scaled_r = U8(ll_round(r)); + in_scaled_g = U8(ll_round(g)); + in_scaled_b = U8(ll_round(b)); + in_scaled_a = U8(ll_round(a)); + } + + if( in_scaled_a ) + { + if( 255 == in_scaled_a ) + { + out[0] = in_scaled_r; + out[1] = in_scaled_g; + out[2] = in_scaled_b; + } + else + { + U8 transparency = 255 - in_scaled_a; + out[0] = fastFractionalMult( out[0], transparency ) + fastFractionalMult( in_scaled_r, in_scaled_a ); + out[1] = fastFractionalMult( out[1], transparency ) + fastFractionalMult( in_scaled_g, in_scaled_a ); + out[2] = fastFractionalMult( out[2], transparency ) + fastFractionalMult( in_scaled_b, in_scaled_a ); + } + } + out += OUT_COMPONENTS; + } } void LLImageRaw::addEmissive(LLImageRaw* src) { - LLImageRaw* dst = this; // Just for clarity. + LLImageRaw* dst = this; // Just for clarity. - if (!validateSrcAndDst(__FUNCTION__, src, dst)) - { - return; - } + if (!validateSrcAndDst(__FUNCTION__, src, dst)) + { + return; + } - llassert((3 == src->getComponents()) || (4 == src->getComponents())); - llassert(3 == dst->getComponents()); + llassert((3 == src->getComponents()) || (4 == src->getComponents())); + llassert(3 == dst->getComponents()); - if( 3 == dst->getComponents() ) - { - if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ) - { + if( 3 == dst->getComponents() ) + { + if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ) + { addEmissiveUnscaled(src); - } - else - { + } + else + { addEmissiveScaled(src); - } - } + } + } } void LLImageRaw::addEmissiveUnscaled(LLImageRaw* src) { - LLImageRaw* dst = this; // Just for clarity. + LLImageRaw* dst = this; // Just for clarity. - llassert((3 == src->getComponents()) || (4 == src->getComponents())); - llassert((3 == dst->getComponents()) || (4 == dst->getComponents())); - llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ); + llassert((3 == src->getComponents()) || (4 == src->getComponents())); + llassert((3 == dst->getComponents()) || (4 == dst->getComponents())); + llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) ); U8* const src_data = src->getData(); U8* const dst_data = dst->getData(); - for(S32 y = 0; y < dst->getHeight(); ++y) - { + for(S32 y = 0; y < dst->getHeight(); ++y) + { const S32 src_row_offset = src->getComponents() * src->getWidth() * y; const S32 dst_row_offset = dst->getComponents() * dst->getWidth() * y; for (S32 x = 0; x < dst->getWidth(); ++x) @@ -1982,17 +1978,17 @@ void LLImageRaw::addEmissiveUnscaled(LLImageRaw* src) dst_pixel[1] = llmin(255, dst_pixel[1] + src_pixel[1]); dst_pixel[2] = llmin(255, dst_pixel[2] + src_pixel[2]); } - } + } } void LLImageRaw::addEmissiveScaled(LLImageRaw* src) { - LLImageRaw* dst = this; // Just for clarity. + LLImageRaw* dst = this; // Just for clarity. - llassert( (4 == src->getComponents()) && (3 == dst->getComponents()) ); + llassert( (4 == src->getComponents()) && (3 == dst->getComponents()) ); LLImageRaw temp(dst->getWidth(), dst->getHeight(), dst->getComponents()); - llassert_always(temp.getDataSize() > 0); + llassert_always(temp.getDataSize() > 0); temp.copyScaled(src); dst->addEmissiveUnscaled(&temp); @@ -2001,164 +1997,164 @@ void LLImageRaw::addEmissiveScaled(LLImageRaw* src) // static bool LLImageRaw::validateSrcAndDst(std::string func, const LLImageRaw* src, const LLImageRaw* dst) { - LLImageDataSharedLock lockIn(src); - LLImageDataLock lockOut(dst); + LLImageDataSharedLock lockIn(src); + LLImageDataLock lockOut(dst); - if (!src || !dst || src->isBufferInvalid() || dst->isBufferInvalid()) - { - LL_WARNS() << func << ": Source: "; - if (!src) LL_CONT << "Null pointer"; - else if (src->isBufferInvalid()) LL_CONT << "Invalid buffer"; - else LL_CONT << "OK"; + if (!src || !dst || src->isBufferInvalid() || dst->isBufferInvalid()) + { + LL_WARNS() << func << ": Source: "; + if (!src) LL_CONT << "Null pointer"; + else if (src->isBufferInvalid()) LL_CONT << "Invalid buffer"; + else LL_CONT << "OK"; - LL_CONT << "; Destination: "; - if (!dst) LL_CONT << "Null pointer"; - else if (dst->isBufferInvalid()) LL_CONT << "Invalid buffer"; - else LL_CONT << "OK"; - LL_CONT << "." << LL_ENDL; + LL_CONT << "; Destination: "; + if (!dst) LL_CONT << "Null pointer"; + else if (dst->isBufferInvalid()) LL_CONT << "Invalid buffer"; + else LL_CONT << "OK"; + LL_CONT << "." << LL_ENDL; - return false; - } - return true; + return false; + } + return true; } //---------------------------------------------------------------------------- static struct { - const char* exten; - EImageCodec codec; + const char* exten; + EImageCodec codec; } file_extensions[] = { - { "bmp", IMG_CODEC_BMP }, - { "tga", IMG_CODEC_TGA }, - { "j2c", IMG_CODEC_J2C }, - { "jp2", IMG_CODEC_J2C }, - { "texture", IMG_CODEC_J2C }, - { "jpg", IMG_CODEC_JPEG }, - { "jpeg", IMG_CODEC_JPEG }, - { "mip", IMG_CODEC_DXT }, - { "dxt", IMG_CODEC_DXT }, - { "png", IMG_CODEC_PNG } + { "bmp", IMG_CODEC_BMP }, + { "tga", IMG_CODEC_TGA }, + { "j2c", IMG_CODEC_J2C }, + { "jp2", IMG_CODEC_J2C }, + { "texture", IMG_CODEC_J2C }, + { "jpg", IMG_CODEC_JPEG }, + { "jpeg", IMG_CODEC_JPEG }, + { "mip", IMG_CODEC_DXT }, + { "dxt", IMG_CODEC_DXT }, + { "png", IMG_CODEC_PNG } }; #define NUM_FILE_EXTENSIONS LL_ARRAY_SIZE(file_extensions) #if 0 static std::string find_file(std::string &name, S8 *codec) { - std::string tname; - for (int i=0; i<(int)(NUM_FILE_EXTENSIONS); i++) - { - tname = name + "." + std::string(file_extensions[i].exten); - llifstream ifs(tname.c_str(), llifstream::binary); - if (ifs.is_open()) - { - ifs.close(); - if (codec) - *codec = file_extensions[i].codec; - return std::string(file_extensions[i].exten); - } - } - return std::string(""); + std::string tname; + for (int i=0; i<(int)(NUM_FILE_EXTENSIONS); i++) + { + tname = name + "." + std::string(file_extensions[i].exten); + llifstream ifs(tname.c_str(), llifstream::binary); + if (ifs.is_open()) + { + ifs.close(); + if (codec) + *codec = file_extensions[i].codec; + return std::string(file_extensions[i].exten); + } + } + return std::string(""); } #endif EImageCodec LLImageBase::getCodecFromExtension(const std::string& exten) { - if (!exten.empty()) - { - for (int i = 0; i < (int)(NUM_FILE_EXTENSIONS); i++) - { - if (exten == file_extensions[i].exten) - return file_extensions[i].codec; - } - } - return IMG_CODEC_INVALID; + if (!exten.empty()) + { + for (int i = 0; i < (int)(NUM_FILE_EXTENSIONS); i++) + { + if (exten == file_extensions[i].exten) + return file_extensions[i].codec; + } + } + return IMG_CODEC_INVALID; } #if 0 bool LLImageRaw::createFromFile(const std::string &filename, bool j2c_lowest_mip_only) { - std::string name = filename; - size_t dotidx = name.rfind('.'); - S8 codec = IMG_CODEC_INVALID; - std::string exten; - - deleteData(); // delete any existing data - - if (dotidx != std::string::npos) - { - exten = name.substr(dotidx+1); - LLStringUtil::toLower(exten); - codec = getCodecFromExtension(exten); - } - else - { - exten = find_file(name, &codec); - name = name + "." + exten; - } - if (codec == IMG_CODEC_INVALID) - { - return false; // format not recognized - } - - llifstream ifs(name.c_str(), llifstream::binary); - if (!ifs.is_open()) - { - // SJB: changed from LL_INFOS() to LL_DEBUGS() to reduce spam - LL_DEBUGS() << "Unable to open image file: " << name << LL_ENDL; - return false; - } - - ifs.seekg (0, std::ios::end); - int length = ifs.tellg(); - if (j2c_lowest_mip_only && length > 2048) - { - length = 2048; - } - ifs.seekg (0, std::ios::beg); - - if (!length) - { - LL_INFOS() << "Zero length file file: " << name << LL_ENDL; - return false; - } - - LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec); - llassert(image.notNull()); - - U8 *buffer = image->allocateData(length); - ifs.read ((char*)buffer, length); - ifs.close(); - - bool success; - - success = image->updateData(); - if (success) - { - if (j2c_lowest_mip_only && codec == IMG_CODEC_J2C) - { - S32 width = image->getWidth(); - S32 height = image->getHeight(); - S32 discard_level = 0; - while (width > 1 && height > 1 && discard_level < MAX_DISCARD_LEVEL) - { - width >>= 1; - height >>= 1; - discard_level++; - } - ((LLImageJ2C *)((LLImageFormatted*)image))->setDiscardLevel(discard_level); - } - success = image->decode(this, 100000.0f); - } - - image = NULL; // deletes image - if (!success) - { - deleteData(); - LL_WARNS() << "Unable to decode image" << name << LL_ENDL; - return false; - } - - return true; + std::string name = filename; + size_t dotidx = name.rfind('.'); + S8 codec = IMG_CODEC_INVALID; + std::string exten; + + deleteData(); // delete any existing data + + if (dotidx != std::string::npos) + { + exten = name.substr(dotidx+1); + LLStringUtil::toLower(exten); + codec = getCodecFromExtension(exten); + } + else + { + exten = find_file(name, &codec); + name = name + "." + exten; + } + if (codec == IMG_CODEC_INVALID) + { + return false; // format not recognized + } + + llifstream ifs(name.c_str(), llifstream::binary); + if (!ifs.is_open()) + { + // SJB: changed from LL_INFOS() to LL_DEBUGS() to reduce spam + LL_DEBUGS() << "Unable to open image file: " << name << LL_ENDL; + return false; + } + + ifs.seekg (0, std::ios::end); + int length = ifs.tellg(); + if (j2c_lowest_mip_only && length > 2048) + { + length = 2048; + } + ifs.seekg (0, std::ios::beg); + + if (!length) + { + LL_INFOS() << "Zero length file file: " << name << LL_ENDL; + return false; + } + + LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec); + llassert(image.notNull()); + + U8 *buffer = image->allocateData(length); + ifs.read ((char*)buffer, length); + ifs.close(); + + bool success; + + success = image->updateData(); + if (success) + { + if (j2c_lowest_mip_only && codec == IMG_CODEC_J2C) + { + S32 width = image->getWidth(); + S32 height = image->getHeight(); + S32 discard_level = 0; + while (width > 1 && height > 1 && discard_level < MAX_DISCARD_LEVEL) + { + width >>= 1; + height >>= 1; + discard_level++; + } + ((LLImageJ2C *)((LLImageFormatted*)image))->setDiscardLevel(discard_level); + } + success = image->decode(this, 100000.0f); + } + + image = NULL; // deletes image + if (!success) + { + deleteData(); + LL_WARNS() << "Unable to decode image" << name << LL_ENDL; + return false; + } + + return true; } #endif //--------------------------------------------------------------------------- @@ -2169,21 +2165,21 @@ bool LLImageRaw::createFromFile(const std::string &filename, bool j2c_lowest_mip S32 LLImageFormatted::sGlobalFormattedMemory = 0; LLImageFormatted::LLImageFormatted(S8 codec) - : LLImageBase(), - mCodec(codec), - mDecoding(0), - mDecoded(0), - mDiscardLevel(-1), - mLevels(0) + : LLImageBase(), + mCodec(codec), + mDecoding(0), + mDecoded(0), + mDiscardLevel(-1), + mLevels(0) { } // virtual LLImageFormatted::~LLImageFormatted() { - // NOTE: ~LLimageBase() call to deleteData() calls LLImageBase::deleteData() - // NOT LLImageFormatted::deleteData() - deleteData(); + // NOTE: ~LLimageBase() call to deleteData() calls LLImageBase::deleteData() + // NOT LLImageFormatted::deleteData() + deleteData(); } //---------------------------------------------------------------------------- @@ -2191,16 +2187,16 @@ LLImageFormatted::~LLImageFormatted() //virtual void LLImageFormatted::resetLastError() { - LLImage::setLastError(""); + LLImage::setLastError(""); } //virtual void LLImageFormatted::setLastError(const std::string& message, const std::string& filename) { - std::string error = message; - if (!filename.empty()) - error += std::string(" FILE: ") + filename; - LLImage::setLastError(error); + std::string error = message; + if (!filename.empty()) + error += std::string(" FILE: ") + filename; + LLImage::setLastError(error); } //---------------------------------------------------------------------------- @@ -2208,97 +2204,97 @@ void LLImageFormatted::setLastError(const std::string& message, const std::strin // static LLImageFormatted* LLImageFormatted::createFromType(S8 codec) { - LLImageFormatted* image; - switch(codec) - { - case IMG_CODEC_BMP: - image = new LLImageBMP(); - break; - case IMG_CODEC_TGA: - image = new LLImageTGA(); - break; - case IMG_CODEC_JPEG: - image = new LLImageJPEG(); - break; - case IMG_CODEC_PNG: - image = new LLImagePNG(); - break; - case IMG_CODEC_J2C: - image = new LLImageJ2C(); - break; - case IMG_CODEC_DXT: - image = new LLImageDXT(); - break; - default: - image = NULL; - break; - } - return image; + LLImageFormatted* image; + switch(codec) + { + case IMG_CODEC_BMP: + image = new LLImageBMP(); + break; + case IMG_CODEC_TGA: + image = new LLImageTGA(); + break; + case IMG_CODEC_JPEG: + image = new LLImageJPEG(); + break; + case IMG_CODEC_PNG: + image = new LLImagePNG(); + break; + case IMG_CODEC_J2C: + image = new LLImageJ2C(); + break; + case IMG_CODEC_DXT: + image = new LLImageDXT(); + break; + default: + image = NULL; + break; + } + return image; } // static LLImageFormatted* LLImageFormatted::createFromExtension(const std::string& instring) { - std::string exten; - size_t dotidx = instring.rfind('.'); - if (dotidx != std::string::npos) - { - exten = instring.substr(dotidx+1); - } - else - { - exten = instring; - } - S8 codec = getCodecFromExtension(exten); - return createFromType(codec); + std::string exten; + size_t dotidx = instring.rfind('.'); + if (dotidx != std::string::npos) + { + exten = instring.substr(dotidx+1); + } + else + { + exten = instring; + } + S8 codec = getCodecFromExtension(exten); + return createFromType(codec); } //---------------------------------------------------------------------------- // virtual void LLImageFormatted::dump() { - LLImageBase::dump(); + LLImageBase::dump(); - LL_INFOS() << "LLImageFormatted" - << " mDecoding " << mDecoding - << " mCodec " << S32(mCodec) - << " mDecoded " << mDecoded - << LL_ENDL; + LL_INFOS() << "LLImageFormatted" + << " mDecoding " << mDecoding + << " mCodec " << S32(mCodec) + << " mDecoded " << mDecoded + << LL_ENDL; } //---------------------------------------------------------------------------- S32 LLImageFormatted::calcDataSize(S32 discard_level) { - if (discard_level < 0) - { - discard_level = mDiscardLevel; - } - S32 w = getWidth() >> discard_level; - S32 h = getHeight() >> discard_level; - w = llmax(w, 1); - h = llmax(h, 1); - return w * h * getComponents(); + if (discard_level < 0) + { + discard_level = mDiscardLevel; + } + S32 w = getWidth() >> discard_level; + S32 h = getHeight() >> discard_level; + w = llmax(w, 1); + h = llmax(h, 1); + return w * h * getComponents(); } S32 LLImageFormatted::calcDiscardLevelBytes(S32 bytes) { - llassert(bytes >= 0); - S32 discard_level = 0; - while (1) - { - S32 bytes_needed = calcDataSize(discard_level); // virtual - if (bytes_needed <= bytes) - { - break; - } - discard_level++; - if (discard_level > MAX_IMAGE_MIP) - { - return -1; - } - } - return discard_level; + llassert(bytes >= 0); + S32 discard_level = 0; + while (1) + { + S32 bytes_needed = calcDataSize(discard_level); // virtual + if (bytes_needed <= bytes) + { + break; + } + discard_level++; + if (discard_level > MAX_IMAGE_MIP) + { + return -1; + } + } + return discard_level; } @@ -2307,31 +2303,31 @@ S32 LLImageFormatted::calcDiscardLevelBytes(S32 bytes) // Subclasses that can handle more than 4 channels should override this function. bool LLImageFormatted::decodeChannels(LLImageRaw* raw_image,F32 decode_time, S32 first_channel, S32 max_channel) { - llassert( (first_channel == 0) && (max_channel == 4) ); - return decode( raw_image, decode_time ); // Loads first 4 channels by default. -} + llassert( (first_channel == 0) && (max_channel == 4) ); + return decode( raw_image, decode_time ); // Loads first 4 channels by default. +} //---------------------------------------------------------------------------- // virtual U8* LLImageFormatted::allocateData(S32 size) { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - U8* res = LLImageBase::allocateData(size); // calls deleteData() - sGlobalFormattedMemory += getDataSize(); - return res; + U8* res = LLImageBase::allocateData(size); // calls deleteData() + sGlobalFormattedMemory += getDataSize(); + return res; } // virtual U8* LLImageFormatted::reallocateData(S32 size) { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - sGlobalFormattedMemory -= getDataSize(); - U8* res = LLImageBase::reallocateData(size); - sGlobalFormattedMemory += getDataSize(); - return res; + sGlobalFormattedMemory -= getDataSize(); + U8* res = LLImageBase::reallocateData(size); + sGlobalFormattedMemory += getDataSize(); + return res; } // virtual @@ -2343,8 +2339,8 @@ void LLImageFormatted::deleteData() { LL_ERRS() << "LLImageFormatted::deleteData() is called during decoding" << LL_ENDL; } - sGlobalFormattedMemory -= getDataSize(); - LLImageBase::deleteData(); + sGlobalFormattedMemory -= getDataSize(); + LLImageBase::deleteData(); } //---------------------------------------------------------------------------- @@ -2352,207 +2348,207 @@ void LLImageFormatted::deleteData() // virtual void LLImageFormatted::sanityCheck() { - LLImageBase::sanityCheck(); + LLImageBase::sanityCheck(); - if (mCodec >= IMG_CODEC_EOF) - { - LL_ERRS() << "Failed LLImageFormatted::sanityCheck " - << "decoding " << S32(mDecoding) - << "decoded " << S32(mDecoded) - << "codec " << S32(mCodec) - << LL_ENDL; - } + if (mCodec >= IMG_CODEC_EOF) + { + LL_ERRS() << "Failed LLImageFormatted::sanityCheck " + << "decoding " << S32(mDecoding) + << "decoded " << S32(mDecoded) + << "codec " << S32(mCodec) + << LL_ENDL; + } } //---------------------------------------------------------------------------- bool LLImageFormatted::copyData(U8 *data, S32 size) { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - if ( data && ((data != getData()) || (size != getDataSize())) ) - { - deleteData(); - allocateData(size); - memcpy(getData(), data, size); /* Flawfinder: ignore */ - } - return true; + if ( data && ((data != getData()) || (size != getDataSize())) ) + { + deleteData(); + allocateData(size); + memcpy(getData(), data, size); /* Flawfinder: ignore */ + } + return true; } // LLImageFormatted becomes the owner of data void LLImageFormatted::setData(U8 *data, S32 size) { - LLImageDataLock lock(this); + LLImageDataLock lock(this); - if (data && data != getData()) - { - deleteData(); - setDataAndSize(data, size); // Access private LLImageBase members + if (data && data != getData()) + { + deleteData(); + setDataAndSize(data, size); // Access private LLImageBase members - sGlobalFormattedMemory += getDataSize(); - } + sGlobalFormattedMemory += getDataSize(); + } } void LLImageFormatted::appendData(U8 *data, S32 size) { - if (data) - { - LLImageDataLock lock(this); - - if (!getData()) - { - setData(data, size); - } - else - { - S32 cursize = getDataSize(); - S32 newsize = cursize + size; - reallocateData(newsize); - memcpy(getData() + cursize, data, size); - ll_aligned_free_16(data); - } - } + if (data) + { + LLImageDataLock lock(this); + + if (!getData()) + { + setData(data, size); + } + else + { + S32 cursize = getDataSize(); + S32 newsize = cursize + size; + reallocateData(newsize); + memcpy(getData() + cursize, data, size); + ll_aligned_free_16(data); + } + } } //---------------------------------------------------------------------------- bool LLImageFormatted::load(const std::string &filename, int load_size) { - resetLastError(); - - S32 file_size = 0; - LLAPRFile infile ; - infile.open(filename, LL_APR_RB, NULL, &file_size); - apr_file_t* apr_file = infile.getFileHandle(); - if (!apr_file) - { - setLastError("Unable to open file for reading", filename); - return false; - } - if (file_size == 0) - { - setLastError("File is empty",filename); - return false; - } - - // Constrain the load size to acceptable values - if ((load_size == 0) || (load_size > file_size)) - { - load_size = file_size; - } - - LLImageDataLock lock(this); - - bool res; - U8 *data = allocateData(load_size); - if (data) - { - apr_size_t bytes_read = load_size; - apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read - if (s != APR_SUCCESS || (S32) bytes_read != load_size) - { - deleteData(); - setLastError("Unable to read file",filename); - res = false; - } - else - { - res = updateData(); - } - } - else - { - setLastError("Allocation failure", filename); - res = false; - } - - return res; + resetLastError(); + + S32 file_size = 0; + LLAPRFile infile ; + infile.open(filename, LL_APR_RB, NULL, &file_size); + apr_file_t* apr_file = infile.getFileHandle(); + if (!apr_file) + { + setLastError("Unable to open file for reading", filename); + return false; + } + if (file_size == 0) + { + setLastError("File is empty",filename); + return false; + } + + // Constrain the load size to acceptable values + if ((load_size == 0) || (load_size > file_size)) + { + load_size = file_size; + } + + LLImageDataLock lock(this); + + bool res; + U8 *data = allocateData(load_size); + if (data) + { + apr_size_t bytes_read = load_size; + apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read + if (s != APR_SUCCESS || (S32) bytes_read != load_size) + { + deleteData(); + setLastError("Unable to read file",filename); + res = false; + } + else + { + res = updateData(); + } + } + else + { + setLastError("Allocation failure", filename); + res = false; + } + + return res; } bool LLImageFormatted::save(const std::string &filename) { - resetLastError(); + resetLastError(); - LLAPRFile outfile ; - outfile.open(filename, LL_APR_WB); - if (!outfile.getFileHandle()) - { - setLastError("Unable to open file for writing", filename); - return false; - } + LLAPRFile outfile ; + outfile.open(filename, LL_APR_WB); + if (!outfile.getFileHandle()) + { + setLastError("Unable to open file for writing", filename); + return false; + } - LLImageDataSharedLock lock(this); + LLImageDataSharedLock lock(this); - S32 result = outfile.write(getData(), getDataSize()); - outfile.close() ; + S32 result = outfile.write(getData(), getDataSize()); + outfile.close() ; return (result != 0); } S8 LLImageFormatted::getCodec() const { - return mCodec; + return mCodec; } static void avg4_colors4(const U8* a, const U8* b, const U8* c, const U8* d, U8* dst) { - dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2); - dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2); - dst[2] = (U8)(((U32)(a[2]) + b[2] + c[2] + d[2])>>2); - dst[3] = (U8)(((U32)(a[3]) + b[3] + c[3] + d[3])>>2); + dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2); + dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2); + dst[2] = (U8)(((U32)(a[2]) + b[2] + c[2] + d[2])>>2); + dst[3] = (U8)(((U32)(a[3]) + b[3] + c[3] + d[3])>>2); } static void avg4_colors3(const U8* a, const U8* b, const U8* c, const U8* d, U8* dst) { - dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2); - dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2); - dst[2] = (U8)(((U32)(a[2]) + b[2] + c[2] + d[2])>>2); + dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2); + dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2); + dst[2] = (U8)(((U32)(a[2]) + b[2] + c[2] + d[2])>>2); } static void avg4_colors2(const U8* a, const U8* b, const U8* c, const U8* d, U8* dst) { - dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2); - dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2); + dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2); + dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2); } void LLImageBase::setDataAndSize(U8 *data, S32 size) -{ - ll_assert_aligned(data, 16); - mData = data; - mDataSize = size; -} +{ + ll_assert_aligned(data, 16); + mData = data; + mDataSize = size; +} //static void LLImageBase::generateMip(const U8* indata, U8* mipdata, S32 width, S32 height, S32 nchannels) { - llassert(width > 0 && height > 0); - U8* data = mipdata; - S32 in_width = width*2; - for (S32 h=0; h<height; h++) - { - for (S32 w=0; w<width; w++) - { - switch(nchannels) - { - case 4: - avg4_colors4(indata, indata+4, indata+4*in_width, indata+4*in_width+4, data); - break; - case 3: - avg4_colors3(indata, indata+3, indata+3*in_width, indata+3*in_width+3, data); - break; - case 2: - avg4_colors2(indata, indata+2, indata+2*in_width, indata+2*in_width+2, data); - break; - case 1: - *(U8*)data = (U8)(((U32)(indata[0]) + indata[1] + indata[in_width] + indata[in_width+1])>>2); - break; - default: - LL_ERRS() << "generateMmip called with bad num channels" << LL_ENDL; - } - indata += nchannels*2; - data += nchannels; - } - indata += nchannels*in_width; // skip odd lines - } + llassert(width > 0 && height > 0); + U8* data = mipdata; + S32 in_width = width*2; + for (S32 h=0; h<height; h++) + { + for (S32 w=0; w<width; w++) + { + switch(nchannels) + { + case 4: + avg4_colors4(indata, indata+4, indata+4*in_width, indata+4*in_width+4, data); + break; + case 3: + avg4_colors3(indata, indata+3, indata+3*in_width, indata+3*in_width+3, data); + break; + case 2: + avg4_colors2(indata, indata+2, indata+2*in_width, indata+2*in_width+2, data); + break; + case 1: + *(U8*)data = (U8)(((U32)(indata[0]) + indata[1] + indata[in_width] + indata[in_width+1])>>2); + break; + default: + LL_ERRS() << "generateMmip called with bad num channels" << LL_ENDL; + } + indata += nchannels*2; + data += nchannels; + } + indata += nchannels*in_width; // skip odd lines + } } @@ -2561,69 +2557,69 @@ void LLImageBase::generateMip(const U8* indata, U8* mipdata, S32 width, S32 heig //static F32 LLImageBase::calc_download_priority(F32 virtual_size, F32 visible_pixels, S32 bytes_sent) { - F32 w_priority; - - F32 bytes_weight = 1.f; - if (!bytes_sent) - { - bytes_weight = 20.f; - } - else if (bytes_sent < 1000) - { - bytes_weight = 1.f; - } - else if (bytes_sent < 2000) - { - bytes_weight = 1.f/1.5f; - } - else if (bytes_sent < 4000) - { - bytes_weight = 1.f/3.f; - } - else if (bytes_sent < 8000) - { - bytes_weight = 1.f/6.f; - } - else if (bytes_sent < 16000) - { - bytes_weight = 1.f/12.f; - } - else if (bytes_sent < 32000) - { - bytes_weight = 1.f/20.f; - } - else if (bytes_sent < 64000) - { - bytes_weight = 1.f/32.f; - } - else - { - bytes_weight = 1.f/64.f; - } - bytes_weight *= bytes_weight; - - - //LL_INFOS() << "VS: " << virtual_size << LL_ENDL; - F32 virtual_size_factor = virtual_size / (10.f*10.f); - - // The goal is for weighted priority to be <= 0 when we've reached a point where - // we've sent enough data. - //LL_INFOS() << "BytesSent: " << bytes_sent << LL_ENDL; - //LL_INFOS() << "BytesWeight: " << bytes_weight << LL_ENDL; - //LL_INFOS() << "PreLog: " << bytes_weight * virtual_size_factor << LL_ENDL; - w_priority = (F32)log10(bytes_weight * virtual_size_factor); - - //LL_INFOS() << "PreScale: " << w_priority << LL_ENDL; - - // We don't want to affect how MANY bytes we send based on the visible pixels, but the order - // in which they're sent. We post-multiply so we don't change the zero point. - if (w_priority > 0.f) - { - F32 pixel_weight = (F32)log10(visible_pixels + 1)*3.0f; - w_priority *= pixel_weight; - } - - return w_priority; + F32 w_priority; + + F32 bytes_weight = 1.f; + if (!bytes_sent) + { + bytes_weight = 20.f; + } + else if (bytes_sent < 1000) + { + bytes_weight = 1.f; + } + else if (bytes_sent < 2000) + { + bytes_weight = 1.f/1.5f; + } + else if (bytes_sent < 4000) + { + bytes_weight = 1.f/3.f; + } + else if (bytes_sent < 8000) + { + bytes_weight = 1.f/6.f; + } + else if (bytes_sent < 16000) + { + bytes_weight = 1.f/12.f; + } + else if (bytes_sent < 32000) + { + bytes_weight = 1.f/20.f; + } + else if (bytes_sent < 64000) + { + bytes_weight = 1.f/32.f; + } + else + { + bytes_weight = 1.f/64.f; + } + bytes_weight *= bytes_weight; + + + //LL_INFOS() << "VS: " << virtual_size << LL_ENDL; + F32 virtual_size_factor = virtual_size / (10.f*10.f); + + // The goal is for weighted priority to be <= 0 when we've reached a point where + // we've sent enough data. + //LL_INFOS() << "BytesSent: " << bytes_sent << LL_ENDL; + //LL_INFOS() << "BytesWeight: " << bytes_weight << LL_ENDL; + //LL_INFOS() << "PreLog: " << bytes_weight * virtual_size_factor << LL_ENDL; + w_priority = (F32)log10(bytes_weight * virtual_size_factor); + + //LL_INFOS() << "PreScale: " << w_priority << LL_ENDL; + + // We don't want to affect how MANY bytes we send based on the visible pixels, but the order + // in which they're sent. We post-multiply so we don't change the zero point. + if (w_priority > 0.f) + { + F32 pixel_weight = (F32)log10(visible_pixels + 1)*3.0f; + w_priority *= pixel_weight; + } + + return w_priority; } //============================================================================ diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index e3fbe68d7b..7f759f7679 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -1,25 +1,25 @@ -/** +/** * @file llimage.h * @brief Object for managing images and their textures. * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -35,21 +35,21 @@ const S32 MIN_IMAGE_MIP = 2; // 4x4, only used for expand/contract power of 2 const S32 MAX_IMAGE_MIP = 12; // 4096x4096 -// *TODO : Use MAX_IMAGE_MIP as max discard level and modify j2c management so that the number +// *TODO : Use MAX_IMAGE_MIP as max discard level and modify j2c management so that the number // of levels is read from the header's file, not inferred from its size. const S32 MAX_DISCARD_LEVEL = 5; // JPEG2000 size constraints // Those are declared here as they are germane to other image constraints used in the viewer // and declared right here. Some come from the JPEG2000 spec, some conventions specific to SL. -const S32 MAX_DECOMPOSITION_LEVELS = 32; // Number of decomposition levels cannot exceed 32 according to jpeg2000 spec -const S32 MIN_DECOMPOSITION_LEVELS = 5; // the SL viewer will *crash* trying to decode images with fewer than 5 decomposition levels (unless image is small that is) -const S32 MAX_PRECINCT_SIZE = 4096; // No reason to be bigger than MAX_IMAGE_SIZE -const S32 MIN_PRECINCT_SIZE = 4; // Can't be smaller than MIN_BLOCK_SIZE -const S32 MAX_BLOCK_SIZE = 64; // Max total block size is 4096, hence 64x64 when using square blocks -const S32 MIN_BLOCK_SIZE = 4; // Min block dim is 4 according to jpeg2000 spec -const S32 MIN_LAYER_SIZE = 2000; // Size of the first quality layer (after header). Must be > to FIRST_PACKET_SIZE!! -const S32 MAX_NB_LAYERS = 64; // Max number of layers we'll entertain in SL (practical limit) +const S32 MAX_DECOMPOSITION_LEVELS = 32; // Number of decomposition levels cannot exceed 32 according to jpeg2000 spec +const S32 MIN_DECOMPOSITION_LEVELS = 5; // the SL viewer will *crash* trying to decode images with fewer than 5 decomposition levels (unless image is small that is) +const S32 MAX_PRECINCT_SIZE = 4096; // No reason to be bigger than MAX_IMAGE_SIZE +const S32 MIN_PRECINCT_SIZE = 4; // Can't be smaller than MIN_BLOCK_SIZE +const S32 MAX_BLOCK_SIZE = 64; // Max total block size is 4096, hence 64x64 when using square blocks +const S32 MIN_BLOCK_SIZE = 4; // Min block dim is 4 according to jpeg2000 spec +const S32 MIN_LAYER_SIZE = 2000; // Size of the first quality layer (after header). Must be > to FIRST_PACKET_SIZE!! +const S32 MAX_NB_LAYERS = 64; // Max number of layers we'll entertain in SL (practical limit) const S32 MIN_IMAGE_SIZE = (1<<MIN_IMAGE_MIP); // 4, only used for expand/contract power of 2 const S32 MAX_IMAGE_SIZE = (1<<MAX_IMAGE_MIP); // 4096 @@ -75,15 +75,15 @@ class LLColor3; typedef enum e_image_codec { - IMG_CODEC_INVALID = 0, - IMG_CODEC_RGB = 1, - IMG_CODEC_J2C = 2, - IMG_CODEC_BMP = 3, - IMG_CODEC_TGA = 4, - IMG_CODEC_JPEG = 5, - IMG_CODEC_DXT = 6, - IMG_CODEC_PNG = 7, - IMG_CODEC_EOF = 8 + IMG_CODEC_INVALID = 0, + IMG_CODEC_RGB = 1, + IMG_CODEC_J2C = 2, + IMG_CODEC_BMP = 3, + IMG_CODEC_TGA = 4, + IMG_CODEC_JPEG = 5, + IMG_CODEC_DXT = 6, + IMG_CODEC_PNG = 7, + IMG_CODEC_EOF = 8 } EImageCodec; //============================================================================ @@ -93,87 +93,86 @@ typedef enum e_image_codec class LLImage { public: - static void initClass(bool use_new_byte_range = false, S32 minimal_reverse_byte_range_percent = 75); - static void cleanupClass(); - - static const std::string& getLastError(); - static void setLastError(const std::string& message); - - static bool useNewByteRange() { return sUseNewByteRange; } - static S32 getReverseByteRangePercent() { return sMinimalReverseByteRangePercent; } - + static void initClass(bool use_new_byte_range = false, S32 minimal_reverse_byte_range_percent = 75); + static void cleanupClass(); + + static const std::string& getLastThreadError(); + static void setLastError(const std::string& message); + + static bool useNewByteRange() { return sUseNewByteRange; } + static S32 getReverseByteRangePercent() { return sMinimalReverseByteRangePercent; } + protected: - static LLMutex* sMutex; - static std::string sLastErrorMessage; - static bool sUseNewByteRange; + static thread_local std::string sLastThreadErrorMessage; + static bool sUseNewByteRange; static S32 sMinimalReverseByteRangePercent; }; //============================================================================ // Image base class -class LLImageBase -: public LLThreadSafeRefCount +class LLImageBase +: public LLThreadSafeRefCount { protected: - virtual ~LLImageBase(); + virtual ~LLImageBase(); - virtual void deleteData(); - virtual U8* allocateData(S32 size = -1); - virtual U8* reallocateData(S32 size = -1); + virtual void deleteData(); + virtual U8* allocateData(S32 size = -1); + virtual U8* reallocateData(S32 size = -1); public: - LLImageBase(); + LLImageBase(); - enum - { - TYPE_NORMAL = 0, - TYPE_AVATAR_BAKE = 1, - }; + enum + { + TYPE_NORMAL = 0, + TYPE_AVATAR_BAKE = 1, + }; - virtual void dump(); - virtual void sanityCheck(); + virtual void dump(); + virtual void sanityCheck(); - U16 getWidth() const { return mWidth; } - U16 getHeight() const { return mHeight; } - S8 getComponents() const { return mComponents; } - S32 getDataSize() const { return mDataSize; } + U16 getWidth() const { return mWidth; } + U16 getHeight() const { return mHeight; } + S8 getComponents() const { return mComponents; } + S32 getDataSize() const { return mDataSize; } - const U8 *getData() const ; - U8 *getData() ; - bool isBufferInvalid() const; + const U8 *getData() const ; + U8 *getData() ; + bool isBufferInvalid() const; - void setSize(S32 width, S32 height, S32 ncomponents); - U8* allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 size = -1); // setSize() + allocateData() - void enableOverSize() {mAllowOverSize = true ;} - void disableOverSize() {mAllowOverSize = false; } + void setSize(S32 width, S32 height, S32 ncomponents); + U8* allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 size = -1); // setSize() + allocateData() + void enableOverSize() {mAllowOverSize = true ;} + void disableOverSize() {mAllowOverSize = false; } protected: - // special accessor to allow direct setting of mData and mDataSize by LLImageFormatted - void setDataAndSize(U8 *data, S32 size); - + // special accessor to allow direct setting of mData and mDataSize by LLImageFormatted + void setDataAndSize(U8 *data, S32 size); + public: - static void generateMip(const U8 *indata, U8* mipdata, int width, int height, S32 nchannels); - - // Function for calculating the download priority for textures - // <= 0 priority means that there's no need for more data. - static F32 calc_download_priority(F32 virtual_size, F32 visible_area, S32 bytes_sent); + static void generateMip(const U8 *indata, U8* mipdata, int width, int height, S32 nchannels); - static EImageCodec getCodecFromExtension(const std::string& exten); + // Function for calculating the download priority for textures + // <= 0 priority means that there's no need for more data. + static F32 calc_download_priority(F32 virtual_size, F32 visible_area, S32 bytes_sent); - //static LLTrace::MemStatHandle sMemStat; + static EImageCodec getCodecFromExtension(const std::string& exten); + + //static LLTrace::MemStatHandle sMemStat; private: - U8 *mData; - S32 mDataSize; + U8 *mData; + S32 mDataSize; - U16 mWidth; - U16 mHeight; + U16 mWidth; + U16 mHeight; - S8 mComponents; + S8 mComponents; - bool mBadBufferAllocation; - bool mAllowOverSize; + bool mBadBufferAllocation; + bool mAllowOverSize; private: mutable LLSharedMutex mDataMutex; @@ -197,36 +196,36 @@ using LLImageDataSharedLock = LLImageBase::DataLock<true>; class LLImageRaw : public LLImageBase { protected: - /*virtual*/ ~LLImageRaw(); - + /*virtual*/ ~LLImageRaw(); + public: - LLImageRaw(); - LLImageRaw(U16 width, U16 height, S8 components); + LLImageRaw(); + LLImageRaw(U16 width, U16 height, S8 components); LLImageRaw(const U8* data, U16 width, U16 height, S8 components); - LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy = false); - // Construct using createFromFile (used by tools) - //LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only = false); + LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy = false); + // Construct using createFromFile (used by tools) + //LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only = false); - /*virtual*/ void deleteData(); - /*virtual*/ U8* allocateData(S32 size = -1); - /*virtual*/ U8* reallocateData(S32 size); + /*virtual*/ void deleteData(); + /*virtual*/ U8* allocateData(S32 size = -1); + /*virtual*/ U8* reallocateData(S32 size); // use in conjunction with "no_copy" constructor to release data pointer before deleting - // so that deletion of this LLImageRaw will not free the memory at the "data" parameter + // so that deletion of this LLImageRaw will not free the memory at the "data" parameter // provided to "no_copy" constructor void releaseData(); - - bool resize(U16 width, U16 height, S8 components); - //U8 * getSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height) const; - bool setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height, - const U8 *data, U32 stride = 0, bool reverse_y = false); + bool resize(U16 width, U16 height, S8 components); + + //U8 * getSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height) const; + bool setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height, + const U8 *data, U32 stride = 0, bool reverse_y = false); - void clear(U8 r=0, U8 g=0, U8 b=0, U8 a=255); + void clear(U8 r=0, U8 g=0, U8 b=0, U8 a=255); + + void verticalFlip(); - void verticalFlip(); - // Returns true if the image is not fully opaque bool checkHasTransparentPixels(); // if the alpha channel is all 100% opaque, delete it @@ -238,48 +237,48 @@ public: static S32 biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE); static S32 expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE); static S32 contractDimToPowerOfTwo(S32 curr_dim, S32 min_dim = MIN_IMAGE_SIZE); - void expandToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, bool scale_image = true); - void contractToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, bool scale_image = true); - void biasedScaleToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE); - bool scale(S32 new_width, S32 new_height, bool scale_image = true); + void expandToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, bool scale_image = true); + void contractToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, bool scale_image = true); + void biasedScaleToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE); + bool scale(S32 new_width, S32 new_height, bool scale_image = true); LLPointer<LLImageRaw> scaled(S32 new_width, S32 new_height); - - // Fill the buffer with a constant color - void fill( const LLColor4U& color ); + + // Fill the buffer with a constant color + void fill( const LLColor4U& color ); // Multiply this raw image by the given color void tint( const LLColor3& color ); - // Copy operations - - //duplicate this raw image if refCount > 1. - LLPointer<LLImageRaw> duplicate(); + // Copy operations - // Src and dst can be any size. Src and dst can each have 3 or 4 components. - void copy( const LLImageRaw* src ); + //duplicate this raw image if refCount > 1. + LLPointer<LLImageRaw> duplicate(); - // Src and dst are same size. Src and dst have same number of components. - void copyUnscaled( const LLImageRaw* src ); - - // Src and dst are same size. Src has 4 components. Dst has 3 components. - void copyUnscaled4onto3( const LLImageRaw* src ); + // Src and dst can be any size. Src and dst can each have 3 or 4 components. + void copy( const LLImageRaw* src ); - // Src and dst are same size. Src has 3 components. Dst has 4 components. - void copyUnscaled3onto4( const LLImageRaw* src ); + // Src and dst are same size. Src and dst have same number of components. + void copyUnscaled( const LLImageRaw* src ); - // Src and dst are same size. Src has 1 component. Dst has 4 components. - // Alpha component is set to source alpha mask component. - // RGB components are set to fill color. - void copyUnscaledAlphaMask( const LLImageRaw* src, const LLColor4U& fill); + // Src and dst are same size. Src has 4 components. Dst has 3 components. + void copyUnscaled4onto3( const LLImageRaw* src ); - // Src and dst can be any size. Src and dst have same number of components. - void copyScaled( const LLImageRaw* src ); + // Src and dst are same size. Src has 3 components. Dst has 4 components. + void copyUnscaled3onto4( const LLImageRaw* src ); + // Src and dst are same size. Src has 1 component. Dst has 4 components. + // Alpha component is set to source alpha mask component. + // RGB components are set to fill color. + void copyUnscaledAlphaMask( const LLImageRaw* src, const LLColor4U& fill); - // Composite operations + // Src and dst can be any size. Src and dst have same number of components. + void copyScaled( const LLImageRaw* src ); - // Src and dst can be any size. Src and dst can each have 3 or 4 components. - void composite( const LLImageRaw* src ); + + // Composite operations + + // Src and dst can be any size. Src and dst can each have 3 or 4 components. + void composite( const LLImageRaw* src ); // Emissive operations used by minimap // Roughly emulates GLTF emissive texture, but is not GLTF-compliant @@ -288,33 +287,33 @@ public: void addEmissiveScaled(LLImageRaw* src); void addEmissiveUnscaled(LLImageRaw* src); protected: - // Src and dst can be any size. Src has 4 components. Dst has 3 components. - void compositeScaled4onto3( const LLImageRaw* src ); + // Src and dst can be any size. Src has 4 components. Dst has 3 components. + void compositeScaled4onto3( const LLImageRaw* src ); - // Src and dst are same size. Src has 4 components. Dst has 3 components. - void compositeUnscaled4onto3( const LLImageRaw* src ); + // Src and dst are same size. Src has 4 components. Dst has 3 components. + void compositeUnscaled4onto3( const LLImageRaw* src ); - // Src and dst can be any size. Src has 3 components. Dst has 4 components. - void copyScaled3onto4( const LLImageRaw* src ); + // Src and dst can be any size. Src has 3 components. Dst has 4 components. + void copyScaled3onto4( const LLImageRaw* src ); - // Src and dst can be any size. Src has 4 components. Dst has 3 components. - void copyScaled4onto3( const LLImageRaw* src ); + // Src and dst can be any size. Src has 4 components. Dst has 3 components. + void copyScaled4onto3( const LLImageRaw* src ); - // Create an image from a local file (generally used in tools) - //bool createFromFile(const std::string& filename, bool j2c_lowest_mip_only = false); + // Create an image from a local file (generally used in tools) + //bool createFromFile(const std::string& filename, bool j2c_lowest_mip_only = false); - void copyLineScaled( const U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step ); - void compositeRowScaled4onto3( const U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len ); + void copyLineScaled( const U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step ); + void compositeRowScaled4onto3( const U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len ); - static U8 fastFractionalMult(U8 a, U8 b); + static U8 fastFractionalMult(U8 a, U8 b); - void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ; + void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ; public: - static S32 sRawImageCount; + static S32 sRawImageCount; private: - static bool validateSrcAndDst(std::string func, const LLImageRaw* src, const LLImageRaw* dst); + static bool validateSrcAndDst(std::string func, const LLImageRaw* src, const LLImageRaw* dst); }; // Compressed representation of image. @@ -322,74 +321,74 @@ private: class LLImageFormatted : public LLImageBase { public: - static LLImageFormatted* createFromType(S8 codec); - static LLImageFormatted* createFromExtension(const std::string& instring); + static LLImageFormatted* createFromType(S8 codec); + static LLImageFormatted* createFromExtension(const std::string& instring); protected: - /*virtual*/ ~LLImageFormatted(); - + /*virtual*/ ~LLImageFormatted(); + public: - LLImageFormatted(S8 codec); - - // LLImageBase - /*virtual*/ void deleteData(); - /*virtual*/ U8* allocateData(S32 size = -1); - /*virtual*/ U8* reallocateData(S32 size); - - /*virtual*/ void dump(); - /*virtual*/ void sanityCheck(); - - // New methods - // subclasses must return a prefered file extension (lowercase without a leading dot) - virtual std::string getExtension() = 0; - // calcHeaderSize() returns the maximum size of header; - // 0 indicates we don't have a header and have to read the entire file - virtual S32 calcHeaderSize() { return 0; }; - // calcDataSize() returns how many bytes to read to load discard_level (including header) - virtual S32 calcDataSize(S32 discard_level); - // calcDiscardLevelBytes() returns the smallest valid discard level based on the number of input bytes - virtual S32 calcDiscardLevelBytes(S32 bytes); - // getRawDiscardLevel() by default returns mDiscardLevel, but may be overridden (LLImageJ2C) - virtual S8 getRawDiscardLevel() { return mDiscardLevel; } - - bool load(const std::string& filename, int load_size = 0); - bool save(const std::string& filename); - - virtual bool updateData() = 0; // pure virtual - void setData(U8 *data, S32 size); - void appendData(U8 *data, S32 size); - - // Loads first 4 channels. - virtual bool decode(LLImageRaw* raw_image, F32 decode_time) = 0; - // Subclasses that can handle more than 4 channels should override this function. - virtual bool decodeChannels(LLImageRaw* raw_image, F32 decode_time, S32 first_channel, S32 max_channel); - - virtual bool encode(const LLImageRaw* raw_image, F32 encode_time) = 0; - - S8 getCodec() const; - bool isDecoding() const { return mDecoding; } - bool isDecoded() const { return mDecoded; } - void setDiscardLevel(S8 discard_level) { mDiscardLevel = discard_level; } - S8 getDiscardLevel() const { return mDiscardLevel; } - S8 getLevels() const { return mLevels; } - void setLevels(S8 nlevels) { mLevels = nlevels; } - - // setLastError needs to be deferred for J2C images since it may be called from a DLL - virtual void resetLastError(); - virtual void setLastError(const std::string& message, const std::string& filename = std::string()); - + LLImageFormatted(S8 codec); + + // LLImageBase + /*virtual*/ void deleteData(); + /*virtual*/ U8* allocateData(S32 size = -1); + /*virtual*/ U8* reallocateData(S32 size); + + /*virtual*/ void dump(); + /*virtual*/ void sanityCheck(); + + // New methods + // subclasses must return a prefered file extension (lowercase without a leading dot) + virtual std::string getExtension() = 0; + // calcHeaderSize() returns the maximum size of header; + // 0 indicates we don't have a header and have to read the entire file + virtual S32 calcHeaderSize() { return 0; }; + // calcDataSize() returns how many bytes to read to load discard_level (including header) + virtual S32 calcDataSize(S32 discard_level); + // calcDiscardLevelBytes() returns the smallest valid discard level based on the number of input bytes + virtual S32 calcDiscardLevelBytes(S32 bytes); + // getRawDiscardLevel() by default returns mDiscardLevel, but may be overridden (LLImageJ2C) + virtual S8 getRawDiscardLevel() { return mDiscardLevel; } + + bool load(const std::string& filename, int load_size = 0); + bool save(const std::string& filename); + + virtual bool updateData() = 0; // pure virtual + void setData(U8 *data, S32 size); + void appendData(U8 *data, S32 size); + + // Loads first 4 channels. + virtual bool decode(LLImageRaw* raw_image, F32 decode_time) = 0; + // Subclasses that can handle more than 4 channels should override this function. + virtual bool decodeChannels(LLImageRaw* raw_image, F32 decode_time, S32 first_channel, S32 max_channel); + + virtual bool encode(const LLImageRaw* raw_image, F32 encode_time) = 0; + + S8 getCodec() const; + bool isDecoding() const { return mDecoding; } + bool isDecoded() const { return mDecoded; } + void setDiscardLevel(S8 discard_level) { mDiscardLevel = discard_level; } + S8 getDiscardLevel() const { return mDiscardLevel; } + S8 getLevels() const { return mLevels; } + void setLevels(S8 nlevels) { mLevels = nlevels; } + + // setLastError needs to be deferred for J2C images since it may be called from a DLL + virtual void resetLastError(); + virtual void setLastError(const std::string& message, const std::string& filename = std::string()); + protected: - bool copyData(U8 *data, S32 size); // calls updateData() - + bool copyData(U8 *data, S32 size); // calls updateData() + protected: - S8 mCodec; - S8 mDecoding; - S8 mDecoded; // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC - S8 mDiscardLevel; // Current resolution level worked on. 0 = full res, 1 = half res, 2 = quarter res, etc... - S8 mLevels; // Number of resolution levels in that image. Min is 1. 0 means unknown. + S8 mCodec; + S8 mDecoding; + S8 mDecoded; // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC + S8 mDiscardLevel; // Current resolution level worked on. 0 = full res, 1 = half res, 2 = quarter res, etc... + S8 mLevels; // Number of resolution levels in that image. Min is 1. 0 means unknown. public: - static S32 sGlobalFormattedMemory; + static S32 sGlobalFormattedMemory; }; #endif diff --git a/indra/llimage/llimagebmp.cpp b/indra/llimage/llimagebmp.cpp index d0881bb9ff..2a328675c2 100644 --- a/indra/llimage/llimagebmp.cpp +++ b/indra/llimage/llimagebmp.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llimagebmp.cpp * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -38,18 +38,18 @@ */ struct LLBMPHeader { - S32 mSize; - S32 mWidth; - S32 mHeight; - S16 mPlanes; - S16 mBitsPerPixel; - S16 mCompression; - S16 mAlignmentPadding; // pads out to next word boundary - S32 mImageSize; - S32 mHorzPelsPerMeter; - S32 mVertPelsPerMeter; - S32 mNumColors; - S32 mNumColorsImportant; + S32 mSize; + S32 mWidth; + S32 mHeight; + S16 mPlanes; + S16 mBitsPerPixel; + S16 mCompression; + S16 mAlignmentPadding; // pads out to next word boundary + S32 mImageSize; + S32 mHorzPelsPerMeter; + S32 mVertPelsPerMeter; + S32 mNumColors; + S32 mNumColorsImportant; }; /** @@ -57,624 +57,624 @@ struct LLBMPHeader */ struct Win95BmpHeaderExtension { - U32 mReadMask; - U32 mGreenMask; - U32 mBlueMask; - U32 mAlphaMask; - U32 mColorSpaceType; - U16 mRed[3]; // Red CIE endpoint - U16 mGreen[3]; // Green CIE endpoint - U16 mBlue[3]; // Blue CIE endpoint - U32 mGamma[3]; // Gamma scale for r g and b + U32 mReadMask; + U32 mGreenMask; + U32 mBlueMask; + U32 mAlphaMask; + U32 mColorSpaceType; + U16 mRed[3]; // Red CIE endpoint + U16 mGreen[3]; // Green CIE endpoint + U16 mBlue[3]; // Blue CIE endpoint + U32 mGamma[3]; // Gamma scale for r g and b }; /** * LLImageBMP */ -LLImageBMP::LLImageBMP() - : - LLImageFormatted(IMG_CODEC_BMP), - mColorPaletteColors( 0 ), - mColorPalette( NULL ), - mBitmapOffset( 0 ), - mBitsPerPixel( 0 ), - mOriginAtTop( false ) +LLImageBMP::LLImageBMP() + : + LLImageFormatted(IMG_CODEC_BMP), + mColorPaletteColors( 0 ), + mColorPalette( NULL ), + mBitmapOffset( 0 ), + mBitsPerPixel( 0 ), + mOriginAtTop( false ) { - mBitfieldMask[0] = 0; - mBitfieldMask[1] = 0; - mBitfieldMask[2] = 0; - mBitfieldMask[3] = 0; + mBitfieldMask[0] = 0; + mBitfieldMask[1] = 0; + mBitfieldMask[2] = 0; + mBitfieldMask[3] = 0; } LLImageBMP::~LLImageBMP() { - delete[] mColorPalette; + delete[] mColorPalette; } bool LLImageBMP::updateData() { - resetLastError(); - - LLImageDataLock lock(this); - - // Check to make sure that this instance has been initialized with data - U8* mdata = getData(); - if (!mdata || (0 == getDataSize())) - { - setLastError("Uninitialized instance of LLImageBMP"); - return false; - } - - // Read the bitmap headers in order to get all the useful info - // about this image - - //////////////////////////////////////////////////////////////////// - // Part 1: "File Header" - // 14 bytes consisting of - // 2 bytes: either BM or BA - // 4 bytes: file size in bytes - // 4 bytes: reserved (always 0) - // 4 bytes: bitmap offset (starting position of image data in bytes) - const S32 FILE_HEADER_SIZE = 14; - if ((mdata[0] != 'B') || (mdata[1] != 'M')) + resetLastError(); + + LLImageDataLock lock(this); + + // Check to make sure that this instance has been initialized with data + U8* mdata = getData(); + if (!mdata || (0 == getDataSize())) + { + setLastError("Uninitialized instance of LLImageBMP"); + return false; + } + + // Read the bitmap headers in order to get all the useful info + // about this image + + //////////////////////////////////////////////////////////////////// + // Part 1: "File Header" + // 14 bytes consisting of + // 2 bytes: either BM or BA + // 4 bytes: file size in bytes + // 4 bytes: reserved (always 0) + // 4 bytes: bitmap offset (starting position of image data in bytes) + const S32 FILE_HEADER_SIZE = 14; + if ((mdata[0] != 'B') || (mdata[1] != 'M')) + { + if ((mdata[0] != 'B') || (mdata[1] != 'A')) + { + setLastError("OS/2 bitmap array BMP files are not supported"); + return false; + } + else + { + setLastError("Does not appear to be a bitmap file"); + return false; + } + } + + mBitmapOffset = mdata[13]; + mBitmapOffset <<= 8; mBitmapOffset += mdata[12]; + mBitmapOffset <<= 8; mBitmapOffset += mdata[11]; + mBitmapOffset <<= 8; mBitmapOffset += mdata[10]; + + + //////////////////////////////////////////////////////////////////// + // Part 2: "Bitmap Header" + const S32 BITMAP_HEADER_SIZE = 40; + LLBMPHeader header; + llassert( sizeof( header ) == BITMAP_HEADER_SIZE ); + + memcpy( /* Flawfinder: ignore */ + (void*)&header, + mdata + FILE_HEADER_SIZE, + BITMAP_HEADER_SIZE); + + // convert BMP header from little endian (no-op on little endian builds) + llendianswizzleone(header.mSize); + llendianswizzleone(header.mWidth); + llendianswizzleone(header.mHeight); + llendianswizzleone(header.mPlanes); + llendianswizzleone(header.mBitsPerPixel); + llendianswizzleone(header.mCompression); + llendianswizzleone(header.mAlignmentPadding); + llendianswizzleone(header.mImageSize); + llendianswizzleone(header.mHorzPelsPerMeter); + llendianswizzleone(header.mVertPelsPerMeter); + llendianswizzleone(header.mNumColors); + llendianswizzleone(header.mNumColorsImportant); + + bool windows_nt_version = false; + bool windows_95_version = false; + if( 12 == header.mSize ) + { + setLastError("Windows 2.x and OS/2 1.x BMP files are not supported"); + return false; + } + else + if( 40 == header.mSize ) + { + if( 3 == header.mCompression ) + { + // Windows NT + windows_nt_version = true; + } + else + { + // Windows 3.x + } + } + else + if( 12 <= header.mSize && header.mSize <= 64 ) + { + setLastError("OS/2 2.x BMP files are not supported"); + return false; + } + else + if( 108 == header.mSize ) + { + // BITMAPV4HEADER + windows_95_version = true; + } + else + if( 108 < header.mSize ) + { + // BITMAPV5HEADER or greater + // Should work as long at Microsoft maintained backwards compatibility (which they did in V4 and V5) + windows_95_version = true; + } + + S32 width = header.mWidth; + S32 height = header.mHeight; + if (height < 0) + { + mOriginAtTop = true; + height = -height; + } + else + { + mOriginAtTop = false; + } + + mBitsPerPixel = header.mBitsPerPixel; + S32 components; + switch( mBitsPerPixel ) + { + case 8: + components = 1; + break; + case 24: + case 32: + components = 3; + break; + case 1: + case 4: + case 16: // Started work on 16, but doesn't work yet + // These are legal, but we don't support them yet. + setLastError("Unsupported bit depth"); + return false; + default: + setLastError("Unrecognized bit depth"); + return false; + } + + setSize(width, height, components); + + switch( header.mCompression ) { - if ((mdata[0] != 'B') || (mdata[1] != 'A')) - { - setLastError("OS/2 bitmap array BMP files are not supported"); - return false; - } - else - { - setLastError("Does not appear to be a bitmap file"); - return false; - } - } - - mBitmapOffset = mdata[13]; - mBitmapOffset <<= 8; mBitmapOffset += mdata[12]; - mBitmapOffset <<= 8; mBitmapOffset += mdata[11]; - mBitmapOffset <<= 8; mBitmapOffset += mdata[10]; - - - //////////////////////////////////////////////////////////////////// - // Part 2: "Bitmap Header" - const S32 BITMAP_HEADER_SIZE = 40; - LLBMPHeader header; - llassert( sizeof( header ) == BITMAP_HEADER_SIZE ); - - memcpy( /* Flawfinder: ignore */ - (void*)&header, - mdata + FILE_HEADER_SIZE, - BITMAP_HEADER_SIZE); - - // convert BMP header from little endian (no-op on little endian builds) - llendianswizzleone(header.mSize); - llendianswizzleone(header.mWidth); - llendianswizzleone(header.mHeight); - llendianswizzleone(header.mPlanes); - llendianswizzleone(header.mBitsPerPixel); - llendianswizzleone(header.mCompression); - llendianswizzleone(header.mAlignmentPadding); - llendianswizzleone(header.mImageSize); - llendianswizzleone(header.mHorzPelsPerMeter); - llendianswizzleone(header.mVertPelsPerMeter); - llendianswizzleone(header.mNumColors); - llendianswizzleone(header.mNumColorsImportant); - - bool windows_nt_version = false; - bool windows_95_version = false; - if( 12 == header.mSize ) - { - setLastError("Windows 2.x and OS/2 1.x BMP files are not supported"); - return false; - } - else - if( 40 == header.mSize ) - { - if( 3 == header.mCompression ) - { - // Windows NT - windows_nt_version = true; - } - else - { - // Windows 3.x - } - } - else - if( 12 <= header.mSize && header.mSize <= 64 ) - { - setLastError("OS/2 2.x BMP files are not supported"); - return false; - } - else - if( 108 == header.mSize ) - { - // BITMAPV4HEADER - windows_95_version = true; - } - else - if( 108 < header.mSize ) - { - // BITMAPV5HEADER or greater - // Should work as long at Microsoft maintained backwards compatibility (which they did in V4 and V5) - windows_95_version = true; - } - - S32 width = header.mWidth; - S32 height = header.mHeight; - if (height < 0) - { - mOriginAtTop = true; - height = -height; - } - else - { - mOriginAtTop = false; - } - - mBitsPerPixel = header.mBitsPerPixel; - S32 components; - switch( mBitsPerPixel ) - { - case 8: - components = 1; - break; - case 24: - case 32: - components = 3; - break; - case 1: - case 4: - case 16: // Started work on 16, but doesn't work yet - // These are legal, but we don't support them yet. - setLastError("Unsupported bit depth"); - return false; - default: - setLastError("Unrecognized bit depth"); - return false; - } - - setSize(width, height, components); - - switch( header.mCompression ) - { - case 0: - // Uncompressed - break; - - case 1: - setLastError("8 bit RLE compression not supported."); - return false; - - case 2: - setLastError("4 bit RLE compression not supported."); - return false; - - case 3: - // Windows NT or Windows 95 - break; - - default: - setLastError("Unsupported compression format."); - return false; - } - - //////////////////////////////////////////////////////////////////// - // Part 3: Bitfield Masks and other color data - S32 extension_size = 0; - if( windows_nt_version ) - { - if( (16 != header.mBitsPerPixel) && (32 != header.mBitsPerPixel) ) - { - setLastError("Bitfield encoding requires 16 or 32 bits per pixel."); - return false; - } - - if( 0 != header.mNumColors ) - { - setLastError("Bitfield encoding is not compatible with a color table."); - return false; - } - - - extension_size = 4 * 3; - memcpy( mBitfieldMask, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, extension_size); /* Flawfinder: ignore */ - } - else - if( windows_95_version ) - { - Win95BmpHeaderExtension win_95_extension; - extension_size = sizeof( win_95_extension ); - - llassert( sizeof( win_95_extension ) + BITMAP_HEADER_SIZE == 108 ); - memcpy( &win_95_extension, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, sizeof( win_95_extension ) ); /* Flawfinder: ignore */ - - if( 3 == header.mCompression ) - { - memcpy( mBitfieldMask, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, 4 * 4); /* Flawfinder: ignore */ - } - - // Color correction ignored for now - } - - - //////////////////////////////////////////////////////////////////// - // Part 4: Color Palette (optional) - // Note: There's no color palette if there are 16 or more bits per pixel - S32 color_palette_size = 0; - mColorPaletteColors = 0; - if( header.mBitsPerPixel < 16 ) - { - if( 0 == header.mNumColors ) - { - mColorPaletteColors = (1 << header.mBitsPerPixel); - } - else - { - mColorPaletteColors = header.mNumColors; - } - } - color_palette_size = mColorPaletteColors * 4; - - if( 0 != mColorPaletteColors ) - { - mColorPalette = new(std::nothrow) U8[color_palette_size]; - if (!mColorPalette) - { + case 0: + // Uncompressed + break; + + case 1: + setLastError("8 bit RLE compression not supported."); + return false; + + case 2: + setLastError("4 bit RLE compression not supported."); + return false; + + case 3: + // Windows NT or Windows 95 + break; + + default: + setLastError("Unsupported compression format."); + return false; + } + + //////////////////////////////////////////////////////////////////// + // Part 3: Bitfield Masks and other color data + S32 extension_size = 0; + if( windows_nt_version ) + { + if( (16 != header.mBitsPerPixel) && (32 != header.mBitsPerPixel) ) + { + setLastError("Bitfield encoding requires 16 or 32 bits per pixel."); + return false; + } + + if( 0 != header.mNumColors ) + { + setLastError("Bitfield encoding is not compatible with a color table."); + return false; + } + + + extension_size = 4 * 3; + memcpy( mBitfieldMask, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, extension_size); /* Flawfinder: ignore */ + } + else + if( windows_95_version ) + { + Win95BmpHeaderExtension win_95_extension; + extension_size = sizeof( win_95_extension ); + + llassert( sizeof( win_95_extension ) + BITMAP_HEADER_SIZE == 108 ); + memcpy( &win_95_extension, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, sizeof( win_95_extension ) ); /* Flawfinder: ignore */ + + if( 3 == header.mCompression ) + { + memcpy( mBitfieldMask, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, 4 * 4); /* Flawfinder: ignore */ + } + + // Color correction ignored for now + } + + + //////////////////////////////////////////////////////////////////// + // Part 4: Color Palette (optional) + // Note: There's no color palette if there are 16 or more bits per pixel + S32 color_palette_size = 0; + mColorPaletteColors = 0; + if( header.mBitsPerPixel < 16 ) + { + if( 0 == header.mNumColors ) + { + mColorPaletteColors = (1 << header.mBitsPerPixel); + } + else + { + mColorPaletteColors = header.mNumColors; + } + } + color_palette_size = mColorPaletteColors * 4; + + if( 0 != mColorPaletteColors ) + { + mColorPalette = new(std::nothrow) U8[color_palette_size]; + if (!mColorPalette) + { LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS() << "Out of memory in LLImageBMP::updateData()" << LL_ENDL; - return false; - } - memcpy( mColorPalette, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE + extension_size, color_palette_size ); /* Flawfinder: ignore */ - } + LL_ERRS() << "Out of memory in LLImageBMP::updateData()" << LL_ENDL; + return false; + } + memcpy( mColorPalette, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE + extension_size, color_palette_size ); /* Flawfinder: ignore */ + } - return true; + return true; } bool LLImageBMP::decode(LLImageRaw* raw_image, F32 decode_time) { - llassert_always(raw_image); - - resetLastError(); - - LLImageDataLock lockIn(this); - LLImageDataLock lockOut(raw_image); - - // Check to make sure that this instance has been initialized with data - const U8* mdata = getData(); - if (!mdata || (0 == getDataSize())) - { - setLastError("llimagebmp trying to decode an image with no data!"); - return false; - } - - if (!raw_image->resize(getWidth(), getHeight(), 3)) - { - setLastError("llimagebmp failed to resize image!"); - return false; - } - - const U8* src = mdata + mBitmapOffset; - U8* dst = raw_image->getData(); - - bool success = false; - - switch( mBitsPerPixel ) - { - case 8: - if( mColorPaletteColors >= 256 ) - { - success = decodeColorTable8( dst, src ); - } - break; - - case 16: - success = decodeColorMask16( dst, src ); - break; - - case 24: - success = decodeTruecolor24( dst, src ); - break; - - case 32: - success = decodeColorMask32( dst, src ); - break; - } - - if( success && mOriginAtTop ) - { - raw_image->verticalFlip(); - } - - return success; + llassert_always(raw_image); + + resetLastError(); + + LLImageDataLock lockIn(this); + LLImageDataLock lockOut(raw_image); + + // Check to make sure that this instance has been initialized with data + const U8* mdata = getData(); + if (!mdata || (0 == getDataSize())) + { + setLastError("llimagebmp trying to decode an image with no data!"); + return false; + } + + if (!raw_image->resize(getWidth(), getHeight(), 3)) + { + setLastError("llimagebmp failed to resize image!"); + return false; + } + + const U8* src = mdata + mBitmapOffset; + U8* dst = raw_image->getData(); + + bool success = false; + + switch( mBitsPerPixel ) + { + case 8: + if( mColorPaletteColors >= 256 ) + { + success = decodeColorTable8( dst, src ); + } + break; + + case 16: + success = decodeColorMask16( dst, src ); + break; + + case 24: + success = decodeTruecolor24( dst, src ); + break; + + case 32: + success = decodeColorMask32( dst, src ); + break; + } + + if( success && mOriginAtTop ) + { + raw_image->verticalFlip(); + } + + return success; } U32 LLImageBMP::countTrailingZeros( U32 m ) { - U32 shift_count = 0; - while( !(m & 1) ) - { - shift_count++; - m >>= 1; - } - return shift_count; + U32 shift_count = 0; + while( !(m & 1) ) + { + shift_count++; + m >>= 1; + } + return shift_count; } bool LLImageBMP::decodeColorMask16( U8* dst, const U8* src ) { - llassert( 16 == mBitsPerPixel ); - - if( !mBitfieldMask[0] && !mBitfieldMask[1] && !mBitfieldMask[2] ) - { - // Use default values - mBitfieldMask[0] = 0x00007C00; - mBitfieldMask[1] = 0x000003E0; - mBitfieldMask[2] = 0x0000001F; - } - - S32 src_row_span = getWidth() * 2; - S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4 - - U32 r_shift = countTrailingZeros( mBitfieldMask[2] ); - U32 g_shift = countTrailingZeros( mBitfieldMask[1] ); - U32 b_shift = countTrailingZeros( mBitfieldMask[0] ); - - for( S32 row = 0; row < getHeight(); row++ ) - { - for( S32 col = 0; col < getWidth(); col++ ) - { - U32 value = *((U16*)src); - dst[0] = U8((value & mBitfieldMask[2]) >> r_shift); // Red - dst[1] = U8((value & mBitfieldMask[1]) >> g_shift); // Green - dst[2] = U8((value & mBitfieldMask[0]) >> b_shift); // Blue - src += 2; - dst += 3; - } - src += alignment_bytes; - } - - return true; + llassert( 16 == mBitsPerPixel ); + + if( !mBitfieldMask[0] && !mBitfieldMask[1] && !mBitfieldMask[2] ) + { + // Use default values + mBitfieldMask[0] = 0x00007C00; + mBitfieldMask[1] = 0x000003E0; + mBitfieldMask[2] = 0x0000001F; + } + + S32 src_row_span = getWidth() * 2; + S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4 + + U32 r_shift = countTrailingZeros( mBitfieldMask[2] ); + U32 g_shift = countTrailingZeros( mBitfieldMask[1] ); + U32 b_shift = countTrailingZeros( mBitfieldMask[0] ); + + for( S32 row = 0; row < getHeight(); row++ ) + { + for( S32 col = 0; col < getWidth(); col++ ) + { + U32 value = *((U16*)src); + dst[0] = U8((value & mBitfieldMask[2]) >> r_shift); // Red + dst[1] = U8((value & mBitfieldMask[1]) >> g_shift); // Green + dst[2] = U8((value & mBitfieldMask[0]) >> b_shift); // Blue + src += 2; + dst += 3; + } + src += alignment_bytes; + } + + return true; } bool LLImageBMP::decodeColorMask32( U8* dst, const U8* src ) { - // Note: alpha is not supported - - llassert( 32 == mBitsPerPixel ); - - if( !mBitfieldMask[0] && !mBitfieldMask[1] && !mBitfieldMask[2] ) - { - // Use default values - mBitfieldMask[0] = 0x00FF0000; - mBitfieldMask[1] = 0x0000FF00; - mBitfieldMask[2] = 0x000000FF; - } - - if (getWidth() * getHeight() * 4 > getDataSize() - mBitmapOffset) - { //here we have situation when data size in src less than actually needed - return false; - } - - S32 src_row_span = getWidth() * 4; - S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4 - - U32 r_shift = countTrailingZeros( mBitfieldMask[0] ); - U32 g_shift = countTrailingZeros( mBitfieldMask[1] ); - U32 b_shift = countTrailingZeros( mBitfieldMask[2] ); - - for( S32 row = 0; row < getHeight(); row++ ) - { - for( S32 col = 0; col < getWidth(); col++ ) - { - U32 value = *((U32*)src); - dst[0] = U8((value & mBitfieldMask[0]) >> r_shift); // Red - dst[1] = U8((value & mBitfieldMask[1]) >> g_shift); // Green - dst[2] = U8((value & mBitfieldMask[2]) >> b_shift); // Blue - src += 4; - dst += 3; - } - src += alignment_bytes; - } - - return true; + // Note: alpha is not supported + + llassert( 32 == mBitsPerPixel ); + + if( !mBitfieldMask[0] && !mBitfieldMask[1] && !mBitfieldMask[2] ) + { + // Use default values + mBitfieldMask[0] = 0x00FF0000; + mBitfieldMask[1] = 0x0000FF00; + mBitfieldMask[2] = 0x000000FF; + } + + if (getWidth() * getHeight() * 4 > getDataSize() - mBitmapOffset) + { //here we have situation when data size in src less than actually needed + return false; + } + + S32 src_row_span = getWidth() * 4; + S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4 + + U32 r_shift = countTrailingZeros( mBitfieldMask[0] ); + U32 g_shift = countTrailingZeros( mBitfieldMask[1] ); + U32 b_shift = countTrailingZeros( mBitfieldMask[2] ); + + for( S32 row = 0; row < getHeight(); row++ ) + { + for( S32 col = 0; col < getWidth(); col++ ) + { + U32 value = *((U32*)src); + dst[0] = U8((value & mBitfieldMask[0]) >> r_shift); // Red + dst[1] = U8((value & mBitfieldMask[1]) >> g_shift); // Green + dst[2] = U8((value & mBitfieldMask[2]) >> b_shift); // Blue + src += 4; + dst += 3; + } + src += alignment_bytes; + } + + return true; } bool LLImageBMP::decodeColorTable8( U8* dst, const U8* src ) { - llassert( (8 == mBitsPerPixel) && (mColorPaletteColors >= 256) ); - - S32 src_row_span = getWidth() * 1; - S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4 - - if ((getWidth() * getHeight()) + getHeight() * alignment_bytes > getDataSize() - mBitmapOffset) - { //here we have situation when data size in src less than actually needed - return false; - } - - for( S32 row = 0; row < getHeight(); row++ ) - { - for( S32 col = 0; col < getWidth(); col++ ) - { - S32 index = 4 * src[0]; - dst[0] = mColorPalette[index + 2]; // Red - dst[1] = mColorPalette[index + 1]; // Green - dst[2] = mColorPalette[index + 0]; // Blue - src++; - dst += 3; - } - src += alignment_bytes; - } - - return true; + llassert( (8 == mBitsPerPixel) && (mColorPaletteColors >= 256) ); + + S32 src_row_span = getWidth() * 1; + S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4 + + if ((getWidth() * getHeight()) + getHeight() * alignment_bytes > getDataSize() - mBitmapOffset) + { //here we have situation when data size in src less than actually needed + return false; + } + + for( S32 row = 0; row < getHeight(); row++ ) + { + for( S32 col = 0; col < getWidth(); col++ ) + { + S32 index = 4 * src[0]; + dst[0] = mColorPalette[index + 2]; // Red + dst[1] = mColorPalette[index + 1]; // Green + dst[2] = mColorPalette[index + 0]; // Blue + src++; + dst += 3; + } + src += alignment_bytes; + } + + return true; } bool LLImageBMP::decodeTruecolor24( U8* dst, const U8* src ) { - llassert( 24 == mBitsPerPixel ); - llassert( 3 == getComponents() ); - S32 src_row_span = getWidth() * 3; - S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4 - - if ((getWidth() * getHeight() * 3) + getHeight() * alignment_bytes > getDataSize() - mBitmapOffset) - { //here we have situation when data size in src less than actually needed - return false; - } - - for( S32 row = 0; row < getHeight(); row++ ) - { - for( S32 col = 0; col < getWidth(); col++ ) - { - dst[0] = src[2]; // Red - dst[1] = src[1]; // Green - dst[2] = src[0]; // Blue - src += 3; - dst += 3; - } - src += alignment_bytes; - } - - return true; + llassert( 24 == mBitsPerPixel ); + llassert( 3 == getComponents() ); + S32 src_row_span = getWidth() * 3; + S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4 + + if ((getWidth() * getHeight() * 3) + getHeight() * alignment_bytes > getDataSize() - mBitmapOffset) + { //here we have situation when data size in src less than actually needed + return false; + } + + for( S32 row = 0; row < getHeight(); row++ ) + { + for( S32 col = 0; col < getWidth(); col++ ) + { + dst[0] = src[2]; // Red + dst[1] = src[1]; // Green + dst[2] = src[0]; // Blue + src += 3; + dst += 3; + } + src += alignment_bytes; + } + + return true; } bool LLImageBMP::encode(const LLImageRaw* raw_image, F32 encode_time) { - llassert_always(raw_image); - - resetLastError(); - - LLImageDataSharedLock lockIn(raw_image); - LLImageDataLock lockOut(this); - - S32 src_components = raw_image->getComponents(); - S32 dst_components = ( src_components < 3 ) ? 1 : 3; - - if( (2 == src_components) || (4 == src_components) ) - { - LL_INFOS() << "Dropping alpha information during BMP encoding" << LL_ENDL; - } - - setSize(raw_image->getWidth(), raw_image->getHeight(), dst_components); - - U8 magic[14]; - LLBMPHeader header; - int header_bytes = 14+sizeof(header); - llassert(header_bytes == 54); - if (getComponents() == 1) - { - header_bytes += 1024; // Need colour LUT. - } - int line_bytes = getComponents() * getWidth(); - int alignment_bytes = (3 * line_bytes) % 4; - line_bytes += alignment_bytes; - int file_bytes = line_bytes*getHeight() + header_bytes; - - // Allocate the new buffer for the data. - if(!allocateData(file_bytes)) //memory allocation failed - { - return false ; - } - - magic[0] = 'B'; magic[1] = 'M'; - magic[2] = (U8) file_bytes; - magic[3] = (U8)(file_bytes>>8); - magic[4] = (U8)(file_bytes>>16); - magic[5] = (U8)(file_bytes>>24); - magic[6] = magic[7] = magic[8] = magic[9] = 0; - magic[10] = (U8) header_bytes; - magic[11] = (U8)(header_bytes>>8); - magic[12] = (U8)(header_bytes>>16); - magic[13] = (U8)(header_bytes>>24); - header.mSize = 40; - header.mWidth = getWidth(); - header.mHeight = getHeight(); - header.mPlanes = 1; - header.mBitsPerPixel = (getComponents()==1)?8:24; - header.mCompression = 0; - header.mAlignmentPadding = 0; - header.mImageSize = 0; + llassert_always(raw_image); + + resetLastError(); + + LLImageDataSharedLock lockIn(raw_image); + LLImageDataLock lockOut(this); + + S32 src_components = raw_image->getComponents(); + S32 dst_components = ( src_components < 3 ) ? 1 : 3; + + if( (2 == src_components) || (4 == src_components) ) + { + LL_INFOS() << "Dropping alpha information during BMP encoding" << LL_ENDL; + } + + setSize(raw_image->getWidth(), raw_image->getHeight(), dst_components); + + U8 magic[14]; + LLBMPHeader header; + int header_bytes = 14+sizeof(header); + llassert(header_bytes == 54); + if (getComponents() == 1) + { + header_bytes += 1024; // Need colour LUT. + } + int line_bytes = getComponents() * getWidth(); + int alignment_bytes = (3 * line_bytes) % 4; + line_bytes += alignment_bytes; + int file_bytes = line_bytes*getHeight() + header_bytes; + + // Allocate the new buffer for the data. + if(!allocateData(file_bytes)) //memory allocation failed + { + return false ; + } + + magic[0] = 'B'; magic[1] = 'M'; + magic[2] = (U8) file_bytes; + magic[3] = (U8)(file_bytes>>8); + magic[4] = (U8)(file_bytes>>16); + magic[5] = (U8)(file_bytes>>24); + magic[6] = magic[7] = magic[8] = magic[9] = 0; + magic[10] = (U8) header_bytes; + magic[11] = (U8)(header_bytes>>8); + magic[12] = (U8)(header_bytes>>16); + magic[13] = (U8)(header_bytes>>24); + header.mSize = 40; + header.mWidth = getWidth(); + header.mHeight = getHeight(); + header.mPlanes = 1; + header.mBitsPerPixel = (getComponents()==1)?8:24; + header.mCompression = 0; + header.mAlignmentPadding = 0; + header.mImageSize = 0; #if LL_DARWIN - header.mHorzPelsPerMeter = header.mVertPelsPerMeter = 2834; // 72dpi + header.mHorzPelsPerMeter = header.mVertPelsPerMeter = 2834; // 72dpi #else - header.mHorzPelsPerMeter = header.mVertPelsPerMeter = 0; + header.mHorzPelsPerMeter = header.mVertPelsPerMeter = 0; #endif - header.mNumColors = header.mNumColorsImportant = 0; - - // convert BMP header to little endian (no-op on little endian builds) - llendianswizzleone(header.mSize); - llendianswizzleone(header.mWidth); - llendianswizzleone(header.mHeight); - llendianswizzleone(header.mPlanes); - llendianswizzleone(header.mBitsPerPixel); - llendianswizzleone(header.mCompression); - llendianswizzleone(header.mAlignmentPadding); - llendianswizzleone(header.mImageSize); - llendianswizzleone(header.mHorzPelsPerMeter); - llendianswizzleone(header.mVertPelsPerMeter); - llendianswizzleone(header.mNumColors); - llendianswizzleone(header.mNumColorsImportant); - - U8* mdata = getData(); - - // Output magic, then header, then the palette table, then the data. - U32 cur_pos = 0; - memcpy(mdata, magic, 14); - cur_pos += 14; - memcpy(mdata+cur_pos, &header, 40); /* Flawfinder: ignore */ - cur_pos += 40; - if (getComponents() == 1) - { - S32 n; - for (n=0; n < 256; n++) - { - mdata[cur_pos++] = (U8)n; - mdata[cur_pos++] = (U8)n; - mdata[cur_pos++] = (U8)n; - mdata[cur_pos++] = 0; - } - } - - // Need to iterate through, because we need to flip the RGB. - const U8* src = raw_image->getData(); - U8* dst = mdata + cur_pos; - - for( S32 row = 0; row < getHeight(); row++ ) - { - for( S32 col = 0; col < getWidth(); col++ ) - { - switch( src_components ) - { - case 1: - *dst++ = *src++; - break; - case 2: - { - U32 lum = src[0]; - U32 alpha = src[1]; - *dst++ = (U8)(lum * alpha / 255); - src += 2; - break; - } - case 3: - case 4: - dst[0] = src[2]; - dst[1] = src[1]; - dst[2] = src[0]; - src += src_components; - dst += 3; - break; - } - - } - for( S32 i = 0; i < alignment_bytes; i++ ) - { - *dst++ = 0; - } - } - - return true; + header.mNumColors = header.mNumColorsImportant = 0; + + // convert BMP header to little endian (no-op on little endian builds) + llendianswizzleone(header.mSize); + llendianswizzleone(header.mWidth); + llendianswizzleone(header.mHeight); + llendianswizzleone(header.mPlanes); + llendianswizzleone(header.mBitsPerPixel); + llendianswizzleone(header.mCompression); + llendianswizzleone(header.mAlignmentPadding); + llendianswizzleone(header.mImageSize); + llendianswizzleone(header.mHorzPelsPerMeter); + llendianswizzleone(header.mVertPelsPerMeter); + llendianswizzleone(header.mNumColors); + llendianswizzleone(header.mNumColorsImportant); + + U8* mdata = getData(); + + // Output magic, then header, then the palette table, then the data. + U32 cur_pos = 0; + memcpy(mdata, magic, 14); + cur_pos += 14; + memcpy(mdata+cur_pos, &header, 40); /* Flawfinder: ignore */ + cur_pos += 40; + if (getComponents() == 1) + { + S32 n; + for (n=0; n < 256; n++) + { + mdata[cur_pos++] = (U8)n; + mdata[cur_pos++] = (U8)n; + mdata[cur_pos++] = (U8)n; + mdata[cur_pos++] = 0; + } + } + + // Need to iterate through, because we need to flip the RGB. + const U8* src = raw_image->getData(); + U8* dst = mdata + cur_pos; + + for( S32 row = 0; row < getHeight(); row++ ) + { + for( S32 col = 0; col < getWidth(); col++ ) + { + switch( src_components ) + { + case 1: + *dst++ = *src++; + break; + case 2: + { + U32 lum = src[0]; + U32 alpha = src[1]; + *dst++ = (U8)(lum * alpha / 255); + src += 2; + break; + } + case 3: + case 4: + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + src += src_components; + dst += 3; + break; + } + + } + for( S32 i = 0; i < alignment_bytes; i++ ) + { + *dst++ = 0; + } + } + + return true; } diff --git a/indra/llimage/llimagebmp.h b/indra/llimage/llimagebmp.h index 295f96e541..237c3e8d53 100644 --- a/indra/llimage/llimagebmp.h +++ b/indra/llimage/llimagebmp.h @@ -1,25 +1,25 @@ -/** +/** * @file llimagebmp.h * @brief Image implementation for BMP. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,31 +34,31 @@ class LLImageBMP : public LLImageFormatted { protected: - virtual ~LLImageBMP(); - + virtual ~LLImageBMP(); + public: - LLImageBMP(); + LLImageBMP(); - /*virtual*/ std::string getExtension() { return std::string("bmp"); } - /*virtual*/ bool updateData(); - /*virtual*/ bool decode(LLImageRaw* raw_image, F32 decode_time); - /*virtual*/ bool encode(const LLImageRaw* raw_image, F32 encode_time); + /*virtual*/ std::string getExtension() { return std::string("bmp"); } + /*virtual*/ bool updateData(); + /*virtual*/ bool decode(LLImageRaw* raw_image, F32 decode_time); + /*virtual*/ bool encode(const LLImageRaw* raw_image, F32 encode_time); protected: - bool decodeColorTable8( U8* dst, const U8* src ); - bool decodeColorMask16( U8* dst, const U8* src ); - bool decodeTruecolor24( U8* dst, const U8* src ); - bool decodeColorMask32( U8* dst, const U8* src ); + bool decodeColorTable8( U8* dst, const U8* src ); + bool decodeColorMask16( U8* dst, const U8* src ); + bool decodeTruecolor24( U8* dst, const U8* src ); + bool decodeColorMask32( U8* dst, const U8* src ); - U32 countTrailingZeros( U32 m ); + U32 countTrailingZeros( U32 m ); protected: - S32 mColorPaletteColors; - U8* mColorPalette; - S32 mBitmapOffset; - S32 mBitsPerPixel; - U32 mBitfieldMask[4]; // rgba - bool mOriginAtTop; + S32 mColorPaletteColors; + U8* mColorPalette; + S32 mBitmapOffset; + S32 mBitsPerPixel; + U32 mBitfieldMask[4]; // rgba + bool mOriginAtTop; }; #endif diff --git a/indra/llimage/llimagedimensionsinfo.cpp b/indra/llimage/llimagedimensionsinfo.cpp index c5e4b76012..d4efbcfad2 100644 --- a/indra/llimage/llimagedimensionsinfo.cpp +++ b/indra/llimage/llimagedimensionsinfo.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llimagedimensionsinfo.cpp * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -35,192 +35,192 @@ static bool sJpegErrorEncountered = false; bool LLImageDimensionsInfo::load(const std::string& src_filename,U32 codec) { - clean(); + clean(); - mSrcFilename = src_filename; + mSrcFilename = src_filename; - S32 file_size = 0; - apr_status_t s = mInfile.open(src_filename, LL_APR_RB, NULL, &file_size); + S32 file_size = 0; + apr_status_t s = mInfile.open(src_filename, LL_APR_RB, NULL, &file_size); - if (s != APR_SUCCESS) - { - setLastError("Unable to open file for reading", src_filename); - return false; - } + if (s != APR_SUCCESS) + { + setLastError("Unable to open file for reading", src_filename); + return false; + } - if (file_size == 0) - { + if (file_size == 0) + { mWarning = "texture_load_empty_file"; - setLastError("File is empty",src_filename); - return false; - } - - switch (codec) - { - case IMG_CODEC_BMP: - return getImageDimensionsBmp(); - case IMG_CODEC_TGA: - return getImageDimensionsTga(); - case IMG_CODEC_JPEG: - return getImageDimensionsJpeg(); - case IMG_CODEC_PNG: - return getImageDimensionsPng(); - default: - return false; - - } + setLastError("File is empty",src_filename); + return false; + } + + switch (codec) + { + case IMG_CODEC_BMP: + return getImageDimensionsBmp(); + case IMG_CODEC_TGA: + return getImageDimensionsTga(); + case IMG_CODEC_JPEG: + return getImageDimensionsJpeg(); + case IMG_CODEC_PNG: + return getImageDimensionsPng(); + default: + return false; + + } } bool LLImageDimensionsInfo::getImageDimensionsBmp() { - // Make sure the file is long enough. - const S32 DATA_LEN = 26; // BMP header (14) + DIB header size (4) + width (4) + height (4) - if (!checkFileLength(DATA_LEN)) - { - LL_WARNS() << "Premature end of file" << LL_ENDL; - return false; - } - - // Read BMP signature. - U8 signature[2]; - mInfile.read((void*)signature, sizeof(signature)/sizeof(signature[0])); - - // Make sure this is actually a BMP file. - // We only support Windows bitmaps (BM), according to LLImageBMP::updateData(). - if (signature[0] != 'B' || signature[1] != 'M') - { - LL_WARNS() << "Not a BMP" << LL_ENDL; + // Make sure the file is long enough. + const S32 DATA_LEN = 26; // BMP header (14) + DIB header size (4) + width (4) + height (4) + if (!checkFileLength(DATA_LEN)) + { + LL_WARNS() << "Premature end of file" << LL_ENDL; + return false; + } + + // Read BMP signature. + U8 signature[2]; + mInfile.read((void*)signature, sizeof(signature)/sizeof(signature[0])); + + // Make sure this is actually a BMP file. + // We only support Windows bitmaps (BM), according to LLImageBMP::updateData(). + if (signature[0] != 'B' || signature[1] != 'M') + { + LL_WARNS() << "Not a BMP" << LL_ENDL; mWarning = "texture_load_format_error"; - return false; - } + return false; + } - // Read image dimensions. - mInfile.seek(APR_CUR, 16); - mWidth = read_reverse_s32(); - mHeight = read_reverse_s32(); + // Read image dimensions. + mInfile.seek(APR_CUR, 16); + mWidth = read_reverse_s32(); + mHeight = read_reverse_s32(); - return true; + return true; } bool LLImageDimensionsInfo::getImageDimensionsTga() { - const S32 TGA_FILE_HEADER_SIZE = 12; + const S32 TGA_FILE_HEADER_SIZE = 12; - // Make sure the file is long enough. - if (!checkFileLength(TGA_FILE_HEADER_SIZE + 1 /* width */ + 1 /* height */)) - { - LL_WARNS() << "Premature end of file" << LL_ENDL; - return false; - } + // Make sure the file is long enough. + if (!checkFileLength(TGA_FILE_HEADER_SIZE + 1 /* width */ + 1 /* height */)) + { + LL_WARNS() << "Premature end of file" << LL_ENDL; + return false; + } - // *TODO: Detect non-TGA files somehow. - mInfile.seek(APR_CUR,TGA_FILE_HEADER_SIZE); - mWidth = read_byte() | read_byte() << 8; - mHeight = read_byte() | read_byte() << 8; + // *TODO: Detect non-TGA files somehow. + mInfile.seek(APR_CUR,TGA_FILE_HEADER_SIZE); + mWidth = read_byte() | read_byte() << 8; + mHeight = read_byte() | read_byte() << 8; - return true; + return true; } bool LLImageDimensionsInfo::getImageDimensionsPng() { - const S32 PNG_MAGIC_SIZE = 8; - - // Make sure the file is long enough. - if (!checkFileLength(PNG_MAGIC_SIZE + 8 + sizeof(S32) * 2 /* width, height */)) - { - LL_WARNS() << "Premature end of file" << LL_ENDL; - return false; - } - - // Read PNG signature. - const U8 png_magic[PNG_MAGIC_SIZE] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; - U8 signature[PNG_MAGIC_SIZE]; - mInfile.read((void*)signature, PNG_MAGIC_SIZE); - - // Make sure it's a PNG file. - if (memcmp(signature, png_magic, PNG_MAGIC_SIZE) != 0) - { - LL_WARNS() << "Not a PNG" << LL_ENDL; + const S32 PNG_MAGIC_SIZE = 8; + + // Make sure the file is long enough. + if (!checkFileLength(PNG_MAGIC_SIZE + 8 + sizeof(S32) * 2 /* width, height */)) + { + LL_WARNS() << "Premature end of file" << LL_ENDL; + return false; + } + + // Read PNG signature. + const U8 png_magic[PNG_MAGIC_SIZE] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; + U8 signature[PNG_MAGIC_SIZE]; + mInfile.read((void*)signature, PNG_MAGIC_SIZE); + + // Make sure it's a PNG file. + if (memcmp(signature, png_magic, PNG_MAGIC_SIZE) != 0) + { + LL_WARNS() << "Not a PNG" << LL_ENDL; mWarning = "texture_load_format_error"; - return false; - } + return false; + } - // Read image dimensions. - mInfile.seek(APR_CUR, 8 /* chunk length + chunk type */); - mWidth = read_s32(); - mHeight = read_s32(); + // Read image dimensions. + mInfile.seek(APR_CUR, 8 /* chunk length + chunk type */); + mWidth = read_s32(); + mHeight = read_s32(); - return true; + return true; } // Called instead of exit() if Libjpeg encounters an error. void on_jpeg_error(j_common_ptr cinfo) { - (void) cinfo; - sJpegErrorEncountered = true; - LL_WARNS() << "Libjpeg has encountered an error!" << LL_ENDL; + (void) cinfo; + sJpegErrorEncountered = true; + LL_WARNS() << "Libjpeg has encountered an error!" << LL_ENDL; } bool LLImageDimensionsInfo::getImageDimensionsJpeg() { - sJpegErrorEncountered = false; - clean(); - FILE *fp = LLFile::fopen(mSrcFilename, "rb"); - if (fp == NULL) - { - setLastError("Unable to open file for reading", mSrcFilename); - return false; - } - - /* Make sure this is a JPEG file. */ - const size_t JPEG_MAGIC_SIZE = 2; - const U8 jpeg_magic[JPEG_MAGIC_SIZE] = {0xFF, 0xD8}; - U8 signature[JPEG_MAGIC_SIZE]; - - if (fread(signature, sizeof(signature), 1, fp) != 1) - { - LL_WARNS() << "Premature end of file" << LL_ENDL; - return false; - } - if (memcmp(signature, jpeg_magic, JPEG_MAGIC_SIZE) != 0) - { - LL_WARNS() << "Not a JPEG" << LL_ENDL; + sJpegErrorEncountered = false; + clean(); + FILE *fp = LLFile::fopen(mSrcFilename, "rb"); + if (fp == NULL) + { + setLastError("Unable to open file for reading", mSrcFilename); + return false; + } + + /* Make sure this is a JPEG file. */ + const size_t JPEG_MAGIC_SIZE = 2; + const U8 jpeg_magic[JPEG_MAGIC_SIZE] = {0xFF, 0xD8}; + U8 signature[JPEG_MAGIC_SIZE]; + + if (fread(signature, sizeof(signature), 1, fp) != 1) + { + LL_WARNS() << "Premature end of file" << LL_ENDL; + return false; + } + if (memcmp(signature, jpeg_magic, JPEG_MAGIC_SIZE) != 0) + { + LL_WARNS() << "Not a JPEG" << LL_ENDL; mWarning = "texture_load_format_error"; - return false; - } - fseek(fp, 0, SEEK_SET); // go back to start of the file - - /* Init jpeg */ - jpeg_error_mgr jerr; - jpeg_decompress_struct cinfo; - cinfo.err = jpeg_std_error(&jerr); - // Call our function instead of exit() if Libjpeg encounters an error. - // This is done to avoid crash in this case (STORM-472). - cinfo.err->error_exit = on_jpeg_error; - - jpeg_create_decompress (&cinfo); - jpeg_stdio_src (&cinfo, fp); - jpeg_read_header (&cinfo, true); - cinfo.out_color_space = JCS_RGB; - jpeg_start_decompress (&cinfo); - - mWidth = cinfo.output_width; - mHeight = cinfo.output_height; - - jpeg_destroy_decompress(&cinfo); - fclose(fp); - - return !sJpegErrorEncountered; + return false; + } + fseek(fp, 0, SEEK_SET); // go back to start of the file + + /* Init jpeg */ + jpeg_error_mgr jerr; + jpeg_decompress_struct cinfo; + cinfo.err = jpeg_std_error(&jerr); + // Call our function instead of exit() if Libjpeg encounters an error. + // This is done to avoid crash in this case (STORM-472). + cinfo.err->error_exit = on_jpeg_error; + + jpeg_create_decompress (&cinfo); + jpeg_stdio_src (&cinfo, fp); + jpeg_read_header (&cinfo, true); + cinfo.out_color_space = JCS_RGB; + jpeg_start_decompress (&cinfo); + + mWidth = cinfo.output_width; + mHeight = cinfo.output_height; + + jpeg_destroy_decompress(&cinfo); + fclose(fp); + + return !sJpegErrorEncountered; } bool LLImageDimensionsInfo::checkFileLength(S32 min_len) { - // Make sure the file is not shorter than min_len bytes. - // so that we don't have to check value returned by each read() or seek(). - char* buf = new char[min_len]; - int nread = mInfile.read(buf, min_len); - delete[] buf; - mInfile.seek(APR_SET, 0); - return nread == min_len; + // Make sure the file is not shorter than min_len bytes. + // so that we don't have to check value returned by each read() or seek(). + char* buf = new char[min_len]; + int nread = mInfile.read(buf, min_len); + delete[] buf; + mInfile.seek(APR_SET, 0); + return nread == min_len; } diff --git a/indra/llimage/llimagedimensionsinfo.h b/indra/llimage/llimagedimensionsinfo.h index ade283bb85..681d66ae4e 100644 --- a/indra/llimage/llimagedimensionsinfo.h +++ b/indra/llimage/llimagedimensionsinfo.h @@ -1,24 +1,24 @@ -/** +/** * @file llimagedimentionsinfo.h * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -37,24 +37,24 @@ class LLImageDimensionsInfo { public: - LLImageDimensionsInfo(): - mData(NULL) - ,mHeight(0) - ,mWidth(0) - {} - ~LLImageDimensionsInfo() - { - clean(); - } - - bool load(const std::string& src_filename,U32 codec); - S32 getWidth() const { return mWidth;} - S32 getHeight() const { return mHeight;} - - const std::string& getLastError() - { - return mLastError; - } + LLImageDimensionsInfo(): + mData(NULL) + ,mHeight(0) + ,mWidth(0) + {} + ~LLImageDimensionsInfo() + { + clean(); + } + + bool load(const std::string& src_filename,U32 codec); + S32 getWidth() const { return mWidth;} + S32 getHeight() const { return mHeight;} + + const std::string& getLastError() + { + return mLastError; + } const std::string& getWarningName() { @@ -63,83 +63,83 @@ public: protected: - void clean() - { - mInfile.close(); - delete[] mData; - mData = NULL; - mWidth = 0; - mHeight = 0; - } - - U8* getData() - { - return mData; - } - - - void setLastError(const std::string& message, const std::string& filename) - { - std::string error = message; - if (!filename.empty()) - error += std::string(" FILE: ") + filename; - mLastError = error; - } - - - bool getImageDimensionsBmp(); - bool getImageDimensionsTga(); - bool getImageDimensionsPng(); - bool getImageDimensionsJpeg(); - - S32 read_s32() - { - char p[4]; - mInfile.read(&p[0],4); - S32 temp = (((S32)p[3]) & 0x000000FF) | - (((S32)p[2] << 8 ) & 0x0000FF00) | - (((S32)p[1] << 16) & 0x00FF0000) | - (((S32)p[0] << 24) & 0xFF000000); - - return temp; - } - S32 read_reverse_s32() - { - char p[4]; - mInfile.read(&p[0],4); - S32 temp = (((S32)p[0]) & 0x000000FF) | - (((S32)p[1] << 8 ) & 0x0000FF00) | - (((S32)p[2] << 16) & 0x00FF0000) | - (((S32)p[3] << 24) & 0xFF000000); - - return temp; - } - - U8 read_byte() - { - U8 bt; - mInfile.read(&bt,1); - return bt; - } - - U16 read_short() - { - return read_byte() << 8 | read_byte(); - } - - /// Check if the file is not shorter than min_len bytes. - bool checkFileLength(S32 min_len); + void clean() + { + mInfile.close(); + delete[] mData; + mData = NULL; + mWidth = 0; + mHeight = 0; + } + + U8* getData() + { + return mData; + } + + + void setLastError(const std::string& message, const std::string& filename) + { + std::string error = message; + if (!filename.empty()) + error += std::string(" FILE: ") + filename; + mLastError = error; + } + + + bool getImageDimensionsBmp(); + bool getImageDimensionsTga(); + bool getImageDimensionsPng(); + bool getImageDimensionsJpeg(); + + S32 read_s32() + { + char p[4]; + mInfile.read(&p[0],4); + S32 temp = (((S32)p[3]) & 0x000000FF) | + (((S32)p[2] << 8 ) & 0x0000FF00) | + (((S32)p[1] << 16) & 0x00FF0000) | + (((S32)p[0] << 24) & 0xFF000000); + + return temp; + } + S32 read_reverse_s32() + { + char p[4]; + mInfile.read(&p[0],4); + S32 temp = (((S32)p[0]) & 0x000000FF) | + (((S32)p[1] << 8 ) & 0x0000FF00) | + (((S32)p[2] << 16) & 0x00FF0000) | + (((S32)p[3] << 24) & 0xFF000000); + + return temp; + } + + U8 read_byte() + { + U8 bt; + mInfile.read(&bt,1); + return bt; + } + + U16 read_short() + { + return read_byte() << 8 | read_byte(); + } + + /// Check if the file is not shorter than min_len bytes. + bool checkFileLength(S32 min_len); protected: - LLAPRFile mInfile ; - std::string mSrcFilename; + LLAPRFile mInfile ; + std::string mSrcFilename; - std::string mLastError; + std::string mLastError; std::string mWarning; - U8* mData; + U8* mData; - S32 mWidth; - S32 mHeight; + S32 mWidth; + S32 mHeight; }; #endif diff --git a/indra/llimage/llimagedxt.cpp b/indra/llimage/llimagedxt.cpp index 07e44c615c..89299258a6 100644 --- a/indra/llimage/llimagedxt.cpp +++ b/indra/llimage/llimagedxt.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llimagedxt.cpp * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -31,139 +31,139 @@ //static void LLImageDXT::checkMinWidthHeight(EFileFormat format, S32& width, S32& height) { - S32 mindim = (format >= FORMAT_DXT1 && format <= FORMAT_DXR5) ? 4 : 1; - width = llmax(width, mindim); - height = llmax(height, mindim); + S32 mindim = (format >= FORMAT_DXT1 && format <= FORMAT_DXR5) ? 4 : 1; + width = llmax(width, mindim); + height = llmax(height, mindim); } //static S32 LLImageDXT::formatBits(EFileFormat format) { - switch (format) - { - case FORMAT_DXT1: return 4; - case FORMAT_DXR1: return 4; - case FORMAT_I8: return 8; - case FORMAT_A8: return 8; - case FORMAT_DXT3: return 8; - case FORMAT_DXR3: return 8; - case FORMAT_DXR5: return 8; - case FORMAT_DXT5: return 8; - case FORMAT_RGB8: return 24; - case FORMAT_RGBA8: return 32; - default: - LL_ERRS() << "LLImageDXT::Unknown format: " << format << LL_ENDL; - return 0; - } + switch (format) + { + case FORMAT_DXT1: return 4; + case FORMAT_DXR1: return 4; + case FORMAT_I8: return 8; + case FORMAT_A8: return 8; + case FORMAT_DXT3: return 8; + case FORMAT_DXR3: return 8; + case FORMAT_DXR5: return 8; + case FORMAT_DXT5: return 8; + case FORMAT_RGB8: return 24; + case FORMAT_RGBA8: return 32; + default: + LL_ERRS() << "LLImageDXT::Unknown format: " << format << LL_ENDL; + return 0; + } }; //static S32 LLImageDXT::formatBytes(EFileFormat format, S32 width, S32 height) { - checkMinWidthHeight(format, width, height); - S32 bytes = ((width*height*formatBits(format)+7)>>3); - S32 aligned = (bytes+3)&~3; - return aligned; + checkMinWidthHeight(format, width, height); + S32 bytes = ((width*height*formatBits(format)+7)>>3); + S32 aligned = (bytes+3)&~3; + return aligned; } //static S32 LLImageDXT::formatComponents(EFileFormat format) { - switch (format) - { - case FORMAT_DXT1: return 3; - case FORMAT_DXR1: return 3; - case FORMAT_I8: return 1; - case FORMAT_A8: return 1; - case FORMAT_DXT3: return 4; - case FORMAT_DXR3: return 4; - case FORMAT_DXT5: return 4; - case FORMAT_DXR5: return 4; - case FORMAT_RGB8: return 3; - case FORMAT_RGBA8: return 4; - default: - LL_ERRS() << "LLImageDXT::Unknown format: " << format << LL_ENDL; - return 0; - } + switch (format) + { + case FORMAT_DXT1: return 3; + case FORMAT_DXR1: return 3; + case FORMAT_I8: return 1; + case FORMAT_A8: return 1; + case FORMAT_DXT3: return 4; + case FORMAT_DXR3: return 4; + case FORMAT_DXT5: return 4; + case FORMAT_DXR5: return 4; + case FORMAT_RGB8: return 3; + case FORMAT_RGBA8: return 4; + default: + LL_ERRS() << "LLImageDXT::Unknown format: " << format << LL_ENDL; + return 0; + } }; -// static +// static LLImageDXT::EFileFormat LLImageDXT::getFormat(S32 fourcc) { - switch(fourcc) - { - case 0x20203849: return FORMAT_I8; - case 0x20203841: return FORMAT_A8; - case 0x20424752: return FORMAT_RGB8; - case 0x41424752: return FORMAT_RGBA8; - case 0x31525844: return FORMAT_DXR1; - case 0x32525844: return FORMAT_DXR2; - case 0x33525844: return FORMAT_DXR3; - case 0x34525844: return FORMAT_DXR4; - case 0x35525844: return FORMAT_DXR5; - case 0x31545844: return FORMAT_DXT1; - case 0x32545844: return FORMAT_DXT2; - case 0x33545844: return FORMAT_DXT3; - case 0x34545844: return FORMAT_DXT4; - case 0x35545844: return FORMAT_DXT5; - default: return FORMAT_UNKNOWN; - } + switch(fourcc) + { + case 0x20203849: return FORMAT_I8; + case 0x20203841: return FORMAT_A8; + case 0x20424752: return FORMAT_RGB8; + case 0x41424752: return FORMAT_RGBA8; + case 0x31525844: return FORMAT_DXR1; + case 0x32525844: return FORMAT_DXR2; + case 0x33525844: return FORMAT_DXR3; + case 0x34525844: return FORMAT_DXR4; + case 0x35525844: return FORMAT_DXR5; + case 0x31545844: return FORMAT_DXT1; + case 0x32545844: return FORMAT_DXT2; + case 0x33545844: return FORMAT_DXT3; + case 0x34545844: return FORMAT_DXT4; + case 0x35545844: return FORMAT_DXT5; + default: return FORMAT_UNKNOWN; + } } //static S32 LLImageDXT::getFourCC(EFileFormat format) { - switch(format) - { - case FORMAT_I8: return 0x20203849; - case FORMAT_A8: return 0x20203841; - case FORMAT_RGB8: return 0x20424752; - case FORMAT_RGBA8: return 0x41424752; - case FORMAT_DXR1: return 0x31525844; - case FORMAT_DXR2: return 0x32525844; - case FORMAT_DXR3: return 0x33525844; - case FORMAT_DXR4: return 0x34525844; - case FORMAT_DXR5: return 0x35525844; - case FORMAT_DXT1: return 0x31545844; - case FORMAT_DXT2: return 0x32545844; - case FORMAT_DXT3: return 0x33545844; - case FORMAT_DXT4: return 0x34545844; - case FORMAT_DXT5: return 0x35545844; - default: return 0x00000000; - } + switch(format) + { + case FORMAT_I8: return 0x20203849; + case FORMAT_A8: return 0x20203841; + case FORMAT_RGB8: return 0x20424752; + case FORMAT_RGBA8: return 0x41424752; + case FORMAT_DXR1: return 0x31525844; + case FORMAT_DXR2: return 0x32525844; + case FORMAT_DXR3: return 0x33525844; + case FORMAT_DXR4: return 0x34525844; + case FORMAT_DXR5: return 0x35525844; + case FORMAT_DXT1: return 0x31545844; + case FORMAT_DXT2: return 0x32545844; + case FORMAT_DXT3: return 0x33545844; + case FORMAT_DXT4: return 0x34545844; + case FORMAT_DXT5: return 0x35545844; + default: return 0x00000000; + } } //static void LLImageDXT::calcDiscardWidthHeight(S32 discard_level, EFileFormat format, S32& width, S32& height) { - while (discard_level > 0 && width > 1 && height > 1) - { - discard_level--; - width >>= 1; - height >>= 1; - } - checkMinWidthHeight(format, width, height); + while (discard_level > 0 && width > 1 && height > 1) + { + discard_level--; + width >>= 1; + height >>= 1; + } + checkMinWidthHeight(format, width, height); } //static S32 LLImageDXT::calcNumMips(S32 width, S32 height) { - S32 nmips = 0; - while (width > 0 && height > 0) - { - width >>= 1; - height >>= 1; - nmips++; - } - return nmips; + S32 nmips = 0; + while (width > 0 && height > 0) + { + width >>= 1; + height >>= 1; + nmips++; + } + return nmips; } //============================================================================ LLImageDXT::LLImageDXT() - : LLImageFormatted(IMG_CODEC_DXT), - mFileFormat(FORMAT_UNKNOWN), - mHeaderSize(0) + : LLImageFormatted(IMG_CODEC_DXT), + mFileFormat(FORMAT_UNKNOWN), + mHeaderSize(0) { } @@ -174,351 +174,351 @@ LLImageDXT::~LLImageDXT() // virtual bool LLImageDXT::updateData() { - resetLastError(); - - LLImageDataLock lock(this); - - U8* data = getData(); - S32 data_size = getDataSize(); - - if (!data || !data_size) - { - setLastError("LLImageDXT uninitialized"); - return false; - } - - S32 width, height, miplevelmax; - dxtfile_header_t* header = (dxtfile_header_t*)data; - if (header->fourcc != 0x20534444) - { - dxtfile_header_old_t* oldheader = (dxtfile_header_old_t*)header; - mHeaderSize = sizeof(dxtfile_header_old_t); - mFileFormat = EFileFormat(oldheader->format); - miplevelmax = llmin(oldheader->maxlevel,MAX_IMAGE_MIP); - width = oldheader->maxwidth; - height = oldheader->maxheight; - } - else - { - mHeaderSize = sizeof(dxtfile_header_t); - mFileFormat = getFormat(header->pixel_fmt.fourcc); - miplevelmax = llmin(header->num_mips-1,MAX_IMAGE_MIP); - width = header->maxwidth; - height = header->maxheight; - } - - if (data_size < mHeaderSize) - { - LL_ERRS() << "LLImageDXT: not enough data" << LL_ENDL; - } - S32 ncomponents = formatComponents(mFileFormat); - setSize(width, height, ncomponents); - - S32 discard = calcDiscardLevelBytes(data_size); - discard = llmin(discard, miplevelmax); - setDiscardLevel(discard); - - return true; + resetLastError(); + + LLImageDataLock lock(this); + + U8* data = getData(); + S32 data_size = getDataSize(); + + if (!data || !data_size) + { + setLastError("LLImageDXT uninitialized"); + return false; + } + + S32 width, height, miplevelmax; + dxtfile_header_t* header = (dxtfile_header_t*)data; + if (header->fourcc != 0x20534444) + { + dxtfile_header_old_t* oldheader = (dxtfile_header_old_t*)header; + mHeaderSize = sizeof(dxtfile_header_old_t); + mFileFormat = EFileFormat(oldheader->format); + miplevelmax = llmin(oldheader->maxlevel,MAX_IMAGE_MIP); + width = oldheader->maxwidth; + height = oldheader->maxheight; + } + else + { + mHeaderSize = sizeof(dxtfile_header_t); + mFileFormat = getFormat(header->pixel_fmt.fourcc); + miplevelmax = llmin(header->num_mips-1,MAX_IMAGE_MIP); + width = header->maxwidth; + height = header->maxheight; + } + + if (data_size < mHeaderSize) + { + LL_ERRS() << "LLImageDXT: not enough data" << LL_ENDL; + } + S32 ncomponents = formatComponents(mFileFormat); + setSize(width, height, ncomponents); + + S32 discard = calcDiscardLevelBytes(data_size); + discard = llmin(discard, miplevelmax); + setDiscardLevel(discard); + + return true; } // discard: 0 = largest (last) mip S32 LLImageDXT::getMipOffset(S32 discard) { - if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXT5) - { - LL_ERRS() << "getMipOffset called with old (unsupported) format" << LL_ENDL; - } - S32 width = getWidth(), height = getHeight(); - S32 num_mips = calcNumMips(width, height); - discard = llclamp(discard, 0, num_mips-1); - S32 last_mip = num_mips-1-discard; - llassert(mHeaderSize > 0); - S32 offset = mHeaderSize; - for (S32 mipidx = num_mips-1; mipidx >= 0; mipidx--) - { - if (mipidx < last_mip) - { - offset += formatBytes(mFileFormat, width, height); - } - width >>= 1; - height >>= 1; - } - return offset; + if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXT5) + { + LL_ERRS() << "getMipOffset called with old (unsupported) format" << LL_ENDL; + } + S32 width = getWidth(), height = getHeight(); + S32 num_mips = calcNumMips(width, height); + discard = llclamp(discard, 0, num_mips-1); + S32 last_mip = num_mips-1-discard; + llassert(mHeaderSize > 0); + S32 offset = mHeaderSize; + for (S32 mipidx = num_mips-1; mipidx >= 0; mipidx--) + { + if (mipidx < last_mip) + { + offset += formatBytes(mFileFormat, width, height); + } + width >>= 1; + height >>= 1; + } + return offset; } void LLImageDXT::setFormat() { - S32 ncomponents = getComponents(); - switch (ncomponents) - { - case 3: mFileFormat = FORMAT_DXR1; break; - case 4: mFileFormat = FORMAT_DXR3; break; - default: LL_ERRS() << "LLImageDXT::setFormat called with ncomponents = " << ncomponents << LL_ENDL; - } - mHeaderSize = calcHeaderSize(); + S32 ncomponents = getComponents(); + switch (ncomponents) + { + case 3: mFileFormat = FORMAT_DXR1; break; + case 4: mFileFormat = FORMAT_DXR3; break; + default: LL_ERRS() << "LLImageDXT::setFormat called with ncomponents = " << ncomponents << LL_ENDL; + } + mHeaderSize = calcHeaderSize(); } - + // virtual bool LLImageDXT::decode(LLImageRaw* raw_image, F32 time) { - // *TODO: Test! This has been tweaked since its intial inception, - // but we don't use it any more! - llassert_always(raw_image); - - if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXR5) - { - LL_WARNS() << "Attempt to decode compressed LLImageDXT to Raw (unsupported)" << LL_ENDL; - return false; - } - - LLImageDataSharedLock lockIn(this); - LLImageDataLock lockOut(raw_image); - - S32 width = getWidth(), height = getHeight(); - S32 ncomponents = getComponents(); - U8* data = NULL; - if (mDiscardLevel >= 0) - { - data = getData() + getMipOffset(mDiscardLevel); - calcDiscardWidthHeight(mDiscardLevel, mFileFormat, width, height); - } - else - { - data = getData() + getMipOffset(0); - } - S32 image_size = formatBytes(mFileFormat, width, height); - - if ((!getData()) || (data + image_size > getData() + getDataSize())) - { - setLastError("LLImageDXT trying to decode an image with not enough data!"); - return false; - } - - if (!raw_image->resize(width, height, ncomponents)) - { - setLastError("llImageDXT failed to resize image!"); - return false; - } - memcpy(raw_image->getData(), data, image_size); /* Flawfinder: ignore */ - - return true; + // *TODO: Test! This has been tweaked since its intial inception, + // but we don't use it any more! + llassert_always(raw_image); + + if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXR5) + { + LL_WARNS() << "Attempt to decode compressed LLImageDXT to Raw (unsupported)" << LL_ENDL; + return false; + } + + LLImageDataSharedLock lockIn(this); + LLImageDataLock lockOut(raw_image); + + S32 width = getWidth(), height = getHeight(); + S32 ncomponents = getComponents(); + U8* data = NULL; + if (mDiscardLevel >= 0) + { + data = getData() + getMipOffset(mDiscardLevel); + calcDiscardWidthHeight(mDiscardLevel, mFileFormat, width, height); + } + else + { + data = getData() + getMipOffset(0); + } + S32 image_size = formatBytes(mFileFormat, width, height); + + if ((!getData()) || (data + image_size > getData() + getDataSize())) + { + setLastError("LLImageDXT trying to decode an image with not enough data!"); + return false; + } + + if (!raw_image->resize(width, height, ncomponents)) + { + setLastError("llImageDXT failed to resize image!"); + return false; + } + memcpy(raw_image->getData(), data, image_size); /* Flawfinder: ignore */ + + return true; } bool LLImageDXT::getMipData(LLPointer<LLImageRaw>& raw, S32 discard) { - if (discard < 0) - { - discard = mDiscardLevel; - } - else if (discard < mDiscardLevel) - { - LL_ERRS() << "Request for invalid discard level" << LL_ENDL; - } - - LLImageDataSharedLock lock(this); - - U8* data = getData() + getMipOffset(discard); - S32 width = 0; - S32 height = 0; - calcDiscardWidthHeight(discard, mFileFormat, width, height); - raw = new LLImageRaw(data, width, height, getComponents()); - return true; + if (discard < 0) + { + discard = mDiscardLevel; + } + else if (discard < mDiscardLevel) + { + LL_ERRS() << "Request for invalid discard level" << LL_ENDL; + } + + LLImageDataSharedLock lock(this); + + U8* data = getData() + getMipOffset(discard); + S32 width = 0; + S32 height = 0; + calcDiscardWidthHeight(discard, mFileFormat, width, height); + raw = new LLImageRaw(data, width, height, getComponents()); + return true; } bool LLImageDXT::encodeDXT(const LLImageRaw* raw_image, F32 time, bool explicit_mips) { - llassert_always(raw_image); - - S32 ncomponents = raw_image->getComponents(); - EFileFormat format; - switch (ncomponents) - { - case 1: - format = FORMAT_A8; - break; - case 3: - format = FORMAT_RGB8; - break; - case 4: - format = FORMAT_RGBA8; - break; - default: - LL_ERRS() << "LLImageDXT::encode: Unhandled channel number: " << ncomponents << LL_ENDL; - return 0; - } - - LLImageDataLock lock(this); - - S32 width = raw_image->getWidth(); - S32 height = raw_image->getHeight(); - - if (explicit_mips) - { - height = (height/3)*2; - } - - setSize(width, height, ncomponents); - mHeaderSize = sizeof(dxtfile_header_t); - mFileFormat = format; - - S32 nmips = calcNumMips(width, height); - S32 w = width; - S32 h = height; - - S32 totbytes = mHeaderSize; - for (S32 mip=0; mip<nmips; mip++) - { - totbytes += formatBytes(format,w,h); - w >>= 1; - h >>= 1; - } - - allocateData(totbytes); - - U8* data = getData(); - dxtfile_header_t* header = (dxtfile_header_t*)data; - llassert(mHeaderSize > 0); - memset(header, 0, mHeaderSize); - header->fourcc = 0x20534444; - header->pixel_fmt.fourcc = getFourCC(format); - header->num_mips = nmips; - header->maxwidth = width; - header->maxheight = height; - - U8* prev_mipdata = 0; - w = width, h = height; - for (S32 mip=0; mip<nmips; mip++) - { - U8* mipdata = data + getMipOffset(mip); - S32 bytes = formatBytes(format, w, h); - if (mip==0) - { - memcpy(mipdata, raw_image->getData(), bytes); /* Flawfinder: ignore */ - } - else if (explicit_mips) - { - extractMip(raw_image->getData(), mipdata, width, height, w, h, format); - } - else - { - generateMip(prev_mipdata, mipdata, w, h, ncomponents); - } - w >>= 1; - h >>= 1; - checkMinWidthHeight(format, w, h); - prev_mipdata = mipdata; - } - - return true; + llassert_always(raw_image); + + S32 ncomponents = raw_image->getComponents(); + EFileFormat format; + switch (ncomponents) + { + case 1: + format = FORMAT_A8; + break; + case 3: + format = FORMAT_RGB8; + break; + case 4: + format = FORMAT_RGBA8; + break; + default: + LL_ERRS() << "LLImageDXT::encode: Unhandled channel number: " << ncomponents << LL_ENDL; + return 0; + } + + LLImageDataLock lock(this); + + S32 width = raw_image->getWidth(); + S32 height = raw_image->getHeight(); + + if (explicit_mips) + { + height = (height/3)*2; + } + + setSize(width, height, ncomponents); + mHeaderSize = sizeof(dxtfile_header_t); + mFileFormat = format; + + S32 nmips = calcNumMips(width, height); + S32 w = width; + S32 h = height; + + S32 totbytes = mHeaderSize; + for (S32 mip=0; mip<nmips; mip++) + { + totbytes += formatBytes(format,w,h); + w >>= 1; + h >>= 1; + } + + allocateData(totbytes); + + U8* data = getData(); + dxtfile_header_t* header = (dxtfile_header_t*)data; + llassert(mHeaderSize > 0); + memset(header, 0, mHeaderSize); + header->fourcc = 0x20534444; + header->pixel_fmt.fourcc = getFourCC(format); + header->num_mips = nmips; + header->maxwidth = width; + header->maxheight = height; + + U8* prev_mipdata = 0; + w = width, h = height; + for (S32 mip=0; mip<nmips; mip++) + { + U8* mipdata = data + getMipOffset(mip); + S32 bytes = formatBytes(format, w, h); + if (mip==0) + { + memcpy(mipdata, raw_image->getData(), bytes); /* Flawfinder: ignore */ + } + else if (explicit_mips) + { + extractMip(raw_image->getData(), mipdata, width, height, w, h, format); + } + else + { + generateMip(prev_mipdata, mipdata, w, h, ncomponents); + } + w >>= 1; + h >>= 1; + checkMinWidthHeight(format, w, h); + prev_mipdata = mipdata; + } + + return true; } // virtual bool LLImageDXT::encode(const LLImageRaw* raw_image, F32 time) { - return encodeDXT(raw_image, time, false); + return encodeDXT(raw_image, time, false); } // virtual bool LLImageDXT::convertToDXR() { - EFileFormat newformat = FORMAT_UNKNOWN; - switch (mFileFormat) - { - case FORMAT_DXR1: - case FORMAT_DXR2: - case FORMAT_DXR3: - case FORMAT_DXR4: - case FORMAT_DXR5: - return false; // nothing to do - case FORMAT_DXT1: newformat = FORMAT_DXR1; break; - case FORMAT_DXT2: newformat = FORMAT_DXR2; break; - case FORMAT_DXT3: newformat = FORMAT_DXR3; break; - case FORMAT_DXT4: newformat = FORMAT_DXR4; break; - case FORMAT_DXT5: newformat = FORMAT_DXR5; break; - default: - LL_WARNS() << "convertToDXR: can not convert format: " << llformat("0x%08x",getFourCC(mFileFormat)) << LL_ENDL; - return false; - } - mFileFormat = newformat; - - LLImageDataLock lock(this); - - S32 width = getWidth(), height = getHeight(); - S32 nmips = calcNumMips(width,height); - S32 total_bytes = getDataSize(); - U8* olddata = getData(); - U8* newdata = (U8*)ll_aligned_malloc_16(total_bytes); - if (!newdata) - { + EFileFormat newformat = FORMAT_UNKNOWN; + switch (mFileFormat) + { + case FORMAT_DXR1: + case FORMAT_DXR2: + case FORMAT_DXR3: + case FORMAT_DXR4: + case FORMAT_DXR5: + return false; // nothing to do + case FORMAT_DXT1: newformat = FORMAT_DXR1; break; + case FORMAT_DXT2: newformat = FORMAT_DXR2; break; + case FORMAT_DXT3: newformat = FORMAT_DXR3; break; + case FORMAT_DXT4: newformat = FORMAT_DXR4; break; + case FORMAT_DXT5: newformat = FORMAT_DXR5; break; + default: + LL_WARNS() << "convertToDXR: can not convert format: " << llformat("0x%08x",getFourCC(mFileFormat)) << LL_ENDL; + return false; + } + mFileFormat = newformat; + + LLImageDataLock lock(this); + + S32 width = getWidth(), height = getHeight(); + S32 nmips = calcNumMips(width,height); + S32 total_bytes = getDataSize(); + U8* olddata = getData(); + U8* newdata = (U8*)ll_aligned_malloc_16(total_bytes); + if (!newdata) + { LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS() << "Out of memory in LLImageDXT::convertToDXR()" << LL_ENDL; - return false; - } - llassert(total_bytes > 0); - memset(newdata, 0, total_bytes); - memcpy(newdata, olddata, mHeaderSize); /* Flawfinder: ignore */ - for (S32 mip=0; mip<nmips; mip++) - { - S32 bytes = formatBytes(mFileFormat, width, height); - S32 newoffset = getMipOffset(mip); - S32 oldoffset = mHeaderSize + (total_bytes - newoffset - bytes); - memcpy(newdata + newoffset, olddata + oldoffset, bytes); /* Flawfinder: ignore */ - width >>= 1; - height >>= 1; - } - dxtfile_header_t* header = (dxtfile_header_t*)newdata; - header->pixel_fmt.fourcc = getFourCC(newformat); - setData(newdata, total_bytes); - updateData(); - return true; + LL_ERRS() << "Out of memory in LLImageDXT::convertToDXR()" << LL_ENDL; + return false; + } + llassert(total_bytes > 0); + memset(newdata, 0, total_bytes); + memcpy(newdata, olddata, mHeaderSize); /* Flawfinder: ignore */ + for (S32 mip=0; mip<nmips; mip++) + { + S32 bytes = formatBytes(mFileFormat, width, height); + S32 newoffset = getMipOffset(mip); + S32 oldoffset = mHeaderSize + (total_bytes - newoffset - bytes); + memcpy(newdata + newoffset, olddata + oldoffset, bytes); /* Flawfinder: ignore */ + width >>= 1; + height >>= 1; + } + dxtfile_header_t* header = (dxtfile_header_t*)newdata; + header->pixel_fmt.fourcc = getFourCC(newformat); + setData(newdata, total_bytes); + updateData(); + return true; } // virtual S32 LLImageDXT::calcHeaderSize() { - return llmax(sizeof(dxtfile_header_old_t), sizeof(dxtfile_header_t)); + return llmax(sizeof(dxtfile_header_old_t), sizeof(dxtfile_header_t)); } // virtual S32 LLImageDXT::calcDataSize(S32 discard_level) { - if (mFileFormat == FORMAT_UNKNOWN) - { - LL_ERRS() << "calcDataSize called with unloaded LLImageDXT" << LL_ENDL; - return 0; - } - if (discard_level < 0) - { - discard_level = mDiscardLevel; - } - S32 bytes = getMipOffset(discard_level); // size of header + previous mips - S32 w = getWidth() >> discard_level; - S32 h = getHeight() >> discard_level; - bytes += formatBytes(mFileFormat,w,h); - return bytes; + if (mFileFormat == FORMAT_UNKNOWN) + { + LL_ERRS() << "calcDataSize called with unloaded LLImageDXT" << LL_ENDL; + return 0; + } + if (discard_level < 0) + { + discard_level = mDiscardLevel; + } + S32 bytes = getMipOffset(discard_level); // size of header + previous mips + S32 w = getWidth() >> discard_level; + S32 h = getHeight() >> discard_level; + bytes += formatBytes(mFileFormat,w,h); + return bytes; } //============================================================================ //static void LLImageDXT::extractMip(const U8 *indata, U8* mipdata, int width, int height, - int mip_width, int mip_height, EFileFormat format) + int mip_width, int mip_height, EFileFormat format) { - int initial_offset = formatBytes(format, width, height); - int line_width = formatBytes(format, width, 1); - int mip_line_width = formatBytes(format, mip_width, 1); - int line_offset = 0; - - for (int ww=width>>1; ww>mip_width; ww>>=1) - { - line_offset += formatBytes(format, ww, 1); - } - - for (int h=0;h<mip_height;++h) - { - int start_offset = initial_offset + line_width * h + line_offset; - memcpy(mipdata + mip_line_width*h, indata + start_offset, mip_line_width); /* Flawfinder: ignore */ - } + int initial_offset = formatBytes(format, width, height); + int line_width = formatBytes(format, width, 1); + int mip_line_width = formatBytes(format, mip_width, 1); + int line_offset = 0; + + for (int ww=width>>1; ww>mip_width; ww>>=1) + { + line_offset += formatBytes(format, ww, 1); + } + + for (int h=0;h<mip_height;++h) + { + int start_offset = initial_offset + line_width * h + line_offset; + memcpy(mipdata + mip_line_width*h, indata + start_offset, mip_line_width); /* Flawfinder: ignore */ + } } //============================================================================ diff --git a/indra/llimage/llimagedxt.h b/indra/llimage/llimagedxt.h index a4a9bcf99c..06b1d3fbe2 100644 --- a/indra/llimage/llimagedxt.h +++ b/indra/llimage/llimagedxt.h @@ -1,24 +1,24 @@ -/** +/** * @file llimagedxt.h * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,108 +34,108 @@ class LLImageDXT : public LLImageFormatted { public: - enum EFileFormat - { - FORMAT_UNKNOWN = 0, - FORMAT_I8 = 1, - FORMAT_A8, - FORMAT_RGB8, - FORMAT_RGBA8, - FORMAT_DXT1, - FORMAT_DXT2, - FORMAT_DXT3, - FORMAT_DXT4, - FORMAT_DXT5, - FORMAT_DXR1, - FORMAT_DXR2, - FORMAT_DXR3, - FORMAT_DXR4, - FORMAT_DXR5, - FORMAT_NOFILE = 0xff, - }; - - struct dxtfile_header_old_t - { - S32 format; - S32 maxlevel; - S32 maxwidth; - S32 maxheight; - }; - - struct dxtfile_header_t - { - S32 fourcc; - // begin DDSURFACEDESC2 struct - S32 header_size; // size of the header - S32 flags; // flags - unused - S32 maxheight; - S32 maxwidth; - S32 image_size; // size of the compressed image - S32 depth; - S32 num_mips; - S32 reserved[11]; - struct pixel_format - { - S32 struct_size; // size of this structure - S32 flags; - S32 fourcc; - S32 bit_count; - S32 r_mask; - S32 g_mask; - S32 b_mask; - S32 a_mask; - } pixel_fmt; - S32 caps[4]; - S32 reserved2; - }; + enum EFileFormat + { + FORMAT_UNKNOWN = 0, + FORMAT_I8 = 1, + FORMAT_A8, + FORMAT_RGB8, + FORMAT_RGBA8, + FORMAT_DXT1, + FORMAT_DXT2, + FORMAT_DXT3, + FORMAT_DXT4, + FORMAT_DXT5, + FORMAT_DXR1, + FORMAT_DXR2, + FORMAT_DXR3, + FORMAT_DXR4, + FORMAT_DXR5, + FORMAT_NOFILE = 0xff, + }; + + struct dxtfile_header_old_t + { + S32 format; + S32 maxlevel; + S32 maxwidth; + S32 maxheight; + }; + + struct dxtfile_header_t + { + S32 fourcc; + // begin DDSURFACEDESC2 struct + S32 header_size; // size of the header + S32 flags; // flags - unused + S32 maxheight; + S32 maxwidth; + S32 image_size; // size of the compressed image + S32 depth; + S32 num_mips; + S32 reserved[11]; + struct pixel_format + { + S32 struct_size; // size of this structure + S32 flags; + S32 fourcc; + S32 bit_count; + S32 r_mask; + S32 g_mask; + S32 b_mask; + S32 a_mask; + } pixel_fmt; + S32 caps[4]; + S32 reserved2; + }; protected: - /*virtual*/ ~LLImageDXT(); + /*virtual*/ ~LLImageDXT(); private: - bool encodeDXT(const LLImageRaw* raw_image, F32 decode_time, bool explicit_mips); - + bool encodeDXT(const LLImageRaw* raw_image, F32 decode_time, bool explicit_mips); + public: - LLImageDXT(); - - /*virtual*/ std::string getExtension() { return std::string("dxt"); } - /*virtual*/ bool updateData(); - - /*virtual*/ bool decode(LLImageRaw* raw_image, F32 decode_time); - /*virtual*/ bool encode(const LLImageRaw* raw_image, F32 encode_time); - - /*virtual*/ S32 calcHeaderSize(); - /*virtual*/ S32 calcDataSize(S32 discard_level = 0); - - bool getMipData(LLPointer<LLImageRaw>& raw, S32 discard=-1); - - void setFormat(); - S32 getMipOffset(S32 discard); - - EFileFormat getFileFormat() { return mFileFormat; } - bool isCompressed() { return (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXR5); } - - bool convertToDXR(); // convert from DXT to DXR - - static void checkMinWidthHeight(EFileFormat format, S32& width, S32& height); - static S32 formatBits(EFileFormat format); - static S32 formatBytes(EFileFormat format, S32 width, S32 height); - static S32 formatOffset(EFileFormat format, S32 width, S32 height, S32 max_width, S32 max_height); - static S32 formatComponents(EFileFormat format); - - static EFileFormat getFormat(S32 fourcc); - static S32 getFourCC(EFileFormat format); - - static void calcDiscardWidthHeight(S32 discard_level, EFileFormat format, S32& width, S32& height); - static S32 calcNumMips(S32 width, S32 height); + LLImageDXT(); + + /*virtual*/ std::string getExtension() { return std::string("dxt"); } + /*virtual*/ bool updateData(); + + /*virtual*/ bool decode(LLImageRaw* raw_image, F32 decode_time); + /*virtual*/ bool encode(const LLImageRaw* raw_image, F32 encode_time); + + /*virtual*/ S32 calcHeaderSize(); + /*virtual*/ S32 calcDataSize(S32 discard_level = 0); + + bool getMipData(LLPointer<LLImageRaw>& raw, S32 discard=-1); + + void setFormat(); + S32 getMipOffset(S32 discard); + + EFileFormat getFileFormat() { return mFileFormat; } + bool isCompressed() { return (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXR5); } + + bool convertToDXR(); // convert from DXT to DXR + + static void checkMinWidthHeight(EFileFormat format, S32& width, S32& height); + static S32 formatBits(EFileFormat format); + static S32 formatBytes(EFileFormat format, S32 width, S32 height); + static S32 formatOffset(EFileFormat format, S32 width, S32 height, S32 max_width, S32 max_height); + static S32 formatComponents(EFileFormat format); + + static EFileFormat getFormat(S32 fourcc); + static S32 getFourCC(EFileFormat format); + + static void calcDiscardWidthHeight(S32 discard_level, EFileFormat format, S32& width, S32& height); + static S32 calcNumMips(S32 width, S32 height); private: - static void extractMip(const U8 *indata, U8* mipdata, int width, int height, - int mip_width, int mip_height, EFileFormat format); - + static void extractMip(const U8 *indata, U8* mipdata, int width, int height, + int mip_width, int mip_height, EFileFormat format); + private: - EFileFormat mFileFormat; - S32 mHeaderSize; + EFileFormat mFileFormat; + S32 mHeaderSize; }; #endif diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp index 61c2e1d742..db21f50b95 100644 --- a/indra/llimage/llimagefilter.cpp +++ b/indra/llimage/llimagefilter.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llimagefilter.cpp * @brief Simple Image Filtering. See https://wiki.lindenlab.com/wiki/SL_Viewer_Image_Filters for complete documentation. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2014, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -54,14 +54,14 @@ LLImageFilter::LLImageFilter(const std::string& file_path) : mStencilMax(1.0) { // Load filter description from file - llifstream filter_xml(file_path.c_str()); - if (filter_xml.is_open()) - { - // Load and parse the file - LLPointer<LLSDParser> parser = new LLSDXMLParser(); - parser->parse(filter_xml, mFilterData, LLSDSerialize::SIZE_UNLIMITED); - filter_xml.close(); - } + llifstream filter_xml(file_path.c_str()); + if (filter_xml.is_open()) + { + // Load and parse the file + LLPointer<LLSDParser> parser = new LLSDXMLParser(); + parser->parse(filter_xml, mFilterData, LLSDSerialize::SIZE_UNLIMITED); + filter_xml.close(); + } } LLImageFilter::~LLImageFilter() @@ -74,7 +74,7 @@ LLImageFilter::~LLImageFilter() } /* - *TODO + *TODO * Rename stencil to mask * Improve perf: use LUT for alpha blending in uniform case * Add gradient coloring as a filter @@ -90,9 +90,9 @@ void LLImageFilter::executeFilter(LLPointer<LLImageRaw> raw_image) LLImageDataLock lock(mImage); - //std::cout << "Filter : size = " << mFilterData.size() << std::endl; - for (S32 i = 0; i < mFilterData.size(); ++i) - { + //std::cout << "Filter : size = " << mFilterData.size() << std::endl; + for (S32 i = 0; i < mFilterData.size(); ++i) + { std::string filter_name = mFilterData[i][0].asString(); // Dump out the filter values (for debug) //std::cout << "Filter : name = " << mFilterData[i][0].asString() << ", params = "; @@ -101,7 +101,7 @@ void LLImageFilter::executeFilter(LLPointer<LLImageRaw> raw_image) // std::cout << mFilterData[i][j].asString() << ", "; //} //std::cout << std::endl; - + if (filter_name == "stencil") { // Get the shape of the stencil, that is how the procedural alpha is computed geometrically @@ -311,54 +311,54 @@ void LLImageFilter::blendStencil(F32 alpha, U8* pixel, U8 red, U8 green, U8 blue void LLImageFilter::colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue) { - const S32 components = mImage->getComponents(); - llassert( components >= 1 && components <= 4 ); - - S32 width = mImage->getWidth(); + const S32 components = mImage->getComponents(); + llassert( components >= 1 && components <= 4 ); + + S32 width = mImage->getWidth(); S32 height = mImage->getHeight(); - - U8* dst_data = mImage->getData(); - for (S32 j = 0; j < height; j++) - { + + U8* dst_data = mImage->getData(); + for (S32 j = 0; j < height; j++) + { for (S32 i = 0; i < width; i++) { // Blend LUT value blendStencil(getStencilAlpha(i,j), dst_data, lut_red[dst_data[VRED]], lut_green[dst_data[VGREEN]], lut_blue[dst_data[VBLUE]]); dst_data += components; } - } + } } void LLImageFilter::colorTransform(const LLMatrix3 &transform) { - const S32 components = mImage->getComponents(); - llassert( components >= 1 && components <= 4 ); - - S32 width = mImage->getWidth(); + const S32 components = mImage->getComponents(); + llassert( components >= 1 && components <= 4 ); + + S32 width = mImage->getWidth(); S32 height = mImage->getHeight(); - - U8* dst_data = mImage->getData(); - for (S32 j = 0; j < height; j++) - { + + U8* dst_data = mImage->getData(); + for (S32 j = 0; j < height; j++) + { for (S32 i = 0; i < width; i++) { // Compute transform LLVector3 src((F32)(dst_data[VRED]),(F32)(dst_data[VGREEN]),(F32)(dst_data[VBLUE])); LLVector3 dst = src * transform; dst.clamp(0.0f,255.0f); - + // Blend result blendStencil(getStencilAlpha(i,j), dst_data, dst.mV[VRED], dst.mV[VGREEN], dst.mV[VBLUE]); dst_data += components; } - } + } } void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_value) { - const S32 components = mImage->getComponents(); - llassert( components >= 1 && components <= 4 ); - + const S32 components = mImage->getComponents(); + llassert( components >= 1 && components <= 4 ); + // Compute normalization factors F32 kernel_min = 0.0; F32 kernel_max = 0.0; @@ -380,44 +380,44 @@ void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_v kernel_min = 0.0; } F32 kernel_range = kernel_max - kernel_min; - + // Allocate temporary buffers and initialize algorithm's data - S32 width = mImage->getWidth(); + S32 width = mImage->getWidth(); S32 height = mImage->getHeight(); - - U8* dst_data = mImage->getData(); - - S32 buffer_size = width * components; - llassert_always(buffer_size > 0); - std::vector<U8> even_buffer(buffer_size); - std::vector<U8> odd_buffer(buffer_size); - + + U8* dst_data = mImage->getData(); + + S32 buffer_size = width * components; + llassert_always(buffer_size > 0); + std::vector<U8> even_buffer(buffer_size); + std::vector<U8> odd_buffer(buffer_size); + U8* south_data = dst_data + buffer_size; U8* east_west_data; U8* north_data; - + // Line 0 : we set the line to 0 (debatable) - memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */ + memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */ for (S32 i = 0; i < width; i++) { blendStencil(getStencilAlpha(i,0), dst_data, 0, 0, 0); dst_data += components; } south_data += buffer_size; - + // All other lines for (S32 j = 1; j < (height-1); j++) - { + { // We need to buffer 2 lines. We flip north and east-west (current) to avoid moving too much memory around if (j % 2) { - memcpy( &odd_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */ + memcpy( &odd_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */ east_west_data = &odd_buffer[0]; north_data = &even_buffer[0]; } else { - memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */ + memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */ east_west_data = &even_buffer[0]; north_data = &odd_buffer[0]; } @@ -461,10 +461,10 @@ void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_v dst.mV[VBLUE] = (dst.mV[VBLUE] - kernel_min)/kernel_range; } dst.clamp(0.0f,255.0f); - + // Blend result blendStencil(getStencilAlpha(i,j), dst_data, dst.mV[VRED], dst.mV[VGREEN], dst.mV[VBLUE]); - + // Next pixel dst_data += components; NW += components; @@ -481,8 +481,8 @@ void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_v blendStencil(getStencilAlpha(width-1,j), dst_data, 0, 0, 0); dst_data += components; south_data += buffer_size; - } - + } + // Last line for (S32 i = 0; i < width; i++) { @@ -493,12 +493,12 @@ void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_v void LLImageFilter::filterScreen(EScreenMode mode, const F32 wave_length, const F32 angle) { - const S32 components = mImage->getComponents(); - llassert( components >= 1 && components <= 4 ); - - S32 width = mImage->getWidth(); + const S32 components = mImage->getComponents(); + llassert( components >= 1 && components <= 4 ); + + S32 width = mImage->getWidth(); S32 height = mImage->getHeight(); - + F32 wave_length_pixels = wave_length * (F32)(height) / 2.0; F32 sin = sinf(angle*DEG_TO_RAD); F32 cos = cosf(angle*DEG_TO_RAD); @@ -510,10 +510,10 @@ void LLImageFilter::filterScreen(EScreenMode mode, const F32 wave_length, const F32 gamma_i = llclampf((float)(powf((float)(i)/255.0,1.0/4.0))); gamma[i] = (U8)(255.0 * gamma_i); } - - U8* dst_data = mImage->getData(); - for (S32 j = 0; j < height; j++) - { + + U8* dst_data = mImage->getData(); + for (S32 j = 0; j < height; j++) + { for (S32 i = 0; i < width; i++) { // Compute screen value @@ -533,12 +533,12 @@ void LLImageFilter::filterScreen(EScreenMode mode, const F32 wave_length, const break; } U8 dst_value = (dst_data[VRED] >= (U8)(value) ? gamma[dst_data[VRED] - (U8)(value)] : 0); - + // Blend result blendStencil(getStencilAlpha(i,j), dst_data, dst_value, dst_value, dst_value); dst_data += components; } - } + } } //============================================================================ @@ -550,7 +550,7 @@ void LLImageFilter::setStencil(EStencilShape shape, EStencilBlendMode mode, F32 mStencilBlendMode = mode; mStencilMin = llmin(llmax(min, -1.0f), 1.0f); mStencilMax = llmin(llmax(max, -1.0f), 1.0f); - + // Each shape will interpret the 4 params differenly. // We compute each systematically, though, clearly, values are meaningless when the shape doesn't correspond to the parameters mStencilCenterX = (S32)(mImage->getWidth() + params[0] * (F32)(mImage->getHeight()))/2; @@ -592,7 +592,7 @@ F32 LLImageFilter::getStencilAlpha(S32 i, S32 j) alpha = (((F32)(i) - mStencilStartX)*mStencilGradX + ((F32)(j) - mStencilStartY)*mStencilGradY) / mStencilGradN; alpha = llclampf(alpha); } - + // We rescale alpha between min and max return (mStencilMin + alpha * (mStencilMax - mStencilMin)); } @@ -612,9 +612,9 @@ U32* LLImageFilter::getBrightnessHistogram() void LLImageFilter::computeHistograms() { - const S32 components = mImage->getComponents(); - llassert( components >= 1 && components <= 4 ); - + const S32 components = mImage->getComponents(); + llassert( components >= 1 && components <= 4 ); + // Allocate memory for the histograms if (!mHistoRed) { @@ -632,7 +632,7 @@ void LLImageFilter::computeHistograms() { mHistoBrightness = (U32*) ll_aligned_malloc_16(256*sizeof(U32)); } - + // Initialize them for (S32 i = 0; i < 256; i++) { @@ -641,12 +641,12 @@ void LLImageFilter::computeHistograms() mHistoBlue[i] = 0; mHistoBrightness[i] = 0; } - + // Compute them - S32 pixels = mImage->getWidth() * mImage->getHeight(); - U8* dst_data = mImage->getData(); - for (S32 i = 0; i < pixels; i++) - { + S32 pixels = mImage->getWidth() * mImage->getHeight(); + U8* dst_data = mImage->getData(); + for (S32 i = 0; i < pixels; i++) + { mHistoRed[dst_data[VRED]]++; mHistoGreen[dst_data[VGREEN]]++; mHistoBlue[dst_data[VBLUE]]++; @@ -654,8 +654,8 @@ void LLImageFilter::computeHistograms() S32 brightness = ((S32)(dst_data[VRED]) + (S32)(dst_data[VGREEN]) + (S32)(dst_data[VBLUE])) / 3; mHistoBrightness[brightness]++; // next pixel... - dst_data += components; - } + dst_data += components; + } } //============================================================================ @@ -686,7 +686,7 @@ void LLImageFilter::filterSaturate(F32 saturation) // Matrix to Lij LLMatrix3 r_a; LLMatrix3 r_b; - + // 45 degre rotation around z r_a.setRows(LLVector3( OO_SQRT2, OO_SQRT2, 0.0), LLVector3(-OO_SQRT2, OO_SQRT2, 0.0), @@ -697,18 +697,18 @@ void LLImageFilter::filterSaturate(F32 saturation) r_b.setRows(LLVector3(oo_sqrt3, 0.0, -sin_54), LLVector3(0.0, 1.0, 0.0), LLVector3(sin_54, 0.0, oo_sqrt3)); - + // Coordinate conversion LLMatrix3 Lij = r_b * r_a; LLMatrix3 Lij_inv = Lij; Lij_inv.transpose(); - + // Local saturation transform LLMatrix3 s; s.setRows(LLVector3(saturation, 0.0, 0.0), LLVector3(0.0, saturation, 0.0), LLVector3(0.0, 0.0, 1.0)); - + // Global saturation transform LLMatrix3 transfo = Lij_inv * s * Lij; colorTransform(transfo); @@ -719,7 +719,7 @@ void LLImageFilter::filterRotate(F32 angle) // Matrix to Lij LLMatrix3 r_a; LLMatrix3 r_b; - + // 45 degre rotation around z r_a.setRows(LLVector3( OO_SQRT2, OO_SQRT2, 0.0), LLVector3(-OO_SQRT2, OO_SQRT2, 0.0), @@ -730,19 +730,19 @@ void LLImageFilter::filterRotate(F32 angle) r_b.setRows(LLVector3(oo_sqrt3, 0.0, -sin_54), LLVector3(0.0, 1.0, 0.0), LLVector3(sin_54, 0.0, oo_sqrt3)); - + // Coordinate conversion LLMatrix3 Lij = r_b * r_a; LLMatrix3 Lij_inv = Lij; Lij_inv.transpose(); - + // Local color rotation transform LLMatrix3 r; angle *= DEG_TO_RAD; r.setRows(LLVector3( cosf(angle), sinf(angle), 0.0), LLVector3(-sinf(angle), cosf(angle), 0.0), LLVector3( 0.0, 0.0, 1.0)); - + // Global color rotation transform LLMatrix3 transfo = Lij_inv * r * Lij; colorTransform(transfo); @@ -753,7 +753,7 @@ void LLImageFilter::filterGamma(F32 gamma, const LLColor3& alpha) U8 gamma_red_lut[256]; U8 gamma_green_lut[256]; U8 gamma_blue_lut[256]; - + for (S32 i = 0; i < 256; i++) { F32 gamma_i = llclampf((float)(powf((float)(i)/255.0,1.0/gamma))); @@ -762,7 +762,7 @@ void LLImageFilter::filterGamma(F32 gamma, const LLColor3& alpha) gamma_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * 255.0 * gamma_i); gamma_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * 255.0 * gamma_i); } - + colorCorrect(gamma_red_lut,gamma_green_lut,gamma_blue_lut); } @@ -770,7 +770,7 @@ void LLImageFilter::filterLinearize(F32 tail, const LLColor3& alpha) { // Get the histogram U32* histo = getBrightnessHistogram(); - + // Compute cumulated histogram U32 cumulated_histo[256]; cumulated_histo[0] = histo[0]; @@ -778,13 +778,13 @@ void LLImageFilter::filterLinearize(F32 tail, const LLColor3& alpha) { cumulated_histo[i] = cumulated_histo[i-1] + histo[i]; } - + // Compute min and max counts minus tail tail = llclampf(tail); S32 total = cumulated_histo[255]; S32 min_c = (S32)((F32)(total) * tail); S32 max_c = (S32)((F32)(total) * (1.0 - tail)); - + // Find min and max values S32 min_v = 0; while (cumulated_histo[min_v] < min_c) @@ -796,7 +796,7 @@ void LLImageFilter::filterLinearize(F32 tail, const LLColor3& alpha) { max_v--; } - + // Compute linear lookup table U8 linear_red_lut[256]; U8 linear_green_lut[256]; @@ -827,7 +827,7 @@ void LLImageFilter::filterLinearize(F32 tail, const LLColor3& alpha) linear_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i); } } - + // Apply lookup table colorCorrect(linear_red_lut,linear_green_lut,linear_blue_lut); } @@ -837,10 +837,10 @@ void LLImageFilter::filterEqualize(S32 nb_classes, const LLColor3& alpha) // Regularize the parameter: must be between 2 and 255 nb_classes = llmax(nb_classes,2); nb_classes = llclampb(nb_classes); - + // Get the histogram U32* histo = getBrightnessHistogram(); - + // Compute cumulated histogram U32 cumulated_histo[256]; cumulated_histo[0] = histo[0]; @@ -848,14 +848,14 @@ void LLImageFilter::filterEqualize(S32 nb_classes, const LLColor3& alpha) { cumulated_histo[i] = cumulated_histo[i-1] + histo[i]; } - + // Compute deltas S32 total = cumulated_histo[255]; S32 delta_count = total / nb_classes; S32 current_count = delta_count; S32 delta_value = 256 / (nb_classes - 1); S32 current_value = 0; - + // Compute equalized lookup table U8 equalize_red_lut[256]; U8 equalize_green_lut[256]; @@ -873,7 +873,7 @@ void LLImageFilter::filterEqualize(S32 nb_classes, const LLColor3& alpha) current_value = llclampb(current_value); } } - + // Apply lookup table colorCorrect(equalize_red_lut,equalize_green_lut,equalize_blue_lut); } @@ -883,18 +883,18 @@ void LLImageFilter::filterColorize(const LLColor3& color, const LLColor3& alpha) U8 red_lut[256]; U8 green_lut[256]; U8 blue_lut[256]; - + F32 red_composite = 255.0 * alpha.mV[0] * color.mV[0]; F32 green_composite = 255.0 * alpha.mV[1] * color.mV[1]; F32 blue_composite = 255.0 * alpha.mV[2] * color.mV[2]; - + for (S32 i = 0; i < 256; i++) { red_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[0]) * (F32)(i) + red_composite))); green_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[1]) * (F32)(i) + green_composite))); blue_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[2]) * (F32)(i) + blue_composite))); } - + colorCorrect(red_lut,green_lut,blue_lut); } @@ -903,9 +903,9 @@ void LLImageFilter::filterContrast(F32 slope, const LLColor3& alpha) U8 contrast_red_lut[256]; U8 contrast_green_lut[256]; U8 contrast_blue_lut[256]; - + F32 translate = 128.0 * (1.0 - slope); - + for (S32 i = 0; i < 256; i++) { U8 value_i = (U8)(llclampb((S32)(slope*i + translate))); @@ -914,7 +914,7 @@ void LLImageFilter::filterContrast(F32 slope, const LLColor3& alpha) contrast_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i); contrast_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i); } - + colorCorrect(contrast_red_lut,contrast_green_lut,contrast_blue_lut); } @@ -923,9 +923,9 @@ void LLImageFilter::filterBrightness(F32 add, const LLColor3& alpha) U8 brightness_red_lut[256]; U8 brightness_green_lut[256]; U8 brightness_blue_lut[256]; - + S32 add_value = (S32)(add * 255.0); - + for (S32 i = 0; i < 256; i++) { U8 value_i = (U8)(llclampb(i + add_value)); @@ -934,7 +934,7 @@ void LLImageFilter::filterBrightness(F32 add, const LLColor3& alpha) brightness_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i); brightness_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i); } - + colorCorrect(brightness_red_lut,brightness_green_lut,brightness_blue_lut); } diff --git a/indra/llimage/llimagefilter.h b/indra/llimage/llimagefilter.h index 16ec395f76..94fe92b205 100644 --- a/indra/llimage/llimagefilter.h +++ b/indra/llimage/llimagefilter.h @@ -1,25 +1,25 @@ -/** +/** * @file llimagefilter.h * @brief Simple Image Filtering. See https://wiki.lindenlab.com/wiki/SL_Viewer_Image_Filters for complete documentation. * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2014, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -37,28 +37,28 @@ class LLMatrix3; typedef enum e_stencil_blend_mode { - STENCIL_BLEND_MODE_BLEND = 0, - STENCIL_BLEND_MODE_ADD = 1, - STENCIL_BLEND_MODE_ABACK = 2, - STENCIL_BLEND_MODE_FADE = 3 + STENCIL_BLEND_MODE_BLEND = 0, + STENCIL_BLEND_MODE_ADD = 1, + STENCIL_BLEND_MODE_ABACK = 2, + STENCIL_BLEND_MODE_FADE = 3 } EStencilBlendMode; typedef enum e_stencil_shape { - STENCIL_SHAPE_UNIFORM = 0, - STENCIL_SHAPE_GRADIENT = 1, - STENCIL_SHAPE_VIGNETTE = 2, - STENCIL_SHAPE_SCAN_LINES = 3 + STENCIL_SHAPE_UNIFORM = 0, + STENCIL_SHAPE_GRADIENT = 1, + STENCIL_SHAPE_VIGNETTE = 2, + STENCIL_SHAPE_SCAN_LINES = 3 } EStencilShape; typedef enum e_screen_mode { - SCREEN_MODE_2DSINE = 0, - SCREEN_MODE_LINE = 1 + SCREEN_MODE_2DSINE = 0, + SCREEN_MODE_LINE = 1 } EScreenMode; //============================================================================ -// LLImageFilter +// LLImageFilter //============================================================================ class LLImageFilter @@ -66,16 +66,16 @@ class LLImageFilter public: LLImageFilter(const std::string& file_path); ~LLImageFilter(); - + void executeFilter(LLPointer<LLImageRaw> raw_image); - + private: // Filter Operations : Transforms void filterGrayScale(); // Convert to grayscale void filterSepia(); // Convert to sepia void filterSaturate(F32 saturation); // < 1.0 desaturates, > 1.0 saturates void filterRotate(F32 angle); // Rotates hue according to angle, angle in degrees - + // Filter Operations : Color Corrections // When specified, the LLColor3 alpha parameter indicates the intensity of the effect for each color channel // acting in effect as an alpha blending factor different for each channel. For instance (1.0,0.0,0.0) will apply @@ -86,7 +86,7 @@ private: void filterColorize(const LLColor3& color, const LLColor3& alpha); // Colorize with color and alpha per channel void filterContrast(F32 slope, const LLColor3& alpha); // Change contrast according to slope: > 1.0 more contrast, < 1.0 less contrast void filterBrightness(F32 add, const LLColor3& alpha); // Change brightness according to add: > 0 brighter, < 0 darker - + // Filter Primitives void colorTransform(const LLMatrix3 &transform); void colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue); @@ -110,22 +110,22 @@ private: U32 *mHistoGreen; U32 *mHistoBlue; U32 *mHistoBrightness; - + // Current Stencil Settings EStencilBlendMode mStencilBlendMode; EStencilShape mStencilShape; F32 mStencilMin; F32 mStencilMax; - + S32 mStencilCenterX; S32 mStencilCenterY; S32 mStencilWidth; F32 mStencilGamma; - + F32 mStencilWavelength; F32 mStencilSine; F32 mStencilCosine; - + F32 mStencilStartX; F32 mStencilStartY; F32 mStencilGradX; diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp index 382da40dcf..0058b91b0f 100644 --- a/indra/llimage/llimagej2c.cpp +++ b/indra/llimage/llimagej2c.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llimagej2c.cpp * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -46,37 +46,37 @@ const std::string sTesterName("ImageCompressionTester"); //static std::string LLImageJ2C::getEngineInfo() { - // All known LLImageJ2CImpl implementation subclasses are cheap to - // construct. - std::unique_ptr<LLImageJ2CImpl> impl(fallbackCreateLLImageJ2CImpl()); - return impl->getEngineInfo(); + // All known LLImageJ2CImpl implementation subclasses are cheap to + // construct. + std::unique_ptr<LLImageJ2CImpl> impl(fallbackCreateLLImageJ2CImpl()); + return impl->getEngineInfo(); } -LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C), - mMaxBytes(0), - mRawDiscardLevel(-1), - mRate(DEFAULT_COMPRESSION_RATE), - mReversible(false), - mAreaUsedForDataSizeCalcs(0) +LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C), + mMaxBytes(0), + mRawDiscardLevel(-1), + mRate(DEFAULT_COMPRESSION_RATE), + mReversible(false), + mAreaUsedForDataSizeCalcs(0) { - mImpl.reset(fallbackCreateLLImageJ2CImpl()); - - // Clear data size table - for( S32 i = 0; i <= MAX_DISCARD_LEVEL; i++) - { // Array size is MAX_DISCARD_LEVEL+1 - mDataSizes[i] = 0; - } - - // If that test log has ben requested but not yet created, create it - if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName)) - { - sTesterp = new LLImageCompressionTester() ; - if (!sTesterp->isValid()) - { - delete sTesterp; - sTesterp = NULL; - } - } + mImpl.reset(fallbackCreateLLImageJ2CImpl()); + + // Clear data size table + for( S32 i = 0; i <= MAX_DISCARD_LEVEL; i++) + { // Array size is MAX_DISCARD_LEVEL+1 + mDataSizes[i] = 0; + } + + // If that test log has ben requested but not yet created, create it + if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName)) + { + sTesterp = new LLImageCompressionTester() ; + if (!sTesterp->isValid()) + { + delete sTesterp; + sTesterp = NULL; + } + } } // virtual @@ -85,71 +85,71 @@ LLImageJ2C::~LLImageJ2C() {} // virtual void LLImageJ2C::resetLastError() { - mLastError.clear(); + mLastError.clear(); } //virtual void LLImageJ2C::setLastError(const std::string& message, const std::string& filename) { - mLastError = message; - if (!filename.empty()) - mLastError += std::string(" FILE: ") + filename; + mLastError = message; + if (!filename.empty()) + mLastError += std::string(" FILE: ") + filename; } // virtual S8 LLImageJ2C::getRawDiscardLevel() { - return mRawDiscardLevel; + return mRawDiscardLevel; } bool LLImageJ2C::updateData() { - bool res = true; - resetLastError(); - - LLImageDataLock lock(this); - - // Check to make sure that this instance has been initialized with data - if (!getData() || (getDataSize() < 16)) - { - setLastError("LLImageJ2C uninitialized"); - res = false; - } - else - { - res = mImpl->getMetadata(*this); - } - - if (res) - { - // SJB: override discard based on mMaxBytes elsewhere - S32 max_bytes = getDataSize(); // mMaxBytes ? mMaxBytes : getDataSize(); - S32 discard = calcDiscardLevelBytes(max_bytes); - setDiscardLevel(discard); - } - - if (!mLastError.empty()) - { - LLImage::setLastError(mLastError); - } - return res; + bool res = true; + resetLastError(); + + LLImageDataLock lock(this); + + // Check to make sure that this instance has been initialized with data + if (!getData() || (getDataSize() < 16)) + { + setLastError("LLImageJ2C uninitialized"); + res = false; + } + else + { + res = mImpl->getMetadata(*this); + } + + if (res) + { + // SJB: override discard based on mMaxBytes elsewhere + S32 max_bytes = getDataSize(); // mMaxBytes ? mMaxBytes : getDataSize(); + S32 discard = calcDiscardLevelBytes(max_bytes); + setDiscardLevel(discard); + } + + if (!mLastError.empty()) + { + LLImage::setLastError(mLastError); + } + return res; } bool LLImageJ2C::initDecode(LLImageRaw &raw_image, int discard_level, int* region) { - setDiscardLevel(discard_level != -1 ? discard_level : 0); - return mImpl->initDecode(*this,raw_image,discard_level,region); + setDiscardLevel(discard_level != -1 ? discard_level : 0); + return mImpl->initDecode(*this,raw_image,discard_level,region); } bool LLImageJ2C::initEncode(LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels) { - return mImpl->initEncode(*this,raw_image,blocks_size,precincts_size,levels); + return mImpl->initEncode(*this,raw_image,blocks_size,precincts_size,levels); } bool LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - return decodeChannels(raw_imagep, decode_time, 0, 4); + return decodeChannels(raw_imagep, decode_time, 0, 4); } @@ -157,305 +157,305 @@ bool LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time) bool LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count ) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - LLTimer elapsed; - - resetLastError(); - - bool res; - { - LLImageDataLock lock(this); - - mDecoding = true; - // Check to make sure that this instance has been initialized with data - if (!getData() || (getDataSize() < 16)) - { - setLastError("LLImageJ2C uninitialized"); - res = true; // done - } - else - { - // Update the raw discard level - updateRawDiscardLevel(); - res = mImpl->decodeImpl(*this, *raw_imagep, decode_time, first_channel, max_channel_count); - } - } - - if (res) - { - if (!mDecoding) - { - // Failed - raw_imagep->deleteData(); - res = false; - } - else - { - mDecoding = false; - } - } - else - { - if (mDecoding) - { - LL_WARNS() << "decodeImpl failed but mDecoding is true" << LL_ENDL; - mDecoding = false; - } - } - - if (!mLastError.empty()) - { - LLImage::setLastError(mLastError); - } - - LLImageCompressionTester* tester = (LLImageCompressionTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); - if (tester) - { - // Decompression stat gathering - // Note that we *do not* take into account the decompression failures data so we might overestimate the time spent processing - - // Always add the decompression time to the stat - tester->updateDecompressionStats(elapsed.getElapsedTimeF32()) ; - if (res) - { - // The whole data stream is finally decompressed when res is returned as true - tester->updateDecompressionStats(this->getDataSize(), raw_imagep->getDataSize()) ; - } - } - - return res; + LLTimer elapsed; + + resetLastError(); + + bool res; + { + LLImageDataLock lock(this); + + mDecoding = true; + // Check to make sure that this instance has been initialized with data + if (!getData() || (getDataSize() < 16)) + { + setLastError("LLImageJ2C uninitialized"); + res = true; // done + } + else + { + // Update the raw discard level + updateRawDiscardLevel(); + res = mImpl->decodeImpl(*this, *raw_imagep, decode_time, first_channel, max_channel_count); + } + } + + if (res) + { + if (!mDecoding) + { + // Failed + raw_imagep->deleteData(); + res = false; + } + else + { + mDecoding = false; + } + } + else + { + if (mDecoding) + { + LL_WARNS() << "decodeImpl failed but mDecoding is true" << LL_ENDL; + mDecoding = false; + } + } + + if (!mLastError.empty()) + { + LLImage::setLastError(mLastError); + } + + LLImageCompressionTester* tester = (LLImageCompressionTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); + if (tester) + { + // Decompression stat gathering + // Note that we *do not* take into account the decompression failures data so we might overestimate the time spent processing + + // Always add the decompression time to the stat + tester->updateDecompressionStats(elapsed.getElapsedTimeF32()) ; + if (res) + { + // The whole data stream is finally decompressed when res is returned as true + tester->updateDecompressionStats(this->getDataSize(), raw_imagep->getDataSize()) ; + } + } + + return res; } bool LLImageJ2C::encode(const LLImageRaw *raw_imagep, F32 encode_time) { - return encode(raw_imagep, NULL, encode_time); + return encode(raw_imagep, NULL, encode_time); } bool LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time) { - LLTimer elapsed; - resetLastError(); - bool res = mImpl->encodeImpl(*this, *raw_imagep, comment_text, encode_time, mReversible); - if (!mLastError.empty()) - { - LLImage::setLastError(mLastError); - } - - LLImageCompressionTester* tester = (LLImageCompressionTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); - if (tester) - { - // Compression stat gathering - // Note that we *do not* take into account the compression failures cases so we night overestimate the time spent processing - - // Always add the compression time to the stat - tester->updateCompressionStats(elapsed.getElapsedTimeF32()) ; - if (res) - { - // The whole data stream is finally compressed when res is returned as true - tester->updateCompressionStats(this->getDataSize(), raw_imagep->getDataSize()) ; - } - } - - return res; + LLTimer elapsed; + resetLastError(); + bool res = mImpl->encodeImpl(*this, *raw_imagep, comment_text, encode_time, mReversible); + if (!mLastError.empty()) + { + LLImage::setLastError(mLastError); + } + + LLImageCompressionTester* tester = (LLImageCompressionTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); + if (tester) + { + // Compression stat gathering + // Note that we *do not* take into account the compression failures cases so we night overestimate the time spent processing + + // Always add the compression time to the stat + tester->updateCompressionStats(elapsed.getElapsedTimeF32()) ; + if (res) + { + // The whole data stream is finally compressed when res is returned as true + tester->updateCompressionStats(this->getDataSize(), raw_imagep->getDataSize()) ; + } + } + + return res; } //static S32 LLImageJ2C::calcHeaderSizeJ2C() { - return FIRST_PACKET_SIZE; // Hack. just needs to be >= actual header size... + return FIRST_PACKET_SIZE; // Hack. just needs to be >= actual header size... } //static S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate) { - // Note: This provides an estimation for the first to last quality layer of a given discard level - // This is however an efficient approximation, as the true discard level boundary would be - // in general too big for fast fetching. - // For details about the equation used here, see https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study - - // Estimate the number of layers. This is consistent with what's done for j2c encoding in LLImageJ2CKDU::encodeImpl(). - S32 nb_layers = 1; - S32 surface = w*h; - S32 s = 64*64; - while (surface > s) - { - nb_layers++; - s *= 4; - } - F32 layer_factor = 3.0f * (7 - llclamp(nb_layers,1,6)); - - // Compute w/pow(2,discard_level) and h/pow(2,discard_level) - w >>= discard_level; - h >>= discard_level; - w = llmax(w, 1); - h = llmax(h, 1); - - // Temporary: compute both new and old range and pick one according to the settings TextureNewByteRange - // *TODO: Take the old code out once we have enough tests done - S32 bytes; - S32 new_bytes = (S32) (sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/layer_factor); - S32 old_bytes = (S32)((F32)(w*h*comp)*rate); - bytes = (LLImage::useNewByteRange() && (new_bytes < old_bytes) ? new_bytes : old_bytes); - bytes = llmax(bytes, calcHeaderSizeJ2C()); - return bytes; + // Note: This provides an estimation for the first to last quality layer of a given discard level + // This is however an efficient approximation, as the true discard level boundary would be + // in general too big for fast fetching. + // For details about the equation used here, see https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study + + // Estimate the number of layers. This is consistent with what's done for j2c encoding in LLImageJ2CKDU::encodeImpl(). + S32 nb_layers = 1; + S32 surface = w*h; + S32 s = 64*64; + while (surface > s) + { + nb_layers++; + s *= 4; + } + F32 layer_factor = 3.0f * (7 - llclamp(nb_layers,1,6)); + + // Compute w/pow(2,discard_level) and h/pow(2,discard_level) + w >>= discard_level; + h >>= discard_level; + w = llmax(w, 1); + h = llmax(h, 1); + + // Temporary: compute both new and old range and pick one according to the settings TextureNewByteRange + // *TODO: Take the old code out once we have enough tests done + S32 bytes; + S32 new_bytes = (S32) (sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/layer_factor); + S32 old_bytes = (S32)((F32)(w*h*comp)*rate); + bytes = (LLImage::useNewByteRange() && (new_bytes < old_bytes) ? new_bytes : old_bytes); + bytes = llmax(bytes, calcHeaderSizeJ2C()); + return bytes; } S32 LLImageJ2C::calcHeaderSize() { - return calcHeaderSizeJ2C(); + return calcHeaderSizeJ2C(); } // calcDataSize() returns how many bytes to read to load discard_level (including header) S32 LLImageJ2C::calcDataSize(S32 discard_level) { - discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL); - if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth()) - || (mDataSizes[0] == 0)) - { - mAreaUsedForDataSizeCalcs = getHeight() * getWidth(); - - S32 level = MAX_DISCARD_LEVEL; // Start at the highest discard - while ( level >= 0 ) - { - mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate); - level--; - } - } - return mDataSizes[discard_level]; + discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL); + if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth()) + || (mDataSizes[0] == 0)) + { + mAreaUsedForDataSizeCalcs = getHeight() * getWidth(); + + S32 level = MAX_DISCARD_LEVEL; // Start at the highest discard + while ( level >= 0 ) + { + mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate); + level--; + } + } + return mDataSizes[discard_level]; } S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes) { - llassert(bytes >= 0); - S32 discard_level = 0; - if (bytes == 0) - { - return MAX_DISCARD_LEVEL; - } - while (1) - { - S32 bytes_needed = calcDataSize(discard_level); - // Use TextureReverseByteRange percent (see settings.xml) of the optimal size to qualify as correct rendering for the given discard level - if (bytes >= (bytes_needed*LLImage::getReverseByteRangePercent()/100)) - { - break; - } - discard_level++; - if (discard_level >= MAX_DISCARD_LEVEL) - { - break; - } - } - return discard_level; + llassert(bytes >= 0); + S32 discard_level = 0; + if (bytes == 0) + { + return MAX_DISCARD_LEVEL; + } + while (1) + { + S32 bytes_needed = calcDataSize(discard_level); + // Use TextureReverseByteRange percent (see settings.xml) of the optimal size to qualify as correct rendering for the given discard level + if (bytes >= (bytes_needed*LLImage::getReverseByteRangePercent()/100)) + { + break; + } + discard_level++; + if (discard_level >= MAX_DISCARD_LEVEL) + { + break; + } + } + return discard_level; } void LLImageJ2C::setMaxBytes(S32 max_bytes) { - mMaxBytes = max_bytes; + mMaxBytes = max_bytes; } void LLImageJ2C::setReversible(const bool reversible) { - mReversible = reversible; + mReversible = reversible; } bool LLImageJ2C::loadAndValidate(const std::string &filename) { - bool res = true; - - resetLastError(); - - S32 file_size = 0; - LLAPRFile infile ; - infile.open(filename, LL_APR_RB, NULL, &file_size); - apr_file_t* apr_file = infile.getFileHandle() ; - if (!apr_file) - { - setLastError("Unable to open file for reading", filename); - res = false; - } - else if (file_size == 0) - { - setLastError("File is empty",filename); - res = false; - } - else - { - U8 *data = (U8*)ll_aligned_malloc_16(file_size); - if (!data) - { - infile.close(); - setLastError("Out of memory", filename); - res = false; - } - else - { - apr_size_t bytes_read = file_size; - apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read - infile.close(); - - if (s != APR_SUCCESS || (S32)bytes_read != file_size) - { - ll_aligned_free_16(data); - setLastError("Unable to read entire file"); - res = false; - } - else - { - res = validate(data, file_size); - } - } - } - - if (!mLastError.empty()) - { - LLImage::setLastError(mLastError); - } - - return res; + bool res = true; + + resetLastError(); + + S32 file_size = 0; + LLAPRFile infile ; + infile.open(filename, LL_APR_RB, NULL, &file_size); + apr_file_t* apr_file = infile.getFileHandle() ; + if (!apr_file) + { + setLastError("Unable to open file for reading", filename); + res = false; + } + else if (file_size == 0) + { + setLastError("File is empty",filename); + res = false; + } + else + { + U8 *data = (U8*)ll_aligned_malloc_16(file_size); + if (!data) + { + infile.close(); + setLastError("Out of memory", filename); + res = false; + } + else + { + apr_size_t bytes_read = file_size; + apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read + infile.close(); + + if (s != APR_SUCCESS || (S32)bytes_read != file_size) + { + ll_aligned_free_16(data); + setLastError("Unable to read entire file"); + res = false; + } + else + { + res = validate(data, file_size); + } + } + } + + if (!mLastError.empty()) + { + LLImage::setLastError(mLastError); + } + + return res; } bool LLImageJ2C::validate(U8 *data, U32 file_size) { - resetLastError(); - - LLImageDataLock lock(this); - - setData(data, file_size); - - bool res = updateData(); - if ( res ) - { - // Check to make sure that this instance has been initialized with data - if (!getData() || (0 == getDataSize())) - { - setLastError("LLImageJ2C uninitialized"); - res = false; - } - else - { - res = mImpl->getMetadata(*this); - } - } - - if (!mLastError.empty()) - { - LLImage::setLastError(mLastError); - } - return res; + resetLastError(); + + LLImageDataLock lock(this); + + setData(data, file_size); + + bool res = updateData(); + if ( res ) + { + // Check to make sure that this instance has been initialized with data + if (!getData() || (0 == getDataSize())) + { + setLastError("LLImageJ2C uninitialized"); + res = false; + } + else + { + res = mImpl->getMetadata(*this); + } + } + + if (!mLastError.empty()) + { + LLImage::setLastError(mLastError); + } + return res; } void LLImageJ2C::decodeFailed() { - mDecoding = false; + mDecoding = false; } void LLImageJ2C::updateRawDiscardLevel() { - mRawDiscardLevel = mMaxBytes ? calcDiscardLevelBytes(mMaxBytes) : mDiscardLevel; + mRawDiscardLevel = mMaxBytes ? calcDiscardLevelBytes(mMaxBytes) : mDiscardLevel; } LLImageJ2CImpl::~LLImageJ2CImpl() @@ -465,126 +465,126 @@ LLImageJ2CImpl::~LLImageJ2CImpl() //---------------------------------------------------------------------------------------------- // Start of LLImageCompressionTester //---------------------------------------------------------------------------------------------- -LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTesterBasic(sTesterName) +LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTesterBasic(sTesterName) { - addMetric("Time Decompression (s)"); - addMetric("Volume In Decompression (kB)"); - addMetric("Volume Out Decompression (kB)"); - addMetric("Decompression Ratio (x:1)"); - addMetric("Perf Decompression (kB/s)"); - - addMetric("Time Compression (s)"); - addMetric("Volume In Compression (kB)"); - addMetric("Volume Out Compression (kB)"); - addMetric("Compression Ratio (x:1)"); - addMetric("Perf Compression (kB/s)"); - - mRunBytesInDecompression = 0; - mRunBytesOutDecompression = 0; - mRunBytesInCompression = 0; - - mTotalBytesInDecompression = 0; - mTotalBytesOutDecompression = 0; - mTotalBytesInCompression = 0; - mTotalBytesOutCompression = 0; - - mTotalTimeDecompression = 0.0f; - mTotalTimeCompression = 0.0f; - mRunTimeDecompression = 0.0f; + addMetric("Time Decompression (s)"); + addMetric("Volume In Decompression (kB)"); + addMetric("Volume Out Decompression (kB)"); + addMetric("Decompression Ratio (x:1)"); + addMetric("Perf Decompression (kB/s)"); + + addMetric("Time Compression (s)"); + addMetric("Volume In Compression (kB)"); + addMetric("Volume Out Compression (kB)"); + addMetric("Compression Ratio (x:1)"); + addMetric("Perf Compression (kB/s)"); + + mRunBytesInDecompression = 0; + mRunBytesOutDecompression = 0; + mRunBytesInCompression = 0; + + mTotalBytesInDecompression = 0; + mTotalBytesOutDecompression = 0; + mTotalBytesInCompression = 0; + mTotalBytesOutCompression = 0; + + mTotalTimeDecompression = 0.0f; + mTotalTimeCompression = 0.0f; + mRunTimeDecompression = 0.0f; } LLImageCompressionTester::~LLImageCompressionTester() { - outputTestResults(); - LLImageJ2C::sTesterp = NULL; + outputTestResults(); + LLImageJ2C::sTesterp = NULL; } -//virtual -void LLImageCompressionTester::outputTestRecord(LLSD *sd) -{ - std::string currentLabel = getCurrentLabelName(); - - F32 decompressionPerf = 0.0f; - F32 compressionPerf = 0.0f; - F32 decompressionRate = 0.0f; - F32 compressionRate = 0.0f; - - F32 totalkBInDecompression = (F32)(mTotalBytesInDecompression) / 1000.f; - F32 totalkBOutDecompression = (F32)(mTotalBytesOutDecompression) / 1000.f; - F32 totalkBInCompression = (F32)(mTotalBytesInCompression) / 1000.f; - F32 totalkBOutCompression = (F32)(mTotalBytesOutCompression) / 1000.f; - - if (!is_approx_zero(mTotalTimeDecompression)) - { - decompressionPerf = totalkBInDecompression / mTotalTimeDecompression; - } - if (!is_approx_zero(totalkBInDecompression)) - { - decompressionRate = totalkBOutDecompression / totalkBInDecompression; - } - if (!is_approx_zero(mTotalTimeCompression)) - { - compressionPerf = totalkBInCompression / mTotalTimeCompression; - } - if (!is_approx_zero(totalkBOutCompression)) - { - compressionRate = totalkBInCompression / totalkBOutCompression; - } - - (*sd)[currentLabel]["Time Decompression (s)"] = (LLSD::Real)mTotalTimeDecompression; - (*sd)[currentLabel]["Volume In Decompression (kB)"] = (LLSD::Real)totalkBInDecompression; - (*sd)[currentLabel]["Volume Out Decompression (kB)"]= (LLSD::Real)totalkBOutDecompression; - (*sd)[currentLabel]["Decompression Ratio (x:1)"] = (LLSD::Real)decompressionRate; - (*sd)[currentLabel]["Perf Decompression (kB/s)"] = (LLSD::Real)decompressionPerf; - - (*sd)[currentLabel]["Time Compression (s)"] = (LLSD::Real)mTotalTimeCompression; - (*sd)[currentLabel]["Volume In Compression (kB)"] = (LLSD::Real)totalkBInCompression; - (*sd)[currentLabel]["Volume Out Compression (kB)"] = (LLSD::Real)totalkBOutCompression; - (*sd)[currentLabel]["Compression Ratio (x:1)"] = (LLSD::Real)compressionRate; - (*sd)[currentLabel]["Perf Compression (kB/s)"] = (LLSD::Real)compressionPerf; +//virtual +void LLImageCompressionTester::outputTestRecord(LLSD *sd) +{ + std::string currentLabel = getCurrentLabelName(); + + F32 decompressionPerf = 0.0f; + F32 compressionPerf = 0.0f; + F32 decompressionRate = 0.0f; + F32 compressionRate = 0.0f; + + F32 totalkBInDecompression = (F32)(mTotalBytesInDecompression) / 1000.f; + F32 totalkBOutDecompression = (F32)(mTotalBytesOutDecompression) / 1000.f; + F32 totalkBInCompression = (F32)(mTotalBytesInCompression) / 1000.f; + F32 totalkBOutCompression = (F32)(mTotalBytesOutCompression) / 1000.f; + + if (!is_approx_zero(mTotalTimeDecompression)) + { + decompressionPerf = totalkBInDecompression / mTotalTimeDecompression; + } + if (!is_approx_zero(totalkBInDecompression)) + { + decompressionRate = totalkBOutDecompression / totalkBInDecompression; + } + if (!is_approx_zero(mTotalTimeCompression)) + { + compressionPerf = totalkBInCompression / mTotalTimeCompression; + } + if (!is_approx_zero(totalkBOutCompression)) + { + compressionRate = totalkBInCompression / totalkBOutCompression; + } + + (*sd)[currentLabel]["Time Decompression (s)"] = (LLSD::Real)mTotalTimeDecompression; + (*sd)[currentLabel]["Volume In Decompression (kB)"] = (LLSD::Real)totalkBInDecompression; + (*sd)[currentLabel]["Volume Out Decompression (kB)"]= (LLSD::Real)totalkBOutDecompression; + (*sd)[currentLabel]["Decompression Ratio (x:1)"] = (LLSD::Real)decompressionRate; + (*sd)[currentLabel]["Perf Decompression (kB/s)"] = (LLSD::Real)decompressionPerf; + + (*sd)[currentLabel]["Time Compression (s)"] = (LLSD::Real)mTotalTimeCompression; + (*sd)[currentLabel]["Volume In Compression (kB)"] = (LLSD::Real)totalkBInCompression; + (*sd)[currentLabel]["Volume Out Compression (kB)"] = (LLSD::Real)totalkBOutCompression; + (*sd)[currentLabel]["Compression Ratio (x:1)"] = (LLSD::Real)compressionRate; + (*sd)[currentLabel]["Perf Compression (kB/s)"] = (LLSD::Real)compressionPerf; } -void LLImageCompressionTester::updateCompressionStats(const F32 deltaTime) +void LLImageCompressionTester::updateCompressionStats(const F32 deltaTime) { - mTotalTimeCompression += deltaTime; + mTotalTimeCompression += deltaTime; } -void LLImageCompressionTester::updateCompressionStats(const S32 bytesCompress, const S32 bytesRaw) +void LLImageCompressionTester::updateCompressionStats(const S32 bytesCompress, const S32 bytesRaw) { - mTotalBytesInCompression += bytesRaw; - mRunBytesInCompression += bytesRaw; - mTotalBytesOutCompression += bytesCompress; - if (mRunBytesInCompression > (1000000)) - { - // Output everything - outputTestResults(); - // Reset the compression data of the run - mRunBytesInCompression = 0; - } + mTotalBytesInCompression += bytesRaw; + mRunBytesInCompression += bytesRaw; + mTotalBytesOutCompression += bytesCompress; + if (mRunBytesInCompression > (1000000)) + { + // Output everything + outputTestResults(); + // Reset the compression data of the run + mRunBytesInCompression = 0; + } } -void LLImageCompressionTester::updateDecompressionStats(const F32 deltaTime) +void LLImageCompressionTester::updateDecompressionStats(const F32 deltaTime) { - mTotalTimeDecompression += deltaTime; + mTotalTimeDecompression += deltaTime; } -void LLImageCompressionTester::updateDecompressionStats(const S32 bytesIn, const S32 bytesOut) +void LLImageCompressionTester::updateDecompressionStats(const S32 bytesIn, const S32 bytesOut) { - mTotalBytesInDecompression += bytesIn; - mRunBytesInDecompression += bytesIn; - mTotalBytesOutDecompression += bytesOut; - mRunBytesOutDecompression += bytesOut; - //if (mRunBytesInDecompression > (1000000)) - if (mRunBytesOutDecompression > (10000000)) - //if ((mTotalTimeDecompression - mRunTimeDecompression) >= (5.0f)) - { - // Output everything - outputTestResults(); - // Reset the decompression data of the run - mRunBytesInDecompression = 0; - mRunBytesOutDecompression = 0; - mRunTimeDecompression = mTotalTimeDecompression; - } + mTotalBytesInDecompression += bytesIn; + mRunBytesInDecompression += bytesIn; + mTotalBytesOutDecompression += bytesOut; + mRunBytesOutDecompression += bytesOut; + //if (mRunBytesInDecompression > (1000000)) + if (mRunBytesOutDecompression > (10000000)) + //if ((mTotalTimeDecompression - mRunTimeDecompression) >= (5.0f)) + { + // Output everything + outputTestResults(); + // Reset the decompression data of the run + mRunBytesInDecompression = 0; + mRunBytesOutDecompression = 0; + mRunTimeDecompression = mTotalTimeDecompression; + } } //---------------------------------------------------------------------------------------------- diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h index b30df6f776..2a32e10bac 100644 --- a/indra/llimage/llimagej2c.h +++ b/indra/llimage/llimagej2c.h @@ -1,25 +1,25 @@ -/** +/** * @file llimagej2c.h * @brief Image implmenation for jpeg2000. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -41,93 +41,93 @@ class LLImageCompressionTester ; class LLImageJ2C : public LLImageFormatted { protected: - virtual ~LLImageJ2C(); + virtual ~LLImageJ2C(); public: - LLImageJ2C(); - - // Base class overrides - /*virtual*/ std::string getExtension() { return std::string("j2c"); } - /*virtual*/ bool updateData(); - /*virtual*/ bool decode(LLImageRaw *raw_imagep, F32 decode_time); - /*virtual*/ bool decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count); - /*virtual*/ bool encode(const LLImageRaw *raw_imagep, F32 encode_time); - /*virtual*/ S32 calcHeaderSize(); - /*virtual*/ S32 calcDataSize(S32 discard_level = 0); - /*virtual*/ S32 calcDiscardLevelBytes(S32 bytes); - /*virtual*/ S8 getRawDiscardLevel(); - // Override these so that we don't try to set a global variable from a DLL - /*virtual*/ void resetLastError(); - /*virtual*/ void setLastError(const std::string& message, const std::string& filename = std::string()); - - bool initDecode(LLImageRaw &raw_image, int discard_level, int* region); - bool initEncode(LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels); - - // Encode with comment text - bool encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time=0.0); - - bool validate(U8 *data, U32 file_size); - bool loadAndValidate(const std::string &filename); - - // Encode accessors - void setReversible(const bool reversible); // Use non-lossy? - void setMaxBytes(S32 max_bytes); - S32 getMaxBytes() const { return mMaxBytes; } - - static S32 calcHeaderSizeJ2C(); - static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = DEFAULT_COMPRESSION_RATE); - - static std::string getEngineInfo(); + LLImageJ2C(); + + // Base class overrides + /*virtual*/ std::string getExtension() { return std::string("j2c"); } + /*virtual*/ bool updateData(); + /*virtual*/ bool decode(LLImageRaw *raw_imagep, F32 decode_time); + /*virtual*/ bool decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count); + /*virtual*/ bool encode(const LLImageRaw *raw_imagep, F32 encode_time); + /*virtual*/ S32 calcHeaderSize(); + /*virtual*/ S32 calcDataSize(S32 discard_level = 0); + /*virtual*/ S32 calcDiscardLevelBytes(S32 bytes); + /*virtual*/ S8 getRawDiscardLevel(); + // Override these so that we don't try to set a global variable from a DLL + /*virtual*/ void resetLastError(); + /*virtual*/ void setLastError(const std::string& message, const std::string& filename = std::string()); + + bool initDecode(LLImageRaw &raw_image, int discard_level, int* region); + bool initEncode(LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels); + + // Encode with comment text + bool encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time=0.0); + + bool validate(U8 *data, U32 file_size); + bool loadAndValidate(const std::string &filename); + + // Encode accessors + void setReversible(const bool reversible); // Use non-lossy? + void setMaxBytes(S32 max_bytes); + S32 getMaxBytes() const { return mMaxBytes; } + + static S32 calcHeaderSizeJ2C(); + static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = DEFAULT_COMPRESSION_RATE); + + static std::string getEngineInfo(); protected: - friend class LLImageJ2CImpl; - friend class LLImageJ2COJ; - friend class LLImageJ2CKDU; - friend class LLImageCompressionTester; - void decodeFailed(); - void updateRawDiscardLevel(); - - S32 mMaxBytes; // Maximum number of bytes of data to use... - - S32 mDataSizes[MAX_DISCARD_LEVEL+1]; // Size of data required to reach a given level - U32 mAreaUsedForDataSizeCalcs; // Height * width used to calculate mDataSizes - - S8 mRawDiscardLevel; - F32 mRate; - bool mReversible; - std::unique_ptr<LLImageJ2CImpl> mImpl; - std::string mLastError; + friend class LLImageJ2CImpl; + friend class LLImageJ2COJ; + friend class LLImageJ2CKDU; + friend class LLImageCompressionTester; + void decodeFailed(); + void updateRawDiscardLevel(); + + S32 mMaxBytes; // Maximum number of bytes of data to use... + + S32 mDataSizes[MAX_DISCARD_LEVEL+1]; // Size of data required to reach a given level + U32 mAreaUsedForDataSizeCalcs; // Height * width used to calculate mDataSizes + + S8 mRawDiscardLevel; + F32 mRate; + bool mReversible; + std::unique_ptr<LLImageJ2CImpl> mImpl; + std::string mLastError; // Image compression/decompression tester - static LLImageCompressionTester* sTesterp; + static LLImageCompressionTester* sTesterp; }; // Derive from this class to implement JPEG2000 decoding class LLImageJ2CImpl { public: - virtual ~LLImageJ2CImpl(); + virtual ~LLImageJ2CImpl(); protected: - // Find out the image size and number of channels. - // Return value: - // true: image size and number of channels was determined - // false: error on decode - virtual bool getMetadata(LLImageJ2C &base) = 0; - // Decode the raw image optionally aborting (to continue later) after - // decode_time seconds. Decode at most max_channel_count and start - // decoding channel first_channel. - // Return value: - // true: decoding complete (even if it failed) - // false: time expired while decoding - virtual bool decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) = 0; - virtual bool encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0, - bool reversible=false) = 0; - virtual bool initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL) = 0; - virtual bool initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0) = 0; - - virtual std::string getEngineInfo() const = 0; - - friend class LLImageJ2C; + // Find out the image size and number of channels. + // Return value: + // true: image size and number of channels was determined + // false: error on decode + virtual bool getMetadata(LLImageJ2C &base) = 0; + // Decode the raw image optionally aborting (to continue later) after + // decode_time seconds. Decode at most max_channel_count and start + // decoding channel first_channel. + // Return value: + // true: decoding complete (even if it failed) + // false: time expired while decoding + virtual bool decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) = 0; + virtual bool encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0, + bool reversible=false) = 0; + virtual bool initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL) = 0; + virtual bool initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0) = 0; + + virtual std::string getEngineInfo() const = 0; + + friend class LLImageJ2C; }; #define LINDEN_J2C_COMMENT_PREFIX "LL_" // Used by LLAppearanceUtility @@ -142,15 +142,15 @@ class LLImageCompressionTester : public LLMetricPerformanceTesterBasic public: LLImageCompressionTester(); ~LLImageCompressionTester(); - + void updateDecompressionStats(const F32 deltaTime) ; void updateDecompressionStats(const S32 bytesIn, const S32 bytesOut) ; void updateCompressionStats(const F32 deltaTime) ; void updateCompressionStats(const S32 bytesIn, const S32 bytesOut) ; - + protected: /*virtual*/ void outputTestRecord(LLSD* sd); - + private: // // Data size @@ -159,9 +159,9 @@ class LLImageCompressionTester : public LLMetricPerformanceTesterBasic U32 mTotalBytesOutDecompression; // Total bytes produced by decompressor U32 mTotalBytesInCompression; // Total bytes fed to compressor U32 mTotalBytesOutCompression; // Total bytes produced by compressor - U32 mRunBytesInDecompression; // Bytes fed to decompressor in this run - U32 mRunBytesOutDecompression; // Bytes produced by the decompressor in this run - U32 mRunBytesInCompression; // Bytes fed to compressor in this run + U32 mRunBytesInDecompression; // Bytes fed to decompressor in this run + U32 mRunBytesOutDecompression; // Bytes produced by the decompressor in this run + U32 mRunBytesInCompression; // Bytes fed to compressor in this run // // Time // diff --git a/indra/llimage/llimagejpeg.cpp b/indra/llimage/llimagejpeg.cpp index a35171601a..0e7ec365d4 100644 --- a/indra/llimage/llimagejpeg.cpp +++ b/indra/llimage/llimagejpeg.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llimagejpeg.cpp * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -31,133 +31,133 @@ #include "llerror.h" #include "llexception.h" -jmp_buf LLImageJPEG::sSetjmpBuffer ; -LLImageJPEG::LLImageJPEG(S32 quality) -: LLImageFormatted(IMG_CODEC_JPEG), - mOutputBuffer( NULL ), - mOutputBufferSize( 0 ), - mEncodeQuality( quality ) // on a scale from 1 to 100 +jmp_buf LLImageJPEG::sSetjmpBuffer ; +LLImageJPEG::LLImageJPEG(S32 quality) +: LLImageFormatted(IMG_CODEC_JPEG), + mOutputBuffer( NULL ), + mOutputBufferSize( 0 ), + mEncodeQuality( quality ) // on a scale from 1 to 100 { } LLImageJPEG::~LLImageJPEG() { - llassert( !mOutputBuffer ); // Should already be deleted at end of encode. - delete[] mOutputBuffer; + llassert( !mOutputBuffer ); // Should already be deleted at end of encode. + delete[] mOutputBuffer; } bool LLImageJPEG::updateData() { - resetLastError(); - - LLImageDataLock lock(this); - - // Check to make sure that this instance has been initialized with data - if (!getData() || (0 == getDataSize())) - { - setLastError("Uninitialized instance of LLImageJPEG"); - return false; - } - - //////////////////////////////////////// - // Step 1: allocate and initialize JPEG decompression object - - // This struct contains the JPEG decompression parameters and pointers to - // working space (which is allocated as needed by the JPEG library). - struct jpeg_decompress_struct cinfo; - cinfo.client_data = this; - - struct jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - - // Customize with our own callbacks - jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller - jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message - jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message - - // - //try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error - //so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao - // - if(setjmp(sSetjmpBuffer)) - { - jpeg_destroy_decompress(&cinfo); - return false; - } - try - { - // Now we can initialize the JPEG decompression object. - jpeg_create_decompress(&cinfo); - - //////////////////////////////////////// - // Step 2: specify data source - // (Code is modified version of jpeg_stdio_src(); - if (cinfo.src == NULL) - { - cinfo.src = (struct jpeg_source_mgr *) - (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, - sizeof(struct jpeg_source_mgr)); - } - cinfo.src->init_source = &LLImageJPEG::decodeInitSource; - cinfo.src->fill_input_buffer = &LLImageJPEG::decodeFillInputBuffer; - cinfo.src->skip_input_data = &LLImageJPEG::decodeSkipInputData; - cinfo.src->resync_to_restart = jpeg_resync_to_restart; // For now, use default method, but we should be able to do better. - cinfo.src->term_source = &LLImageJPEG::decodeTermSource; - - cinfo.src->bytes_in_buffer = getDataSize(); - cinfo.src->next_input_byte = getData(); - - //////////////////////////////////////// - // Step 3: read file parameters with jpeg_read_header() - jpeg_read_header( &cinfo, true ); - - // Data set by jpeg_read_header - setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB) - - /* - // More data set by jpeg_read_header - cinfo.num_components; - cinfo.jpeg_color_space; // Colorspace of image - cinfo.saw_JFIF_marker; // true if a JFIF APP0 marker was seen - cinfo.JFIF_major_version; // Version information from JFIF marker - cinfo.JFIF_minor_version; // - cinfo.density_unit; // Resolution data from JFIF marker - cinfo.X_density; - cinfo.Y_density; - cinfo.saw_Adobe_marker; // true if an Adobe APP14 marker was seen - cinfo.Adobe_transform; // Color transform code from Adobe marker - */ - } - catch (int) - { - jpeg_destroy_decompress(&cinfo); - - return false; - } - //////////////////////////////////////// - // Step 4: Release JPEG decompression object - jpeg_destroy_decompress(&cinfo); - - return true; + resetLastError(); + + LLImageDataLock lock(this); + + // Check to make sure that this instance has been initialized with data + if (!getData() || (0 == getDataSize())) + { + setLastError("Uninitialized instance of LLImageJPEG"); + return false; + } + + //////////////////////////////////////// + // Step 1: allocate and initialize JPEG decompression object + + // This struct contains the JPEG decompression parameters and pointers to + // working space (which is allocated as needed by the JPEG library). + struct jpeg_decompress_struct cinfo; + cinfo.client_data = this; + + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + + // Customize with our own callbacks + jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller + jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message + jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message + + // + //try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error + //so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao + // + if(setjmp(sSetjmpBuffer)) + { + jpeg_destroy_decompress(&cinfo); + return false; + } + try + { + // Now we can initialize the JPEG decompression object. + jpeg_create_decompress(&cinfo); + + //////////////////////////////////////// + // Step 2: specify data source + // (Code is modified version of jpeg_stdio_src(); + if (cinfo.src == NULL) + { + cinfo.src = (struct jpeg_source_mgr *) + (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, + sizeof(struct jpeg_source_mgr)); + } + cinfo.src->init_source = &LLImageJPEG::decodeInitSource; + cinfo.src->fill_input_buffer = &LLImageJPEG::decodeFillInputBuffer; + cinfo.src->skip_input_data = &LLImageJPEG::decodeSkipInputData; + cinfo.src->resync_to_restart = jpeg_resync_to_restart; // For now, use default method, but we should be able to do better. + cinfo.src->term_source = &LLImageJPEG::decodeTermSource; + + cinfo.src->bytes_in_buffer = getDataSize(); + cinfo.src->next_input_byte = getData(); + + //////////////////////////////////////// + // Step 3: read file parameters with jpeg_read_header() + jpeg_read_header( &cinfo, true ); + + // Data set by jpeg_read_header + setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB) + + /* + // More data set by jpeg_read_header + cinfo.num_components; + cinfo.jpeg_color_space; // Colorspace of image + cinfo.saw_JFIF_marker; // true if a JFIF APP0 marker was seen + cinfo.JFIF_major_version; // Version information from JFIF marker + cinfo.JFIF_minor_version; // + cinfo.density_unit; // Resolution data from JFIF marker + cinfo.X_density; + cinfo.Y_density; + cinfo.saw_Adobe_marker; // true if an Adobe APP14 marker was seen + cinfo.Adobe_transform; // Color transform code from Adobe marker + */ + } + catch (int) + { + jpeg_destroy_decompress(&cinfo); + + return false; + } + //////////////////////////////////////// + // Step 4: Release JPEG decompression object + jpeg_destroy_decompress(&cinfo); + + return true; } // Initialize source --- called by jpeg_read_header // before any data is actually read. void LLImageJPEG::decodeInitSource( j_decompress_ptr cinfo ) { - // no work necessary here + // no work necessary here } // Fill the input buffer --- called whenever buffer is emptied. boolean LLImageJPEG::decodeFillInputBuffer( j_decompress_ptr cinfo ) { -// jpeg_source_mgr* src = cinfo->src; -// LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; +// jpeg_source_mgr* src = cinfo->src; +// LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; - // Should never get here, since we provide the entire buffer up front. - ERREXIT(cinfo, JERR_INPUT_EMPTY); + // Should never get here, since we provide the entire buffer up front. + ERREXIT(cinfo, JERR_INPUT_EMPTY); - return true; + return true; } // Skip data --- used to skip over a potentially large amount of @@ -171,8 +171,8 @@ boolean LLImageJPEG::decodeFillInputBuffer( j_decompress_ptr cinfo ) // buffer is the application writer's problem. void LLImageJPEG::decodeSkipInputData (j_decompress_ptr cinfo, long num_bytes) { - jpeg_source_mgr* src = cinfo->src; -// LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; + jpeg_source_mgr* src = cinfo->src; +// LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; src->next_input_byte += (size_t) num_bytes; src->bytes_in_buffer -= (size_t) num_bytes; @@ -187,161 +187,161 @@ void LLImageJPEG::decodeTermSource (j_decompress_ptr cinfo) // Returns true when done, whether or not decode was successful. bool LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time) { - llassert_always(raw_image); - - resetLastError(); - - LLImageDataLock lockIn(this); - LLImageDataLock lockOut(raw_image); - - // Check to make sure that this instance has been initialized with data - if (!getData() || (0 == getDataSize())) - { - setLastError("LLImageJPEG trying to decode an image with no data!"); - return true; // done - } - - S32 row_stride = 0; - U8* raw_image_data = NULL; - - //////////////////////////////////////// - // Step 1: allocate and initialize JPEG decompression object - - // This struct contains the JPEG decompression parameters and pointers to - // working space (which is allocated as needed by the JPEG library). - struct jpeg_decompress_struct cinfo; - - struct jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - - // Customize with our own callbacks - jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller - jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message - jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message - - // - //try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error - //so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao - // - if(setjmp(sSetjmpBuffer)) - { - jpeg_destroy_decompress(&cinfo); - return true; // done - } - try - { - // Now we can initialize the JPEG decompression object. - jpeg_create_decompress(&cinfo); - - //////////////////////////////////////// - // Step 2: specify data source - // (Code is modified version of jpeg_stdio_src(); - if (cinfo.src == NULL) - { - cinfo.src = (struct jpeg_source_mgr *) - (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, - sizeof(struct jpeg_source_mgr)); - } - cinfo.src->init_source = &LLImageJPEG::decodeInitSource; - cinfo.src->fill_input_buffer = &LLImageJPEG::decodeFillInputBuffer; - cinfo.src->skip_input_data = &LLImageJPEG::decodeSkipInputData; - cinfo.src->resync_to_restart = jpeg_resync_to_restart; // For now, use default method, but we should be able to do better. - cinfo.src->term_source = &LLImageJPEG::decodeTermSource; - cinfo.src->bytes_in_buffer = getDataSize(); - cinfo.src->next_input_byte = getData(); - - //////////////////////////////////////// - // Step 3: read file parameters with jpeg_read_header() - - jpeg_read_header(&cinfo, true); - - // We can ignore the return value from jpeg_read_header since - // (a) suspension is not possible with our data source, and - // (b) we passed true to reject a tables-only JPEG file as an error. - // See libjpeg.doc for more info. - - setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB) - - if (!raw_image->resize(getWidth(), getHeight(), getComponents())) - { - throw std::bad_alloc(); - } - raw_image_data = raw_image->getData(); - - - //////////////////////////////////////// - // Step 4: set parameters for decompression - cinfo.out_color_components = 3; - cinfo.out_color_space = JCS_RGB; - - - //////////////////////////////////////// - // Step 5: Start decompressor - - jpeg_start_decompress(&cinfo); - // We can ignore the return value since suspension is not possible - // with our data source. - - // We may need to do some setup of our own at this point before reading - // the data. After jpeg_start_decompress() we have the correct scaled - // output image dimensions available, as well as the output colormap - // if we asked for color quantization. - // In this example, we need to make an output work buffer of the right size. - - // JSAMPLEs per row in output buffer - row_stride = cinfo.output_width * cinfo.output_components; - - //////////////////////////////////////// - // Step 6: while (scan lines remain to be read) - // jpeg_read_scanlines(...); - - // Here we use the library's state variable cinfo.output_scanline as the - // loop counter, so that we don't have to keep track ourselves. - - // Move pointer to last line - raw_image_data += row_stride * (cinfo.output_height - 1); - - while (cinfo.output_scanline < cinfo.output_height) - { - // jpeg_read_scanlines expects an array of pointers to scanlines. - // Here the array is only one element long, but you could ask for - // more than one scanline at a time if that's more convenient. - - jpeg_read_scanlines(&cinfo, &raw_image_data, 1); - raw_image_data -= row_stride; // move pointer up a line - } - - //////////////////////////////////////// - // Step 7: Finish decompression - jpeg_finish_decompress(&cinfo); - - //////////////////////////////////////// - // Step 8: Release JPEG decompression object - jpeg_destroy_decompress(&cinfo); - } - - catch (std::bad_alloc&) - { - setLastError( "Out of memory"); - jpeg_destroy_decompress(&cinfo); - return true; // done - } - - catch (int) - { - jpeg_destroy_decompress(&cinfo); - return true; // done - } - - // Check to see whether any corrupt-data warnings occurred - if( jerr.num_warnings != 0 ) - { - // TODO: extract the warning to find out what went wrong. - setLastError( "Unable to decode JPEG image."); - return true; // done - } - - return true; + llassert_always(raw_image); + + resetLastError(); + + LLImageDataLock lockIn(this); + LLImageDataLock lockOut(raw_image); + + // Check to make sure that this instance has been initialized with data + if (!getData() || (0 == getDataSize())) + { + setLastError("LLImageJPEG trying to decode an image with no data!"); + return true; // done + } + + S32 row_stride = 0; + U8* raw_image_data = NULL; + + //////////////////////////////////////// + // Step 1: allocate and initialize JPEG decompression object + + // This struct contains the JPEG decompression parameters and pointers to + // working space (which is allocated as needed by the JPEG library). + struct jpeg_decompress_struct cinfo; + + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + + // Customize with our own callbacks + jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller + jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message + jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message + + // + //try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error + //so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao + // + if(setjmp(sSetjmpBuffer)) + { + jpeg_destroy_decompress(&cinfo); + return true; // done + } + try + { + // Now we can initialize the JPEG decompression object. + jpeg_create_decompress(&cinfo); + + //////////////////////////////////////// + // Step 2: specify data source + // (Code is modified version of jpeg_stdio_src(); + if (cinfo.src == NULL) + { + cinfo.src = (struct jpeg_source_mgr *) + (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, + sizeof(struct jpeg_source_mgr)); + } + cinfo.src->init_source = &LLImageJPEG::decodeInitSource; + cinfo.src->fill_input_buffer = &LLImageJPEG::decodeFillInputBuffer; + cinfo.src->skip_input_data = &LLImageJPEG::decodeSkipInputData; + cinfo.src->resync_to_restart = jpeg_resync_to_restart; // For now, use default method, but we should be able to do better. + cinfo.src->term_source = &LLImageJPEG::decodeTermSource; + cinfo.src->bytes_in_buffer = getDataSize(); + cinfo.src->next_input_byte = getData(); + + //////////////////////////////////////// + // Step 3: read file parameters with jpeg_read_header() + + jpeg_read_header(&cinfo, true); + + // We can ignore the return value from jpeg_read_header since + // (a) suspension is not possible with our data source, and + // (b) we passed true to reject a tables-only JPEG file as an error. + // See libjpeg.doc for more info. + + setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB) + + if (!raw_image->resize(getWidth(), getHeight(), getComponents())) + { + throw std::bad_alloc(); + } + raw_image_data = raw_image->getData(); + + + //////////////////////////////////////// + // Step 4: set parameters for decompression + cinfo.out_color_components = 3; + cinfo.out_color_space = JCS_RGB; + + + //////////////////////////////////////// + // Step 5: Start decompressor + + jpeg_start_decompress(&cinfo); + // We can ignore the return value since suspension is not possible + // with our data source. + + // We may need to do some setup of our own at this point before reading + // the data. After jpeg_start_decompress() we have the correct scaled + // output image dimensions available, as well as the output colormap + // if we asked for color quantization. + // In this example, we need to make an output work buffer of the right size. + + // JSAMPLEs per row in output buffer + row_stride = cinfo.output_width * cinfo.output_components; + + //////////////////////////////////////// + // Step 6: while (scan lines remain to be read) + // jpeg_read_scanlines(...); + + // Here we use the library's state variable cinfo.output_scanline as the + // loop counter, so that we don't have to keep track ourselves. + + // Move pointer to last line + raw_image_data += row_stride * (cinfo.output_height - 1); + + while (cinfo.output_scanline < cinfo.output_height) + { + // jpeg_read_scanlines expects an array of pointers to scanlines. + // Here the array is only one element long, but you could ask for + // more than one scanline at a time if that's more convenient. + + jpeg_read_scanlines(&cinfo, &raw_image_data, 1); + raw_image_data -= row_stride; // move pointer up a line + } + + //////////////////////////////////////// + // Step 7: Finish decompression + jpeg_finish_decompress(&cinfo); + + //////////////////////////////////////// + // Step 8: Release JPEG decompression object + jpeg_destroy_decompress(&cinfo); + } + + catch (std::bad_alloc&) + { + setLastError( "Out of memory"); + jpeg_destroy_decompress(&cinfo); + return true; // done + } + + catch (int) + { + jpeg_destroy_decompress(&cinfo); + return true; // done + } + + // Check to see whether any corrupt-data warnings occurred + if( jerr.num_warnings != 0 ) + { + // TODO: extract the warning to find out what went wrong. + setLastError( "Unable to decode JPEG image."); + return true; // done + } + + return true; } @@ -357,12 +357,12 @@ void LLImageJPEG::encodeInitDestination ( j_compress_ptr cinfo ) // Empty the output buffer --- called whenever buffer fills up. -// +// // In typical applications, this should write the entire output buffer // (ignoring the current state of next_output_byte & free_in_buffer), // reset the pointer & count to the start of the buffer, and return true // indicating that the buffer has been dumped. -// +// // In applications that need to be able to suspend compression due to output // overrun, a false return indicates that the buffer cannot be emptied now. // In this situation, the compressor will return to its caller (possibly with @@ -370,7 +370,7 @@ void LLImageJPEG::encodeInitDestination ( j_compress_ptr cinfo ) // application should resume compression after it has made more room in the // output buffer. Note that there are substantial restrictions on the use of // suspension --- see the documentation. -// +// // When suspending, the compressor will back up to a convenient restart point // (typically the start of the current MCU). next_output_byte & free_in_buffer // indicate where the restart point will be if the current call returns false. @@ -383,7 +383,7 @@ boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo ) // Should very rarely happen, since our output buffer is // as large as the input to start out with. - + // Double the buffer size; S32 new_buffer_size = self->mOutputBufferSize * 2; U8* new_buffer = new(std::nothrow) U8[ new_buffer_size ]; @@ -392,7 +392,7 @@ boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo ) self->setLastError("Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )"); LLTHROW(LLContinueError("Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )")); } - memcpy( new_buffer, self->mOutputBuffer, self->mOutputBufferSize ); /* Flawfinder: ignore */ + memcpy( new_buffer, self->mOutputBuffer, self->mOutputBufferSize ); /* Flawfinder: ignore */ delete[] self->mOutputBuffer; self->mOutputBuffer = new_buffer; @@ -405,35 +405,35 @@ boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo ) // Terminate destination --- called by jpeg_finish_compress // after all data has been written. Usually needs to flush buffer. -// +// // NB: *not* called by jpeg_abort or jpeg_destroy; surrounding // application must deal with any cleanup that should happen even // for error exit. void LLImageJPEG::encodeTermDestination( j_compress_ptr cinfo ) { - LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; + LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; - LLImageDataLock lock(self); + LLImageDataLock lock(self); - S32 file_bytes = (S32)(self->mOutputBufferSize - cinfo->dest->free_in_buffer); - self->allocateData(file_bytes); + S32 file_bytes = (S32)(self->mOutputBufferSize - cinfo->dest->free_in_buffer); + self->allocateData(file_bytes); - memcpy( self->getData(), self->mOutputBuffer, file_bytes ); /* Flawfinder: ignore */ + memcpy( self->getData(), self->mOutputBuffer, file_bytes ); /* Flawfinder: ignore */ } -// static -void LLImageJPEG::errorExit( j_common_ptr cinfo ) +// static +void LLImageJPEG::errorExit( j_common_ptr cinfo ) { - //LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; + //LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data; - // Always display the message - (*cinfo->err->output_message)(cinfo); + // Always display the message + (*cinfo->err->output_message)(cinfo); - // Let the memory manager delete any temp files - jpeg_destroy(cinfo); + // Let the memory manager delete any temp files + jpeg_destroy(cinfo); - // Return control to the setjmp point - longjmp(sSetjmpBuffer, 1) ; + // Return control to the setjmp point + longjmp(sSetjmpBuffer, 1) ; } // Decide whether to emit a trace or warning message. @@ -444,231 +444,231 @@ void LLImageJPEG::errorExit( j_common_ptr cinfo ) // 2,3,...: successively more detailed tracing messages. // An application might override this method if it wanted to abort on warnings // or change the policy about which messages to display. -// static +// static void LLImageJPEG::errorEmitMessage( j_common_ptr cinfo, int msg_level ) { struct jpeg_error_mgr * err = cinfo->err; - if (msg_level < 0) + if (msg_level < 0) { - // It's a warning message. Since corrupt files may generate many warnings, - // the policy implemented here is to show only the first warning, - // unless trace_level >= 3. - if (err->num_warnings == 0 || err->trace_level >= 3) - { - (*err->output_message) (cinfo); - } - // Always count warnings in num_warnings. - err->num_warnings++; + // It's a warning message. Since corrupt files may generate many warnings, + // the policy implemented here is to show only the first warning, + // unless trace_level >= 3. + if (err->num_warnings == 0 || err->trace_level >= 3) + { + (*err->output_message) (cinfo); + } + // Always count warnings in num_warnings. + err->num_warnings++; } - else + else { - // It's a trace message. Show it if trace_level >= msg_level. - if (err->trace_level >= msg_level) - { - (*err->output_message) (cinfo); - } + // It's a trace message. Show it if trace_level >= msg_level. + if (err->trace_level >= msg_level) + { + (*err->output_message) (cinfo); + } } } -// static +// static void LLImageJPEG::errorOutputMessage( j_common_ptr cinfo ) { - // Create the message - char buffer[JMSG_LENGTH_MAX]; /* Flawfinder: ignore */ - (*cinfo->err->format_message) (cinfo, buffer); + // Create the message + char buffer[JMSG_LENGTH_MAX]; /* Flawfinder: ignore */ + (*cinfo->err->format_message) (cinfo, buffer); - std::string error = buffer ; - LLImage::setLastError(error); + std::string error = buffer ; + LLImage::setLastError(error); - bool is_decode = (cinfo->is_decompressor != 0); - LL_WARNS() << "LLImageJPEG " << (is_decode ? "decode " : "encode ") << " failed: " << buffer << LL_ENDL; + bool is_decode = (cinfo->is_decompressor != 0); + LL_WARNS() << "LLImageJPEG " << (is_decode ? "decode " : "encode ") << " failed: " << buffer << LL_ENDL; } bool LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time ) { - llassert_always(raw_image); - - resetLastError(); - - LLImageDataSharedLock lockIn(raw_image); - LLImageDataLock lockOut(this); - - switch( raw_image->getComponents() ) - { - case 1: - case 3: - break; - default: - setLastError("Unable to encode a JPEG image that doesn't have 1 or 3 components."); - return false; - } - - setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents()); - - // Allocate a temporary buffer big enough to hold the entire compressed image (and then some) - // (Note: we make it bigger in emptyOutputBuffer() if we need to) - delete[] mOutputBuffer; - mOutputBufferSize = getWidth() * getHeight() * getComponents() + 1024; - mOutputBuffer = new(std::nothrow) U8[ mOutputBufferSize ]; - if (mOutputBuffer == NULL) - { - mOutputBufferSize = 0; - setLastError("Failed to allocate output buffer"); - return false; - } - - const U8* raw_image_data = NULL; - S32 row_stride = 0; - - //////////////////////////////////////// - // Step 1: allocate and initialize JPEG compression object - - // This struct contains the JPEG compression parameters and pointers to - // working space (which is allocated as needed by the JPEG library). - struct jpeg_compress_struct cinfo; - cinfo.client_data = this; - - // We have to set up the error handler first, in case the initialization - // step fails. (Unlikely, but it could happen if you are out of memory.) - // This routine fills in the contents of struct jerr, and returns jerr's - // address which we place into the link field in cinfo. - struct jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - - // Customize with our own callbacks - jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller - jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message - jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message - - // - //try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error - //so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao - // - if( setjmp(sSetjmpBuffer) ) - { - // If we get here, the JPEG code has signaled an error. - // We need to clean up the JPEG object, close the input file, and return. - jpeg_destroy_compress(&cinfo); - delete[] mOutputBuffer; - mOutputBuffer = NULL; - mOutputBufferSize = 0; - return false; - } - - try - { - - // Now we can initialize the JPEG compression object. - jpeg_create_compress(&cinfo); - - //////////////////////////////////////// - // Step 2: specify data destination - // (code is a modified form of jpeg_stdio_dest() ) - if( cinfo.dest == NULL) - { - cinfo.dest = (struct jpeg_destination_mgr *) - (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, - sizeof(struct jpeg_destination_mgr)); - } - cinfo.dest->next_output_byte = mOutputBuffer; // => next byte to write in buffer - cinfo.dest->free_in_buffer = mOutputBufferSize; // # of byte spaces remaining in buffer - cinfo.dest->init_destination = &LLImageJPEG::encodeInitDestination; - cinfo.dest->empty_output_buffer = &LLImageJPEG::encodeEmptyOutputBuffer; - cinfo.dest->term_destination = &LLImageJPEG::encodeTermDestination; - - //////////////////////////////////////// - // Step 3: set parameters for compression - // - // First we supply a description of the input image. - // Four fields of the cinfo struct must be filled in: - - cinfo.image_width = getWidth(); // image width and height, in pixels - cinfo.image_height = getHeight(); - - switch( getComponents() ) - { - case 1: - cinfo.input_components = 1; // # of color components per pixel - cinfo.in_color_space = JCS_GRAYSCALE; // colorspace of input image - break; - case 3: - cinfo.input_components = 3; // # of color components per pixel - cinfo.in_color_space = JCS_RGB; // colorspace of input image - break; - default: - setLastError("Unable to encode a JPEG image that doesn't have 1 or 3 components."); - return false; - } - - // Now use the library's routine to set default compression parameters. - // (You must set at least cinfo.in_color_space before calling this, - // since the defaults depend on the source color space.) - jpeg_set_defaults(&cinfo); - - // Now you can set any non-default parameters you wish to. - jpeg_set_quality(&cinfo, mEncodeQuality, true ); // limit to baseline-JPEG values - - //////////////////////////////////////// - // Step 4: Start compressor - // - // true ensures that we will write a complete interchange-JPEG file. - // Pass true unless you are very sure of what you're doing. - - jpeg_start_compress(&cinfo, true); - - //////////////////////////////////////// - // Step 5: while (scan lines remain to be written) - // jpeg_write_scanlines(...); - - // Here we use the library's state variable cinfo.next_scanline as the - // loop counter, so that we don't have to keep track ourselves. - // To keep things simple, we pass one scanline per call; you can pass - // more if you wish, though. - - row_stride = getWidth() * getComponents(); // JSAMPLEs per row in image_buffer - - // NOTE: For compatibility with LLImage, we need to invert the rows. - raw_image_data = raw_image->getData(); - - const U8* last_row_data = raw_image_data + (getHeight()-1) * row_stride; - - JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s] - while (cinfo.next_scanline < cinfo.image_height) - { - // jpeg_write_scanlines expects an array of pointers to scanlines. - // Here the array is only one element long, but you could pass - // more than one scanline at a time if that's more convenient. - - //Ugly const uncast here (jpeg_write_scanlines should take a const* but doesn't) - //row_pointer[0] = (JSAMPROW)(raw_image_data + (cinfo.next_scanline * row_stride)); - row_pointer[0] = (JSAMPROW)(last_row_data - (cinfo.next_scanline * row_stride)); - - jpeg_write_scanlines(&cinfo, row_pointer, 1); - } - - //////////////////////////////////////// - // Step 6: Finish compression - jpeg_finish_compress(&cinfo); - - // After finish_compress, we can release the temp output buffer. - delete[] mOutputBuffer; - mOutputBuffer = NULL; - mOutputBufferSize = 0; - - //////////////////////////////////////// - // Step 7: release JPEG compression object - jpeg_destroy_compress(&cinfo); - } - - catch(int) - { - jpeg_destroy_compress(&cinfo); - delete[] mOutputBuffer; - mOutputBuffer = NULL; - mOutputBufferSize = 0; - return false; - } - - return true; + llassert_always(raw_image); + + resetLastError(); + + LLImageDataSharedLock lockIn(raw_image); + LLImageDataLock lockOut(this); + + switch( raw_image->getComponents() ) + { + case 1: + case 3: + break; + default: + setLastError("Unable to encode a JPEG image that doesn't have 1 or 3 components."); + return false; + } + + setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents()); + + // Allocate a temporary buffer big enough to hold the entire compressed image (and then some) + // (Note: we make it bigger in emptyOutputBuffer() if we need to) + delete[] mOutputBuffer; + mOutputBufferSize = getWidth() * getHeight() * getComponents() + 1024; + mOutputBuffer = new(std::nothrow) U8[ mOutputBufferSize ]; + if (mOutputBuffer == NULL) + { + mOutputBufferSize = 0; + setLastError("Failed to allocate output buffer"); + return false; + } + + const U8* raw_image_data = NULL; + S32 row_stride = 0; + + //////////////////////////////////////// + // Step 1: allocate and initialize JPEG compression object + + // This struct contains the JPEG compression parameters and pointers to + // working space (which is allocated as needed by the JPEG library). + struct jpeg_compress_struct cinfo; + cinfo.client_data = this; + + // We have to set up the error handler first, in case the initialization + // step fails. (Unlikely, but it could happen if you are out of memory.) + // This routine fills in the contents of struct jerr, and returns jerr's + // address which we place into the link field in cinfo. + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + + // Customize with our own callbacks + jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller + jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message + jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message + + // + //try/catch will crash on Mac and Linux if LLImageJPEG::errorExit throws an error + //so as instead, we use setjmp/longjmp to avoid this crash, which is the best we can get. --bao + // + if( setjmp(sSetjmpBuffer) ) + { + // If we get here, the JPEG code has signaled an error. + // We need to clean up the JPEG object, close the input file, and return. + jpeg_destroy_compress(&cinfo); + delete[] mOutputBuffer; + mOutputBuffer = NULL; + mOutputBufferSize = 0; + return false; + } + + try + { + + // Now we can initialize the JPEG compression object. + jpeg_create_compress(&cinfo); + + //////////////////////////////////////// + // Step 2: specify data destination + // (code is a modified form of jpeg_stdio_dest() ) + if( cinfo.dest == NULL) + { + cinfo.dest = (struct jpeg_destination_mgr *) + (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, + sizeof(struct jpeg_destination_mgr)); + } + cinfo.dest->next_output_byte = mOutputBuffer; // => next byte to write in buffer + cinfo.dest->free_in_buffer = mOutputBufferSize; // # of byte spaces remaining in buffer + cinfo.dest->init_destination = &LLImageJPEG::encodeInitDestination; + cinfo.dest->empty_output_buffer = &LLImageJPEG::encodeEmptyOutputBuffer; + cinfo.dest->term_destination = &LLImageJPEG::encodeTermDestination; + + //////////////////////////////////////// + // Step 3: set parameters for compression + // + // First we supply a description of the input image. + // Four fields of the cinfo struct must be filled in: + + cinfo.image_width = getWidth(); // image width and height, in pixels + cinfo.image_height = getHeight(); + + switch( getComponents() ) + { + case 1: + cinfo.input_components = 1; // # of color components per pixel + cinfo.in_color_space = JCS_GRAYSCALE; // colorspace of input image + break; + case 3: + cinfo.input_components = 3; // # of color components per pixel + cinfo.in_color_space = JCS_RGB; // colorspace of input image + break; + default: + setLastError("Unable to encode a JPEG image that doesn't have 1 or 3 components."); + return false; + } + + // Now use the library's routine to set default compression parameters. + // (You must set at least cinfo.in_color_space before calling this, + // since the defaults depend on the source color space.) + jpeg_set_defaults(&cinfo); + + // Now you can set any non-default parameters you wish to. + jpeg_set_quality(&cinfo, mEncodeQuality, true ); // limit to baseline-JPEG values + + //////////////////////////////////////// + // Step 4: Start compressor + // + // true ensures that we will write a complete interchange-JPEG file. + // Pass true unless you are very sure of what you're doing. + + jpeg_start_compress(&cinfo, true); + + //////////////////////////////////////// + // Step 5: while (scan lines remain to be written) + // jpeg_write_scanlines(...); + + // Here we use the library's state variable cinfo.next_scanline as the + // loop counter, so that we don't have to keep track ourselves. + // To keep things simple, we pass one scanline per call; you can pass + // more if you wish, though. + + row_stride = getWidth() * getComponents(); // JSAMPLEs per row in image_buffer + + // NOTE: For compatibility with LLImage, we need to invert the rows. + raw_image_data = raw_image->getData(); + + const U8* last_row_data = raw_image_data + (getHeight()-1) * row_stride; + + JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s] + while (cinfo.next_scanline < cinfo.image_height) + { + // jpeg_write_scanlines expects an array of pointers to scanlines. + // Here the array is only one element long, but you could pass + // more than one scanline at a time if that's more convenient. + + //Ugly const uncast here (jpeg_write_scanlines should take a const* but doesn't) + //row_pointer[0] = (JSAMPROW)(raw_image_data + (cinfo.next_scanline * row_stride)); + row_pointer[0] = (JSAMPROW)(last_row_data - (cinfo.next_scanline * row_stride)); + + jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + //////////////////////////////////////// + // Step 6: Finish compression + jpeg_finish_compress(&cinfo); + + // After finish_compress, we can release the temp output buffer. + delete[] mOutputBuffer; + mOutputBuffer = NULL; + mOutputBufferSize = 0; + + //////////////////////////////////////// + // Step 7: release JPEG compression object + jpeg_destroy_compress(&cinfo); + } + + catch(int) + { + jpeg_destroy_compress(&cinfo); + delete[] mOutputBuffer; + mOutputBuffer = NULL; + mOutputBufferSize = 0; + return false; + } + + return true; } diff --git a/indra/llimage/llimagejpeg.h b/indra/llimage/llimagejpeg.h index d674b40b8f..add6657117 100644 --- a/indra/llimage/llimagejpeg.h +++ b/indra/llimage/llimagejpeg.h @@ -1,25 +1,25 @@ -/** +/** * @file llimagejpeg.h * @brief This class compresses and decompresses JPEG files * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -45,41 +45,41 @@ extern "C" { class LLImageJPEG : public LLImageFormatted { protected: - virtual ~LLImageJPEG(); - + virtual ~LLImageJPEG(); + public: - LLImageJPEG(S32 quality = 75); + LLImageJPEG(S32 quality = 75); - /*virtual*/ std::string getExtension() { return std::string("jpg"); } - /*virtual*/ bool updateData(); - /*virtual*/ bool decode(LLImageRaw* raw_image, F32 decode_time); - /*virtual*/ bool encode(const LLImageRaw* raw_image, F32 encode_time); + /*virtual*/ std::string getExtension() { return std::string("jpg"); } + /*virtual*/ bool updateData(); + /*virtual*/ bool decode(LLImageRaw* raw_image, F32 decode_time); + /*virtual*/ bool encode(const LLImageRaw* raw_image, F32 encode_time); - void setEncodeQuality( S32 q ) { mEncodeQuality = q; } // on a scale from 1 to 100 - S32 getEncodeQuality() { return mEncodeQuality; } + void setEncodeQuality( S32 q ) { mEncodeQuality = q; } // on a scale from 1 to 100 + S32 getEncodeQuality() { return mEncodeQuality; } - // Callbacks registered with jpeglib - static void encodeInitDestination ( j_compress_ptr cinfo ); - static boolean encodeEmptyOutputBuffer(j_compress_ptr cinfo); - static void encodeTermDestination(j_compress_ptr cinfo); + // Callbacks registered with jpeglib + static void encodeInitDestination ( j_compress_ptr cinfo ); + static boolean encodeEmptyOutputBuffer(j_compress_ptr cinfo); + static void encodeTermDestination(j_compress_ptr cinfo); - static void decodeInitSource(j_decompress_ptr cinfo); - static boolean decodeFillInputBuffer(j_decompress_ptr cinfo); - static void decodeSkipInputData(j_decompress_ptr cinfo, long num_bytes); - static void decodeTermSource(j_decompress_ptr cinfo); + static void decodeInitSource(j_decompress_ptr cinfo); + static boolean decodeFillInputBuffer(j_decompress_ptr cinfo); + static void decodeSkipInputData(j_decompress_ptr cinfo, long num_bytes); + static void decodeTermSource(j_decompress_ptr cinfo); - static void errorExit(j_common_ptr cinfo); - static void errorEmitMessage(j_common_ptr cinfo, int msg_level); - static void errorOutputMessage(j_common_ptr cinfo); + static void errorExit(j_common_ptr cinfo); + static void errorEmitMessage(j_common_ptr cinfo, int msg_level); + static void errorOutputMessage(j_common_ptr cinfo); protected: - U8* mOutputBuffer; // temp buffer used during encoding - S32 mOutputBufferSize; // bytes in mOuputBuffer + U8* mOutputBuffer; // temp buffer used during encoding + S32 mOutputBufferSize; // bytes in mOuputBuffer - S32 mEncodeQuality; // on a scale from 1 to 100 + S32 mEncodeQuality; // on a scale from 1 to 100 private: - static jmp_buf sSetjmpBuffer; // To allow the library to abort. + static jmp_buf sSetjmpBuffer; // To allow the library to abort. }; #endif // LL_LLIMAGEJPEG_H diff --git a/indra/llimage/llimagepng.cpp b/indra/llimage/llimagepng.cpp index 5d956bfb4e..d75084cf08 100644 --- a/indra/llimage/llimagepng.cpp +++ b/indra/llimage/llimagepng.cpp @@ -5,21 +5,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -27,6 +27,7 @@ #include "linden_common.h" #include "stdtypes.h" #include "llerror.h" +#include "llexception.h" #include "llimage.h" #include "llpngwrapper.h" @@ -51,33 +52,48 @@ bool LLImagePNG::updateData() { resetLastError(); - LLImageDataLock lock(this); - - // Check to make sure that this instance has been initialized with data - if (!getData() || (0 == getDataSize())) + try + { + LLImageDataLock lock(this); + + // Check to make sure that this instance has been initialized with data + if (!getData() || (0 == getDataSize())) + { + setLastError("Uninitialized instance of LLImagePNG"); + return false; + } + + // Decode the PNG data and extract sizing information + LLPngWrapper pngWrapper; + if (!pngWrapper.isValidPng(getData())) + { + setLastError("LLImagePNG data does not have a valid PNG header!"); + return false; + } + + LLPngWrapper::ImageInfo infop; + if (!pngWrapper.readPng(getData(), getDataSize(), NULL, &infop)) + { + setLastError(pngWrapper.getErrorMessage()); + return false; + } + + setSize(infop.mWidth, infop.mHeight, infop.mComponents); + } + catch (const LLContinueError& msg) { - setLastError("Uninitialized instance of LLImagePNG"); + setLastError(msg.what()); + LOG_UNHANDLED_EXCEPTION(""); + return false; + } + catch (...) + { + setLastError("LLImagePNG"); + LOG_UNHANDLED_EXCEPTION(""); return false; } - // Decode the PNG data and extract sizing information - LLPngWrapper pngWrapper; - if (!pngWrapper.isValidPng(getData())) - { - setLastError("LLImagePNG data does not have a valid PNG header!"); - return false; - } - - LLPngWrapper::ImageInfo infop; - if (! pngWrapper.readPng(getData(), getDataSize(), NULL, &infop)) - { - setLastError(pngWrapper.getErrorMessage()); - return false; - } - - setSize(infop.mWidth, infop.mHeight, infop.mComponents); - - return true; + return true; } // Virtual @@ -85,7 +101,7 @@ bool LLImagePNG::updateData() // used within SecondLife. bool LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time) { - llassert_always(raw_image); + llassert_always(raw_image); resetLastError(); @@ -99,63 +115,63 @@ bool LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time) return false; } - // Decode the PNG data into the raw image - LLPngWrapper pngWrapper; - if (!pngWrapper.isValidPng(getData())) - { - setLastError("LLImagePNG data does not have a valid PNG header!"); - return false; - } - - if (! pngWrapper.readPng(getData(), getDataSize(), raw_image)) - { - setLastError(pngWrapper.getErrorMessage()); - return false; - } - - return true; + // Decode the PNG data into the raw image + LLPngWrapper pngWrapper; + if (!pngWrapper.isValidPng(getData())) + { + setLastError("LLImagePNG data does not have a valid PNG header!"); + return false; + } + + if (! pngWrapper.readPng(getData(), getDataSize(), raw_image)) + { + setLastError(pngWrapper.getErrorMessage()); + return false; + } + + return true; } // Virtual // Encode the in memory RGB image into PNG format. bool LLImagePNG::encode(const LLImageRaw* raw_image, F32 encode_time) { - llassert_always(raw_image); + llassert_always(raw_image); resetLastError(); - LLImageDataSharedLock lockIn(raw_image); - LLImageDataLock lockOut(this); - - // Image logical size - setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents()); - - // Temporary buffer to hold the encoded image. Note: the final image - // size should be much smaller due to compression. - U32 bufferSize = getWidth() * getHeight() * getComponents() + 8192; - U8* tmpWriteBuffer = new(std::nothrow) U8[ bufferSize ]; - if (!tmpWriteBuffer) - { - setLastError("LLImagePNG::out of memory"); - return false; - } - - // Delegate actual encoding work to wrapper - LLPngWrapper pngWrapper; - if (!pngWrapper.writePng(raw_image, tmpWriteBuffer, bufferSize)) - { - setLastError(pngWrapper.getErrorMessage()); - delete[] tmpWriteBuffer; - return false; - } - - // Resize internal buffer and copy from temp - U32 encodedSize = pngWrapper.getFinalSize(); - allocateData(encodedSize); - memcpy(getData(), tmpWriteBuffer, encodedSize); - - delete[] tmpWriteBuffer; - - return true; + LLImageDataSharedLock lockIn(raw_image); + LLImageDataLock lockOut(this); + + // Image logical size + setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents()); + + // Temporary buffer to hold the encoded image. Note: the final image + // size should be much smaller due to compression. + U32 bufferSize = getWidth() * getHeight() * getComponents() + 8192; + U8* tmpWriteBuffer = new(std::nothrow) U8[ bufferSize ]; + if (!tmpWriteBuffer) + { + setLastError("LLImagePNG::out of memory"); + return false; + } + + // Delegate actual encoding work to wrapper + LLPngWrapper pngWrapper; + if (!pngWrapper.writePng(raw_image, tmpWriteBuffer, bufferSize)) + { + setLastError(pngWrapper.getErrorMessage()); + delete[] tmpWriteBuffer; + return false; + } + + // Resize internal buffer and copy from temp + U32 encodedSize = pngWrapper.getFinalSize(); + allocateData(encodedSize); + memcpy(getData(), tmpWriteBuffer, encodedSize); + + delete[] tmpWriteBuffer; + + return true; } diff --git a/indra/llimage/llimagepng.h b/indra/llimage/llimagepng.h index ef16f2996f..cc03b85a2d 100644 --- a/indra/llimage/llimagepng.h +++ b/indra/llimage/llimagepng.h @@ -1,24 +1,24 @@ -/* +/* * @file llimagepng.h * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -32,15 +32,15 @@ class LLImagePNG : public LLImageFormatted { protected: - ~LLImagePNG(); + ~LLImagePNG(); public: - LLImagePNG(); + LLImagePNG(); - /*virtual*/ std::string getExtension() { return std::string("png"); } - /*virtual*/ bool updateData(); - /*virtual*/ bool decode(LLImageRaw* raw_image, F32 decode_time); - /*virtual*/ bool encode(const LLImageRaw* raw_image, F32 encode_time); + /*virtual*/ std::string getExtension() { return std::string("png"); } + /*virtual*/ bool updateData(); + /*virtual*/ bool decode(LLImageRaw* raw_image, F32 decode_time); + /*virtual*/ bool encode(const LLImageRaw* raw_image, F32 encode_time); }; #endif diff --git a/indra/llimage/llimagetga.cpp b/indra/llimage/llimagetga.cpp index 0201cc6413..b168f343e7 100644 --- a/indra/llimage/llimagetga.cpp +++ b/indra/llimage/llimagetga.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llimagetga.cpp * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,20 +34,20 @@ // For expanding 5-bit pixel values to 8-bit with best rounding // static -const U8 LLImageTGA::s5to8bits[32] = - { - 0, 8, 16, 25, 33, 41, 49, 58, - 66, 74, 82, 90, 99, 107, 115, 123, - 132, 140, 148, 156, 165, 173, 181, 189, - 197, 206, 214, 222, 230, 239, 247, 255 - }; +const U8 LLImageTGA::s5to8bits[32] = + { + 0, 8, 16, 25, 33, 41, 49, 58, + 66, 74, 82, 90, 99, 107, 115, 123, + 132, 140, 148, 156, 165, 173, 181, 189, + 197, 206, 214, 222, 230, 239, 247, 255 + }; inline void LLImageTGA::decodeTruecolorPixel15( U8* dst, const U8* src ) { // We expand 5 bit data to 8 bit sample width. // The format of the 16-bit (LSB first) input word is // xRRRRRGGGGGBBBBB - U32 t = U32(src[0]) + (U32(src[1]) << 8); + U32 t = U32(src[0]) + (U32(src[1]) << 8); dst[2] = s5to8bits[t & 0x1F]; // blue t >>= 5; dst[1] = s5to8bits[t & 0x1F]; // green @@ -55,996 +55,996 @@ inline void LLImageTGA::decodeTruecolorPixel15( U8* dst, const U8* src ) dst[0] = s5to8bits[t & 0x1F]; // red } -LLImageTGA::LLImageTGA() - : LLImageFormatted(IMG_CODEC_TGA), - mColorMap( NULL ), - mColorMapStart( 0 ), - mColorMapLength( 0 ), - mColorMapBytesPerEntry( 0 ), - mIs15Bit( false ), - - mAttributeBits(0), - mColorMapDepth(0), - mColorMapIndexHi(0), - mColorMapIndexLo(0), - mColorMapLengthHi(0), - mColorMapLengthLo(0), - mColorMapType(0), - mDataOffset(0), - mHeightHi(0), - mHeightLo(0), - mIDLength(0), - mImageType(0), - mInterleave(0), - mOriginRightBit(0), - mOriginTopBit(0), - mPixelSize(0), - mWidthHi(0), - mWidthLo(0), - mXOffsetHi(0), - mXOffsetLo(0), - mYOffsetHi(0), - mYOffsetLo(0) +LLImageTGA::LLImageTGA() + : LLImageFormatted(IMG_CODEC_TGA), + mColorMap( NULL ), + mColorMapStart( 0 ), + mColorMapLength( 0 ), + mColorMapBytesPerEntry( 0 ), + mIs15Bit( false ), + + mAttributeBits(0), + mColorMapDepth(0), + mColorMapIndexHi(0), + mColorMapIndexLo(0), + mColorMapLengthHi(0), + mColorMapLengthLo(0), + mColorMapType(0), + mDataOffset(0), + mHeightHi(0), + mHeightLo(0), + mIDLength(0), + mImageType(0), + mInterleave(0), + mOriginRightBit(0), + mOriginTopBit(0), + mPixelSize(0), + mWidthHi(0), + mWidthLo(0), + mXOffsetHi(0), + mXOffsetLo(0), + mYOffsetHi(0), + mYOffsetLo(0) { } -LLImageTGA::LLImageTGA(const std::string& file_name) - : LLImageFormatted(IMG_CODEC_TGA), - mColorMap( NULL ), - mColorMapStart( 0 ), - mColorMapLength( 0 ), - mColorMapBytesPerEntry( 0 ), - mIs15Bit( false ) +LLImageTGA::LLImageTGA(const std::string& file_name) + : LLImageFormatted(IMG_CODEC_TGA), + mColorMap( NULL ), + mColorMapStart( 0 ), + mColorMapLength( 0 ), + mColorMapBytesPerEntry( 0 ), + mIs15Bit( false ) { - loadFile(file_name); + loadFile(file_name); } LLImageTGA::~LLImageTGA() { - delete [] mColorMap; + delete [] mColorMap; } bool LLImageTGA::updateData() { - resetLastError(); - - LLImageDataLock lock(this); - - // Check to make sure that this instance has been initialized with data - if (!getData() || (0 == getDataSize())) - { - setLastError("LLImageTGA uninitialized"); - return false; - } - - // Pull image information from the header... - U8 flags; - U8 junk[256]; - - /**************************************************************************** - ** - ** For more information about the original Truevision TGA(tm) file format, - ** or for additional information about the new extensions to the - ** Truevision TGA file, refer to the "Truevision TGA File Format - ** Specification Version 2.0" available from Truevision or your - ** Truevision dealer. - ** - ** FILE STRUCTURE FOR THE ORIGINAL TRUEVISION TGA FILE - ** FIELD 1 : NUMBER OF CHARACTERS IN ID FIELD (1 BYTES) - ** FIELD 2 : COLOR MAP TYPE (1 BYTES) - ** FIELD 3 : IMAGE TYPE CODE (1 BYTES) - ** = 0 NO IMAGE DATA INCLUDED - ** = (0001) 1 UNCOMPRESSED, COLOR-MAPPED IMAGE - ** = (0010) 2 UNCOMPRESSED, TRUE-COLOR IMAGE - ** = (0011) 3 UNCOMPRESSED, BLACK AND WHITE IMAGE - ** = (1001) 9 RUN-LENGTH ENCODED COLOR-MAPPED IMAGE - ** = (1010) 10 RUN-LENGTH ENCODED TRUE-COLOR IMAGE - ** = (1011) 11 RUN-LENGTH ENCODED BLACK AND WHITE IMAGE - ** FIELD 4 : COLOR MAP SPECIFICATION (5 BYTES) - ** 4.1 : COLOR MAP ORIGIN (2 BYTES) - ** 4.2 : COLOR MAP LENGTH (2 BYTES) - ** 4.3 : COLOR MAP ENTRY SIZE (2 BYTES) - ** FIELD 5 : IMAGE SPECIFICATION (10 BYTES) - ** 5.1 : X-ORIGIN OF IMAGE (2 BYTES) - ** 5.2 : Y-ORIGIN OF IMAGE (2 BYTES) - ** 5.3 : WIDTH OF IMAGE (2 BYTES) - ** 5.4 : HEIGHT OF IMAGE (2 BYTES) - ** 5.5 : IMAGE PIXEL SIZE (1 BYTE) - ** 5.6 : IMAGE DESCRIPTOR BYTE (1 BYTE) - ** FIELD 6 : IMAGE ID FIELD (LENGTH SPECIFIED BY FIELD 1) - ** FIELD 7 : COLOR MAP DATA (BIT WIDTH SPECIFIED BY FIELD 4.3 AND - ** NUMBER OF COLOR MAP ENTRIES SPECIFIED IN FIELD 4.2) - ** FIELD 8 : IMAGE DATA FIELD (WIDTH AND HEIGHT SPECIFIED IN - ** FIELD 5.3 AND 5.4) - ****************************************************************************/ - - mDataOffset = 0; - mIDLength = *(getData()+mDataOffset++); - mColorMapType = *(getData()+mDataOffset++); - mImageType = *(getData()+mDataOffset++); - mColorMapIndexLo = *(getData()+mDataOffset++); - mColorMapIndexHi = *(getData()+mDataOffset++); - mColorMapLengthLo = *(getData()+mDataOffset++); - mColorMapLengthHi = *(getData()+mDataOffset++); - mColorMapDepth = *(getData()+mDataOffset++); - mXOffsetLo = *(getData()+mDataOffset++); - mXOffsetHi = *(getData()+mDataOffset++); - mYOffsetLo = *(getData()+mDataOffset++); - mYOffsetHi = *(getData()+mDataOffset++); - mWidthLo = *(getData()+mDataOffset++); - mWidthHi = *(getData()+mDataOffset++); - mHeightLo = *(getData()+mDataOffset++); - mHeightHi = *(getData()+mDataOffset++); - mPixelSize = *(getData()+mDataOffset++); - flags = *(getData()+mDataOffset++); - mAttributeBits = flags & 0xf; - mOriginRightBit = (flags & 0x10) >> 4; - mOriginTopBit = (flags & 0x20) >> 5; - mInterleave = (flags & 0xc0) >> 6; - - switch( mImageType ) - { - case 0: - // No image data included in file - setLastError("Unable to load file. TGA file contains no image data."); - return false; - case 1: - // Colormapped uncompressed - if( 8 != mPixelSize ) - { - setLastError("Unable to load file. Colormapped images must have 8 bits per pixel."); - return false; - } - break; - case 2: - // Truecolor uncompressed - break; - case 3: - // Monochrome uncompressed - if( 8 != mPixelSize ) - { - setLastError("Unable to load file. Monochrome images must have 8 bits per pixel."); - return false; - } - break; - case 9: - // Colormapped, RLE - break; - case 10: - // Truecolor, RLE - break; - case 11: - // Monochrome, RLE - if( 8 != mPixelSize ) - { - setLastError("Unable to load file. Monochrome images must have 8 bits per pixel."); - return false; - } - break; - default: - setLastError("Unable to load file. Unrecoginzed TGA image type."); - return false; - } - - // discard the ID field, if any - if (mIDLength) - { - memcpy(junk, getData()+mDataOffset, mIDLength); /* Flawfinder: ignore */ - mDataOffset += mIDLength; - } - - // check to see if there's a colormap since even rgb files can have them - S32 color_map_bytes = 0; - if( (1 == mColorMapType) && (mColorMapDepth > 0) ) - { - mColorMapStart = (S32(mColorMapIndexHi) << 8) + mColorMapIndexLo; - mColorMapLength = (S32(mColorMapLengthHi) << 8) + mColorMapLengthLo; - - if( mColorMapDepth > 24 ) - { - mColorMapBytesPerEntry = 4; - } - else - if( mColorMapDepth > 16 ) - { - mColorMapBytesPerEntry = 3; - } - else - if( mColorMapDepth > 8 ) - { - mColorMapBytesPerEntry = 2; - } - else - { - mColorMapBytesPerEntry = 1; - } - color_map_bytes = mColorMapLength * mColorMapBytesPerEntry; - - // Note: although it's legal for TGA files to have color maps and not use them - // (some programs actually do this and use the color map for other ends), we'll - // only allocate memory for one if _we_ intend to use it. - if ( (1 == mImageType) || (9 == mImageType) ) - { - mColorMap = new(std::nothrow) U8[ color_map_bytes ]; - if (!mColorMap) - { + resetLastError(); + + LLImageDataLock lock(this); + + // Check to make sure that this instance has been initialized with data + if (!getData() || (0 == getDataSize())) + { + setLastError("LLImageTGA uninitialized"); + return false; + } + + // Pull image information from the header... + U8 flags; + U8 junk[256]; + + /**************************************************************************** + ** + ** For more information about the original Truevision TGA(tm) file format, + ** or for additional information about the new extensions to the + ** Truevision TGA file, refer to the "Truevision TGA File Format + ** Specification Version 2.0" available from Truevision or your + ** Truevision dealer. + ** + ** FILE STRUCTURE FOR THE ORIGINAL TRUEVISION TGA FILE + ** FIELD 1 : NUMBER OF CHARACTERS IN ID FIELD (1 BYTES) + ** FIELD 2 : COLOR MAP TYPE (1 BYTES) + ** FIELD 3 : IMAGE TYPE CODE (1 BYTES) + ** = 0 NO IMAGE DATA INCLUDED + ** = (0001) 1 UNCOMPRESSED, COLOR-MAPPED IMAGE + ** = (0010) 2 UNCOMPRESSED, TRUE-COLOR IMAGE + ** = (0011) 3 UNCOMPRESSED, BLACK AND WHITE IMAGE + ** = (1001) 9 RUN-LENGTH ENCODED COLOR-MAPPED IMAGE + ** = (1010) 10 RUN-LENGTH ENCODED TRUE-COLOR IMAGE + ** = (1011) 11 RUN-LENGTH ENCODED BLACK AND WHITE IMAGE + ** FIELD 4 : COLOR MAP SPECIFICATION (5 BYTES) + ** 4.1 : COLOR MAP ORIGIN (2 BYTES) + ** 4.2 : COLOR MAP LENGTH (2 BYTES) + ** 4.3 : COLOR MAP ENTRY SIZE (2 BYTES) + ** FIELD 5 : IMAGE SPECIFICATION (10 BYTES) + ** 5.1 : X-ORIGIN OF IMAGE (2 BYTES) + ** 5.2 : Y-ORIGIN OF IMAGE (2 BYTES) + ** 5.3 : WIDTH OF IMAGE (2 BYTES) + ** 5.4 : HEIGHT OF IMAGE (2 BYTES) + ** 5.5 : IMAGE PIXEL SIZE (1 BYTE) + ** 5.6 : IMAGE DESCRIPTOR BYTE (1 BYTE) + ** FIELD 6 : IMAGE ID FIELD (LENGTH SPECIFIED BY FIELD 1) + ** FIELD 7 : COLOR MAP DATA (BIT WIDTH SPECIFIED BY FIELD 4.3 AND + ** NUMBER OF COLOR MAP ENTRIES SPECIFIED IN FIELD 4.2) + ** FIELD 8 : IMAGE DATA FIELD (WIDTH AND HEIGHT SPECIFIED IN + ** FIELD 5.3 AND 5.4) + ****************************************************************************/ + + mDataOffset = 0; + mIDLength = *(getData()+mDataOffset++); + mColorMapType = *(getData()+mDataOffset++); + mImageType = *(getData()+mDataOffset++); + mColorMapIndexLo = *(getData()+mDataOffset++); + mColorMapIndexHi = *(getData()+mDataOffset++); + mColorMapLengthLo = *(getData()+mDataOffset++); + mColorMapLengthHi = *(getData()+mDataOffset++); + mColorMapDepth = *(getData()+mDataOffset++); + mXOffsetLo = *(getData()+mDataOffset++); + mXOffsetHi = *(getData()+mDataOffset++); + mYOffsetLo = *(getData()+mDataOffset++); + mYOffsetHi = *(getData()+mDataOffset++); + mWidthLo = *(getData()+mDataOffset++); + mWidthHi = *(getData()+mDataOffset++); + mHeightLo = *(getData()+mDataOffset++); + mHeightHi = *(getData()+mDataOffset++); + mPixelSize = *(getData()+mDataOffset++); + flags = *(getData()+mDataOffset++); + mAttributeBits = flags & 0xf; + mOriginRightBit = (flags & 0x10) >> 4; + mOriginTopBit = (flags & 0x20) >> 5; + mInterleave = (flags & 0xc0) >> 6; + + switch( mImageType ) + { + case 0: + // No image data included in file + setLastError("Unable to load file. TGA file contains no image data."); + return false; + case 1: + // Colormapped uncompressed + if( 8 != mPixelSize ) + { + setLastError("Unable to load file. Colormapped images must have 8 bits per pixel."); + return false; + } + break; + case 2: + // Truecolor uncompressed + break; + case 3: + // Monochrome uncompressed + if( 8 != mPixelSize ) + { + setLastError("Unable to load file. Monochrome images must have 8 bits per pixel."); + return false; + } + break; + case 9: + // Colormapped, RLE + break; + case 10: + // Truecolor, RLE + break; + case 11: + // Monochrome, RLE + if( 8 != mPixelSize ) + { + setLastError("Unable to load file. Monochrome images must have 8 bits per pixel."); + return false; + } + break; + default: + setLastError("Unable to load file. Unrecoginzed TGA image type."); + return false; + } + + // discard the ID field, if any + if (mIDLength) + { + memcpy(junk, getData()+mDataOffset, mIDLength); /* Flawfinder: ignore */ + mDataOffset += mIDLength; + } + + // check to see if there's a colormap since even rgb files can have them + S32 color_map_bytes = 0; + if( (1 == mColorMapType) && (mColorMapDepth > 0) ) + { + mColorMapStart = (S32(mColorMapIndexHi) << 8) + mColorMapIndexLo; + mColorMapLength = (S32(mColorMapLengthHi) << 8) + mColorMapLengthLo; + + if( mColorMapDepth > 24 ) + { + mColorMapBytesPerEntry = 4; + } + else + if( mColorMapDepth > 16 ) + { + mColorMapBytesPerEntry = 3; + } + else + if( mColorMapDepth > 8 ) + { + mColorMapBytesPerEntry = 2; + } + else + { + mColorMapBytesPerEntry = 1; + } + color_map_bytes = mColorMapLength * mColorMapBytesPerEntry; + + // Note: although it's legal for TGA files to have color maps and not use them + // (some programs actually do this and use the color map for other ends), we'll + // only allocate memory for one if _we_ intend to use it. + if ( (1 == mImageType) || (9 == mImageType) ) + { + mColorMap = new(std::nothrow) U8[ color_map_bytes ]; + if (!mColorMap) + { LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS() << "Out of Memory in bool LLImageTGA::updateData()" << LL_ENDL; - return false; - } - memcpy( mColorMap, getData() + mDataOffset, color_map_bytes ); /* Flawfinder: ignore */ - } - - mDataOffset += color_map_bytes; - } - - // heights are read as bytes to prevent endian problems - S32 height = (S32(mHeightHi) << 8) + mHeightLo; - S32 width = (S32(mWidthHi) << 8) + mWidthLo; - - // make sure that it's a pixel format that we understand - S32 bits_per_pixel; - if( mColorMap ) - { - bits_per_pixel = mColorMapDepth; - } - else - { - bits_per_pixel = mPixelSize; - } - - S32 components; - switch(bits_per_pixel) - { - case 24: - components = 3; - break; - case 32: - components = 4; -// Don't enforce this. ACDSee doesn't bother to set the attributes bits correctly. Arrgh! -// if( mAttributeBits != 8 ) -// { -// setLastError("Unable to load file. 32 bit TGA image does not have 8 bits of alpha."); -// return false; -// } - mAttributeBits = 8; - break; - case 15: - case 16: - components = 3; - mIs15Bit = true; // 16th bit is used for Targa hardware interupts and is ignored. - break; - case 8: - components = 1; - break; - default: - setLastError("Unable to load file. Unknown pixel size."); - return false; - } - setSize(width, height, components); - - return true; + LL_ERRS() << "Out of Memory in bool LLImageTGA::updateData()" << LL_ENDL; + return false; + } + memcpy( mColorMap, getData() + mDataOffset, color_map_bytes ); /* Flawfinder: ignore */ + } + + mDataOffset += color_map_bytes; + } + + // heights are read as bytes to prevent endian problems + S32 height = (S32(mHeightHi) << 8) + mHeightLo; + S32 width = (S32(mWidthHi) << 8) + mWidthLo; + + // make sure that it's a pixel format that we understand + S32 bits_per_pixel; + if( mColorMap ) + { + bits_per_pixel = mColorMapDepth; + } + else + { + bits_per_pixel = mPixelSize; + } + + S32 components; + switch(bits_per_pixel) + { + case 24: + components = 3; + break; + case 32: + components = 4; +// Don't enforce this. ACDSee doesn't bother to set the attributes bits correctly. Arrgh! +// if( mAttributeBits != 8 ) +// { +// setLastError("Unable to load file. 32 bit TGA image does not have 8 bits of alpha."); +// return false; +// } + mAttributeBits = 8; + break; + case 15: + case 16: + components = 3; + mIs15Bit = true; // 16th bit is used for Targa hardware interupts and is ignored. + break; + case 8: + components = 1; + break; + default: + setLastError("Unable to load file. Unknown pixel size."); + return false; + } + setSize(width, height, components); + + return true; } bool LLImageTGA::decode(LLImageRaw* raw_image, F32 decode_time) { - llassert_always(raw_image); - - LLImageDataSharedLock lockIn(this); - LLImageDataLock lockOut(raw_image); - - // Check to make sure that this instance has been initialized with data - if (!getData() || (0 == getDataSize())) - { - setLastError("LLImageTGA trying to decode an image with no data!"); - return false; - } - - // Copy everything after the header. - - if( !raw_image->resize(getWidth(), getHeight(), getComponents())) - { - setLastError("LLImageTGA::out of memory"); - return false; - } - - if( (getComponents() != 1) && - (getComponents() != 3) && - (getComponents() != 4) ) - { - setLastError("TGA images with a number of components other than 1, 3, and 4 are not supported."); - return false; - } - - if( raw_image->isBufferInvalid()) - { - setLastError("LLImageTGA::out of memory"); - return false; - } - - if( mOriginRightBit ) - { - setLastError("TGA images with origin on right side are not supported."); - return false; - } - - bool flipped = (mOriginTopBit != 0); - bool rle_compressed = ((mImageType & 0x08) != 0); - - if( mColorMap ) - { - return decodeColorMap( raw_image, rle_compressed, flipped ); - } - else - { - return decodeTruecolor( raw_image, rle_compressed, flipped ); - } + llassert_always(raw_image); + + LLImageDataSharedLock lockIn(this); + LLImageDataLock lockOut(raw_image); + + // Check to make sure that this instance has been initialized with data + if (!getData() || (0 == getDataSize())) + { + setLastError("LLImageTGA trying to decode an image with no data!"); + return false; + } + + // Copy everything after the header. + + if( !raw_image->resize(getWidth(), getHeight(), getComponents())) + { + setLastError("LLImageTGA::out of memory"); + return false; + } + + if( (getComponents() != 1) && + (getComponents() != 3) && + (getComponents() != 4) ) + { + setLastError("TGA images with a number of components other than 1, 3, and 4 are not supported."); + return false; + } + + if( raw_image->isBufferInvalid()) + { + setLastError("LLImageTGA::out of memory"); + return false; + } + + if( mOriginRightBit ) + { + setLastError("TGA images with origin on right side are not supported."); + return false; + } + + bool flipped = (mOriginTopBit != 0); + bool rle_compressed = ((mImageType & 0x08) != 0); + + if( mColorMap ) + { + return decodeColorMap( raw_image, rle_compressed, flipped ); + } + else + { + return decodeTruecolor( raw_image, rle_compressed, flipped ); + } } bool LLImageTGA::decodeTruecolor( LLImageRaw* raw_image, bool rle, bool flipped ) { - bool success = false; - bool alpha_opaque = false; - if( rle ) - { - - switch( getComponents() ) - { - case 1: - success = decodeTruecolorRle8( raw_image ); - break; - case 3: - if( mIs15Bit ) - { - success = decodeTruecolorRle15( raw_image ); - } - else - { - success = decodeTruecolorRle24( raw_image ); - } - break; - case 4: - success = decodeTruecolorRle32( raw_image, alpha_opaque ); - if (alpha_opaque) - { - // alpha was entirely opaque - // convert to 24 bit image - LLPointer<LLImageRaw> compacted_image = new LLImageRaw(raw_image->getWidth(), raw_image->getHeight(), 3); - if (compacted_image->isBufferInvalid()) - { - success = false; - break; - } - compacted_image->copy(raw_image); - raw_image->resize(raw_image->getWidth(), raw_image->getHeight(), 3); - raw_image->copy(compacted_image); - } - break; - } - } - else - { - bool alpha_opaque; - success = decodeTruecolorNonRle( raw_image, alpha_opaque ); - if (alpha_opaque && raw_image->getComponents() == 4) - { - // alpha was entirely opaque - // convert to 24 bit image - LLPointer<LLImageRaw> compacted_image = new LLImageRaw(raw_image->getWidth(), raw_image->getHeight(), 3); - if (compacted_image->isBufferInvalid()) - { - success = false; - } - else - { - compacted_image->copy(raw_image); - raw_image->resize(raw_image->getWidth(), raw_image->getHeight(), 3); - raw_image->copy(compacted_image); - } - } - } - - if( success && flipped ) - { - // This works because the Targa definition requires that RLE blocks never - // encode pixels from more than one scanline. - // (On the other hand, it's not as fast as writing separate flipped versions as - // we did with TruecolorNonRle.) - raw_image->verticalFlip(); - } - - return success; + bool success = false; + bool alpha_opaque = false; + if( rle ) + { + + switch( getComponents() ) + { + case 1: + success = decodeTruecolorRle8( raw_image ); + break; + case 3: + if( mIs15Bit ) + { + success = decodeTruecolorRle15( raw_image ); + } + else + { + success = decodeTruecolorRle24( raw_image ); + } + break; + case 4: + success = decodeTruecolorRle32( raw_image, alpha_opaque ); + if (alpha_opaque) + { + // alpha was entirely opaque + // convert to 24 bit image + LLPointer<LLImageRaw> compacted_image = new LLImageRaw(raw_image->getWidth(), raw_image->getHeight(), 3); + if (compacted_image->isBufferInvalid()) + { + success = false; + break; + } + compacted_image->copy(raw_image); + raw_image->resize(raw_image->getWidth(), raw_image->getHeight(), 3); + raw_image->copy(compacted_image); + } + break; + } + } + else + { + bool alpha_opaque; + success = decodeTruecolorNonRle( raw_image, alpha_opaque ); + if (alpha_opaque && raw_image->getComponents() == 4) + { + // alpha was entirely opaque + // convert to 24 bit image + LLPointer<LLImageRaw> compacted_image = new LLImageRaw(raw_image->getWidth(), raw_image->getHeight(), 3); + if (compacted_image->isBufferInvalid()) + { + success = false; + } + else + { + compacted_image->copy(raw_image); + raw_image->resize(raw_image->getWidth(), raw_image->getHeight(), 3); + raw_image->copy(compacted_image); + } + } + } + + if( success && flipped ) + { + // This works because the Targa definition requires that RLE blocks never + // encode pixels from more than one scanline. + // (On the other hand, it's not as fast as writing separate flipped versions as + // we did with TruecolorNonRle.) + raw_image->verticalFlip(); + } + + return success; } bool LLImageTGA::decodeTruecolorNonRle( LLImageRaw* raw_image, bool &alpha_opaque ) { - alpha_opaque = true; - - // Origin is the bottom left - U8* dst = raw_image->getData(); - U8* src = getData() + mDataOffset; - - S32 pixels = getWidth() * getHeight(); - - if (pixels * (mIs15Bit ? 2 : getComponents()) > getDataSize() - mDataOffset) - { //here we have situation when data size in src less than actually needed - return false; - } - - if (getComponents() == 4) - { - while( pixels-- ) - { - // Our data is stored in RGBA. TGA stores them as BGRA (little-endian ARGB) - dst[0] = src[2]; // Red - dst[1] = src[1]; // Green - dst[2] = src[0]; // Blue - dst[3] = src[3]; // Alpha - if (dst[3] != 255) - { - alpha_opaque = false; - } - dst += 4; - src += 4; - } - } - else if (getComponents() == 3) - { - if( mIs15Bit ) - { - while( pixels-- ) - { - decodeTruecolorPixel15( dst, src ); - dst += 3; - src += 2; - } - } - else - { - while( pixels-- ) - { - dst[0] = src[2]; // Red - dst[1] = src[1]; // Green - dst[2] = src[0]; // Blue - dst += 3; - src += 3; - } - } - } - else if (getComponents() == 1) - { - memcpy(dst, src, pixels); /* Flawfinder: ignore */ - } - - return true; + alpha_opaque = true; + + // Origin is the bottom left + U8* dst = raw_image->getData(); + U8* src = getData() + mDataOffset; + + S32 pixels = getWidth() * getHeight(); + + if (pixels * (mIs15Bit ? 2 : getComponents()) > getDataSize() - mDataOffset) + { //here we have situation when data size in src less than actually needed + return false; + } + + if (getComponents() == 4) + { + while( pixels-- ) + { + // Our data is stored in RGBA. TGA stores them as BGRA (little-endian ARGB) + dst[0] = src[2]; // Red + dst[1] = src[1]; // Green + dst[2] = src[0]; // Blue + dst[3] = src[3]; // Alpha + if (dst[3] != 255) + { + alpha_opaque = false; + } + dst += 4; + src += 4; + } + } + else if (getComponents() == 3) + { + if( mIs15Bit ) + { + while( pixels-- ) + { + decodeTruecolorPixel15( dst, src ); + dst += 3; + src += 2; + } + } + else + { + while( pixels-- ) + { + dst[0] = src[2]; // Red + dst[1] = src[1]; // Green + dst[2] = src[0]; // Blue + dst += 3; + src += 3; + } + } + } + else if (getComponents() == 1) + { + memcpy(dst, src, pixels); /* Flawfinder: ignore */ + } + + return true; } void LLImageTGA::decodeColorMapPixel8( U8* dst, const U8* src ) { - S32 index = llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 ); - dst[0] = mColorMap[ index ]; + S32 index = llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 ); + dst[0] = mColorMap[ index ]; } void LLImageTGA::decodeColorMapPixel15( U8* dst, const U8* src ) { - S32 index = llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 ); - decodeTruecolorPixel15( dst, mColorMap + 2 * index ); + S32 index = llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 ); + decodeTruecolorPixel15( dst, mColorMap + 2 * index ); } void LLImageTGA::decodeColorMapPixel24( U8* dst, const U8* src ) { - S32 index = 3 * llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 ); - dst[0] = mColorMap[ index + 2 ]; // Red - dst[1] = mColorMap[ index + 1 ]; // Green - dst[2] = mColorMap[ index + 0 ]; // Blue + S32 index = 3 * llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 ); + dst[0] = mColorMap[ index + 2 ]; // Red + dst[1] = mColorMap[ index + 1 ]; // Green + dst[2] = mColorMap[ index + 0 ]; // Blue } void LLImageTGA::decodeColorMapPixel32( U8* dst, const U8* src ) { - S32 index = 4 * llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 ); - dst[0] = mColorMap[ index + 2 ]; // Red - dst[1] = mColorMap[ index + 1 ]; // Green - dst[2] = mColorMap[ index + 0 ]; // Blue - dst[3] = mColorMap[ index + 3 ]; // Alpha + S32 index = 4 * llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 ); + dst[0] = mColorMap[ index + 2 ]; // Red + dst[1] = mColorMap[ index + 1 ]; // Green + dst[2] = mColorMap[ index + 0 ]; // Blue + dst[3] = mColorMap[ index + 3 ]; // Alpha } bool LLImageTGA::decodeColorMap( LLImageRaw* raw_image, bool rle, bool flipped ) { - // If flipped, origin is the top left. Need to reverse the order of the rows. - // Otherwise the origin is the bottom left. - - if( 8 != mPixelSize ) - { - return false; - } - - U8* src = getData() + mDataOffset; - U8* dst = raw_image->getData(); // start from the top - - void (LLImageTGA::*pixel_decoder)( U8*, const U8* ); - - switch( mColorMapBytesPerEntry ) - { - case 1: pixel_decoder = &LLImageTGA::decodeColorMapPixel8; break; - case 2: pixel_decoder = &LLImageTGA::decodeColorMapPixel15; break; - case 3: pixel_decoder = &LLImageTGA::decodeColorMapPixel24; break; - case 4: pixel_decoder = &LLImageTGA::decodeColorMapPixel32; break; - default: llassert(0); return false; - } - - if( rle ) - { - U8* last_dst = dst + getComponents() * (getHeight() * getWidth() - 1); - while( dst <= last_dst ) - { - // Read RLE block header - U8 block_header_byte = *src; - src++; - - U8 block_pixel_count = (block_header_byte & 0x7F) + 1; - if( block_header_byte & 0x80 ) - { - // Encoded (duplicate-pixel) block - do - { - (this->*pixel_decoder)( dst, src ); - dst += getComponents(); - block_pixel_count--; - } - while( block_pixel_count > 0 ); - src++; - } - else - { - // Unencoded block - do - { - (this->*pixel_decoder)( dst, src ); - dst += getComponents(); - src++; - block_pixel_count--; - } - while( block_pixel_count > 0 ); - } - } - - raw_image->verticalFlip(); - } - else - { - S32 src_row_bytes = getWidth(); - S32 dst_row_bytes = getWidth() * getComponents(); - - if( flipped ) - { - U8* src_last_row_start = src + (getHeight() - 1) * src_row_bytes; - src = src_last_row_start; // start from the bottom - src_row_bytes *= -1; - } - - - S32 i; - S32 j; - - for( S32 row = 0; row < getHeight(); row++ ) - { - for( i = 0, j = 0; j < getWidth(); i += getComponents(), j++ ) - { - (this->*pixel_decoder)( dst + i, src + j ); - } - - dst += dst_row_bytes; - src += src_row_bytes; - } - } - - return true; + // If flipped, origin is the top left. Need to reverse the order of the rows. + // Otherwise the origin is the bottom left. + + if( 8 != mPixelSize ) + { + return false; + } + + U8* src = getData() + mDataOffset; + U8* dst = raw_image->getData(); // start from the top + + void (LLImageTGA::*pixel_decoder)( U8*, const U8* ); + + switch( mColorMapBytesPerEntry ) + { + case 1: pixel_decoder = &LLImageTGA::decodeColorMapPixel8; break; + case 2: pixel_decoder = &LLImageTGA::decodeColorMapPixel15; break; + case 3: pixel_decoder = &LLImageTGA::decodeColorMapPixel24; break; + case 4: pixel_decoder = &LLImageTGA::decodeColorMapPixel32; break; + default: llassert(0); return false; + } + + if( rle ) + { + U8* last_dst = dst + getComponents() * (getHeight() * getWidth() - 1); + while( dst <= last_dst ) + { + // Read RLE block header + U8 block_header_byte = *src; + src++; + + U8 block_pixel_count = (block_header_byte & 0x7F) + 1; + if( block_header_byte & 0x80 ) + { + // Encoded (duplicate-pixel) block + do + { + (this->*pixel_decoder)( dst, src ); + dst += getComponents(); + block_pixel_count--; + } + while( block_pixel_count > 0 ); + src++; + } + else + { + // Unencoded block + do + { + (this->*pixel_decoder)( dst, src ); + dst += getComponents(); + src++; + block_pixel_count--; + } + while( block_pixel_count > 0 ); + } + } + + raw_image->verticalFlip(); + } + else + { + S32 src_row_bytes = getWidth(); + S32 dst_row_bytes = getWidth() * getComponents(); + + if( flipped ) + { + U8* src_last_row_start = src + (getHeight() - 1) * src_row_bytes; + src = src_last_row_start; // start from the bottom + src_row_bytes *= -1; + } + + + S32 i; + S32 j; + + for( S32 row = 0; row < getHeight(); row++ ) + { + for( i = 0, j = 0; j < getWidth(); i += getComponents(), j++ ) + { + (this->*pixel_decoder)( dst + i, src + j ); + } + + dst += dst_row_bytes; + src += src_row_bytes; + } + } + + return true; } bool LLImageTGA::encode(const LLImageRaw* raw_image, F32 encode_time) { - llassert_always(raw_image); - - LLImageDataSharedLock lockIn(raw_image); - LLImageDataLock lockOut(this); - - deleteData(); - - setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents()); - - // Data from header - mIDLength = 0; // Length of identifier string - mColorMapType = 0; // 0 = No Map - - // Supported: 2 = Uncompressed true color, 3 = uncompressed monochrome without colormap - switch( getComponents() ) - { - case 1: - mImageType = 3; - break; - case 2: // Interpret as intensity plus alpha - case 3: - case 4: - mImageType = 2; - break; - default: - return false; - } - - // Color map stuff (unsupported) - mColorMapIndexLo = 0; // First color map entry (low order byte) - mColorMapIndexHi = 0; // First color map entry (high order byte) - mColorMapLengthLo = 0; // Color map length (low order byte) - mColorMapLengthHi = 0; // Color map length (high order byte) - mColorMapDepth = 0; // Size of color map entry (15, 16, 24, or 32 bits) - - // Image offset relative to origin. - mXOffsetLo = 0; // X offset from origin (low order byte) - mXOffsetHi = 0; // X offset from origin (hi order byte) - mYOffsetLo = 0; // Y offset from origin (low order byte) - mYOffsetHi = 0; // Y offset from origin (hi order byte) - - // Height and width - mWidthLo = U8(getWidth() & 0xFF); // Width (low order byte) - mWidthHi = U8((getWidth() >> 8) & 0xFF); // Width (hi order byte) - mHeightLo = U8(getHeight() & 0xFF); // Height (low order byte) - mHeightHi = U8((getHeight() >> 8) & 0xFF); // Height (hi order byte) - - S32 bytes_per_pixel; - switch( getComponents() ) - { - case 1: - bytes_per_pixel = 1; - break; - case 3: - bytes_per_pixel = 3; - break; - case 2: // Interpret as intensity plus alpha. Store as RGBA. - case 4: - bytes_per_pixel = 4; - break; - default: - return false; - } - mPixelSize = U8(bytes_per_pixel * 8); // 8, 16, 24, 32 bits per pixel - - mAttributeBits = (4 == bytes_per_pixel) ? 8 : 0; // 4 bits: number of attribute bits (alpha) per pixel - mOriginRightBit = 0; // 1 bit: origin, 0 = left, 1 = right - mOriginTopBit = 0; // 1 bit: origin, 0 = bottom, 1 = top - mInterleave = 0; // 2 bits: interleaved flag, 0 = none, 1 = interleaved 2, 2 = interleaved 4 - - - const S32 TGA_HEADER_SIZE = 18; - const S32 COLOR_MAP_SIZE = 0; - mDataOffset = TGA_HEADER_SIZE + mIDLength + COLOR_MAP_SIZE; // Offset from start of data to the actual header. - - S32 pixels = getWidth() * getHeight(); - S32 datasize = mDataOffset + bytes_per_pixel * pixels; - U8* dst = allocateData(datasize); - - // Write header - *(dst++) = mIDLength; - *(dst++) = mColorMapType; - *(dst++) = mImageType; - *(dst++) = mColorMapIndexLo; - *(dst++) = mColorMapIndexHi; - *(dst++) = mColorMapLengthLo; - *(dst++) = mColorMapLengthHi; - *(dst++) = mColorMapDepth; - *(dst++) = mXOffsetLo; - *(dst++) = mXOffsetHi; - *(dst++) = mYOffsetLo; - *(dst++) = mYOffsetHi; - *(dst++) = mWidthLo; - *(dst++) = mWidthHi; - *(dst++) = mHeightLo; - *(dst++) = mHeightHi; - *(dst++) = mPixelSize; - *(dst++) = - ((mInterleave & 3) << 5) | - ((mOriginTopBit & 1) << 4) | - ((mOriginRightBit & 1) << 3) | - ((mAttributeBits & 0xF) << 0); - - // Write pixels - const U8* src = raw_image->getData(); - llassert( dst == getData() + mDataOffset ); - S32 i = 0; - S32 j = 0; - switch( getComponents() ) - { - case 1: - memcpy( dst, src, bytes_per_pixel * pixels ); /* Flawfinder: ignore */ - break; - - case 2: - while( pixels-- ) - { - dst[i + 0] = src[j + 0]; // intensity - dst[i + 1] = src[j + 0]; // intensity - dst[i + 2] = src[j + 0]; // intensity - dst[i + 3] = src[j + 1]; // alpha - i += 4; - j += 2; - } - break; - - case 3: - while( pixels-- ) - { - dst[i + 0] = src[i + 2]; // blue - dst[i + 1] = src[i + 1]; // green - dst[i + 2] = src[i + 0]; // red - i += 3; - } - break; - - case 4: - while( pixels-- ) - { - dst[i + 0] = src[i + 2]; // blue - dst[i + 1] = src[i + 1]; // green - dst[i + 2] = src[i + 0]; // red - dst[i + 3] = src[i + 3]; // alpha - i += 4; - } - break; - } - - return true; + llassert_always(raw_image); + + LLImageDataSharedLock lockIn(raw_image); + LLImageDataLock lockOut(this); + + deleteData(); + + setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents()); + + // Data from header + mIDLength = 0; // Length of identifier string + mColorMapType = 0; // 0 = No Map + + // Supported: 2 = Uncompressed true color, 3 = uncompressed monochrome without colormap + switch( getComponents() ) + { + case 1: + mImageType = 3; + break; + case 2: // Interpret as intensity plus alpha + case 3: + case 4: + mImageType = 2; + break; + default: + return false; + } + + // Color map stuff (unsupported) + mColorMapIndexLo = 0; // First color map entry (low order byte) + mColorMapIndexHi = 0; // First color map entry (high order byte) + mColorMapLengthLo = 0; // Color map length (low order byte) + mColorMapLengthHi = 0; // Color map length (high order byte) + mColorMapDepth = 0; // Size of color map entry (15, 16, 24, or 32 bits) + + // Image offset relative to origin. + mXOffsetLo = 0; // X offset from origin (low order byte) + mXOffsetHi = 0; // X offset from origin (hi order byte) + mYOffsetLo = 0; // Y offset from origin (low order byte) + mYOffsetHi = 0; // Y offset from origin (hi order byte) + + // Height and width + mWidthLo = U8(getWidth() & 0xFF); // Width (low order byte) + mWidthHi = U8((getWidth() >> 8) & 0xFF); // Width (hi order byte) + mHeightLo = U8(getHeight() & 0xFF); // Height (low order byte) + mHeightHi = U8((getHeight() >> 8) & 0xFF); // Height (hi order byte) + + S32 bytes_per_pixel; + switch( getComponents() ) + { + case 1: + bytes_per_pixel = 1; + break; + case 3: + bytes_per_pixel = 3; + break; + case 2: // Interpret as intensity plus alpha. Store as RGBA. + case 4: + bytes_per_pixel = 4; + break; + default: + return false; + } + mPixelSize = U8(bytes_per_pixel * 8); // 8, 16, 24, 32 bits per pixel + + mAttributeBits = (4 == bytes_per_pixel) ? 8 : 0; // 4 bits: number of attribute bits (alpha) per pixel + mOriginRightBit = 0; // 1 bit: origin, 0 = left, 1 = right + mOriginTopBit = 0; // 1 bit: origin, 0 = bottom, 1 = top + mInterleave = 0; // 2 bits: interleaved flag, 0 = none, 1 = interleaved 2, 2 = interleaved 4 + + + const S32 TGA_HEADER_SIZE = 18; + const S32 COLOR_MAP_SIZE = 0; + mDataOffset = TGA_HEADER_SIZE + mIDLength + COLOR_MAP_SIZE; // Offset from start of data to the actual header. + + S32 pixels = getWidth() * getHeight(); + S32 datasize = mDataOffset + bytes_per_pixel * pixels; + U8* dst = allocateData(datasize); + + // Write header + *(dst++) = mIDLength; + *(dst++) = mColorMapType; + *(dst++) = mImageType; + *(dst++) = mColorMapIndexLo; + *(dst++) = mColorMapIndexHi; + *(dst++) = mColorMapLengthLo; + *(dst++) = mColorMapLengthHi; + *(dst++) = mColorMapDepth; + *(dst++) = mXOffsetLo; + *(dst++) = mXOffsetHi; + *(dst++) = mYOffsetLo; + *(dst++) = mYOffsetHi; + *(dst++) = mWidthLo; + *(dst++) = mWidthHi; + *(dst++) = mHeightLo; + *(dst++) = mHeightHi; + *(dst++) = mPixelSize; + *(dst++) = + ((mInterleave & 3) << 5) | + ((mOriginTopBit & 1) << 4) | + ((mOriginRightBit & 1) << 3) | + ((mAttributeBits & 0xF) << 0); + + // Write pixels + const U8* src = raw_image->getData(); + llassert( dst == getData() + mDataOffset ); + S32 i = 0; + S32 j = 0; + switch( getComponents() ) + { + case 1: + memcpy( dst, src, bytes_per_pixel * pixels ); /* Flawfinder: ignore */ + break; + + case 2: + while( pixels-- ) + { + dst[i + 0] = src[j + 0]; // intensity + dst[i + 1] = src[j + 0]; // intensity + dst[i + 2] = src[j + 0]; // intensity + dst[i + 3] = src[j + 1]; // alpha + i += 4; + j += 2; + } + break; + + case 3: + while( pixels-- ) + { + dst[i + 0] = src[i + 2]; // blue + dst[i + 1] = src[i + 1]; // green + dst[i + 2] = src[i + 0]; // red + i += 3; + } + break; + + case 4: + while( pixels-- ) + { + dst[i + 0] = src[i + 2]; // blue + dst[i + 1] = src[i + 1]; // green + dst[i + 2] = src[i + 0]; // red + dst[i + 3] = src[i + 3]; // alpha + i += 4; + } + break; + } + + return true; } bool LLImageTGA::decodeTruecolorRle32( LLImageRaw* raw_image, bool &alpha_opaque ) { - llassert( getComponents() == 4 ); - alpha_opaque = true; - - U8* dst = raw_image->getData(); - U32* dst_pixels = (U32*) dst; - - U8* src = getData() + mDataOffset; - U8* last_src = src + getDataSize(); - - U32 rgba; - U8* rgba_byte_p = (U8*) &rgba; - - U32* last_dst_pixel = dst_pixels + getHeight() * getWidth() - 1; - while( dst_pixels <= last_dst_pixel ) - { - // Read RLE block header - - if (src >= last_src) - return false; - - U8 block_header_byte = *src; - src++; - - U32 block_pixel_count = (block_header_byte & 0x7F) + 1; - if( block_header_byte & 0x80 ) - { - // Encoded (duplicate-pixel) block - - if (src + 3 >= last_src) - return false; - - rgba_byte_p[0] = src[2]; - rgba_byte_p[1] = src[1]; - rgba_byte_p[2] = src[0]; - rgba_byte_p[3] = src[3]; - if (rgba_byte_p[3] != 255) - { - alpha_opaque = false; - } - - src += 4; - U32 value = rgba; - do - { - *dst_pixels = value; - dst_pixels++; - block_pixel_count--; - } - while( block_pixel_count > 0 ); - } - else - { - // Unencoded block - do - { - if (src + 3 >= last_src) - return false; - - ((U8*)dst_pixels)[0] = src[2]; - ((U8*)dst_pixels)[1] = src[1]; - ((U8*)dst_pixels)[2] = src[0]; - ((U8*)dst_pixels)[3] = src[3]; - if (src[3] != 255) - { - alpha_opaque = false; - } - src += 4; - dst_pixels++; - block_pixel_count--; - } - while( block_pixel_count > 0 ); - } - } - - return true; + llassert( getComponents() == 4 ); + alpha_opaque = true; + + U8* dst = raw_image->getData(); + U32* dst_pixels = (U32*) dst; + + U8* src = getData() + mDataOffset; + U8* last_src = src + getDataSize(); + + U32 rgba; + U8* rgba_byte_p = (U8*) &rgba; + + U32* last_dst_pixel = dst_pixels + getHeight() * getWidth() - 1; + while( dst_pixels <= last_dst_pixel ) + { + // Read RLE block header + + if (src >= last_src) + return false; + + U8 block_header_byte = *src; + src++; + + U32 block_pixel_count = (block_header_byte & 0x7F) + 1; + if( block_header_byte & 0x80 ) + { + // Encoded (duplicate-pixel) block + + if (src + 3 >= last_src) + return false; + + rgba_byte_p[0] = src[2]; + rgba_byte_p[1] = src[1]; + rgba_byte_p[2] = src[0]; + rgba_byte_p[3] = src[3]; + if (rgba_byte_p[3] != 255) + { + alpha_opaque = false; + } + + src += 4; + U32 value = rgba; + do + { + *dst_pixels = value; + dst_pixels++; + block_pixel_count--; + } + while( block_pixel_count > 0 ); + } + else + { + // Unencoded block + do + { + if (src + 3 >= last_src) + return false; + + ((U8*)dst_pixels)[0] = src[2]; + ((U8*)dst_pixels)[1] = src[1]; + ((U8*)dst_pixels)[2] = src[0]; + ((U8*)dst_pixels)[3] = src[3]; + if (src[3] != 255) + { + alpha_opaque = false; + } + src += 4; + dst_pixels++; + block_pixel_count--; + } + while( block_pixel_count > 0 ); + } + } + + return true; } bool LLImageTGA::decodeTruecolorRle15( LLImageRaw* raw_image ) { - llassert( getComponents() == 3 ); - llassert( mIs15Bit ); - - U8* dst = raw_image->getData(); - U8* src = getData() + mDataOffset; - - U8* last_src = src + getDataSize(); - U8* last_dst = dst + getComponents() * (getHeight() * getWidth() - 1); - - while( dst <= last_dst ) - { - // Read RLE block header - - if (src >= last_src) - return false; - - U8 block_header_byte = *src; - src++; - - U8 block_pixel_count = (block_header_byte & 0x7F) + 1; - if( block_header_byte & 0x80 ) - { - // Encoded (duplicate-pixel) block - do - { - if (src + 2 >= last_src) - return false; - - decodeTruecolorPixel15( dst, src ); // slow - dst += 3; - block_pixel_count--; - } - while( block_pixel_count > 0 ); - src += 2; - } - else - { - // Unencoded block - do - { - if (src + 2 >= last_src) - return false; - - decodeTruecolorPixel15( dst, src ); - dst += 3; - src += 2; - block_pixel_count--; - } - while( block_pixel_count > 0 ); - } - } - - return true; + llassert( getComponents() == 3 ); + llassert( mIs15Bit ); + + U8* dst = raw_image->getData(); + U8* src = getData() + mDataOffset; + + U8* last_src = src + getDataSize(); + U8* last_dst = dst + getComponents() * (getHeight() * getWidth() - 1); + + while( dst <= last_dst ) + { + // Read RLE block header + + if (src >= last_src) + return false; + + U8 block_header_byte = *src; + src++; + + U8 block_pixel_count = (block_header_byte & 0x7F) + 1; + if( block_header_byte & 0x80 ) + { + // Encoded (duplicate-pixel) block + do + { + if (src + 2 >= last_src) + return false; + + decodeTruecolorPixel15( dst, src ); // slow + dst += 3; + block_pixel_count--; + } + while( block_pixel_count > 0 ); + src += 2; + } + else + { + // Unencoded block + do + { + if (src + 2 >= last_src) + return false; + + decodeTruecolorPixel15( dst, src ); + dst += 3; + src += 2; + block_pixel_count--; + } + while( block_pixel_count > 0 ); + } + } + + return true; } bool LLImageTGA::decodeTruecolorRle24( LLImageRaw* raw_image ) { - llassert( getComponents() == 3 ); - - U8* dst = raw_image->getData(); - U8* src = getData() + mDataOffset; - - U8* last_src = src + getDataSize(); - U8* last_dst = dst + getComponents() * (getHeight() * getWidth() - 1); - - while( dst <= last_dst ) - { - // Read RLE block header - - if (src >= last_src) - return false; - - U8 block_header_byte = *src; - src++; - - U8 block_pixel_count = (block_header_byte & 0x7F) + 1; - if( block_header_byte & 0x80 ) - { - // Encoded (duplicate-pixel) block - do - { - if (src + 2 >= last_src) - return false; - dst[0] = src[2]; - dst[1] = src[1]; - dst[2] = src[0]; - dst += 3; - block_pixel_count--; - } - while( block_pixel_count > 0 ); - src += 3; - } - else - { - // Unencoded block - do - { - if (src + 2 >= last_src) - return false; - - dst[0] = src[2]; - dst[1] = src[1]; - dst[2] = src[0]; - dst += 3; - src += 3; - block_pixel_count--; - } - while( block_pixel_count > 0 ); - } - } - - return true; + llassert( getComponents() == 3 ); + + U8* dst = raw_image->getData(); + U8* src = getData() + mDataOffset; + + U8* last_src = src + getDataSize(); + U8* last_dst = dst + getComponents() * (getHeight() * getWidth() - 1); + + while( dst <= last_dst ) + { + // Read RLE block header + + if (src >= last_src) + return false; + + U8 block_header_byte = *src; + src++; + + U8 block_pixel_count = (block_header_byte & 0x7F) + 1; + if( block_header_byte & 0x80 ) + { + // Encoded (duplicate-pixel) block + do + { + if (src + 2 >= last_src) + return false; + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + dst += 3; + block_pixel_count--; + } + while( block_pixel_count > 0 ); + src += 3; + } + else + { + // Unencoded block + do + { + if (src + 2 >= last_src) + return false; + + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + dst += 3; + src += 3; + block_pixel_count--; + } + while( block_pixel_count > 0 ); + } + } + + return true; } bool LLImageTGA::decodeTruecolorRle8( LLImageRaw* raw_image ) { - llassert( getComponents() == 1 ); - - U8* dst = raw_image->getData(); - U8* src = getData() + mDataOffset; - - U8* last_src = src + getDataSize(); - U8* last_dst = dst + getHeight() * getWidth() - 1; - - while( dst <= last_dst ) - { - // Read RLE block header - - if (src >= last_src) - return false; - - U8 block_header_byte = *src; - src++; - - U8 block_pixel_count = (block_header_byte & 0x7F) + 1; - if( block_header_byte & 0x80 ) - { - if (src >= last_src) - return false; - - // Encoded (duplicate-pixel) block - memset( dst, *src, block_pixel_count ); - dst += block_pixel_count; - src++; - } - else - { - // Unencoded block - do - { - if (src >= last_src) - return false; - - *dst = *src; - dst++; - src++; - block_pixel_count--; - } - while( block_pixel_count > 0 ); - } - } - - return true; + llassert( getComponents() == 1 ); + + U8* dst = raw_image->getData(); + U8* src = getData() + mDataOffset; + + U8* last_src = src + getDataSize(); + U8* last_dst = dst + getHeight() * getWidth() - 1; + + while( dst <= last_dst ) + { + // Read RLE block header + + if (src >= last_src) + return false; + + U8 block_header_byte = *src; + src++; + + U8 block_pixel_count = (block_header_byte & 0x7F) + 1; + if( block_header_byte & 0x80 ) + { + if (src >= last_src) + return false; + + // Encoded (duplicate-pixel) block + memset( dst, *src, block_pixel_count ); + dst += block_pixel_count; + src++; + } + else + { + // Unencoded block + do + { + if (src >= last_src) + return false; + + *dst = *src; + dst++; + src++; + block_pixel_count--; + } + while( block_pixel_count > 0 ); + } + } + + return true; } @@ -1052,177 +1052,177 @@ bool LLImageTGA::decodeTruecolorRle8( LLImageRaw* raw_image ) // Processing happens during the decode for speed. bool LLImageTGA::decodeAndProcess( LLImageRaw* raw_image, F32 domain, F32 weight ) { - llassert_always(raw_image); - - // "Domain" isn't really the right word. It refers to the width of the - // ramp portion of the function that relates input and output pixel values. - // A domain of 0 gives a step function. - // - // | /---------------- - // O| / | - // u| / | - // t| / | - // p|------------------/ | - // u| | | - // t|<---------------->|<-->| - // | "offset" "domain" - // | - // --+---Input-------------------------------- - // | - - LLImageDataSharedLock lockIn(this); - LLImageDataLock lockOut(raw_image); - - if (!getData() || (0 == getDataSize())) - { - setLastError("LLImageTGA trying to decode an image with no data!"); - return false; - } - - // Only works for unflipped monochrome RLE images - if( (getComponents() != 1) || (mImageType != 11) || mOriginTopBit || mOriginRightBit ) - { - LL_ERRS() << "LLImageTGA trying to alpha-gradient process an image that's not a standard RLE, one component image" << LL_ENDL; - return false; - } - - if( !raw_image->resize(getWidth(), getHeight(), getComponents()) ) - { - LL_ERRS() << "LLImageTGA: Failed to resize image" << LL_ENDL; - return false; - } - - U8* dst = raw_image->getData(); - U8* src = getData() + mDataOffset; - U8* last_dst = dst + getHeight() * getWidth() - 1; - - if( domain > 0 ) - { - // Process using a look-up table (lut) - const S32 LUT_LEN = 256; - U8 lut[LUT_LEN]; - S32 i; - - F32 scale = 1.f / domain; - F32 offset = (1.f - domain) * llclampf( 1.f - weight ); - F32 bias = -(scale * offset); - - for( i = 0; i < LUT_LEN; i++ ) - { - lut[i] = (U8)llclampb( 255.f * ( i/255.f * scale + bias ) ); - } - - while( dst <= last_dst ) - { - // Read RLE block header - U8 block_header_byte = *src; - src++; - - U8 block_pixel_count = (block_header_byte & 0x7F) + 1; - if( block_header_byte & 0x80 ) - { - // Encoded (duplicate-pixel) block - memset( dst, lut[ *src ], block_pixel_count ); - dst += block_pixel_count; - src++; - } - else - { - // Unencoded block - do - { - *dst = lut[ *src ]; - dst++; - src++; - block_pixel_count--; - } - while( block_pixel_count > 0 ); - } - } - } - else - { - // Process using a simple comparison agains a threshold - const U8 threshold = (U8)(0xFF * llclampf( 1.f - weight )); - - while( dst <= last_dst ) - { - // Read RLE block header - U8 block_header_byte = *src; - src++; - - U8 block_pixel_count = (block_header_byte & 0x7F) + 1; - if( block_header_byte & 0x80 ) - { - // Encoded (duplicate-pixel) block - memset( dst, ((*src >= threshold) ? 0xFF : 0), block_pixel_count ); - dst += block_pixel_count; - src++; - } - else - { - // Unencoded block - do - { - *dst = (*src >= threshold) ? 0xFF : 0; - dst++; - src++; - block_pixel_count--; - } - while( block_pixel_count > 0 ); - } - } - } - return true; + llassert_always(raw_image); + + // "Domain" isn't really the right word. It refers to the width of the + // ramp portion of the function that relates input and output pixel values. + // A domain of 0 gives a step function. + // + // | /---------------- + // O| / | + // u| / | + // t| / | + // p|------------------/ | + // u| | | + // t|<---------------->|<-->| + // | "offset" "domain" + // | + // --+---Input-------------------------------- + // | + + LLImageDataSharedLock lockIn(this); + LLImageDataLock lockOut(raw_image); + + if (!getData() || (0 == getDataSize())) + { + setLastError("LLImageTGA trying to decode an image with no data!"); + return false; + } + + // Only works for unflipped monochrome RLE images + if( (getComponents() != 1) || (mImageType != 11) || mOriginTopBit || mOriginRightBit ) + { + LL_ERRS() << "LLImageTGA trying to alpha-gradient process an image that's not a standard RLE, one component image" << LL_ENDL; + return false; + } + + if( !raw_image->resize(getWidth(), getHeight(), getComponents()) ) + { + LL_ERRS() << "LLImageTGA: Failed to resize image" << LL_ENDL; + return false; + } + + U8* dst = raw_image->getData(); + U8* src = getData() + mDataOffset; + U8* last_dst = dst + getHeight() * getWidth() - 1; + + if( domain > 0 ) + { + // Process using a look-up table (lut) + const S32 LUT_LEN = 256; + U8 lut[LUT_LEN]; + S32 i; + + F32 scale = 1.f / domain; + F32 offset = (1.f - domain) * llclampf( 1.f - weight ); + F32 bias = -(scale * offset); + + for( i = 0; i < LUT_LEN; i++ ) + { + lut[i] = (U8)llclampb( 255.f * ( i/255.f * scale + bias ) ); + } + + while( dst <= last_dst ) + { + // Read RLE block header + U8 block_header_byte = *src; + src++; + + U8 block_pixel_count = (block_header_byte & 0x7F) + 1; + if( block_header_byte & 0x80 ) + { + // Encoded (duplicate-pixel) block + memset( dst, lut[ *src ], block_pixel_count ); + dst += block_pixel_count; + src++; + } + else + { + // Unencoded block + do + { + *dst = lut[ *src ]; + dst++; + src++; + block_pixel_count--; + } + while( block_pixel_count > 0 ); + } + } + } + else + { + // Process using a simple comparison agains a threshold + const U8 threshold = (U8)(0xFF * llclampf( 1.f - weight )); + + while( dst <= last_dst ) + { + // Read RLE block header + U8 block_header_byte = *src; + src++; + + U8 block_pixel_count = (block_header_byte & 0x7F) + 1; + if( block_header_byte & 0x80 ) + { + // Encoded (duplicate-pixel) block + memset( dst, ((*src >= threshold) ? 0xFF : 0), block_pixel_count ); + dst += block_pixel_count; + src++; + } + else + { + // Unencoded block + do + { + *dst = (*src >= threshold) ? 0xFF : 0; + dst++; + src++; + block_pixel_count--; + } + while( block_pixel_count > 0 ); + } + } + } + return true; } // Reads a .tga file and creates an LLImageTGA with its data. bool LLImageTGA::loadFile( const std::string& path ) { - S32 len = path.size(); - if( len < 5 ) - { - return false; - } - - std::string extension = gDirUtilp->getExtension(path); - if( "tga" != extension ) - { - return false; - } - - LLFILE* file = LLFile::fopen(path, "rb"); /* Flawfinder: ignore */ - if( !file ) - { - LL_WARNS() << "Couldn't open file " << path << LL_ENDL; - return false; - } - - S32 file_size = 0; - if (!fseek(file, 0, SEEK_END)) - { - file_size = ftell(file); - fseek(file, 0, SEEK_SET); - } - - U8* buffer = allocateData(file_size); - S32 bytes_read = fread(buffer, 1, file_size, file); - if( bytes_read != file_size ) - { - deleteData(); - LL_WARNS() << "Couldn't read file " << path << LL_ENDL; - return false; - } - - fclose( file ); - - if( !updateData() ) - { - LL_WARNS() << "Couldn't decode file " << path << LL_ENDL; - deleteData(); - return false; - } - return true; + S32 len = path.size(); + if( len < 5 ) + { + return false; + } + + std::string extension = gDirUtilp->getExtension(path); + if( "tga" != extension ) + { + return false; + } + + LLFILE* file = LLFile::fopen(path, "rb"); /* Flawfinder: ignore */ + if( !file ) + { + LL_WARNS() << "Couldn't open file " << path << LL_ENDL; + return false; + } + + S32 file_size = 0; + if (!fseek(file, 0, SEEK_END)) + { + file_size = ftell(file); + fseek(file, 0, SEEK_SET); + } + + U8* buffer = allocateData(file_size); + S32 bytes_read = fread(buffer, 1, file_size, file); + if( bytes_read != file_size ) + { + deleteData(); + LL_WARNS() << "Couldn't read file " << path << LL_ENDL; + return false; + } + + fclose( file ); + + if( !updateData() ) + { + LL_WARNS() << "Couldn't decode file " << path << LL_ENDL; + deleteData(); + return false; + } + return true; } diff --git a/indra/llimage/llimagetga.h b/indra/llimage/llimagetga.h index b1f34dcdad..a6aafc5fed 100644 --- a/indra/llimage/llimagetga.h +++ b/indra/llimage/llimagetga.h @@ -1,25 +1,25 @@ -/** +/** * @file llimagetga.h * @brief Image implementation to compresses and decompressed TGA files. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,75 +34,75 @@ class LLImageTGA : public LLImageFormatted { protected: - virtual ~LLImageTGA(); - + virtual ~LLImageTGA(); + public: - LLImageTGA(); - LLImageTGA(const std::string& file_name); + LLImageTGA(); + LLImageTGA(const std::string& file_name); + + /*virtual*/ std::string getExtension() { return std::string("tga"); } + /*virtual*/ bool updateData(); + /*virtual*/ bool decode(LLImageRaw* raw_image, F32 decode_time=0.0); + /*virtual*/ bool encode(const LLImageRaw* raw_image, F32 encode_time=0.0); - /*virtual*/ std::string getExtension() { return std::string("tga"); } - /*virtual*/ bool updateData(); - /*virtual*/ bool decode(LLImageRaw* raw_image, F32 decode_time=0.0); - /*virtual*/ bool encode(const LLImageRaw* raw_image, F32 encode_time=0.0); + bool decodeAndProcess(LLImageRaw* raw_image, F32 domain, F32 weight); - bool decodeAndProcess(LLImageRaw* raw_image, F32 domain, F32 weight); - private: - bool decodeTruecolor( LLImageRaw* raw_image, bool rle, bool flipped ); + bool decodeTruecolor( LLImageRaw* raw_image, bool rle, bool flipped ); + + bool decodeTruecolorRle8( LLImageRaw* raw_image ); + bool decodeTruecolorRle15( LLImageRaw* raw_image ); + bool decodeTruecolorRle24( LLImageRaw* raw_image ); + bool decodeTruecolorRle32( LLImageRaw* raw_image, bool &alpha_opaque ); + + void decodeTruecolorPixel15( U8* dst, const U8* src ); - bool decodeTruecolorRle8( LLImageRaw* raw_image ); - bool decodeTruecolorRle15( LLImageRaw* raw_image ); - bool decodeTruecolorRle24( LLImageRaw* raw_image ); - bool decodeTruecolorRle32( LLImageRaw* raw_image, bool &alpha_opaque ); + bool decodeTruecolorNonRle( LLImageRaw* raw_image, bool &alpha_opaque ); - void decodeTruecolorPixel15( U8* dst, const U8* src ); + bool decodeColorMap( LLImageRaw* raw_image, bool rle, bool flipped ); - bool decodeTruecolorNonRle( LLImageRaw* raw_image, bool &alpha_opaque ); - - bool decodeColorMap( LLImageRaw* raw_image, bool rle, bool flipped ); + void decodeColorMapPixel8(U8* dst, const U8* src); + void decodeColorMapPixel15(U8* dst, const U8* src); + void decodeColorMapPixel24(U8* dst, const U8* src); + void decodeColorMapPixel32(U8* dst, const U8* src); - void decodeColorMapPixel8(U8* dst, const U8* src); - void decodeColorMapPixel15(U8* dst, const U8* src); - void decodeColorMapPixel24(U8* dst, const U8* src); - void decodeColorMapPixel32(U8* dst, const U8* src); + bool loadFile(const std::string& file_name); - bool loadFile(const std::string& file_name); - private: - // Class specific data - U32 mDataOffset; // Offset from start of data to the actual header. - - // Data from header - U8 mIDLength; // Length of identifier string - U8 mColorMapType; // 0 = No Map - U8 mImageType; // Supported: 2 = Uncompressed true color, 3 = uncompressed monochrome without colormap - U8 mColorMapIndexLo; // First color map entry (low order byte) - U8 mColorMapIndexHi; // First color map entry (high order byte) - U8 mColorMapLengthLo; // Color map length (low order byte) - U8 mColorMapLengthHi; // Color map length (high order byte) - U8 mColorMapDepth; // Size of color map entry (15, 16, 24, or 32 bits) - U8 mXOffsetLo; // X offset of image (low order byte) - U8 mXOffsetHi; // X offset of image (hi order byte) - U8 mYOffsetLo; // Y offset of image (low order byte) - U8 mYOffsetHi; // Y offset of image (hi order byte) - U8 mWidthLo; // Width (low order byte) - U8 mWidthHi; // Width (hi order byte) - U8 mHeightLo; // Height (low order byte) - U8 mHeightHi; // Height (hi order byte) - U8 mPixelSize; // 8, 16, 24, 32 bits per pixel - U8 mAttributeBits; // 4 bits: number of attributes per pixel - U8 mOriginRightBit; // 1 bit: origin, 0 = left, 1 = right - U8 mOriginTopBit; // 1 bit: origin, 0 = bottom, 1 = top - U8 mInterleave; // 2 bits: interleaved flag, 0 = none, 1 = interleaved 2, 2 = interleaved 4 - - U8* mColorMap; - S32 mColorMapStart; - S32 mColorMapLength; - S32 mColorMapBytesPerEntry; - - bool mIs15Bit; - - static const U8 s5to8bits[32]; + // Class specific data + U32 mDataOffset; // Offset from start of data to the actual header. + + // Data from header + U8 mIDLength; // Length of identifier string + U8 mColorMapType; // 0 = No Map + U8 mImageType; // Supported: 2 = Uncompressed true color, 3 = uncompressed monochrome without colormap + U8 mColorMapIndexLo; // First color map entry (low order byte) + U8 mColorMapIndexHi; // First color map entry (high order byte) + U8 mColorMapLengthLo; // Color map length (low order byte) + U8 mColorMapLengthHi; // Color map length (high order byte) + U8 mColorMapDepth; // Size of color map entry (15, 16, 24, or 32 bits) + U8 mXOffsetLo; // X offset of image (low order byte) + U8 mXOffsetHi; // X offset of image (hi order byte) + U8 mYOffsetLo; // Y offset of image (low order byte) + U8 mYOffsetHi; // Y offset of image (hi order byte) + U8 mWidthLo; // Width (low order byte) + U8 mWidthHi; // Width (hi order byte) + U8 mHeightLo; // Height (low order byte) + U8 mHeightHi; // Height (hi order byte) + U8 mPixelSize; // 8, 16, 24, 32 bits per pixel + U8 mAttributeBits; // 4 bits: number of attributes per pixel + U8 mOriginRightBit; // 1 bit: origin, 0 = left, 1 = right + U8 mOriginTopBit; // 1 bit: origin, 0 = bottom, 1 = top + U8 mInterleave; // 2 bits: interleaved flag, 0 = none, 1 = interleaved 2, 2 = interleaved 4 + + U8* mColorMap; + S32 mColorMapStart; + S32 mColorMapLength; + S32 mColorMapBytesPerEntry; + + bool mIs15Bit; + + static const U8 s5to8bits[32]; }; #endif diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp index f97d991ca6..accbd9964b 100644 --- a/indra/llimage/llimageworker.cpp +++ b/indra/llimage/llimageworker.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llimageworker.cpp * @brief Base class for images. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,31 +34,31 @@ class ImageRequest { public: - ImageRequest(const LLPointer<LLImageFormatted>& image, + ImageRequest(const LLPointer<LLImageFormatted>& image, S32 discard, bool needs_aux, const LLPointer<LLImageDecodeThread::Responder>& responder, U32 request_id); - virtual ~ImageRequest(); + virtual ~ImageRequest(); - /*virtual*/ bool processRequest(); - /*virtual*/ void finishRequest(bool completed); + /*virtual*/ bool processRequest(); + /*virtual*/ void finishRequest(bool completed); private: - // LLPointers stored in ImageRequest MUST be LLPointer instances rather - // than references: we need to increment the refcount when storing these. - // input - LLPointer<LLImageFormatted> mFormattedImage; - S32 mDiscardLevel; + // LLPointers stored in ImageRequest MUST be LLPointer instances rather + // than references: we need to increment the refcount when storing these. + // input + LLPointer<LLImageFormatted> mFormattedImage; + S32 mDiscardLevel; U32 mRequestId; - bool mNeedsAux; - // output - LLPointer<LLImageRaw> mDecodedImageRaw; - LLPointer<LLImageRaw> mDecodedImageAux; + bool mNeedsAux; + // output + LLPointer<LLImageRaw> mDecodedImageRaw; + LLPointer<LLImageRaw> mDecodedImageAux; bool mDecodedRaw; bool mDecodedAux; - LLPointer<LLImageDecodeThread::Responder> mResponder; -}; + LLPointer<LLImageDecodeThread::Responder> mResponder; + std::string mErrorString;}; //---------------------------------------------------------------------------- @@ -71,7 +71,7 @@ LLImageDecodeThread::LLImageDecodeThread(bool /*threaded*/) mThreadPool->start(); } -//virtual +//virtual LLImageDecodeThread::~LLImageDecodeThread() {} @@ -89,7 +89,7 @@ size_t LLImageDecodeThread::getPending() } LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage( - const LLPointer<LLImageFormatted>& image, + const LLPointer<LLImageFormatted>& image, S32 discard, bool needs_aux, const LLPointer<LLImageDecodeThread::Responder>& responder) @@ -130,21 +130,21 @@ ImageRequest::ImageRequest(const LLPointer<LLImageFormatted>& image, bool needs_aux, const LLPointer<LLImageDecodeThread::Responder>& responder, U32 request_id) - : mFormattedImage(image), - mDiscardLevel(discard), - mNeedsAux(needs_aux), - mDecodedRaw(false), - mDecodedAux(false), - mResponder(responder), - mRequestId(request_id) + : mFormattedImage(image), + mDiscardLevel(discard), + mNeedsAux(needs_aux), + mDecodedRaw(false), + mDecodedAux(false), + mResponder(responder), + mRequestId(request_id) { } ImageRequest::~ImageRequest() { - mDecodedImageRaw = NULL; - mDecodedImageAux = NULL; - mFormattedImage = NULL; + mDecodedImageRaw = NULL; + mDecodedImageAux = NULL; + mFormattedImage = NULL; } //---------------------------------------------------------------------------- @@ -155,65 +155,71 @@ bool ImageRequest::processRequest() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - if (mFormattedImage.isNull()) - return true; - - const F32 decode_time_slice = 0.f; //disable time slicing - bool done = true; - - LLImageDataLock lockFormatted(mFormattedImage); - LLImageDataLock lockDecodedRaw(mDecodedImageRaw); - LLImageDataLock lockDecodedAux(mDecodedImageAux); - - if (!mDecodedRaw) - { - // Decode primary channels - if (mDecodedImageRaw.isNull()) - { - // parse formatted header - if (!mFormattedImage->updateData()) - { - return true; // done (failed) - } - if (!(mFormattedImage->getWidth() * mFormattedImage->getHeight() * mFormattedImage->getComponents())) - { - return true; // done (failed) - } - if (mDiscardLevel >= 0) - { - mFormattedImage->setDiscardLevel(mDiscardLevel); - } - mDecodedImageRaw = new LLImageRaw(mFormattedImage->getWidth(), - mFormattedImage->getHeight(), - mFormattedImage->getComponents()); - } - done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); - // some decoders are removing data when task is complete and there were errors - mDecodedRaw = done && mDecodedImageRaw->getData(); - } - if (done && mNeedsAux && !mDecodedAux) - { - // Decode aux channel - if (!mDecodedImageAux) - { - mDecodedImageAux = new LLImageRaw(mFormattedImage->getWidth(), - mFormattedImage->getHeight(), - 1); - } - done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4); - mDecodedAux = done && mDecodedImageAux->getData(); - } - - return done; + if (mFormattedImage.isNull()) + return true; + + const F32 decode_time_slice = 0.f; //disable time slicing + bool done = true; + + LLImageDataLock lockFormatted(mFormattedImage); + LLImageDataLock lockDecodedRaw(mDecodedImageRaw); + LLImageDataLock lockDecodedAux(mDecodedImageAux); + + if (!mDecodedRaw) + { + // Decode primary channels + if (mDecodedImageRaw.isNull()) + { + // parse formatted header + if (!mFormattedImage->updateData()) + { + return true; // done (failed) + } + if ((mFormattedImage->getWidth() * mFormattedImage->getHeight() * mFormattedImage->getComponents()) == 0) + { + return true; // done (failed) + } + if (mDiscardLevel >= 0) + { + mFormattedImage->setDiscardLevel(mDiscardLevel); + } + mDecodedImageRaw = new LLImageRaw(mFormattedImage->getWidth(), + mFormattedImage->getHeight(), + mFormattedImage->getComponents()); + } + done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); + // some decoders are removing data when task is complete and there were errors + mDecodedRaw = done && mDecodedImageRaw->getData(); + + // Pick up errors from decoding + mErrorString = LLImage::getLastThreadError(); + } + if (done && mNeedsAux && !mDecodedAux && mFormattedImage.notNull()) + { + // Decode aux channel + if (!mDecodedImageAux) + { + mDecodedImageAux = new LLImageRaw(mFormattedImage->getWidth(), + mFormattedImage->getHeight(), + 1); + } + done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4); + mDecodedAux = done && mDecodedImageAux->getData(); + + // Pick up errors from decoding + mErrorString = LLImage::getLastThreadError(); + } + + return done; } void ImageRequest::finishRequest(bool completed) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - if (mResponder.notNull()) - { - bool success = completed && mDecodedRaw && (!mNeedsAux || mDecodedAux); - mResponder->completed(success, mDecodedImageRaw, mDecodedImageAux, mRequestId); - } - // Will automatically be deleted + if (mResponder.notNull()) + { + bool success = completed && mDecodedRaw && (!mNeedsAux || mDecodedAux); + mResponder->completed(success, mErrorString, mDecodedImageRaw, mDecodedImageAux, mRequestId); + } + // Will automatically be deleted } diff --git a/indra/llimage/llimageworker.h b/indra/llimage/llimageworker.h index e7ad7ed4d0..9e01f0bacd 100644 --- a/indra/llimage/llimageworker.h +++ b/indra/llimage/llimageworker.h @@ -1,25 +1,25 @@ -/** +/** * @file llimageworker.h * @brief Object for managing images and their textures. * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,33 +34,33 @@ class LLImageDecodeThread { public: - class Responder : public LLThreadSafeRefCount - { - protected: - virtual ~Responder(); - public: - virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux, U32 request_id) = 0; - }; + class Responder : public LLThreadSafeRefCount + { + protected: + virtual ~Responder(); + public: + virtual void completed(bool success, const std::string& error_message, LLImageRaw* raw, LLImageRaw* aux, U32 request_id) = 0; + }; public: - LLImageDecodeThread(bool threaded = true); - virtual ~LLImageDecodeThread(); + LLImageDecodeThread(bool threaded = true); + virtual ~LLImageDecodeThread(); - // meant to resemble LLQueuedThread::handle_t - typedef U32 handle_t; - handle_t decodeImage(const LLPointer<LLImageFormatted>& image, - S32 discard, bool needs_aux, - const LLPointer<Responder>& responder); - size_t getPending(); - size_t update(F32 max_time_ms); + // meant to resemble LLQueuedThread::handle_t + typedef U32 handle_t; + handle_t decodeImage(const LLPointer<LLImageFormatted>& image, + S32 discard, bool needs_aux, + const LLPointer<Responder>& responder); + size_t getPending(); + size_t update(F32 max_time_ms); S32 getTotalDecodeCount() { return mDecodeCount; } - void shutdown(); + void shutdown(); private: - // As of SL-17483, LLImageDecodeThread is no longer itself an - // LLQueuedThread - instead this is the API by which we submit work to the - // "ImageDecode" ThreadPool. - std::unique_ptr<LL::ThreadPool> mThreadPool; + // As of SL-17483, LLImageDecodeThread is no longer itself an + // LLQueuedThread - instead this is the API by which we submit work to the + // "ImageDecode" ThreadPool. + std::unique_ptr<LL::ThreadPool> mThreadPool; LLAtomicU32 mDecodeCount; }; diff --git a/indra/llimage/llmapimagetype.h b/indra/llimage/llmapimagetype.h index 0a040d3db9..4fd5ab479f 100644 --- a/indra/llimage/llmapimagetype.h +++ b/indra/llimage/llmapimagetype.h @@ -1,24 +1,24 @@ -/** +/** * @file llmapimagetype.h * * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -28,13 +28,13 @@ typedef enum e_map_image_type { - MIT_TERRAIN = 0, - MIT_POPULAR = 1, - MIT_OBJECTS = 2, - MIT_OBJECTS_FOR_SALE = 3, - MIT_LAND_TO_BUY = 4, - MIT_OBJECT_NEW = 5, - MIT_EOF = 6 + MIT_TERRAIN = 0, + MIT_POPULAR = 1, + MIT_OBJECTS = 2, + MIT_OBJECTS_FOR_SALE = 3, + MIT_LAND_TO_BUY = 4, + MIT_OBJECT_NEW = 5, + MIT_EOF = 6 } EMapImageType; #endif diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp index baf1330011..a5fb7a3167 100644 --- a/indra/llimage/llpngwrapper.cpp +++ b/indra/llimage/llpngwrapper.cpp @@ -5,21 +5,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -46,86 +46,86 @@ struct PngError: public LLContinueError // --------------------------------------------------------------------------- LLPngWrapper::LLPngWrapper() - : mReadPngPtr( NULL ), - mReadInfoPtr( NULL ), - mWritePngPtr( NULL ), - mWriteInfoPtr( NULL ), - mRowPointers( NULL ), - mWidth( 0 ), - mHeight( 0 ), - mBitDepth( 0 ), - mColorType( 0 ), - mChannels( 0 ), - mInterlaceType( 0 ), - mCompressionType( 0 ), - mFilterMethod( 0 ), - mFinalSize( 0 ), - mGamma(0.f) + : mReadPngPtr( NULL ), + mReadInfoPtr( NULL ), + mWritePngPtr( NULL ), + mWriteInfoPtr( NULL ), + mRowPointers( NULL ), + mWidth( 0 ), + mHeight( 0 ), + mBitDepth( 0 ), + mColorType( 0 ), + mChannels( 0 ), + mInterlaceType( 0 ), + mCompressionType( 0 ), + mFilterMethod( 0 ), + mFinalSize( 0 ), + mGamma(0.f) { } LLPngWrapper::~LLPngWrapper() { - releaseResources(); + releaseResources(); } // Checks the src for a valid PNG header bool LLPngWrapper::isValidPng(U8* src) { - const int PNG_BYTES_TO_CHECK = 8; + const int PNG_BYTES_TO_CHECK = 8; - int sig = png_sig_cmp((png_bytep)src, (png_size_t)0, PNG_BYTES_TO_CHECK); - if (sig != 0) - { - mErrorMessage = "Invalid or corrupt PNG file"; - return false; - } + int sig = png_sig_cmp((png_bytep)src, (png_size_t)0, PNG_BYTES_TO_CHECK); + if (sig != 0) + { + mErrorMessage = "Invalid or corrupt PNG file"; + return false; + } - return true; + return true; } // Called by the libpng library when a fatal encoding or decoding error // occurs. We throw PngError and let our try/catch block clean up. void LLPngWrapper::errorHandler(png_structp png_ptr, png_const_charp msg) { - LLTHROW(PngError(msg)); + LLTHROW(PngError(msg)); } // Called by the libpng library when reading (decoding) the PNG file. We // copy the PNG data from our internal buffer into the PNG's data buffer. void LLPngWrapper::readDataCallback(png_structp png_ptr, png_bytep dest, png_size_t length) { - PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr); - if(dataInfo->mOffset + length > dataInfo->mDataSize) - { - png_error(png_ptr, "Data read error. Requested data size exceeds available data size."); - return; - } - - U8 *src = &dataInfo->mData[dataInfo->mOffset]; - memcpy(dest, src, length); - dataInfo->mOffset += static_cast<U32>(length); + PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr); + if(dataInfo->mOffset + length > dataInfo->mDataSize) + { + png_error(png_ptr, "Data read error. Requested data size exceeds available data size."); + return; + } + + U8 *src = &dataInfo->mData[dataInfo->mOffset]; + memcpy(dest, src, length); + dataInfo->mOffset += static_cast<U32>(length); } // Called by the libpng library when writing (encoding) the PNG file. We // copy the encoded result into our data buffer. void LLPngWrapper::writeDataCallback(png_structp png_ptr, png_bytep src, png_size_t length) { - PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr); - if (dataInfo->mOffset + length > dataInfo->mDataSize) - { - png_error(png_ptr, "Data write error. Requested data size exceeds available data size."); - return; - } - U8 *dest = &dataInfo->mData[dataInfo->mOffset]; - memcpy(dest, src, length); - dataInfo->mOffset += static_cast<U32>(length); + PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr); + if (dataInfo->mOffset + length > dataInfo->mDataSize) + { + png_error(png_ptr, "Data write error. Requested data size exceeds available data size."); + return; + } + U8 *dest = &dataInfo->mData[dataInfo->mOffset]; + memcpy(dest, src, length); + dataInfo->mOffset += static_cast<U32>(length); } // Flush the write output pointer void LLPngWrapper::writeFlush(png_structp png_ptr) { - // no-op since we're just writing to memory + // no-op since we're just writing to memory } // Read the PNG file using the libpng. The low-level interface is used here @@ -136,273 +136,280 @@ void LLPngWrapper::writeFlush(png_structp png_ptr) // must assign row-pointers in "reverse" order. bool LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInfo *infop) { - try - { - // Create and initialize the png structures - mReadPngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, - this, &errorHandler, NULL); - if (mReadPngPtr == NULL) - { - LLTHROW(PngError("Problem creating png read structure")); - } - - // Allocate/initialize the memory for image information. - mReadInfoPtr = png_create_info_struct(mReadPngPtr); - - // Set up the input control - PngDataInfo dataPtr; - dataPtr.mData = src; - dataPtr.mOffset = 0; - dataPtr.mDataSize = dataSize; - - png_set_read_fn(mReadPngPtr, &dataPtr, &readDataCallback); - png_set_sig_bytes(mReadPngPtr, 0); - - // setup low-level read and get header information - png_read_info(mReadPngPtr, mReadInfoPtr); - png_get_IHDR(mReadPngPtr, mReadInfoPtr, &mWidth, &mHeight, - &mBitDepth, &mColorType, &mInterlaceType, - &mCompressionType, &mFilterMethod); - - // Normalize the image, then get updated image information - // after transformations have been applied - normalizeImage(); - updateMetaData(); - - // If a raw object is supplied, read the PNG image into its - // data space - if (rawImage != NULL) - { - LLImageDataLock lock(rawImage); - - if (!rawImage->resize(static_cast<U16>(mWidth), - static_cast<U16>(mHeight), mChannels)) - { - LLTHROW(PngError("Failed to resize image")); - } - U8 *dest = rawImage->getData(); - int offset = mWidth * mChannels; - - // Set up the row pointers and read the image - mRowPointers = new U8* [mHeight]; - for (U32 i=0; i < mHeight; i++) - { - mRowPointers[i] = &dest[(mHeight-i-1)*offset]; - } - - png_read_image(mReadPngPtr, mRowPointers); - - // Finish up, ensures all metadata are updated - png_read_end(mReadPngPtr, NULL); - } - - // If an info object is supplied, copy the relevant info - if (infop != NULL) - { - infop->mHeight = static_cast<U16>(mHeight); - infop->mWidth = static_cast<U16>(mWidth); - infop->mComponents = mChannels; - } - - mFinalSize = dataPtr.mOffset; - } - catch (const PngError& msg) - { - mErrorMessage = msg.what(); - releaseResources(); - return (false); - } - catch (std::bad_alloc&) - { - mErrorMessage = "LLPngWrapper"; - releaseResources(); - return (false); - } - - // Clean up and return - releaseResources(); - return (true); + try + { + // Create and initialize the png structures + mReadPngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + this, &errorHandler, NULL); + if (mReadPngPtr == NULL) + { + LLTHROW(PngError("Problem creating png read structure")); + } + + // Allocate/initialize the memory for image information. + mReadInfoPtr = png_create_info_struct(mReadPngPtr); + + // Set up the input control + PngDataInfo dataPtr; + dataPtr.mData = src; + dataPtr.mOffset = 0; + dataPtr.mDataSize = dataSize; + + png_set_read_fn(mReadPngPtr, &dataPtr, &readDataCallback); + png_set_sig_bytes(mReadPngPtr, 0); + + // setup low-level read and get header information + png_read_info(mReadPngPtr, mReadInfoPtr); + png_get_IHDR(mReadPngPtr, mReadInfoPtr, &mWidth, &mHeight, + &mBitDepth, &mColorType, &mInterlaceType, + &mCompressionType, &mFilterMethod); + + // Normalize the image, then get updated image information + // after transformations have been applied + normalizeImage(); + updateMetaData(); + + // If a raw object is supplied, read the PNG image into its + // data space + if (rawImage != NULL) + { + LLImageDataLock lock(rawImage); + + if (!rawImage->resize(static_cast<U16>(mWidth), + static_cast<U16>(mHeight), mChannels)) + { + LLTHROW(PngError("Failed to resize image")); + } + U8 *dest = rawImage->getData(); + int offset = mWidth * mChannels; + + // Set up the row pointers and read the image + mRowPointers = new U8* [mHeight]; + for (U32 i=0; i < mHeight; i++) + { + mRowPointers[i] = &dest[(mHeight-i-1)*offset]; + } + + png_read_image(mReadPngPtr, mRowPointers); + + // Finish up, ensures all metadata are updated + png_read_end(mReadPngPtr, NULL); + } + + // If an info object is supplied, copy the relevant info + if (infop != NULL) + { + infop->mHeight = static_cast<U16>(mHeight); + infop->mWidth = static_cast<U16>(mWidth); + infop->mComponents = mChannels; + } + + mFinalSize = dataPtr.mOffset; + } + catch (const PngError& msg) + { + mErrorMessage = msg.what(); + releaseResources(); + return (false); + } + catch (std::bad_alloc&) + { + mErrorMessage = "LLPngWrapper"; + releaseResources(); + return (false); + } + catch (...) + { + mErrorMessage = "LLPngWrapper"; + releaseResources(); + LOG_UNHANDLED_EXCEPTION(""); + return (false); + } + + // Clean up and return + releaseResources(); + return (true); } // Do transformations to normalize the input to 8-bpp RGBA void LLPngWrapper::normalizeImage() { - // 1. Expand any palettes - // 2. Convert grayscales to RGB - // 3. Create alpha layer from transparency - // 4. Ensure 8-bpp for all images - // 5. Set (or guess) gamma - - if (mColorType == PNG_COLOR_TYPE_PALETTE) - { - png_set_palette_to_rgb(mReadPngPtr); - } + // 1. Expand any palettes + // 2. Convert grayscales to RGB + // 3. Create alpha layer from transparency + // 4. Ensure 8-bpp for all images + // 5. Set (or guess) gamma + + if (mColorType == PNG_COLOR_TYPE_PALETTE) + { + png_set_palette_to_rgb(mReadPngPtr); + } if (mColorType == PNG_COLOR_TYPE_GRAY && mBitDepth < 8) - { - png_set_expand_gray_1_2_4_to_8(mReadPngPtr); - } - if (mColorType == PNG_COLOR_TYPE_GRAY - || mColorType == PNG_COLOR_TYPE_GRAY_ALPHA) - { - png_set_gray_to_rgb(mReadPngPtr); - } - if (png_get_valid(mReadPngPtr, mReadInfoPtr, PNG_INFO_tRNS)) - { - png_set_tRNS_to_alpha(mReadPngPtr); - } - if (mBitDepth < 8) - { - png_set_packing(mReadPngPtr); - } - else if (mBitDepth == 16) - { - png_set_strip_16(mReadPngPtr); - } - - const F64 SCREEN_GAMMA = 2.2; - if (png_get_gAMA(mReadPngPtr, mReadInfoPtr, &mGamma)) - { - png_set_gamma(mReadPngPtr, SCREEN_GAMMA, mGamma); - } - else - { - png_set_gamma(mReadPngPtr, SCREEN_GAMMA, 1/SCREEN_GAMMA); - } + { + png_set_expand_gray_1_2_4_to_8(mReadPngPtr); + } + if (mColorType == PNG_COLOR_TYPE_GRAY + || mColorType == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_gray_to_rgb(mReadPngPtr); + } + if (png_get_valid(mReadPngPtr, mReadInfoPtr, PNG_INFO_tRNS)) + { + png_set_tRNS_to_alpha(mReadPngPtr); + } + if (mBitDepth < 8) + { + png_set_packing(mReadPngPtr); + } + else if (mBitDepth == 16) + { + png_set_strip_16(mReadPngPtr); + } + + const F64 SCREEN_GAMMA = 2.2; + if (png_get_gAMA(mReadPngPtr, mReadInfoPtr, &mGamma)) + { + png_set_gamma(mReadPngPtr, SCREEN_GAMMA, mGamma); + } + else + { + png_set_gamma(mReadPngPtr, SCREEN_GAMMA, 1/SCREEN_GAMMA); + } } // Read out the image meta-data void LLPngWrapper::updateMetaData() { - png_read_update_info(mReadPngPtr, mReadInfoPtr); + png_read_update_info(mReadPngPtr, mReadInfoPtr); mWidth = png_get_image_width(mReadPngPtr, mReadInfoPtr); mHeight = png_get_image_height(mReadPngPtr, mReadInfoPtr); mBitDepth = png_get_bit_depth(mReadPngPtr, mReadInfoPtr); mColorType = png_get_color_type(mReadPngPtr, mReadInfoPtr); - mChannels = png_get_channels(mReadPngPtr, mReadInfoPtr); + mChannels = png_get_channels(mReadPngPtr, mReadInfoPtr); } // Method to write raw image into PNG at dest. The raw scanline begins // at the bottom of the image per SecondLife conventions. bool LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest, size_t destSize) { - try - { - S8 numComponents = rawImage->getComponents(); - switch (numComponents) - { - case 1: - mColorType = PNG_COLOR_TYPE_GRAY; - break; - case 2: - mColorType = PNG_COLOR_TYPE_GRAY_ALPHA; - break; - case 3: - mColorType = PNG_COLOR_TYPE_RGB; - break; - case 4: - mColorType = PNG_COLOR_TYPE_RGB_ALPHA; - break; - default: - mColorType = -1; - } - - if (mColorType == -1) - { - LLTHROW(PngError("Unsupported image: unexpected number of channels")); - } - - mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, - NULL, &errorHandler, NULL); - if (!mWritePngPtr) - { - LLTHROW(PngError("Problem creating png write structure")); - } - - mWriteInfoPtr = png_create_info_struct(mWritePngPtr); - - // Setup write function - PngDataInfo dataPtr; - dataPtr.mData = dest; - dataPtr.mOffset = 0; - dataPtr.mDataSize = destSize; - png_set_write_fn(mWritePngPtr, &dataPtr, &writeDataCallback, &writeFlush); - - // Setup image params - mWidth = rawImage->getWidth(); - mHeight = rawImage->getHeight(); - mBitDepth = 8; // Fixed to 8-bpp in SL - mChannels = numComponents; - mInterlaceType = PNG_INTERLACE_NONE; - mCompressionType = PNG_COMPRESSION_TYPE_DEFAULT; - mFilterMethod = PNG_FILTER_TYPE_DEFAULT; - - // Write header - png_set_IHDR(mWritePngPtr, mWriteInfoPtr, mWidth, mHeight, - mBitDepth, mColorType, mInterlaceType, - mCompressionType, mFilterMethod); - - // Get data and compute row size - const U8* data = rawImage->getData(); - int offset = mWidth * mChannels; - - // Ready to write, start with the header - png_write_info(mWritePngPtr, mWriteInfoPtr); - - // Write image (sorry, must const-cast for libpng) - const U8 * rowPointer; - for (U32 i=0; i < mHeight; i++) - { - rowPointer = &data[(mHeight-1-i)*offset]; - png_write_row(mWritePngPtr, const_cast<png_bytep>(rowPointer)); - } - - // Finish up - png_write_end(mWritePngPtr, mWriteInfoPtr); - mFinalSize = dataPtr.mOffset; - } - catch (const PngError& msg) - { - mErrorMessage = msg.what(); - releaseResources(); - return (false); - } - - releaseResources(); - return true; + try + { + S8 numComponents = rawImage->getComponents(); + switch (numComponents) + { + case 1: + mColorType = PNG_COLOR_TYPE_GRAY; + break; + case 2: + mColorType = PNG_COLOR_TYPE_GRAY_ALPHA; + break; + case 3: + mColorType = PNG_COLOR_TYPE_RGB; + break; + case 4: + mColorType = PNG_COLOR_TYPE_RGB_ALPHA; + break; + default: + mColorType = -1; + } + + if (mColorType == -1) + { + LLTHROW(PngError("Unsupported image: unexpected number of channels")); + } + + mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, &errorHandler, NULL); + if (!mWritePngPtr) + { + LLTHROW(PngError("Problem creating png write structure")); + } + + mWriteInfoPtr = png_create_info_struct(mWritePngPtr); + + // Setup write function + PngDataInfo dataPtr; + dataPtr.mData = dest; + dataPtr.mOffset = 0; + dataPtr.mDataSize = destSize; + png_set_write_fn(mWritePngPtr, &dataPtr, &writeDataCallback, &writeFlush); + + // Setup image params + mWidth = rawImage->getWidth(); + mHeight = rawImage->getHeight(); + mBitDepth = 8; // Fixed to 8-bpp in SL + mChannels = numComponents; + mInterlaceType = PNG_INTERLACE_NONE; + mCompressionType = PNG_COMPRESSION_TYPE_DEFAULT; + mFilterMethod = PNG_FILTER_TYPE_DEFAULT; + + // Write header + png_set_IHDR(mWritePngPtr, mWriteInfoPtr, mWidth, mHeight, + mBitDepth, mColorType, mInterlaceType, + mCompressionType, mFilterMethod); + + // Get data and compute row size + const U8* data = rawImage->getData(); + int offset = mWidth * mChannels; + + // Ready to write, start with the header + png_write_info(mWritePngPtr, mWriteInfoPtr); + + // Write image (sorry, must const-cast for libpng) + const U8 * rowPointer; + for (U32 i=0; i < mHeight; i++) + { + rowPointer = &data[(mHeight-1-i)*offset]; + png_write_row(mWritePngPtr, const_cast<png_bytep>(rowPointer)); + } + + // Finish up + png_write_end(mWritePngPtr, mWriteInfoPtr); + mFinalSize = dataPtr.mOffset; + } + catch (const PngError& msg) + { + mErrorMessage = msg.what(); + releaseResources(); + return (false); + } + + releaseResources(); + return true; } // Cleanup various internal structures void LLPngWrapper::releaseResources() { - if (mReadPngPtr || mReadInfoPtr) - { - png_destroy_read_struct(&mReadPngPtr, &mReadInfoPtr, NULL); - mReadPngPtr = NULL; - mReadInfoPtr = NULL; - } - - if (mWritePngPtr || mWriteInfoPtr) - { - png_destroy_write_struct(&mWritePngPtr, &mWriteInfoPtr); - mWritePngPtr = NULL; - mWriteInfoPtr = NULL; - } - - if (mRowPointers) - { - delete[] mRowPointers; - mRowPointers = NULL; - } + if (mReadPngPtr || mReadInfoPtr) + { + png_destroy_read_struct(&mReadPngPtr, &mReadInfoPtr, NULL); + mReadPngPtr = NULL; + mReadInfoPtr = NULL; + } + + if (mWritePngPtr || mWriteInfoPtr) + { + png_destroy_write_struct(&mWritePngPtr, &mWriteInfoPtr); + mWritePngPtr = NULL; + mWriteInfoPtr = NULL; + } + + if (mRowPointers) + { + delete[] mRowPointers; + mRowPointers = NULL; + } } // Get final image size after compression U32 LLPngWrapper::getFinalSize() { - return mFinalSize; + return mFinalSize; } // Get last error message, if any const std::string& LLPngWrapper::getErrorMessage() { - return mErrorMessage; + return mErrorMessage; } diff --git a/indra/llimage/llpngwrapper.h b/indra/llimage/llpngwrapper.h index b17e8d1f40..3ada8ac7c2 100644 --- a/indra/llimage/llpngwrapper.h +++ b/indra/llimage/llpngwrapper.h @@ -4,21 +4,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -32,66 +32,66 @@ class LLPngWrapper { public: - LLPngWrapper(); - virtual ~LLPngWrapper(); + LLPngWrapper(); + virtual ~LLPngWrapper(); public: - struct ImageInfo - { - U16 mWidth; - U16 mHeight; - S8 mComponents; - }; + struct ImageInfo + { + U16 mWidth; + U16 mHeight; + S8 mComponents; + }; bool isValidPng(U8* src); bool readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInfo *infop = NULL); bool writePng(const LLImageRaw* rawImage, U8* dst, size_t destSize); - U32 getFinalSize(); - const std::string& getErrorMessage(); + U32 getFinalSize(); + const std::string& getErrorMessage(); protected: - void normalizeImage(); - void updateMetaData(); + void normalizeImage(); + void updateMetaData(); private: - // Structure for writing/reading PNG data to/from memory - // as opposed to using a file. - struct PngDataInfo - { - U8 *mData; - U32 mOffset; - S32 mDataSize; - }; + // Structure for writing/reading PNG data to/from memory + // as opposed to using a file. + struct PngDataInfo + { + U8 *mData; + U32 mOffset; + S32 mDataSize; + }; - static void writeFlush(png_structp png_ptr); - static void errorHandler(png_structp png_ptr, png_const_charp msg); - static void readDataCallback(png_structp png_ptr, png_bytep dest, png_size_t length); - static void writeDataCallback(png_structp png_ptr, png_bytep src, png_size_t length); + static void writeFlush(png_structp png_ptr); + static void errorHandler(png_structp png_ptr, png_const_charp msg); + static void readDataCallback(png_structp png_ptr, png_bytep dest, png_size_t length); + static void writeDataCallback(png_structp png_ptr, png_bytep src, png_size_t length); - void releaseResources(); + void releaseResources(); - png_structp mReadPngPtr; - png_infop mReadInfoPtr; - png_structp mWritePngPtr; - png_infop mWriteInfoPtr; + png_structp mReadPngPtr; + png_infop mReadInfoPtr; + png_structp mWritePngPtr; + png_infop mWriteInfoPtr; - U8 **mRowPointers; + U8 **mRowPointers; - png_uint_32 mWidth; - png_uint_32 mHeight; - S32 mBitDepth; - S32 mColorType; - S32 mChannels; - S32 mInterlaceType; - S32 mCompressionType; - S32 mFilterMethod; + png_uint_32 mWidth; + png_uint_32 mHeight; + S32 mBitDepth; + S32 mColorType; + S32 mChannels; + S32 mInterlaceType; + S32 mCompressionType; + S32 mFilterMethod; - U32 mFinalSize; + U32 mFinalSize; - F64 mGamma; + F64 mGamma; - std::string mErrorMessage; + std::string mErrorMessage; }; #endif diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp index 5bd884874b..36be885912 100644 --- a/indra/llimage/tests/llimageworker_test.cpp +++ b/indra/llimage/tests/llimageworker_test.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llimageworker_test.cpp * @author Merov Linden * @date 2009-04-28 @@ -6,28 +6,28 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ // Precompiled header: almost always required for newview cpp files #include "linden_common.h" -// Class to test +// Class to test #include "../llimageworker.h" // For timer class #include "../llcommon/lltimer.h" @@ -38,13 +38,13 @@ // ------------------------------------------------------------------------------------------- // Stubbing: Declarations required to link and run the class being tested -// Notes: +// Notes: // * Add here stubbed implementation of the few classes and methods used in the class to be tested // * Add as little as possible (let the link errors guide you) // * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) // * A simulator for a class can be implemented here. Please comment and document thoroughly. -LLImageBase::LLImageBase() +LLImageBase::LLImageBase() : mData(NULL), mDataSize(0), mWidth(0), @@ -68,6 +68,7 @@ U8* LLImageRaw::allocateData(S32 size) { return NULL; } U8* LLImageRaw::reallocateData(S32 size) { return NULL; } const U8* LLImageBase::getData() const { return NULL; } U8* LLImageBase::getData() { return NULL; } +const std::string& LLImage::getLastThreadError() { static std::string msg; return msg; } // End Stubbing // ------------------------------------------------------------------------------------------- @@ -78,91 +79,91 @@ U8* LLImageBase::getData() { return NULL; } namespace tut { - // Test wrapper declarations + // Test wrapper declarations - // Note: We derive the responder class for 2 reasons: - // 1. It's a pure virtual class and we can't compile without completed() being implemented - // 2. We actually need a responder to test that the thread work test completed - // We implement this making no assumption on what's done in the thread or worker - // though, just that the responder's completed() method is called in the end. - // Note on responders: responders are ref counted and *will* be deleted by the request they are - // attached to when the queued request is deleted. The recommended way of using them is to - // create them when creating a request, put a callback method in completed() and not rely on - // anything to survive in the responder object once completed() has been called. Let the request - // do the deletion and clean up itself. - class responder_test : public LLImageDecodeThread::Responder - { - public: - responder_test(bool* res) - { - done = res; - *done = false; - } - virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux, U32 request_id) - { - *done = true; - } - private: - // This is what can be thought of as the minimal implementation of a responder - // Done will be switched to true when completed() is called and can be tested - // outside the responder. A better way of doing this is to store a callback here. - bool* done; - }; + // Note: We derive the responder class for 2 reasons: + // 1. It's a pure virtual class and we can't compile without completed() being implemented + // 2. We actually need a responder to test that the thread work test completed + // We implement this making no assumption on what's done in the thread or worker + // though, just that the responder's completed() method is called in the end. + // Note on responders: responders are ref counted and *will* be deleted by the request they are + // attached to when the queued request is deleted. The recommended way of using them is to + // create them when creating a request, put a callback method in completed() and not rely on + // anything to survive in the responder object once completed() has been called. Let the request + // do the deletion and clean up itself. + class responder_test : public LLImageDecodeThread::Responder + { + public: + responder_test(bool* res) + { + done = res; + *done = false; + } + virtual void completed(bool success, const std::string& error_message, LLImageRaw* raw, LLImageRaw* aux, U32 request_id) + { + *done = true; + } + private: + // This is what can be thought of as the minimal implementation of a responder + // Done will be switched to true when completed() is called and can be tested + // outside the responder. A better way of doing this is to store a callback here. + bool* done; + }; - // Test wrapper declaration : decode thread - struct imagedecodethread_test - { - // Instance to be tested - LLImageDecodeThread* mThread; - // Constructor and destructor of the test wrapper - imagedecodethread_test() - { - mThread = NULL; - } - ~imagedecodethread_test() - { - delete mThread; - } - }; + // Test wrapper declaration : decode thread + struct imagedecodethread_test + { + // Instance to be tested + LLImageDecodeThread* mThread; + // Constructor and destructor of the test wrapper + imagedecodethread_test() + { + mThread = NULL; + } + ~imagedecodethread_test() + { + delete mThread; + } + }; - // Tut templating thingamagic: test group, object and test instance - typedef test_group<imagedecodethread_test> imagedecodethread_t; - typedef imagedecodethread_t::object imagedecodethread_object_t; - tut::imagedecodethread_t tut_imagedecodethread("LLImageDecodeThread"); + // Tut templating thingamagic: test group, object and test instance + typedef test_group<imagedecodethread_test> imagedecodethread_t; + typedef imagedecodethread_t::object imagedecodethread_object_t; + tut::imagedecodethread_t tut_imagedecodethread("LLImageDecodeThread"); - // --------------------------------------------------------------------------------------- - // Test functions - // Notes: - // * Test as many as you possibly can without requiring a full blown simulation of everything - // * The tests are executed in sequence so the test instance state may change between calls - // * Remember that you cannot test private methods with tut - // --------------------------------------------------------------------------------------- + // --------------------------------------------------------------------------------------- + // Test functions + // Notes: + // * Test as many as you possibly can without requiring a full blown simulation of everything + // * The tests are executed in sequence so the test instance state may change between calls + // * Remember that you cannot test private methods with tut + // --------------------------------------------------------------------------------------- - // --------------------------------------------------------------------------------------- - // Test the LLImageDecodeThread interface - // --------------------------------------------------------------------------------------- + // --------------------------------------------------------------------------------------- + // Test the LLImageDecodeThread interface + // --------------------------------------------------------------------------------------- - template<> template<> - void imagedecodethread_object_t::test<1>() - { - // Test a *threaded* instance of the class - mThread = new LLImageDecodeThread(true); - ensure("LLImageDecodeThread: threaded constructor failed", mThread != NULL); - // Insert something in the queue - bool done = false; - LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, 0, false, new responder_test(&done)); - // Verifies we get back a valid handle - ensure("LLImageDecodeThread: threaded decodeImage(), returned handle is null", decodeHandle != 0); - // Wait till the thread has time to handle the work order (though it doesn't do much per work order...) - const U32 INCREMENT_TIME = 500; // 500 milliseconds - const U32 MAX_TIME = 20 * INCREMENT_TIME; // Do the loop 20 times max, i.e. wait 10 seconds but no more - U32 total_time = 0; - while ((done == false) && (total_time < MAX_TIME)) - { - ms_sleep(INCREMENT_TIME); - total_time += INCREMENT_TIME; - } - // Verifies that the responder has now been called - ensure("LLImageDecodeThread: threaded work unit not processed", done == true); - } + template<> template<> + void imagedecodethread_object_t::test<1>() + { + // Test a *threaded* instance of the class + mThread = new LLImageDecodeThread(true); + ensure("LLImageDecodeThread: threaded constructor failed", mThread != NULL); + // Insert something in the queue + bool done = false; + LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, 0, false, new responder_test(&done)); + // Verifies we get back a valid handle + ensure("LLImageDecodeThread: threaded decodeImage(), returned handle is null", decodeHandle != 0); + // Wait till the thread has time to handle the work order (though it doesn't do much per work order...) + const U32 INCREMENT_TIME = 500; // 500 milliseconds + const U32 MAX_TIME = 20 * INCREMENT_TIME; // Do the loop 20 times max, i.e. wait 10 seconds but no more + U32 total_time = 0; + while ((done == false) && (total_time < MAX_TIME)) + { + ms_sleep(INCREMENT_TIME); + total_time += INCREMENT_TIME; + } + // Verifies that the responder has now been called + ensure("LLImageDecodeThread: threaded work unit not processed", done == true); + } } |