summaryrefslogtreecommitdiff
path: root/indra/llimage
diff options
context:
space:
mode:
authorAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
committerAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
commit1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch)
treeab243607f74f78200787bba5b9b88f07ef1b966f /indra/llimage
parent6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff)
parente1623bb276f83a43ce7a197e388720c05bdefe61 (diff)
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts: # autobuild.xml # indra/cmake/CMakeLists.txt # indra/cmake/GoogleMock.cmake # indra/llaudio/llaudioengine_fmodstudio.cpp # indra/llaudio/llaudioengine_fmodstudio.h # indra/llaudio/lllistener_fmodstudio.cpp # indra/llaudio/lllistener_fmodstudio.h # indra/llaudio/llstreamingaudio_fmodstudio.cpp # indra/llaudio/llstreamingaudio_fmodstudio.h # indra/llcharacter/llmultigesture.cpp # indra/llcharacter/llmultigesture.h # indra/llimage/llimage.cpp # indra/llimage/llimagepng.cpp # indra/llimage/llimageworker.cpp # indra/llimage/tests/llimageworker_test.cpp # indra/llmessage/tests/llmockhttpclient.h # indra/llprimitive/llgltfmaterial.h # indra/llrender/llfontfreetype.cpp # indra/llui/llcombobox.cpp # indra/llui/llfolderview.cpp # indra/llui/llfolderviewmodel.h # indra/llui/lllineeditor.cpp # indra/llui/lllineeditor.h # indra/llui/lltextbase.cpp # indra/llui/lltextbase.h # indra/llui/lltexteditor.cpp # indra/llui/lltextvalidate.cpp # indra/llui/lltextvalidate.h # indra/llui/lluictrl.h # indra/llui/llview.cpp # indra/llwindow/llwindowmacosx.cpp # indra/newview/app_settings/settings.xml # indra/newview/llappearancemgr.cpp # indra/newview/llappearancemgr.h # indra/newview/llavatarpropertiesprocessor.cpp # indra/newview/llavatarpropertiesprocessor.h # indra/newview/llbreadcrumbview.cpp # indra/newview/llbreadcrumbview.h # indra/newview/llbreastmotion.cpp # indra/newview/llbreastmotion.h # indra/newview/llconversationmodel.h # indra/newview/lldensityctrl.cpp # indra/newview/lldensityctrl.h # indra/newview/llface.inl # indra/newview/llfloatereditsky.cpp # indra/newview/llfloatereditwater.cpp # indra/newview/llfloateremojipicker.h # indra/newview/llfloaterimsessiontab.cpp # indra/newview/llfloaterprofiletexture.cpp # indra/newview/llfloaterprofiletexture.h # indra/newview/llgesturemgr.cpp # indra/newview/llgesturemgr.h # indra/newview/llimpanel.cpp # indra/newview/llimpanel.h # indra/newview/llinventorybridge.cpp # indra/newview/llinventorybridge.h # indra/newview/llinventoryclipboard.cpp # indra/newview/llinventoryclipboard.h # indra/newview/llinventoryfunctions.cpp # indra/newview/llinventoryfunctions.h # indra/newview/llinventorygallery.cpp # indra/newview/lllistbrowser.cpp # indra/newview/lllistbrowser.h # indra/newview/llpanelobjectinventory.cpp # indra/newview/llpanelprofile.cpp # indra/newview/llpanelprofile.h # indra/newview/llpreviewgesture.cpp # indra/newview/llsavedsettingsglue.cpp # indra/newview/llsavedsettingsglue.h # indra/newview/lltooldraganddrop.cpp # indra/newview/llurllineeditorctrl.cpp # indra/newview/llvectorperfoptions.cpp # indra/newview/llvectorperfoptions.h # indra/newview/llviewerparceloverlay.cpp # indra/newview/llviewertexlayer.cpp # indra/newview/llviewertexturelist.cpp # indra/newview/macmain.h # indra/test/test.cpp
Diffstat (limited to 'indra/llimage')
-rw-r--r--indra/llimage/llimage.cpp4972
-rw-r--r--indra/llimage/llimage.h761
-rw-r--r--indra/llimage/llimagebmp.cpp1360
-rw-r--r--indra/llimage/llimagebmp.h128
-rw-r--r--indra/llimage/llimagedimensionsinfo.cpp452
-rw-r--r--indra/llimage/llimagedimensionsinfo.h190
-rw-r--r--indra/llimage/llimagedxt.cpp1048
-rw-r--r--indra/llimage/llimagedxt.h198
-rw-r--r--indra/llimage/llimagefilter.cpp1882
-rw-r--r--indra/llimage/llimagefilter.h48
-rw-r--r--indra/llimage/llimagej2c.cpp1186
-rw-r--r--indra/llimage/llimagej2c.h170
-rw-r--r--indra/llimage/llimagejpeg.cpp1348
-rw-r--r--indra/llimage/llimagejpeg.h170
-rw-r--r--indra/llimage/llimagepng.cpp338
-rw-r--r--indra/llimage/llimagepng.h22
-rw-r--r--indra/llimage/llimagetga.cpp2456
-rw-r--r--indra/llimage/llimagetga.h128
-rw-r--r--indra/llimage/llimageworker.cpp444
-rw-r--r--indra/llimage/llimageworker.h134
-rw-r--r--indra/llimage/llmapimagetype.h24
-rw-r--r--indra/llimage/llpngwrapper.cpp823
-rw-r--r--indra/llimage/llpngwrapper.h194
-rw-r--r--indra/llimage/tests/llimageworker_test.cpp179
24 files changed, 9340 insertions, 9315 deletions
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 281ae12491..4ba6d94842 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -1,2488 +1,2484 @@
-/**
- * @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$
- */
-
-#include "linden_common.h"
-
-#include "llimageworker.h"
-#include "llimage.h"
-
-#include "llmath.h"
-#include "v4coloru.h"
-
-#include "llimagebmp.h"
-#include "llimagetga.h"
-#include "llimagej2c.h"
-#include "llimagejpeg.h"
-#include "llimagepng.h"
-#include "llimagedxt.h"
-#include "llmemory.h"
-
-#include <boost/preprocessor.hpp>
-
-//..................................................................................
-//..................................................................................
-// Helper macrose's for generate cycle unwrap templates
-//..................................................................................
-#define _UNROL_GEN_TPL_arg_0(arg)
-#define _UNROL_GEN_TPL_arg_1(arg) arg
-
-#define _UNROL_GEN_TPL_comma_0
-#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))))
-
-#define _UNROL_GEN_TPL_ARGS(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))))
-
-#define _UNROL_GEN_TPL_TYPE_ARGS(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));
-
-#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) \
- } \
-};
-//..................................................................................
-#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)
-
-#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)
-//..................................................................................
-//..................................................................................
-
-
-//..................................................................................
-// Generated unrolling loop templates with specializations
-//..................................................................................
-//example: for(c = 0; c < ch; ++c) comp[c] = cx[0] = 0;
-UNROLL_GEN_TPL(uroll_zeroze_cx_comp, (S32 *)(cx)(S32 *)(comp), (cx[_idx] = comp[_idx] = 0), (1)(3)(4));
-//example: for(c = 0; c < ch; ++c) comp[c] >>= 4;
-UNROLL_GEN_TPL(uroll_comp_rshftasgn_constval, (S32 *)(comp)(const S32)(cval), (comp[_idx] >>= cval), (1)(3)(4));
-//example: for(c = 0; c < ch; ++c) comp[c] = (cx[c] >> 5) * yap;
-UNROLL_GEN_TPL(uroll_comp_asgn_cx_rshft_cval_all_mul_val, (S32 *)(comp)(S32 *)(cx)(const S32)(cval)(S32)(val), (comp[_idx] = (cx[_idx] >> cval) * val), (1)(3)(4));
-//example: for(c = 0; c < ch; ++c) comp[c] += (cx[c] >> 5) * Cy;
-UNROLL_GEN_TPL(uroll_comp_plusasgn_cx_rshft_cval_all_mul_val, (S32 *)(comp)(S32 *)(cx)(const S32)(cval)(S32)(val), (comp[_idx] += (cx[_idx] >> cval) * val), (1)(3)(4));
-//example: for(c = 0; c < ch; ++c) comp[c] += pix[c] * info.xapoints[x];
-UNROLL_GEN_TPL(uroll_inp_plusasgn_pix_mul_val, (S32 *)(comp)(const U8 *)(pix)(S32)(val), (comp[_idx] += pix[_idx] * val), (1)(3)(4));
-//example: for(c = 0; c < ch; ++c) cx[c] = pix[c] * info.xapoints[x];
-UNROLL_GEN_TPL(uroll_inp_asgn_pix_mul_val, (S32 *)(comp)(const U8 *)(pix)(S32)(val), (comp[_idx] = pix[_idx] * val), (1)(3)(4));
-//example: for(c = 0; c < ch; ++c) comp[c] = ((cx[c] * info.yapoints[y]) + (comp[c] * (256 - info.yapoints[y]))) >> 16;
-UNROLL_GEN_TPL(uroll_comp_asgn_cx_mul_apoint_plus_comp_mul_inv_apoint_allshifted_16_r, (S32 *)(comp)(S32 *)(cx)(S32)(apoint), (comp[_idx] = ((cx[_idx] * apoint) + (comp[_idx] * (256 - apoint))) >> 16), (1)(3)(4));
-//example: for(c = 0; c < ch; ++c) comp[c] = (comp[c] + pix[c] * info.yapoints[y]) >> 8;
-UNROLL_GEN_TPL(uroll_comp_asgn_comp_plus_pix_mul_apoint_allshifted_8_r, (S32 *)(comp)(const U8 *)(pix)(S32)(apoint), (comp[_idx] = (comp[_idx] + pix[_idx] * apoint) >> 8), (1)(3)(4));
-//example: for(c = 0; c < ch; ++c) comp[c] = ((comp[c]*(256 - info.xapoints[x])) + ((cx[c] * info.xapoints[x]))) >> 12;
-UNROLL_GEN_TPL(uroll_comp_asgn_comp_mul_inv_apoint_plus_cx_mul_apoint_allshifted_12_r, (S32 *)(comp)(S32)(apoint)(S32 *)(cx), (comp[_idx] = ((comp[_idx] * (256-apoint)) + (cx[_idx] * apoint)) >> 12), (1)(3)(4));
-//example: for(c = 0; c < ch; ++c) *dptr++ = comp[c]&0xff;
-UNROLL_GEN_TPL(uroll_uref_dptr_inc_asgn_comp_and_ff, (U8 *&)(dptr)(S32 *)(comp), (*dptr++ = comp[_idx]&0xff), (1)(3)(4));
-//example: for(c = 0; c < ch; ++c) *dptr++ = (sptr[info.xpoints[x]*ch + c])&0xff;
-UNROLL_GEN_TPL(uroll_uref_dptr_inc_asgn_sptr_apoint_plus_idx_alland_ff, (U8 *&)(dptr)(const U8 *)(sptr)(S32)(apoint), (*dptr++ = sptr[apoint + _idx]&0xff), (1)(3)(4));
-//example: for(c = 0; c < ch; ++c) *dptr++ = (comp[c]>>10)&0xff;
-UNROLL_GEN_TPL(uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff, (U8 *&)(dptr)(S32 *)(comp)(const S32)(cval), (*dptr++ = (comp[_idx]>>cval)&0xff), (1)(3)(4));
-//..................................................................................
-
-
-template<U8 ch>
-struct scale_info
-{
-public:
- 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;
-
-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);
- }
-
-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);
- }
- }
- }
-};
-
-
-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
-}
-
-//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);
-
- 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;
- }
-
-}
-
-//---------------------------------------------------------------------------
-// LLImage
-//---------------------------------------------------------------------------
-
-//static
-std::string LLImage::sLastErrorMessage;
-LLMutex* LLImage::sMutex = NULL;
-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;
- sMinimalReverseByteRangePercent = minimal_reverse_byte_range_percent;
- sMutex = new LLMutex();
-}
-
-//static
-void LLImage::cleanupClass()
-{
- delete sMutex;
- sMutex = NULL;
-}
-
-//static
-const std::string& LLImage::getLastError()
-{
- static const std::string noerr("No Error");
- return sLastErrorMessage.empty() ? noerr : sLastErrorMessage;
-}
-
-//static
-void LLImage::setLastError(const std::string& message)
-{
- sLastErrorMessage = message;
-}
-
-//---------------------------------------------------------------------------
-// LLImageBase
-//---------------------------------------------------------------------------
-
-LLImageBase::LLImageBase()
-: mData(NULL),
- mDataSize(0),
- mWidth(0),
- mHeight(0),
- mComponents(0),
- mBadBufferAllocation(false),
- mAllowOverSize(false)
-{}
-
-// virtual
-LLImageBase::~LLImageBase()
-{
- deleteData(); // virtual
-}
-
-// virtual
-void LLImageBase::dump()
-{
- 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;
- }
-}
-
-// virtual
-void LLImageBase::deleteData()
-{
- 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;
-}
-
-// 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;
-}
-
-const U8* LLImageBase::getData() const
-{
- 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;
- }
-
- return mData;
-}
-
-bool LLImageBase::isBufferInvalid() const
-{
- return mBadBufferAllocation || mData == NULL;
-}
-
-void LLImageBase::setSize(S32 width, S32 height, S32 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
-}
-
-//---------------------------------------------------------------------------
-// LLImageRaw
-//---------------------------------------------------------------------------
-
-S32 LLImageRaw::sRawImageCount = 0;
-
-LLImageRaw::LLImageRaw()
- : LLImageBase()
-{
- ++sRawImageCount;
-}
-
-LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components)
- : LLImageBase()
-{
- //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)
- : LLImageBase()
-{
- if (allocateDataSize(width, height, components))
- {
- memcpy(getData(), data, width * height * components);
- }
-}
-
-LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy)
- : LLImageBase()
-{
- 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()
-//{
-// createFromFile(filename, j2c_lowest_mip_only);
-//}
-
-LLImageRaw::~LLImageRaw()
-{
- // NOTE: ~LLimageBase() call to deleteData() calls LLImageBase::deleteData()
- // NOT LLImageRaw::deleteData()
- deleteData();
- --sRawImageCount;
-}
-
-// virtual
-U8* LLImageRaw::allocateData(S32 size)
-{
- LLImageDataLock lock(this);
-
- U8* res = LLImageBase::allocateData(size);
- return res;
-}
-
-// virtual
-U8* LLImageRaw::reallocateData(S32 size)
-{
- LLImageDataLock lock(this);
-
- U8* res = LLImageBase::reallocateData(size);
- return res;
-}
-
-void LLImageRaw::releaseData()
-{
- LLImageDataLock lock(this);
-
- LLImageBase::setSize(0, 0, 0);
- LLImageBase::setDataAndSize(nullptr, 0);
-}
-
-// virtual
-void LLImageRaw::deleteData()
-{
- LLImageDataLock lock(this);
-
- LLImageBase::deleteData();
-}
-
-void LLImageRaw::setDataAndSize(U8 *data, S32 width, S32 height, S8 components)
-{
- LLImageDataLock lock(this);
-
- if(data == getData())
- {
- return ;
- }
-
- deleteData();
-
- LLImageBase::setSize(width, height, components) ;
- LLImageBase::setDataAndSize(data, width * height * components) ;
-}
-
-bool LLImageRaw::resize(U16 width, U16 height, S8 components)
-{
- LLImageDataLock lock(this);
-
- if ((getWidth() == width) && (getHeight() == height) && (getComponents() == components) && !isBufferInvalid())
- {
- return true;
- }
- // Reallocate the data buffer.
- deleteData();
-
- allocateDataSize(width,height,components);
-
- return !isBufferInvalid();
-}
-
-bool LLImageRaw::setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height,
- const U8 *data, U32 stride, bool reverse_y)
-{
- LLImageDataLock lock(this);
-
- if (!getData())
- {
- return false;
- }
- if (!data)
- {
- return false;
- }
-
- // 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);
- }
-
- 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++;
- }
- }
-}
-
-// Reverses the order of the rows in the image
-void LLImageRaw::verticalFlip()
-{
- 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 );
- }
-}
-
-
-bool LLImageRaw::optimizeAwayAlpha()
-{
- LLImageDataLock lock(this);
-
- if (getComponents() == 4)
- {
- U8* data = getData();
- U32 pixels = getWidth() * getHeight();
-
- // check alpha channel for all 255
- for (U32 i = 0; i < pixels; ++i)
- {
- if (data[i * 4 + 3] != 255)
- {
- return false;
- }
- }
-
- // alpha channel is all 255, make a new copy of data without alpha channel
- U8* new_data = (U8*) ll_aligned_malloc_16(getWidth() * getHeight() * 3);
-
- for (U32 i = 0; i < pixels; ++i)
- {
- U32 di = i * 3;
- U32 si = i * 4;
- for (U32 j = 0; j < 3; ++j)
- {
- new_data[di+j] = data[si+j];
- }
- }
-
- setDataAndSize(new_data, getWidth(), getHeight(), 3);
-
- return true;
- }
-
- return false;
-}
-
-void LLImageRaw::expandToPowerOfTwo(S32 max_dim, bool scale_image)
-{
- LLImageDataLock lock(this);
-
- // Find new sizes
- S32 new_width = expandDimToPowerOfTwo(getWidth(), max_dim);
- S32 new_height = expandDimToPowerOfTwo(getHeight(), max_dim);
-
- scale( new_width, new_height, scale_image );
-}
-
-void LLImageRaw::contractToPowerOfTwo(S32 max_dim, bool scale_image)
-{
- LLImageDataLock lock(this);
-
- // 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 );
-}
-
-// 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;
-}
-
-// 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;
- }
- 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;
- }
- return new_dim;
-}
-
-void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim)
-{
- LLImageDataLock lock(this);
-
- // Find new sizes
- S32 new_width = biasedDimToPowerOfTwo(getWidth(),max_dim);
- S32 new_height = biasedDimToPowerOfTwo(getHeight(),max_dim);
-
- 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);
-}
-
-
-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());
- 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;
-
- LLImageRaw* dst = this; // Just for clarity.
-
- LLImageDataLock lock(this);
-
- 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);
-
- // 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() );
- }
-}
-
-
-// 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.
-
- LLImageDataLock lock(this);
-
- 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
- {
-
- 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;
- }
-}
-
-
-void LLImageRaw::copyUnscaledAlphaMask( const LLImageRaw* src, const LLColor4U& fill)
-{
- LLImageRaw* dst = this; // Just for clarity.
-
- LLImageDataSharedLock lockIn(src);
- LLImageDataLock lockOut(this);
-
- if (!validateSrcAndDst("LLImageRaw::copyUnscaledAlphaMask", src, dst))
- {
- return;
- }
-
- 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;
- }
-}
-
-
-// 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;
- }
- }
-}
-
-LLPointer<LLImageRaw> LLImageRaw::duplicate()
-{
- if(getNumRefs() < 2)
- {
- return this; //nobody else refences to this image, no need to duplicate.
- }
-
- LLImageDataSharedLock lock(this);
-
- //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 );
- }
- }
-}
-
-// 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.
-
- 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()) );
-
- 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()) );
-
- // 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()) );
-
- // 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.
-
- LLImageDataLock lock(this);
-
- 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;
- }
-}
-
-
-// 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.
-
- LLImageDataLock lock(this);
-
- 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;
- }
-}
-
-
-// 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.
-
- LLImageDataSharedLock lockIn(src);
- LLImageDataLock lockOut(this);
-
- if (!validateSrcAndDst("LLImageRaw::copyScaled", src, dst))
- {
- return;
- }
-
- 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;
- }
-
- 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);
-
- // 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 );
- }
- */
-}
-
-
-bool LLImageRaw::scale( S32 new_width, S32 new_height, bool scale_image_data )
-{
- LLImageDataLock lock(this);
-
- S32 components = getComponents();
- if (components != 1 && components != 3 && components != 4)
- {
- LL_WARNS() << "Invalid getComponents value (" << components << ")" << LL_ENDL;
- return false;
- }
-
- if (isBufferInvalid())
- {
- LL_WARNS() << "Invalid image buffer" << LL_ENDL;
- return false;
- }
-
- S32 old_width = getWidth();
- S32 old_height = getHeight();
-
- if( (old_width == new_width) && (old_height == new_height) )
- {
- return true; // Nothing to do.
- }
-
- // Reallocate the data buffer.
-
- if (scale_image_data)
- {
- S32 new_data_size = new_width * new_height * components;
-
- if (new_data_size > 0)
- {
- U8 *new_data = (U8*)ll_aligned_malloc_16(new_data_size);
- if(NULL == new_data)
- {
- 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);
-
- if (!new_buffer)
- {
- LL_WARNS() << "Failed to allocate new image data buffer" << LL_ENDL;
- return false;
- }
-
- for( S32 row = 0; row < new_height; row++ )
- {
- 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)
- {
- // 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);
- }
- }
- }
- catch (std::bad_alloc&) // for temp_buffer
- {
- LL_WARNS() << "Failed to allocate temporary image buffer" << LL_ENDL;
- return false;
- }
-
- return true ;
-}
-
-LLPointer<LLImageRaw> LLImageRaw::scaled(S32 new_width, S32 new_height)
-{
- LLPointer<LLImageRaw> result;
-
- LLImageDataLock lock(this);
-
- S32 components = getComponents();
- if (components != 1 && components != 3 && components != 4)
- {
- LL_WARNS() << "Invalid getComponents value (" << components << ")" << LL_ENDL;
- return result;
- }
-
- if (isBufferInvalid())
- {
- LL_WARNS() << "Invalid image buffer" << LL_ENDL;
- return result;
- }
-
- S32 old_width = getWidth();
- S32 old_height = getHeight();
-
- if ((old_width == new_width) && (old_height == new_height))
- {
- result = new LLImageRaw(old_width, old_height, components);
- if (!result || result->isBufferInvalid())
- {
- LL_WARNS() << "Failed to allocate new image" << LL_ENDL;
- return result;
- }
- memcpy(result->getData(), getData(), getDataSize());
- }
- else
- {
- S32 new_data_size = new_width * new_height * components;
-
- if (new_data_size > 0)
- {
- result = new LLImageRaw(new_width, new_height, components);
- if (!result || result->isBufferInvalid())
- {
- LL_WARNS() << "Failed to allocate new image" << LL_ENDL;
- return result;
- }
- bilinear_scale(getData(), old_width, old_height, components, old_width*components, result->getData(), new_width, new_height, components, new_width*components);
- }
- }
-
- return result;
-}
-
-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));
- }
- }
-}
-
-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;
- }
-}
-
-// static
-bool LLImageRaw::validateSrcAndDst(std::string func, const LLImageRaw* src, const LLImageRaw* 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";
-
- 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;
-}
-
-//----------------------------------------------------------------------------
-
-static struct
-{
- 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 }
-};
-#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("");
-}
-#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 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;
-}
-#endif
-//---------------------------------------------------------------------------
-// LLImageFormatted
-//---------------------------------------------------------------------------
-
-//static
-S32 LLImageFormatted::sGlobalFormattedMemory = 0;
-
-LLImageFormatted::LLImageFormatted(S8 codec)
- : 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();
-}
-
-//----------------------------------------------------------------------------
-
-//virtual
-void LLImageFormatted::resetLastError()
-{
- 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);
-}
-
-//----------------------------------------------------------------------------
-
-// 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;
-}
-
-// 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);
-}
-//----------------------------------------------------------------------------
-
-// virtual
-void LLImageFormatted::dump()
-{
- LLImageBase::dump();
-
- 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();
-}
-
-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;
-}
-
-
-//----------------------------------------------------------------------------
-
-// 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.
-}
-
-//----------------------------------------------------------------------------
-
-// virtual
-U8* LLImageFormatted::allocateData(S32 size)
-{
- LLImageDataLock lock(this);
-
- U8* res = LLImageBase::allocateData(size); // calls deleteData()
- sGlobalFormattedMemory += getDataSize();
- return res;
-}
-
-// virtual
-U8* LLImageFormatted::reallocateData(S32 size)
-{
- LLImageDataLock lock(this);
-
- sGlobalFormattedMemory -= getDataSize();
- U8* res = LLImageBase::reallocateData(size);
- sGlobalFormattedMemory += getDataSize();
- return res;
-}
-
-// virtual
-void LLImageFormatted::deleteData()
-{
- LLImageDataLock lock(this);
-
- if (mDecoding)
- {
- LL_ERRS() << "LLImageFormatted::deleteData() is called during decoding" << LL_ENDL;
- }
- sGlobalFormattedMemory -= getDataSize();
- LLImageBase::deleteData();
-}
-
-//----------------------------------------------------------------------------
-
-// virtual
-void LLImageFormatted::sanityCheck()
-{
- LLImageBase::sanityCheck();
-
- 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);
-
- 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);
-
- if (data && data != getData())
- {
- deleteData();
- setDataAndSize(data, size); // Access private LLImageBase members
-
- 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);
- }
- }
-}
-
-//----------------------------------------------------------------------------
-
-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;
-}
-
-bool LLImageFormatted::save(const std::string &filename)
-{
- resetLastError();
-
- LLAPRFile outfile ;
- outfile.open(filename, LL_APR_WB);
- if (!outfile.getFileHandle())
- {
- setLastError("Unable to open file for writing", filename);
- return false;
- }
-
- LLImageDataSharedLock lock(this);
-
- S32 result = outfile.write(getData(), getDataSize());
- outfile.close() ;
- return (result != 0);
-}
-
-S8 LLImageFormatted::getCodec() const
-{
- 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);
-}
-
-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);
-}
-
-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);
-}
-
-void LLImageBase::setDataAndSize(U8 *data, S32 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
- }
-}
-
-
-//============================================================================
-
-//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;
-}
-
-//============================================================================
+/**
+ * @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$
+ */
+
+#include "linden_common.h"
+
+#include "llimageworker.h"
+#include "llimage.h"
+
+#include "llmath.h"
+#include "v4coloru.h"
+
+#include "llimagebmp.h"
+#include "llimagetga.h"
+#include "llimagej2c.h"
+#include "llimagejpeg.h"
+#include "llimagepng.h"
+#include "llimagedxt.h"
+#include "llmemory.h"
+
+#include <boost/preprocessor.hpp>
+
+//..................................................................................
+//..................................................................................
+// Helper macrose's for generate cycle unwrap templates
+//..................................................................................
+#define _UNROL_GEN_TPL_arg_0(arg)
+#define _UNROL_GEN_TPL_arg_1(arg) arg
+
+#define _UNROL_GEN_TPL_comma_0
+#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))))
+
+#define _UNROL_GEN_TPL_ARGS(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))))
+
+#define _UNROL_GEN_TPL_TYPE_ARGS(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));
+
+#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) \
+ } \
+};
+//..................................................................................
+#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)
+
+#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)
+//..................................................................................
+//..................................................................................
+
+
+//..................................................................................
+// Generated unrolling loop templates with specializations
+//..................................................................................
+//example: for(c = 0; c < ch; ++c) comp[c] = cx[0] = 0;
+UNROLL_GEN_TPL(uroll_zeroze_cx_comp, (S32 *)(cx)(S32 *)(comp), (cx[_idx] = comp[_idx] = 0), (1)(3)(4));
+//example: for(c = 0; c < ch; ++c) comp[c] >>= 4;
+UNROLL_GEN_TPL(uroll_comp_rshftasgn_constval, (S32 *)(comp)(const S32)(cval), (comp[_idx] >>= cval), (1)(3)(4));
+//example: for(c = 0; c < ch; ++c) comp[c] = (cx[c] >> 5) * yap;
+UNROLL_GEN_TPL(uroll_comp_asgn_cx_rshft_cval_all_mul_val, (S32 *)(comp)(S32 *)(cx)(const S32)(cval)(S32)(val), (comp[_idx] = (cx[_idx] >> cval) * val), (1)(3)(4));
+//example: for(c = 0; c < ch; ++c) comp[c] += (cx[c] >> 5) * Cy;
+UNROLL_GEN_TPL(uroll_comp_plusasgn_cx_rshft_cval_all_mul_val, (S32 *)(comp)(S32 *)(cx)(const S32)(cval)(S32)(val), (comp[_idx] += (cx[_idx] >> cval) * val), (1)(3)(4));
+//example: for(c = 0; c < ch; ++c) comp[c] += pix[c] * info.xapoints[x];
+UNROLL_GEN_TPL(uroll_inp_plusasgn_pix_mul_val, (S32 *)(comp)(const U8 *)(pix)(S32)(val), (comp[_idx] += pix[_idx] * val), (1)(3)(4));
+//example: for(c = 0; c < ch; ++c) cx[c] = pix[c] * info.xapoints[x];
+UNROLL_GEN_TPL(uroll_inp_asgn_pix_mul_val, (S32 *)(comp)(const U8 *)(pix)(S32)(val), (comp[_idx] = pix[_idx] * val), (1)(3)(4));
+//example: for(c = 0; c < ch; ++c) comp[c] = ((cx[c] * info.yapoints[y]) + (comp[c] * (256 - info.yapoints[y]))) >> 16;
+UNROLL_GEN_TPL(uroll_comp_asgn_cx_mul_apoint_plus_comp_mul_inv_apoint_allshifted_16_r, (S32 *)(comp)(S32 *)(cx)(S32)(apoint), (comp[_idx] = ((cx[_idx] * apoint) + (comp[_idx] * (256 - apoint))) >> 16), (1)(3)(4));
+//example: for(c = 0; c < ch; ++c) comp[c] = (comp[c] + pix[c] * info.yapoints[y]) >> 8;
+UNROLL_GEN_TPL(uroll_comp_asgn_comp_plus_pix_mul_apoint_allshifted_8_r, (S32 *)(comp)(const U8 *)(pix)(S32)(apoint), (comp[_idx] = (comp[_idx] + pix[_idx] * apoint) >> 8), (1)(3)(4));
+//example: for(c = 0; c < ch; ++c) comp[c] = ((comp[c]*(256 - info.xapoints[x])) + ((cx[c] * info.xapoints[x]))) >> 12;
+UNROLL_GEN_TPL(uroll_comp_asgn_comp_mul_inv_apoint_plus_cx_mul_apoint_allshifted_12_r, (S32 *)(comp)(S32)(apoint)(S32 *)(cx), (comp[_idx] = ((comp[_idx] * (256-apoint)) + (cx[_idx] * apoint)) >> 12), (1)(3)(4));
+//example: for(c = 0; c < ch; ++c) *dptr++ = comp[c]&0xff;
+UNROLL_GEN_TPL(uroll_uref_dptr_inc_asgn_comp_and_ff, (U8 *&)(dptr)(S32 *)(comp), (*dptr++ = comp[_idx]&0xff), (1)(3)(4));
+//example: for(c = 0; c < ch; ++c) *dptr++ = (sptr[info.xpoints[x]*ch + c])&0xff;
+UNROLL_GEN_TPL(uroll_uref_dptr_inc_asgn_sptr_apoint_plus_idx_alland_ff, (U8 *&)(dptr)(const U8 *)(sptr)(S32)(apoint), (*dptr++ = sptr[apoint + _idx]&0xff), (1)(3)(4));
+//example: for(c = 0; c < ch; ++c) *dptr++ = (comp[c]>>10)&0xff;
+UNROLL_GEN_TPL(uroll_uref_dptr_inc_asgn_comp_rshft_cval_and_ff, (U8 *&)(dptr)(S32 *)(comp)(const S32)(cval), (*dptr++ = (comp[_idx]>>cval)&0xff), (1)(3)(4));
+//..................................................................................
+
+
+template<U8 ch>
+struct scale_info
+{
+public:
+ 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;
+
+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);
+ }
+
+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);
+ }
+ }
+ }
+};
+
+
+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
+}
+
+//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);
+
+ 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;
+ }
+
+}
+
+//---------------------------------------------------------------------------
+// LLImage
+//---------------------------------------------------------------------------
+
+//static
+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;
+ sMinimalReverseByteRangePercent = minimal_reverse_byte_range_percent;
+}
+
+//static
+void LLImage::cleanupClass()
+{
+}
+
+//static
+const std::string& LLImage::getLastThreadError()
+{
+ static const std::string noerr("No Error");
+ return sLastThreadErrorMessage.empty() ? noerr : sLastThreadErrorMessage;
+}
+
+//static
+void LLImage::setLastError(const std::string& message)
+{
+ sLastThreadErrorMessage = message;
+}
+
+//---------------------------------------------------------------------------
+// LLImageBase
+//---------------------------------------------------------------------------
+
+LLImageBase::LLImageBase()
+: mData(NULL),
+ mDataSize(0),
+ mWidth(0),
+ mHeight(0),
+ mComponents(0),
+ mBadBufferAllocation(false),
+ mAllowOverSize(false)
+{}
+
+// virtual
+LLImageBase::~LLImageBase()
+{
+ deleteData(); // virtual
+}
+
+// virtual
+void LLImageBase::dump()
+{
+ 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;
+ }
+}
+
+// virtual
+void LLImageBase::deleteData()
+{
+ 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;
+}
+
+// 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;
+}
+
+const U8* LLImageBase::getData() const
+{
+ 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;
+ }
+
+ return mData;
+}
+
+bool LLImageBase::isBufferInvalid() const
+{
+ return mBadBufferAllocation || mData == NULL;
+}
+
+void LLImageBase::setSize(S32 width, S32 height, S32 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
+}
+
+//---------------------------------------------------------------------------
+// LLImageRaw
+//---------------------------------------------------------------------------
+
+S32 LLImageRaw::sRawImageCount = 0;
+
+LLImageRaw::LLImageRaw()
+ : LLImageBase()
+{
+ ++sRawImageCount;
+}
+
+LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components)
+ : LLImageBase()
+{
+ //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)
+ : LLImageBase()
+{
+ if (allocateDataSize(width, height, components))
+ {
+ memcpy(getData(), data, width * height * components);
+ }
+}
+
+LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy)
+ : LLImageBase()
+{
+ 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()
+//{
+// createFromFile(filename, j2c_lowest_mip_only);
+//}
+
+LLImageRaw::~LLImageRaw()
+{
+ // NOTE: ~LLimageBase() call to deleteData() calls LLImageBase::deleteData()
+ // NOT LLImageRaw::deleteData()
+ deleteData();
+ --sRawImageCount;
+}
+
+// virtual
+U8* LLImageRaw::allocateData(S32 size)
+{
+ LLImageDataLock lock(this);
+
+ U8* res = LLImageBase::allocateData(size);
+ return res;
+}
+
+// virtual
+U8* LLImageRaw::reallocateData(S32 size)
+{
+ LLImageDataLock lock(this);
+
+ U8* res = LLImageBase::reallocateData(size);
+ return res;
+}
+
+void LLImageRaw::releaseData()
+{
+ LLImageDataLock lock(this);
+
+ LLImageBase::setSize(0, 0, 0);
+ LLImageBase::setDataAndSize(nullptr, 0);
+}
+
+// virtual
+void LLImageRaw::deleteData()
+{
+ LLImageDataLock lock(this);
+
+ LLImageBase::deleteData();
+}
+
+void LLImageRaw::setDataAndSize(U8 *data, S32 width, S32 height, S8 components)
+{
+ LLImageDataLock lock(this);
+
+ if(data == getData())
+ {
+ return ;
+ }
+
+ deleteData();
+
+ LLImageBase::setSize(width, height, components) ;
+ LLImageBase::setDataAndSize(data, width * height * components) ;
+}
+
+bool LLImageRaw::resize(U16 width, U16 height, S8 components)
+{
+ LLImageDataLock lock(this);
+
+ if ((getWidth() == width) && (getHeight() == height) && (getComponents() == components) && !isBufferInvalid())
+ {
+ return true;
+ }
+ // Reallocate the data buffer.
+ deleteData();
+
+ allocateDataSize(width,height,components);
+
+ return !isBufferInvalid();
+}
+
+bool LLImageRaw::setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height,
+ const U8 *data, U32 stride, bool reverse_y)
+{
+ LLImageDataLock lock(this);
+
+ if (!getData())
+ {
+ return false;
+ }
+ if (!data)
+ {
+ return false;
+ }
+
+ // 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);
+ }
+
+ 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++;
+ }
+ }
+}
+
+// Reverses the order of the rows in the image
+void LLImageRaw::verticalFlip()
+{
+ 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 );
+ }
+}
+
+
+bool LLImageRaw::optimizeAwayAlpha()
+{
+ LLImageDataLock lock(this);
+
+ if (getComponents() == 4)
+ {
+ U8* data = getData();
+ U32 pixels = getWidth() * getHeight();
+
+ // check alpha channel for all 255
+ for (U32 i = 0; i < pixels; ++i)
+ {
+ if (data[i * 4 + 3] != 255)
+ {
+ return false;
+ }
+ }
+
+ // alpha channel is all 255, make a new copy of data without alpha channel
+ U8* new_data = (U8*) ll_aligned_malloc_16(getWidth() * getHeight() * 3);
+
+ for (U32 i = 0; i < pixels; ++i)
+ {
+ U32 di = i * 3;
+ U32 si = i * 4;
+ for (U32 j = 0; j < 3; ++j)
+ {
+ new_data[di+j] = data[si+j];
+ }
+ }
+
+ setDataAndSize(new_data, getWidth(), getHeight(), 3);
+
+ return true;
+ }
+
+ return false;
+}
+
+void LLImageRaw::expandToPowerOfTwo(S32 max_dim, bool scale_image)
+{
+ LLImageDataLock lock(this);
+
+ // Find new sizes
+ S32 new_width = expandDimToPowerOfTwo(getWidth(), max_dim);
+ S32 new_height = expandDimToPowerOfTwo(getHeight(), max_dim);
+
+ scale( new_width, new_height, scale_image );
+}
+
+void LLImageRaw::contractToPowerOfTwo(S32 max_dim, bool scale_image)
+{
+ LLImageDataLock lock(this);
+
+ // 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 );
+}
+
+// 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;
+}
+
+// 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;
+ }
+ 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;
+ }
+ return new_dim;
+}
+
+void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim)
+{
+ LLImageDataLock lock(this);
+
+ // Find new sizes
+ S32 new_width = biasedDimToPowerOfTwo(getWidth(),max_dim);
+ S32 new_height = biasedDimToPowerOfTwo(getHeight(),max_dim);
+
+ 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);
+}
+
+
+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());
+ 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;
+
+ LLImageRaw* dst = this; // Just for clarity.
+
+ LLImageDataLock lock(this);
+
+ 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);
+
+ // 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() );
+ }
+}
+
+
+// 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.
+
+ LLImageDataLock lock(this);
+
+ 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
+ {
+
+ 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;
+ }
+}
+
+
+void LLImageRaw::copyUnscaledAlphaMask( const LLImageRaw* src, const LLColor4U& fill)
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ LLImageDataSharedLock lockIn(src);
+ LLImageDataLock lockOut(this);
+
+ if (!validateSrcAndDst("LLImageRaw::copyUnscaledAlphaMask", src, dst))
+ {
+ return;
+ }
+
+ 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;
+ }
+}
+
+
+// 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;
+ }
+ }
+}
+
+LLPointer<LLImageRaw> LLImageRaw::duplicate()
+{
+ if(getNumRefs() < 2)
+ {
+ return this; //nobody else refences to this image, no need to duplicate.
+ }
+
+ LLImageDataSharedLock lock(this);
+
+ //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 );
+ }
+ }
+}
+
+// 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.
+
+ 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()) );
+
+ 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()) );
+
+ // 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()) );
+
+ // 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.
+
+ LLImageDataLock lock(this);
+
+ 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;
+ }
+}
+
+
+// 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.
+
+ LLImageDataLock lock(this);
+
+ 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;
+ }
+}
+
+
+// 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.
+
+ LLImageDataSharedLock lockIn(src);
+ LLImageDataLock lockOut(this);
+
+ if (!validateSrcAndDst("LLImageRaw::copyScaled", src, dst))
+ {
+ return;
+ }
+
+ 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;
+ }
+
+ 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);
+
+ // 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 );
+ }
+ */
+}
+
+
+bool LLImageRaw::scale( S32 new_width, S32 new_height, bool scale_image_data )
+{
+ LLImageDataLock lock(this);
+
+ S32 components = getComponents();
+ if (components != 1 && components != 3 && components != 4)
+ {
+ LL_WARNS() << "Invalid getComponents value (" << components << ")" << LL_ENDL;
+ return false;
+ }
+
+ if (isBufferInvalid())
+ {
+ LL_WARNS() << "Invalid image buffer" << LL_ENDL;
+ return false;
+ }
+
+ S32 old_width = getWidth();
+ S32 old_height = getHeight();
+
+ if( (old_width == new_width) && (old_height == new_height) )
+ {
+ return true; // Nothing to do.
+ }
+
+ // Reallocate the data buffer.
+
+ if (scale_image_data)
+ {
+ S32 new_data_size = new_width * new_height * components;
+
+ if (new_data_size > 0)
+ {
+ U8 *new_data = (U8*)ll_aligned_malloc_16(new_data_size);
+ if(NULL == new_data)
+ {
+ 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);
+
+ if (!new_buffer)
+ {
+ LL_WARNS() << "Failed to allocate new image data buffer" << LL_ENDL;
+ return false;
+ }
+
+ for( S32 row = 0; row < new_height; row++ )
+ {
+ 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)
+ {
+ // 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);
+ }
+ }
+ }
+ catch (std::bad_alloc&) // for temp_buffer
+ {
+ LL_WARNS() << "Failed to allocate temporary image buffer" << LL_ENDL;
+ return false;
+ }
+
+ return true ;
+}
+
+LLPointer<LLImageRaw> LLImageRaw::scaled(S32 new_width, S32 new_height)
+{
+ LLPointer<LLImageRaw> result;
+
+ LLImageDataLock lock(this);
+
+ S32 components = getComponents();
+ if (components != 1 && components != 3 && components != 4)
+ {
+ LL_WARNS() << "Invalid getComponents value (" << components << ")" << LL_ENDL;
+ return result;
+ }
+
+ if (isBufferInvalid())
+ {
+ LL_WARNS() << "Invalid image buffer" << LL_ENDL;
+ return result;
+ }
+
+ S32 old_width = getWidth();
+ S32 old_height = getHeight();
+
+ if ((old_width == new_width) && (old_height == new_height))
+ {
+ result = new LLImageRaw(old_width, old_height, components);
+ if (!result || result->isBufferInvalid())
+ {
+ LL_WARNS() << "Failed to allocate new image" << LL_ENDL;
+ return result;
+ }
+ memcpy(result->getData(), getData(), getDataSize());
+ }
+ else
+ {
+ S32 new_data_size = new_width * new_height * components;
+
+ if (new_data_size > 0)
+ {
+ result = new LLImageRaw(new_width, new_height, components);
+ if (!result || result->isBufferInvalid())
+ {
+ LL_WARNS() << "Failed to allocate new image" << LL_ENDL;
+ return result;
+ }
+ bilinear_scale(getData(), old_width, old_height, components, old_width*components, result->getData(), new_width, new_height, components, new_width*components);
+ }
+ }
+
+ return result;
+}
+
+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));
+ }
+ }
+}
+
+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;
+ }
+}
+
+// static
+bool LLImageRaw::validateSrcAndDst(std::string func, const LLImageRaw* src, const LLImageRaw* 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";
+
+ 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;
+}
+
+//----------------------------------------------------------------------------
+
+static struct
+{
+ 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 }
+};
+#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("");
+}
+#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 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;
+}
+#endif
+//---------------------------------------------------------------------------
+// LLImageFormatted
+//---------------------------------------------------------------------------
+
+//static
+S32 LLImageFormatted::sGlobalFormattedMemory = 0;
+
+LLImageFormatted::LLImageFormatted(S8 codec)
+ : 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();
+}
+
+//----------------------------------------------------------------------------
+
+//virtual
+void LLImageFormatted::resetLastError()
+{
+ 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);
+}
+
+//----------------------------------------------------------------------------
+
+// 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;
+}
+
+// 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);
+}
+//----------------------------------------------------------------------------
+
+// virtual
+void LLImageFormatted::dump()
+{
+ LLImageBase::dump();
+
+ 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();
+}
+
+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;
+}
+
+
+//----------------------------------------------------------------------------
+
+// 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.
+}
+
+//----------------------------------------------------------------------------
+
+// virtual
+U8* LLImageFormatted::allocateData(S32 size)
+{
+ LLImageDataLock lock(this);
+
+ U8* res = LLImageBase::allocateData(size); // calls deleteData()
+ sGlobalFormattedMemory += getDataSize();
+ return res;
+}
+
+// virtual
+U8* LLImageFormatted::reallocateData(S32 size)
+{
+ LLImageDataLock lock(this);
+
+ sGlobalFormattedMemory -= getDataSize();
+ U8* res = LLImageBase::reallocateData(size);
+ sGlobalFormattedMemory += getDataSize();
+ return res;
+}
+
+// virtual
+void LLImageFormatted::deleteData()
+{
+ LLImageDataLock lock(this);
+
+ if (mDecoding)
+ {
+ LL_ERRS() << "LLImageFormatted::deleteData() is called during decoding" << LL_ENDL;
+ }
+ sGlobalFormattedMemory -= getDataSize();
+ LLImageBase::deleteData();
+}
+
+//----------------------------------------------------------------------------
+
+// virtual
+void LLImageFormatted::sanityCheck()
+{
+ LLImageBase::sanityCheck();
+
+ 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);
+
+ 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);
+
+ if (data && data != getData())
+ {
+ deleteData();
+ setDataAndSize(data, size); // Access private LLImageBase members
+
+ 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);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+
+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;
+}
+
+bool LLImageFormatted::save(const std::string &filename)
+{
+ resetLastError();
+
+ LLAPRFile outfile ;
+ outfile.open(filename, LL_APR_WB);
+ if (!outfile.getFileHandle())
+ {
+ setLastError("Unable to open file for writing", filename);
+ return false;
+ }
+
+ LLImageDataSharedLock lock(this);
+
+ S32 result = outfile.write(getData(), getDataSize());
+ outfile.close() ;
+ return (result != 0);
+}
+
+S8 LLImageFormatted::getCodec() const
+{
+ 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);
+}
+
+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);
+}
+
+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);
+}
+
+void LLImageBase::setDataAndSize(U8 *data, S32 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
+ }
+}
+
+
+//============================================================================
+
+//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;
+}
+
+//============================================================================
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index cc6a58f417..5ebadec555 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -1,381 +1,380 @@
-/**
- * @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$
- */
-
-#ifndef LL_LLIMAGE_H
-#define LL_LLIMAGE_H
-
-#include "lluuid.h"
-#include "llstring.h"
-#include "llpointer.h"
-#include "lltrace.h"
-
-const S32 MIN_IMAGE_MIP = 2; // 4x4, only used for expand/contract power of 2
-const S32 MAX_IMAGE_MIP = 11; // 2048x2048
-
-// *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 = 2048; // 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); // 2048
-const S32 MIN_IMAGE_AREA = MIN_IMAGE_SIZE * MIN_IMAGE_SIZE;
-const S32 MAX_IMAGE_AREA = MAX_IMAGE_SIZE * MAX_IMAGE_SIZE;
-const S32 MAX_IMAGE_COMPONENTS = 8;
-const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; //2048 * 2048 * 8 = 16 MB
-
-// Note! These CANNOT be changed without modifying simulator code
-// *TODO: change both to 1024 when SIM texture fetching is deprecated
-const S32 FIRST_PACKET_SIZE = 600;
-const S32 MAX_IMG_PACKET_SIZE = 1000;
-const S32 HTTP_PACKET_SIZE = 1496;
-
-// Base classes for images.
-// There are two major parts for the image:
-// The compressed representation, and the decompressed representation.
-
-class LLImageFormatted;
-class LLImageRaw;
-class LLColor4U;
-
-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
-} EImageCodec;
-
-//============================================================================
-// library initialization class
-// LLImage is frequently used in threads so do not convert it to LLSingleton
-
-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; }
-
-protected:
- static LLMutex* sMutex;
- static std::string sLastErrorMessage;
- static bool sUseNewByteRange;
- static S32 sMinimalReverseByteRangePercent;
-};
-
-//============================================================================
-// Image base class
-
-class LLImageBase
-: public LLThreadSafeRefCount
-{
-protected:
- virtual ~LLImageBase();
-
- virtual void deleteData();
- virtual U8* allocateData(S32 size = -1);
- virtual U8* reallocateData(S32 size = -1);
-
-public:
- LLImageBase();
-
- enum
- {
- TYPE_NORMAL = 0,
- TYPE_AVATAR_BAKE = 1,
- };
-
- 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; }
-
- 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; }
-
-protected:
- // 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 EImageCodec getCodecFromExtension(const std::string& exten);
-
- //static LLTrace::MemStatHandle sMemStat;
-
-private:
- U8 *mData;
- S32 mDataSize;
-
- U16 mWidth;
- U16 mHeight;
-
- S8 mComponents;
-
- bool mBadBufferAllocation;
- bool mAllowOverSize;
-
-private:
- mutable LLSharedMutex mDataMutex;
-
-public:
- template<bool SHARED>
- class DataLock : LLSharedMutexLockTemplate<SHARED>
- {
- public:
- DataLock(const LLImageBase* image)
- : LLSharedMutexLockTemplate<SHARED>(image ? &image->mDataMutex : nullptr)
- {
- }
- };
-};
-
-using LLImageDataLock = LLImageBase::DataLock<false>;
-using LLImageDataSharedLock = LLImageBase::DataLock<true>;
-
-// Raw representation of an image (used for textures, and other uncompressed formats
-class LLImageRaw : public LLImageBase
-{
-protected:
- /*virtual*/ ~LLImageRaw();
-
-public:
- 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);
-
- /*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
- // 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);
-
- void clear(U8 r=0, U8 g=0, U8 b=0, U8 a=255);
-
- void verticalFlip();
-
- // if the alpha channel is all 100% opaque, delete it
- // returns true if alpha channel was deleted
- bool optimizeAwayAlpha();
-
- 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);
- LLPointer<LLImageRaw> scaled(S32 new_width, S32 new_height);
-
- // Fill the buffer with a constant color
- void fill( const LLColor4U& color );
-
- // Copy operations
-
- //duplicate this raw image if refCount > 1.
- LLPointer<LLImageRaw> duplicate();
-
- // 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 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 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);
-
- // Src and dst can be any size. Src and dst have same number of components.
- void copyScaled( 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 );
-
-protected:
- // 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 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 );
-
- // 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 );
-
- static U8 fastFractionalMult(U8 a, U8 b);
-
- void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ;
-
-public:
- static S32 sRawImageCount;
-
-private:
- static bool validateSrcAndDst(std::string func, const LLImageRaw* src, const LLImageRaw* dst);
-};
-
-// Compressed representation of image.
-// Subclass from this class for the different representations (J2C, bmp)
-class LLImageFormatted : public LLImageBase
-{
-public:
- static LLImageFormatted* createFromType(S8 codec);
- static LLImageFormatted* createFromExtension(const std::string& instring);
-
-protected:
- /*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());
-
-protected:
- 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.
-
-public:
- static S32 sGlobalFormattedMemory;
-};
-
-#endif
+/**
+ * @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$
+ */
+
+#ifndef LL_LLIMAGE_H
+#define LL_LLIMAGE_H
+
+#include "lluuid.h"
+#include "llstring.h"
+#include "llpointer.h"
+#include "lltrace.h"
+
+const S32 MIN_IMAGE_MIP = 2; // 4x4, only used for expand/contract power of 2
+const S32 MAX_IMAGE_MIP = 11; // 2048x2048
+
+// *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 = 2048; // 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); // 2048
+const S32 MIN_IMAGE_AREA = MIN_IMAGE_SIZE * MIN_IMAGE_SIZE;
+const S32 MAX_IMAGE_AREA = MAX_IMAGE_SIZE * MAX_IMAGE_SIZE;
+const S32 MAX_IMAGE_COMPONENTS = 8;
+const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; //2048 * 2048 * 8 = 16 MB
+
+// Note! These CANNOT be changed without modifying simulator code
+// *TODO: change both to 1024 when SIM texture fetching is deprecated
+const S32 FIRST_PACKET_SIZE = 600;
+const S32 MAX_IMG_PACKET_SIZE = 1000;
+const S32 HTTP_PACKET_SIZE = 1496;
+
+// Base classes for images.
+// There are two major parts for the image:
+// The compressed representation, and the decompressed representation.
+
+class LLImageFormatted;
+class LLImageRaw;
+class LLColor4U;
+
+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
+} EImageCodec;
+
+//============================================================================
+// library initialization class
+// LLImage is frequently used in threads so do not convert it to LLSingleton
+
+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& getLastThreadError();
+ static void setLastError(const std::string& message);
+
+ static bool useNewByteRange() { return sUseNewByteRange; }
+ static S32 getReverseByteRangePercent() { return sMinimalReverseByteRangePercent; }
+
+protected:
+ static thread_local std::string sLastThreadErrorMessage;
+ static bool sUseNewByteRange;
+ static S32 sMinimalReverseByteRangePercent;
+};
+
+//============================================================================
+// Image base class
+
+class LLImageBase
+: public LLThreadSafeRefCount
+{
+protected:
+ virtual ~LLImageBase();
+
+ virtual void deleteData();
+ virtual U8* allocateData(S32 size = -1);
+ virtual U8* reallocateData(S32 size = -1);
+
+public:
+ LLImageBase();
+
+ enum
+ {
+ TYPE_NORMAL = 0,
+ TYPE_AVATAR_BAKE = 1,
+ };
+
+ 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; }
+
+ 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; }
+
+protected:
+ // 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 EImageCodec getCodecFromExtension(const std::string& exten);
+
+ //static LLTrace::MemStatHandle sMemStat;
+
+private:
+ U8 *mData;
+ S32 mDataSize;
+
+ U16 mWidth;
+ U16 mHeight;
+
+ S8 mComponents;
+
+ bool mBadBufferAllocation;
+ bool mAllowOverSize;
+
+private:
+ mutable LLSharedMutex mDataMutex;
+
+public:
+ template<bool SHARED>
+ class DataLock : LLSharedMutexLockTemplate<SHARED>
+ {
+ public:
+ DataLock(const LLImageBase* image)
+ : LLSharedMutexLockTemplate<SHARED>(image ? &image->mDataMutex : nullptr)
+ {
+ }
+ };
+};
+
+using LLImageDataLock = LLImageBase::DataLock<false>;
+using LLImageDataSharedLock = LLImageBase::DataLock<true>;
+
+// Raw representation of an image (used for textures, and other uncompressed formats
+class LLImageRaw : public LLImageBase
+{
+protected:
+ /*virtual*/ ~LLImageRaw();
+
+public:
+ 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);
+
+ /*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
+ // 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);
+
+ void clear(U8 r=0, U8 g=0, U8 b=0, U8 a=255);
+
+ void verticalFlip();
+
+ // if the alpha channel is all 100% opaque, delete it
+ // returns true if alpha channel was deleted
+ bool optimizeAwayAlpha();
+
+ 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);
+ LLPointer<LLImageRaw> scaled(S32 new_width, S32 new_height);
+
+ // Fill the buffer with a constant color
+ void fill( const LLColor4U& color );
+
+ // Copy operations
+
+ //duplicate this raw image if refCount > 1.
+ LLPointer<LLImageRaw> duplicate();
+
+ // 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 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 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);
+
+ // Src and dst can be any size. Src and dst have same number of components.
+ void copyScaled( 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 );
+
+protected:
+ // 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 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 );
+
+ // 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 );
+
+ static U8 fastFractionalMult(U8 a, U8 b);
+
+ void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ;
+
+public:
+ static S32 sRawImageCount;
+
+private:
+ static bool validateSrcAndDst(std::string func, const LLImageRaw* src, const LLImageRaw* dst);
+};
+
+// Compressed representation of image.
+// Subclass from this class for the different representations (J2C, bmp)
+class LLImageFormatted : public LLImageBase
+{
+public:
+ static LLImageFormatted* createFromType(S8 codec);
+ static LLImageFormatted* createFromExtension(const std::string& instring);
+
+protected:
+ /*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());
+
+protected:
+ 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.
+
+public:
+ static S32 sGlobalFormattedMemory;
+};
+
+#endif
diff --git a/indra/llimage/llimagebmp.cpp b/indra/llimage/llimagebmp.cpp
index d0881bb9ff..aa5e07a8f4 100644
--- a/indra/llimage/llimagebmp.cpp
+++ b/indra/llimage/llimagebmp.cpp
@@ -1,680 +1,680 @@
-/**
- * @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$
- */
-
-#include "linden_common.h"
-
-#include "llimagebmp.h"
-#include "llerror.h"
-
-#include "llendianswizzle.h"
-
-
-/**
- * @struct LLBMPHeader
- *
- * This struct helps deal with bmp files.
- */
-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;
-};
-
-/**
- * @struct Win95BmpHeaderExtension
- */
-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
-};
-
-/**
- * LLImageBMP
- */
-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;
-}
-
-LLImageBMP::~LLImageBMP()
-{
- 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'))
- {
- 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)
- {
- 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 */
- }
-
- 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;
-}
-
-U32 LLImageBMP::countTrailingZeros( U32 m )
-{
- 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;
-}
-
-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;
-}
-
-
-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;
-}
-
-
-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;
-}
-
-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;
-#if LL_DARWIN
- header.mHorzPelsPerMeter = header.mVertPelsPerMeter = 2834; // 72dpi
-#else
- 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;
-}
+/**
+ * @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$
+ */
+
+#include "linden_common.h"
+
+#include "llimagebmp.h"
+#include "llerror.h"
+
+#include "llendianswizzle.h"
+
+
+/**
+ * @struct LLBMPHeader
+ *
+ * This struct helps deal with bmp files.
+ */
+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;
+};
+
+/**
+ * @struct Win95BmpHeaderExtension
+ */
+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
+};
+
+/**
+ * LLImageBMP
+ */
+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;
+}
+
+LLImageBMP::~LLImageBMP()
+{
+ 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'))
+ {
+ 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)
+ {
+ 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 */
+ }
+
+ 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;
+}
+
+U32 LLImageBMP::countTrailingZeros( U32 m )
+{
+ 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;
+}
+
+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;
+}
+
+
+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;
+}
+
+
+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;
+}
+
+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;
+#if LL_DARWIN
+ header.mHorzPelsPerMeter = header.mVertPelsPerMeter = 2834; // 72dpi
+#else
+ 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;
+}
diff --git a/indra/llimage/llimagebmp.h b/indra/llimage/llimagebmp.h
index 295f96e541..65977210f6 100644
--- a/indra/llimage/llimagebmp.h
+++ b/indra/llimage/llimagebmp.h
@@ -1,64 +1,64 @@
-/**
- * @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$
- */
-
-#ifndef LL_LLIMAGEBMP_H
-#define LL_LLIMAGEBMP_H
-
-#include "llimage.h"
-
-// This class compresses and decompressed BMP files
-
-class LLImageBMP : public LLImageFormatted
-{
-protected:
- virtual ~LLImageBMP();
-
-public:
- 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);
-
-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 );
-
- U32 countTrailingZeros( U32 m );
-
-protected:
- S32 mColorPaletteColors;
- U8* mColorPalette;
- S32 mBitmapOffset;
- S32 mBitsPerPixel;
- U32 mBitfieldMask[4]; // rgba
- bool mOriginAtTop;
-};
-
-#endif
+/**
+ * @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$
+ */
+
+#ifndef LL_LLIMAGEBMP_H
+#define LL_LLIMAGEBMP_H
+
+#include "llimage.h"
+
+// This class compresses and decompressed BMP files
+
+class LLImageBMP : public LLImageFormatted
+{
+protected:
+ virtual ~LLImageBMP();
+
+public:
+ 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);
+
+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 );
+
+ U32 countTrailingZeros( U32 m );
+
+protected:
+ 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..ec1a8e535d 100644
--- a/indra/llimage/llimagedimensionsinfo.cpp
+++ b/indra/llimage/llimagedimensionsinfo.cpp
@@ -1,226 +1,226 @@
-/**
- * @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$
- */
-
-#include "linden_common.h"
-#include "stdtypes.h"
-
-#include "llimagejpeg.h"
-
-#include "llimagedimensionsinfo.h"
-
-// Value is true if one of Libjpeg's functions has encountered an error while working.
-static bool sJpegErrorEncountered = false;
-
-bool LLImageDimensionsInfo::load(const std::string& src_filename,U32 codec)
-{
- clean();
-
- mSrcFilename = src_filename;
-
- 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 (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;
-
- }
-}
-
-
-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;
- mWarning = "texture_load_format_error";
- return false;
- }
-
- // Read image dimensions.
- mInfile.seek(APR_CUR, 16);
- mWidth = read_reverse_s32();
- mHeight = read_reverse_s32();
-
- return true;
-}
-
-bool LLImageDimensionsInfo::getImageDimensionsTga()
-{
- 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;
- }
-
- // *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;
-}
-
-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;
- mWarning = "texture_load_format_error";
- return false;
- }
-
- // Read image dimensions.
- mInfile.seek(APR_CUR, 8 /* chunk length + chunk type */);
- mWidth = read_s32();
- mHeight = read_s32();
-
- 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;
-}
-
-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;
- 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;
-}
-
-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;
-}
+/**
+ * @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$
+ */
+
+#include "linden_common.h"
+#include "stdtypes.h"
+
+#include "llimagejpeg.h"
+
+#include "llimagedimensionsinfo.h"
+
+// Value is true if one of Libjpeg's functions has encountered an error while working.
+static bool sJpegErrorEncountered = false;
+
+bool LLImageDimensionsInfo::load(const std::string& src_filename,U32 codec)
+{
+ clean();
+
+ mSrcFilename = src_filename;
+
+ 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 (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;
+
+ }
+}
+
+
+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;
+ mWarning = "texture_load_format_error";
+ return false;
+ }
+
+ // Read image dimensions.
+ mInfile.seek(APR_CUR, 16);
+ mWidth = read_reverse_s32();
+ mHeight = read_reverse_s32();
+
+ return true;
+}
+
+bool LLImageDimensionsInfo::getImageDimensionsTga()
+{
+ 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;
+ }
+
+ // *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;
+}
+
+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;
+ mWarning = "texture_load_format_error";
+ return false;
+ }
+
+ // Read image dimensions.
+ mInfile.seek(APR_CUR, 8 /* chunk length + chunk type */);
+ mWidth = read_s32();
+ mHeight = read_s32();
+
+ 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;
+}
+
+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;
+ 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;
+}
+
+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;
+}
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..20543c6ff7 100644
--- a/indra/llimage/llimagedxt.cpp
+++ b/indra/llimage/llimagedxt.cpp
@@ -1,524 +1,524 @@
-/**
- * @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$
- */
-
-#include "linden_common.h"
-
-#include "llimagedxt.h"
-#include "llmemory.h"
-
-//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);
-}
-
-//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;
- }
-};
-
-//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;
-}
-
-//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;
- }
-};
-
-// 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;
- }
-}
-
-//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;
- }
-}
-
-//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);
-}
-
-//static
-S32 LLImageDXT::calcNumMips(S32 width, S32 height)
-{
- 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)
-{
-}
-
-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;
-}
-
-// 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;
-}
-
-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();
-}
-
-// 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;
-}
-
-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;
-}
-
-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;
-}
-
-// virtual
-bool LLImageDXT::encode(const LLImageRaw* raw_image, F32 time)
-{
- 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)
- {
- 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;
-}
-
-// virtual
-S32 LLImageDXT::calcHeaderSize()
-{
- 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;
-}
-
-//============================================================================
-
-//static
-void LLImageDXT::extractMip(const U8 *indata, U8* mipdata, int width, int height,
- 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 */
- }
-}
-
-//============================================================================
+/**
+ * @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$
+ */
+
+#include "linden_common.h"
+
+#include "llimagedxt.h"
+#include "llmemory.h"
+
+//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);
+}
+
+//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;
+ }
+};
+
+//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;
+}
+
+//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;
+ }
+};
+
+// 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;
+ }
+}
+
+//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;
+ }
+}
+
+//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);
+}
+
+//static
+S32 LLImageDXT::calcNumMips(S32 width, S32 height)
+{
+ 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)
+{
+}
+
+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;
+}
+
+// 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;
+}
+
+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();
+}
+
+// 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;
+}
+
+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;
+}
+
+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;
+}
+
+// virtual
+bool LLImageDXT::encode(const LLImageRaw* raw_image, F32 time)
+{
+ 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)
+ {
+ 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;
+}
+
+// virtual
+S32 LLImageDXT::calcHeaderSize()
+{
+ 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;
+}
+
+//============================================================================
+
+//static
+void LLImageDXT::extractMip(const U8 *indata, U8* mipdata, int width, int height,
+ 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 */
+ }
+}
+
+//============================================================================
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..10770d814d 100644
--- a/indra/llimage/llimagefilter.cpp
+++ b/indra/llimage/llimagefilter.cpp
@@ -1,941 +1,941 @@
-/**
- * @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$
- */
-
-#include "linden_common.h"
-
-#include "llimagefilter.h"
-
-#include "llmath.h"
-#include "v3color.h"
-#include "v4coloru.h"
-#include "m3math.h"
-#include "v3math.h"
-#include "llsdserialize.h"
-#include "llstring.h"
-
-//---------------------------------------------------------------------------
-// LLImageFilter
-//---------------------------------------------------------------------------
-
-LLImageFilter::LLImageFilter(const std::string& file_path) :
- mFilterData(LLSD::emptyArray()),
- mImage(NULL),
- mHistoRed(NULL),
- mHistoGreen(NULL),
- mHistoBlue(NULL),
- mHistoBrightness(NULL),
- mStencilBlendMode(STENCIL_BLEND_MODE_BLEND),
- mStencilShape(STENCIL_SHAPE_UNIFORM),
- mStencilGamma(1.0),
- mStencilMin(0.0),
- 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();
- }
-}
-
-LLImageFilter::~LLImageFilter()
-{
- mImage = NULL;
- ll_aligned_free_16(mHistoRed);
- ll_aligned_free_16(mHistoGreen);
- ll_aligned_free_16(mHistoBlue);
- ll_aligned_free_16(mHistoBrightness);
-}
-
-/*
- *TODO
- * Rename stencil to mask
- * Improve perf: use LUT for alpha blending in uniform case
- * Add gradient coloring as a filter
- */
-
-//============================================================================
-// Apply the filter data to the image passed as parameter
-//============================================================================
-
-void LLImageFilter::executeFilter(LLPointer<LLImageRaw> raw_image)
-{
- mImage = raw_image;
-
- LLImageDataLock lock(mImage);
-
- //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 = ";
- //for (S32 j = 1; j < mFilterData[i].size(); ++j)
- //{
- // 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
- std::string filter_shape = mFilterData[i][1].asString();
- EStencilShape shape = STENCIL_SHAPE_UNIFORM;
- if (filter_shape == "uniform")
- {
- shape = STENCIL_SHAPE_UNIFORM;
- }
- else if (filter_shape == "gradient")
- {
- shape = STENCIL_SHAPE_GRADIENT;
- }
- else if (filter_shape == "vignette")
- {
- shape = STENCIL_SHAPE_VIGNETTE;
- }
- else if (filter_shape == "scanlines")
- {
- shape = STENCIL_SHAPE_SCAN_LINES;
- }
- // Get the blend mode of the stencil, that is how the effect is blended in the background through the stencil
- std::string filter_mode = mFilterData[i][2].asString();
- EStencilBlendMode mode = STENCIL_BLEND_MODE_BLEND;
- if (filter_mode == "blend")
- {
- mode = STENCIL_BLEND_MODE_BLEND;
- }
- else if (filter_mode == "add")
- {
- mode = STENCIL_BLEND_MODE_ADD;
- }
- else if (filter_mode == "add_back")
- {
- mode = STENCIL_BLEND_MODE_ABACK;
- }
- else if (filter_mode == "fade")
- {
- mode = STENCIL_BLEND_MODE_FADE;
- }
- // Get the float params: mandatory min, max then the optional parameters (4 max)
- F32 min = (F32)(mFilterData[i][3].asReal());
- F32 max = (F32)(mFilterData[i][4].asReal());
- F32 params[4] = {0.0, 0.0, 0.0, 0.0};
- for (S32 j = 5; (j < mFilterData[i].size()) && (j < 9); j++)
- {
- params[j-5] = (F32)(mFilterData[i][j].asReal());
- }
- // Set the stencil
- setStencil(shape,mode,min,max,params);
- }
- else if (filter_name == "sepia")
- {
- filterSepia();
- }
- else if (filter_name == "grayscale")
- {
- filterGrayScale();
- }
- else if (filter_name == "saturate")
- {
- filterSaturate((float)(mFilterData[i][1].asReal()));
- }
- else if (filter_name == "rotate")
- {
- filterRotate((float)(mFilterData[i][1].asReal()));
- }
- else if (filter_name == "gamma")
- {
- LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
- filterGamma((float)(mFilterData[i][1].asReal()),color);
- }
- else if (filter_name == "colorize")
- {
- LLColor3 color((float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()));
- LLColor3 alpha((F32)(mFilterData[i][4].asReal()),(float)(mFilterData[i][5].asReal()),(float)(mFilterData[i][6].asReal()));
- filterColorize(color,alpha);
- }
- else if (filter_name == "contrast")
- {
- LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
- filterContrast((float)(mFilterData[i][1].asReal()),color);
- }
- else if (filter_name == "brighten")
- {
- LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
- filterBrightness((float)(mFilterData[i][1].asReal()),color);
- }
- else if (filter_name == "darken")
- {
- LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
- filterBrightness((float)(-mFilterData[i][1].asReal()),color);
- }
- else if (filter_name == "linearize")
- {
- LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
- filterLinearize((float)(mFilterData[i][1].asReal()),color);
- }
- else if (filter_name == "posterize")
- {
- LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
- filterEqualize((S32)(mFilterData[i][1].asReal()),color);
- }
- else if (filter_name == "screen")
- {
- std::string screen_name = mFilterData[i][1].asString();
- EScreenMode mode = SCREEN_MODE_2DSINE;
- if (screen_name == "2Dsine")
- {
- mode = SCREEN_MODE_2DSINE;
- }
- else if (screen_name == "line")
- {
- mode = SCREEN_MODE_LINE;
- }
- filterScreen(mode,(F32)(mFilterData[i][2].asReal()),(F32)(mFilterData[i][3].asReal()));
- }
- else if (filter_name == "blur")
- {
- LLMatrix3 kernel;
- for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++)
- for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
- kernel.mMatrix[i][j] = 1.0;
- convolve(kernel,true,false);
- }
- else if (filter_name == "sharpen")
- {
- LLMatrix3 kernel;
- for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
- for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
- kernel.mMatrix[k][j] = -1.0;
- kernel.mMatrix[1][1] = 9.0;
- convolve(kernel,false,false);
- }
- else if (filter_name == "gradient")
- {
- LLMatrix3 kernel;
- for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
- for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
- kernel.mMatrix[k][j] = -1.0;
- kernel.mMatrix[1][1] = 8.0;
- convolve(kernel,false,true);
- }
- else if (filter_name == "convolve")
- {
- LLMatrix3 kernel;
- S32 index = 1;
- bool normalize = (mFilterData[i][index++].asReal() > 0.0);
- bool abs_value = (mFilterData[i][index++].asReal() > 0.0);
- for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
- for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
- kernel.mMatrix[k][j] = mFilterData[i][index++].asReal();
- convolve(kernel,normalize,abs_value);
- }
- else if (filter_name == "colortransform")
- {
- LLMatrix3 transform;
- S32 index = 1;
- for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
- for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
- transform.mMatrix[k][j] = mFilterData[i][index++].asReal();
- transform.transpose();
- colorTransform(transform);
- }
- else
- {
- LL_WARNS() << "Filter unknown, cannot execute filter command : " << filter_name << LL_ENDL;
- }
- }
-}
-
-//============================================================================
-// Filter Primitives
-//============================================================================
-
-void LLImageFilter::blendStencil(F32 alpha, U8* pixel, U8 red, U8 green, U8 blue)
-{
- F32 inv_alpha = 1.0 - alpha;
- switch (mStencilBlendMode)
- {
- case STENCIL_BLEND_MODE_BLEND:
- // Classic blend of incoming color with the background image
- pixel[VRED] = inv_alpha * pixel[VRED] + alpha * red;
- pixel[VGREEN] = inv_alpha * pixel[VGREEN] + alpha * green;
- pixel[VBLUE] = inv_alpha * pixel[VBLUE] + alpha * blue;
- break;
- case STENCIL_BLEND_MODE_ADD:
- // Add incoming color to the background image
- pixel[VRED] = llclampb(pixel[VRED] + alpha * red);
- pixel[VGREEN] = llclampb(pixel[VGREEN] + alpha * green);
- pixel[VBLUE] = llclampb(pixel[VBLUE] + alpha * blue);
- break;
- case STENCIL_BLEND_MODE_ABACK:
- // Add back background image to the incoming color
- pixel[VRED] = llclampb(inv_alpha * pixel[VRED] + red);
- pixel[VGREEN] = llclampb(inv_alpha * pixel[VGREEN] + green);
- pixel[VBLUE] = llclampb(inv_alpha * pixel[VBLUE] + blue);
- break;
- case STENCIL_BLEND_MODE_FADE:
- // Fade incoming color to black
- pixel[VRED] = alpha * red;
- pixel[VGREEN] = alpha * green;
- pixel[VBLUE] = alpha * blue;
- break;
- }
-}
-
-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();
- S32 height = mImage->getHeight();
-
- 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();
- S32 height = mImage->getHeight();
-
- 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 );
-
- // Compute normalization factors
- F32 kernel_min = 0.0;
- F32 kernel_max = 0.0;
- for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++)
- {
- for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
- {
- if (kernel.mMatrix[i][j] >= 0.0)
- kernel_max += kernel.mMatrix[i][j];
- else
- kernel_min += kernel.mMatrix[i][j];
- }
- }
- if (abs_value)
- {
- kernel_max = llabs(kernel_max);
- kernel_min = llabs(kernel_min);
- kernel_max = llmax(kernel_max,kernel_min);
- kernel_min = 0.0;
- }
- F32 kernel_range = kernel_max - kernel_min;
-
- // Allocate temporary buffers and initialize algorithm's data
- 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* 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 */
- 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 */
- east_west_data = &odd_buffer[0];
- north_data = &even_buffer[0];
- }
- else
- {
- memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */
- east_west_data = &even_buffer[0];
- north_data = &odd_buffer[0];
- }
- // First pixel : set to 0
- blendStencil(getStencilAlpha(0,j), dst_data, 0, 0, 0);
- dst_data += components;
- // Set pointers to kernel
- U8* NW = north_data;
- U8* N = NW+components;
- U8* NE = N+components;
- U8* W = east_west_data;
- U8* C = W+components;
- U8* E = C+components;
- U8* SW = south_data;
- U8* S = SW+components;
- U8* SE = S+components;
- // All other pixels
- for (S32 i = 1; i < (width-1); i++)
- {
- // Compute convolution
- LLVector3 dst;
- dst.mV[VRED] = (kernel.mMatrix[0][0]*NW[VRED] + kernel.mMatrix[0][1]*N[VRED] + kernel.mMatrix[0][2]*NE[VRED] +
- kernel.mMatrix[1][0]*W[VRED] + kernel.mMatrix[1][1]*C[VRED] + kernel.mMatrix[1][2]*E[VRED] +
- kernel.mMatrix[2][0]*SW[VRED] + kernel.mMatrix[2][1]*S[VRED] + kernel.mMatrix[2][2]*SE[VRED]);
- dst.mV[VGREEN] = (kernel.mMatrix[0][0]*NW[VGREEN] + kernel.mMatrix[0][1]*N[VGREEN] + kernel.mMatrix[0][2]*NE[VGREEN] +
- kernel.mMatrix[1][0]*W[VGREEN] + kernel.mMatrix[1][1]*C[VGREEN] + kernel.mMatrix[1][2]*E[VGREEN] +
- kernel.mMatrix[2][0]*SW[VGREEN] + kernel.mMatrix[2][1]*S[VGREEN] + kernel.mMatrix[2][2]*SE[VGREEN]);
- dst.mV[VBLUE] = (kernel.mMatrix[0][0]*NW[VBLUE] + kernel.mMatrix[0][1]*N[VBLUE] + kernel.mMatrix[0][2]*NE[VBLUE] +
- kernel.mMatrix[1][0]*W[VBLUE] + kernel.mMatrix[1][1]*C[VBLUE] + kernel.mMatrix[1][2]*E[VBLUE] +
- kernel.mMatrix[2][0]*SW[VBLUE] + kernel.mMatrix[2][1]*S[VBLUE] + kernel.mMatrix[2][2]*SE[VBLUE]);
- if (abs_value)
- {
- dst.mV[VRED] = llabs(dst.mV[VRED]);
- dst.mV[VGREEN] = llabs(dst.mV[VGREEN]);
- dst.mV[VBLUE] = llabs(dst.mV[VBLUE]);
- }
- if (normalize)
- {
- dst.mV[VRED] = (dst.mV[VRED] - kernel_min)/kernel_range;
- dst.mV[VGREEN] = (dst.mV[VGREEN] - kernel_min)/kernel_range;
- 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;
- N += components;
- NE += components;
- W += components;
- C += components;
- E += components;
- SW += components;
- S += components;
- SE += components;
- }
- // Last pixel : set to 0
- 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++)
- {
- blendStencil(getStencilAlpha(i,0), dst_data, 0, 0, 0);
- dst_data += components;
- }
-}
-
-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();
- 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);
-
- // Precompute the gamma table : gives us the gray level to use when cutting outside the screen (prevents strong aliasing on the screen)
- U8 gamma[256];
- for (S32 i = 0; i < 256; i++)
- {
- 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++)
- {
- for (S32 i = 0; i < width; i++)
- {
- // Compute screen value
- F32 value = 0.0;
- F32 di = 0.0;
- F32 dj = 0.0;
- switch (mode)
- {
- case SCREEN_MODE_2DSINE:
- di = cos*i + sin*j;
- dj = -sin*i + cos*j;
- value = (sinf(2*F_PI*di/wave_length_pixels)*sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0;
- break;
- case SCREEN_MODE_LINE:
- dj = sin*i - cos*j;
- value = (sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0;
- 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;
- }
- }
-}
-
-//============================================================================
-// Procedural Stencils
-//============================================================================
-void LLImageFilter::setStencil(EStencilShape shape, EStencilBlendMode mode, F32 min, F32 max, F32* params)
-{
- mStencilShape = shape;
- 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;
- mStencilCenterY = (S32)(mImage->getHeight() + params[1] * (F32)(mImage->getHeight()))/2;
- mStencilWidth = (S32)(params[2] * (F32)(mImage->getHeight()))/2;
- mStencilGamma = (params[3] <= 0.0 ? 1.0 : params[3]);
-
- mStencilWavelength = (params[0] <= 0.0 ? 10.0 : params[0] * (F32)(mImage->getHeight()) / 2.0);
- mStencilSine = sinf(params[1]*DEG_TO_RAD);
- mStencilCosine = cosf(params[1]*DEG_TO_RAD);
-
- mStencilStartX = ((F32)(mImage->getWidth()) + params[0] * (F32)(mImage->getHeight()))/2.0;
- mStencilStartY = ((F32)(mImage->getHeight()) + params[1] * (F32)(mImage->getHeight()))/2.0;
- F32 end_x = ((F32)(mImage->getWidth()) + params[2] * (F32)(mImage->getHeight()))/2.0;
- F32 end_y = ((F32)(mImage->getHeight()) + params[3] * (F32)(mImage->getHeight()))/2.0;
- mStencilGradX = end_x - mStencilStartX;
- mStencilGradY = end_y - mStencilStartY;
- mStencilGradN = mStencilGradX*mStencilGradX + mStencilGradY*mStencilGradY;
-}
-
-F32 LLImageFilter::getStencilAlpha(S32 i, S32 j)
-{
- F32 alpha = 1.0; // That init actually takes care of the STENCIL_SHAPE_UNIFORM case...
- if (mStencilShape == STENCIL_SHAPE_VIGNETTE)
- {
- // alpha is a modified gaussian value, with a center and fading in a circular pattern toward the edges
- // The gamma parameter controls the intensity of the drop down from alpha 1.0 (center) to 0.0
- F32 d_center_square = (i - mStencilCenterX)*(i - mStencilCenterX) + (j - mStencilCenterY)*(j - mStencilCenterY);
- alpha = powf(F_E, -(powf((d_center_square/(mStencilWidth*mStencilWidth)),mStencilGamma)/2.0f));
- }
- else if (mStencilShape == STENCIL_SHAPE_SCAN_LINES)
- {
- // alpha varies according to a squared sine function.
- F32 d = mStencilSine*i - mStencilCosine*j;
- alpha = (sinf(2*F_PI*d/mStencilWavelength) > 0.0 ? 1.0 : 0.0);
- }
- else if (mStencilShape == STENCIL_SHAPE_GRADIENT)
- {
- 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));
-}
-
-//============================================================================
-// Histograms
-//============================================================================
-
-U32* LLImageFilter::getBrightnessHistogram()
-{
- if (!mHistoBrightness)
- {
- computeHistograms();
- }
- return mHistoBrightness;
-}
-
-void LLImageFilter::computeHistograms()
-{
- const S32 components = mImage->getComponents();
- llassert( components >= 1 && components <= 4 );
-
- // Allocate memory for the histograms
- if (!mHistoRed)
- {
- mHistoRed = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
- }
- if (!mHistoGreen)
- {
- mHistoGreen = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
- }
- if (!mHistoBlue)
- {
- mHistoBlue = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
- }
- if (!mHistoBrightness)
- {
- mHistoBrightness = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
- }
-
- // Initialize them
- for (S32 i = 0; i < 256; i++)
- {
- mHistoRed[i] = 0;
- mHistoGreen[i] = 0;
- 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++)
- {
- mHistoRed[dst_data[VRED]]++;
- mHistoGreen[dst_data[VGREEN]]++;
- mHistoBlue[dst_data[VBLUE]]++;
- // Note: this is a very simple shorthand for brightness but it's OK for our use
- S32 brightness = ((S32)(dst_data[VRED]) + (S32)(dst_data[VGREEN]) + (S32)(dst_data[VBLUE])) / 3;
- mHistoBrightness[brightness]++;
- // next pixel...
- dst_data += components;
- }
-}
-
-//============================================================================
-// Secondary Filters
-//============================================================================
-
-void LLImageFilter::filterGrayScale()
-{
- LLMatrix3 gray_scale;
- LLVector3 luminosity(0.2125, 0.7154, 0.0721);
- gray_scale.setRows(luminosity, luminosity, luminosity);
- gray_scale.transpose();
- colorTransform(gray_scale);
-}
-
-void LLImageFilter::filterSepia()
-{
- LLMatrix3 sepia;
- sepia.setRows(LLVector3(0.3588, 0.7044, 0.1368),
- LLVector3(0.2990, 0.5870, 0.1140),
- LLVector3(0.2392, 0.4696, 0.0912));
- sepia.transpose();
- colorTransform(sepia);
-}
-
-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),
- LLVector3( 0.0, 0.0, 1.0));
- // 54.73 degre rotation around y
- float oo_sqrt3 = 1.0f / F_SQRT3;
- float sin_54 = F_SQRT2 * oo_sqrt3;
- 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);
-}
-
-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),
- LLVector3( 0.0, 0.0, 1.0));
- // 54.73 degre rotation around y
- float oo_sqrt3 = 1.0f / F_SQRT3;
- float sin_54 = F_SQRT2 * oo_sqrt3;
- 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);
-}
-
-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)));
- // Blend in with alpha values
- gamma_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * 255.0 * gamma_i);
- 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);
-}
-
-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];
- for (S32 i = 1; i < 256; i++)
- {
- 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)
- {
- min_v++;
- }
- S32 max_v = 255;
- while (cumulated_histo[max_v] > max_c)
- {
- max_v--;
- }
-
- // Compute linear lookup table
- U8 linear_red_lut[256];
- U8 linear_green_lut[256];
- U8 linear_blue_lut[256];
- if (max_v == min_v)
- {
- // Degenerated binary split case
- for (S32 i = 0; i < 256; i++)
- {
- U8 value_i = (i < min_v ? 0 : 255);
- // Blend in with alpha values
- linear_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
- linear_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
- linear_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
- }
- }
- else
- {
- // Linearize between min and max
- F32 slope = 255.0 / (F32)(max_v - min_v);
- F32 translate = -min_v * slope;
- for (S32 i = 0; i < 256; i++)
- {
- U8 value_i = (U8)(llclampb((S32)(slope*i + translate)));
- // Blend in with alpha values
- linear_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
- linear_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
- 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);
-}
-
-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];
- for (S32 i = 1; i < 256; i++)
- {
- 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];
- U8 equalize_blue_lut[256];
- for (S32 i = 0; i < 256; i++)
- {
- // Blend in current_value with alpha values
- equalize_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * current_value);
- equalize_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * current_value);
- equalize_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * current_value);
- if (cumulated_histo[i] >= current_count)
- {
- current_count += delta_count;
- current_value += delta_value;
- current_value = llclampb(current_value);
- }
- }
-
- // Apply lookup table
- colorCorrect(equalize_red_lut,equalize_green_lut,equalize_blue_lut);
-}
-
-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);
-}
-
-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)));
- // Blend in with alpha values
- contrast_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
- 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);
-}
-
-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));
- // Blend in with alpha values
- brightness_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
- 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);
-}
-
-//============================================================================
+/**
+ * @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$
+ */
+
+#include "linden_common.h"
+
+#include "llimagefilter.h"
+
+#include "llmath.h"
+#include "v3color.h"
+#include "v4coloru.h"
+#include "m3math.h"
+#include "v3math.h"
+#include "llsdserialize.h"
+#include "llstring.h"
+
+//---------------------------------------------------------------------------
+// LLImageFilter
+//---------------------------------------------------------------------------
+
+LLImageFilter::LLImageFilter(const std::string& file_path) :
+ mFilterData(LLSD::emptyArray()),
+ mImage(NULL),
+ mHistoRed(NULL),
+ mHistoGreen(NULL),
+ mHistoBlue(NULL),
+ mHistoBrightness(NULL),
+ mStencilBlendMode(STENCIL_BLEND_MODE_BLEND),
+ mStencilShape(STENCIL_SHAPE_UNIFORM),
+ mStencilGamma(1.0),
+ mStencilMin(0.0),
+ 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();
+ }
+}
+
+LLImageFilter::~LLImageFilter()
+{
+ mImage = NULL;
+ ll_aligned_free_16(mHistoRed);
+ ll_aligned_free_16(mHistoGreen);
+ ll_aligned_free_16(mHistoBlue);
+ ll_aligned_free_16(mHistoBrightness);
+}
+
+/*
+ *TODO
+ * Rename stencil to mask
+ * Improve perf: use LUT for alpha blending in uniform case
+ * Add gradient coloring as a filter
+ */
+
+//============================================================================
+// Apply the filter data to the image passed as parameter
+//============================================================================
+
+void LLImageFilter::executeFilter(LLPointer<LLImageRaw> raw_image)
+{
+ mImage = raw_image;
+
+ LLImageDataLock lock(mImage);
+
+ //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 = ";
+ //for (S32 j = 1; j < mFilterData[i].size(); ++j)
+ //{
+ // 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
+ std::string filter_shape = mFilterData[i][1].asString();
+ EStencilShape shape = STENCIL_SHAPE_UNIFORM;
+ if (filter_shape == "uniform")
+ {
+ shape = STENCIL_SHAPE_UNIFORM;
+ }
+ else if (filter_shape == "gradient")
+ {
+ shape = STENCIL_SHAPE_GRADIENT;
+ }
+ else if (filter_shape == "vignette")
+ {
+ shape = STENCIL_SHAPE_VIGNETTE;
+ }
+ else if (filter_shape == "scanlines")
+ {
+ shape = STENCIL_SHAPE_SCAN_LINES;
+ }
+ // Get the blend mode of the stencil, that is how the effect is blended in the background through the stencil
+ std::string filter_mode = mFilterData[i][2].asString();
+ EStencilBlendMode mode = STENCIL_BLEND_MODE_BLEND;
+ if (filter_mode == "blend")
+ {
+ mode = STENCIL_BLEND_MODE_BLEND;
+ }
+ else if (filter_mode == "add")
+ {
+ mode = STENCIL_BLEND_MODE_ADD;
+ }
+ else if (filter_mode == "add_back")
+ {
+ mode = STENCIL_BLEND_MODE_ABACK;
+ }
+ else if (filter_mode == "fade")
+ {
+ mode = STENCIL_BLEND_MODE_FADE;
+ }
+ // Get the float params: mandatory min, max then the optional parameters (4 max)
+ F32 min = (F32)(mFilterData[i][3].asReal());
+ F32 max = (F32)(mFilterData[i][4].asReal());
+ F32 params[4] = {0.0, 0.0, 0.0, 0.0};
+ for (S32 j = 5; (j < mFilterData[i].size()) && (j < 9); j++)
+ {
+ params[j-5] = (F32)(mFilterData[i][j].asReal());
+ }
+ // Set the stencil
+ setStencil(shape,mode,min,max,params);
+ }
+ else if (filter_name == "sepia")
+ {
+ filterSepia();
+ }
+ else if (filter_name == "grayscale")
+ {
+ filterGrayScale();
+ }
+ else if (filter_name == "saturate")
+ {
+ filterSaturate((float)(mFilterData[i][1].asReal()));
+ }
+ else if (filter_name == "rotate")
+ {
+ filterRotate((float)(mFilterData[i][1].asReal()));
+ }
+ else if (filter_name == "gamma")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterGamma((float)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "colorize")
+ {
+ LLColor3 color((float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()));
+ LLColor3 alpha((F32)(mFilterData[i][4].asReal()),(float)(mFilterData[i][5].asReal()),(float)(mFilterData[i][6].asReal()));
+ filterColorize(color,alpha);
+ }
+ else if (filter_name == "contrast")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterContrast((float)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "brighten")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterBrightness((float)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "darken")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterBrightness((float)(-mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "linearize")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterLinearize((float)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "posterize")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterEqualize((S32)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "screen")
+ {
+ std::string screen_name = mFilterData[i][1].asString();
+ EScreenMode mode = SCREEN_MODE_2DSINE;
+ if (screen_name == "2Dsine")
+ {
+ mode = SCREEN_MODE_2DSINE;
+ }
+ else if (screen_name == "line")
+ {
+ mode = SCREEN_MODE_LINE;
+ }
+ filterScreen(mode,(F32)(mFilterData[i][2].asReal()),(F32)(mFilterData[i][3].asReal()));
+ }
+ else if (filter_name == "blur")
+ {
+ LLMatrix3 kernel;
+ for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ kernel.mMatrix[i][j] = 1.0;
+ convolve(kernel,true,false);
+ }
+ else if (filter_name == "sharpen")
+ {
+ LLMatrix3 kernel;
+ for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ kernel.mMatrix[k][j] = -1.0;
+ kernel.mMatrix[1][1] = 9.0;
+ convolve(kernel,false,false);
+ }
+ else if (filter_name == "gradient")
+ {
+ LLMatrix3 kernel;
+ for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ kernel.mMatrix[k][j] = -1.0;
+ kernel.mMatrix[1][1] = 8.0;
+ convolve(kernel,false,true);
+ }
+ else if (filter_name == "convolve")
+ {
+ LLMatrix3 kernel;
+ S32 index = 1;
+ bool normalize = (mFilterData[i][index++].asReal() > 0.0);
+ bool abs_value = (mFilterData[i][index++].asReal() > 0.0);
+ for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ kernel.mMatrix[k][j] = mFilterData[i][index++].asReal();
+ convolve(kernel,normalize,abs_value);
+ }
+ else if (filter_name == "colortransform")
+ {
+ LLMatrix3 transform;
+ S32 index = 1;
+ for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ transform.mMatrix[k][j] = mFilterData[i][index++].asReal();
+ transform.transpose();
+ colorTransform(transform);
+ }
+ else
+ {
+ LL_WARNS() << "Filter unknown, cannot execute filter command : " << filter_name << LL_ENDL;
+ }
+ }
+}
+
+//============================================================================
+// Filter Primitives
+//============================================================================
+
+void LLImageFilter::blendStencil(F32 alpha, U8* pixel, U8 red, U8 green, U8 blue)
+{
+ F32 inv_alpha = 1.0 - alpha;
+ switch (mStencilBlendMode)
+ {
+ case STENCIL_BLEND_MODE_BLEND:
+ // Classic blend of incoming color with the background image
+ pixel[VRED] = inv_alpha * pixel[VRED] + alpha * red;
+ pixel[VGREEN] = inv_alpha * pixel[VGREEN] + alpha * green;
+ pixel[VBLUE] = inv_alpha * pixel[VBLUE] + alpha * blue;
+ break;
+ case STENCIL_BLEND_MODE_ADD:
+ // Add incoming color to the background image
+ pixel[VRED] = llclampb(pixel[VRED] + alpha * red);
+ pixel[VGREEN] = llclampb(pixel[VGREEN] + alpha * green);
+ pixel[VBLUE] = llclampb(pixel[VBLUE] + alpha * blue);
+ break;
+ case STENCIL_BLEND_MODE_ABACK:
+ // Add back background image to the incoming color
+ pixel[VRED] = llclampb(inv_alpha * pixel[VRED] + red);
+ pixel[VGREEN] = llclampb(inv_alpha * pixel[VGREEN] + green);
+ pixel[VBLUE] = llclampb(inv_alpha * pixel[VBLUE] + blue);
+ break;
+ case STENCIL_BLEND_MODE_FADE:
+ // Fade incoming color to black
+ pixel[VRED] = alpha * red;
+ pixel[VGREEN] = alpha * green;
+ pixel[VBLUE] = alpha * blue;
+ break;
+ }
+}
+
+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();
+ S32 height = mImage->getHeight();
+
+ 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();
+ S32 height = mImage->getHeight();
+
+ 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 );
+
+ // Compute normalization factors
+ F32 kernel_min = 0.0;
+ F32 kernel_max = 0.0;
+ for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++)
+ {
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ {
+ if (kernel.mMatrix[i][j] >= 0.0)
+ kernel_max += kernel.mMatrix[i][j];
+ else
+ kernel_min += kernel.mMatrix[i][j];
+ }
+ }
+ if (abs_value)
+ {
+ kernel_max = llabs(kernel_max);
+ kernel_min = llabs(kernel_min);
+ kernel_max = llmax(kernel_max,kernel_min);
+ kernel_min = 0.0;
+ }
+ F32 kernel_range = kernel_max - kernel_min;
+
+ // Allocate temporary buffers and initialize algorithm's data
+ 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* 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 */
+ 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 */
+ east_west_data = &odd_buffer[0];
+ north_data = &even_buffer[0];
+ }
+ else
+ {
+ memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */
+ east_west_data = &even_buffer[0];
+ north_data = &odd_buffer[0];
+ }
+ // First pixel : set to 0
+ blendStencil(getStencilAlpha(0,j), dst_data, 0, 0, 0);
+ dst_data += components;
+ // Set pointers to kernel
+ U8* NW = north_data;
+ U8* N = NW+components;
+ U8* NE = N+components;
+ U8* W = east_west_data;
+ U8* C = W+components;
+ U8* E = C+components;
+ U8* SW = south_data;
+ U8* S = SW+components;
+ U8* SE = S+components;
+ // All other pixels
+ for (S32 i = 1; i < (width-1); i++)
+ {
+ // Compute convolution
+ LLVector3 dst;
+ dst.mV[VRED] = (kernel.mMatrix[0][0]*NW[VRED] + kernel.mMatrix[0][1]*N[VRED] + kernel.mMatrix[0][2]*NE[VRED] +
+ kernel.mMatrix[1][0]*W[VRED] + kernel.mMatrix[1][1]*C[VRED] + kernel.mMatrix[1][2]*E[VRED] +
+ kernel.mMatrix[2][0]*SW[VRED] + kernel.mMatrix[2][1]*S[VRED] + kernel.mMatrix[2][2]*SE[VRED]);
+ dst.mV[VGREEN] = (kernel.mMatrix[0][0]*NW[VGREEN] + kernel.mMatrix[0][1]*N[VGREEN] + kernel.mMatrix[0][2]*NE[VGREEN] +
+ kernel.mMatrix[1][0]*W[VGREEN] + kernel.mMatrix[1][1]*C[VGREEN] + kernel.mMatrix[1][2]*E[VGREEN] +
+ kernel.mMatrix[2][0]*SW[VGREEN] + kernel.mMatrix[2][1]*S[VGREEN] + kernel.mMatrix[2][2]*SE[VGREEN]);
+ dst.mV[VBLUE] = (kernel.mMatrix[0][0]*NW[VBLUE] + kernel.mMatrix[0][1]*N[VBLUE] + kernel.mMatrix[0][2]*NE[VBLUE] +
+ kernel.mMatrix[1][0]*W[VBLUE] + kernel.mMatrix[1][1]*C[VBLUE] + kernel.mMatrix[1][2]*E[VBLUE] +
+ kernel.mMatrix[2][0]*SW[VBLUE] + kernel.mMatrix[2][1]*S[VBLUE] + kernel.mMatrix[2][2]*SE[VBLUE]);
+ if (abs_value)
+ {
+ dst.mV[VRED] = llabs(dst.mV[VRED]);
+ dst.mV[VGREEN] = llabs(dst.mV[VGREEN]);
+ dst.mV[VBLUE] = llabs(dst.mV[VBLUE]);
+ }
+ if (normalize)
+ {
+ dst.mV[VRED] = (dst.mV[VRED] - kernel_min)/kernel_range;
+ dst.mV[VGREEN] = (dst.mV[VGREEN] - kernel_min)/kernel_range;
+ 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;
+ N += components;
+ NE += components;
+ W += components;
+ C += components;
+ E += components;
+ SW += components;
+ S += components;
+ SE += components;
+ }
+ // Last pixel : set to 0
+ 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++)
+ {
+ blendStencil(getStencilAlpha(i,0), dst_data, 0, 0, 0);
+ dst_data += components;
+ }
+}
+
+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();
+ 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);
+
+ // Precompute the gamma table : gives us the gray level to use when cutting outside the screen (prevents strong aliasing on the screen)
+ U8 gamma[256];
+ for (S32 i = 0; i < 256; i++)
+ {
+ 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++)
+ {
+ for (S32 i = 0; i < width; i++)
+ {
+ // Compute screen value
+ F32 value = 0.0;
+ F32 di = 0.0;
+ F32 dj = 0.0;
+ switch (mode)
+ {
+ case SCREEN_MODE_2DSINE:
+ di = cos*i + sin*j;
+ dj = -sin*i + cos*j;
+ value = (sinf(2*F_PI*di/wave_length_pixels)*sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0;
+ break;
+ case SCREEN_MODE_LINE:
+ dj = sin*i - cos*j;
+ value = (sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0;
+ 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;
+ }
+ }
+}
+
+//============================================================================
+// Procedural Stencils
+//============================================================================
+void LLImageFilter::setStencil(EStencilShape shape, EStencilBlendMode mode, F32 min, F32 max, F32* params)
+{
+ mStencilShape = shape;
+ 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;
+ mStencilCenterY = (S32)(mImage->getHeight() + params[1] * (F32)(mImage->getHeight()))/2;
+ mStencilWidth = (S32)(params[2] * (F32)(mImage->getHeight()))/2;
+ mStencilGamma = (params[3] <= 0.0 ? 1.0 : params[3]);
+
+ mStencilWavelength = (params[0] <= 0.0 ? 10.0 : params[0] * (F32)(mImage->getHeight()) / 2.0);
+ mStencilSine = sinf(params[1]*DEG_TO_RAD);
+ mStencilCosine = cosf(params[1]*DEG_TO_RAD);
+
+ mStencilStartX = ((F32)(mImage->getWidth()) + params[0] * (F32)(mImage->getHeight()))/2.0;
+ mStencilStartY = ((F32)(mImage->getHeight()) + params[1] * (F32)(mImage->getHeight()))/2.0;
+ F32 end_x = ((F32)(mImage->getWidth()) + params[2] * (F32)(mImage->getHeight()))/2.0;
+ F32 end_y = ((F32)(mImage->getHeight()) + params[3] * (F32)(mImage->getHeight()))/2.0;
+ mStencilGradX = end_x - mStencilStartX;
+ mStencilGradY = end_y - mStencilStartY;
+ mStencilGradN = mStencilGradX*mStencilGradX + mStencilGradY*mStencilGradY;
+}
+
+F32 LLImageFilter::getStencilAlpha(S32 i, S32 j)
+{
+ F32 alpha = 1.0; // That init actually takes care of the STENCIL_SHAPE_UNIFORM case...
+ if (mStencilShape == STENCIL_SHAPE_VIGNETTE)
+ {
+ // alpha is a modified gaussian value, with a center and fading in a circular pattern toward the edges
+ // The gamma parameter controls the intensity of the drop down from alpha 1.0 (center) to 0.0
+ F32 d_center_square = (i - mStencilCenterX)*(i - mStencilCenterX) + (j - mStencilCenterY)*(j - mStencilCenterY);
+ alpha = powf(F_E, -(powf((d_center_square/(mStencilWidth*mStencilWidth)),mStencilGamma)/2.0f));
+ }
+ else if (mStencilShape == STENCIL_SHAPE_SCAN_LINES)
+ {
+ // alpha varies according to a squared sine function.
+ F32 d = mStencilSine*i - mStencilCosine*j;
+ alpha = (sinf(2*F_PI*d/mStencilWavelength) > 0.0 ? 1.0 : 0.0);
+ }
+ else if (mStencilShape == STENCIL_SHAPE_GRADIENT)
+ {
+ 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));
+}
+
+//============================================================================
+// Histograms
+//============================================================================
+
+U32* LLImageFilter::getBrightnessHistogram()
+{
+ if (!mHistoBrightness)
+ {
+ computeHistograms();
+ }
+ return mHistoBrightness;
+}
+
+void LLImageFilter::computeHistograms()
+{
+ const S32 components = mImage->getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ // Allocate memory for the histograms
+ if (!mHistoRed)
+ {
+ mHistoRed = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
+ }
+ if (!mHistoGreen)
+ {
+ mHistoGreen = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
+ }
+ if (!mHistoBlue)
+ {
+ mHistoBlue = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
+ }
+ if (!mHistoBrightness)
+ {
+ mHistoBrightness = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
+ }
+
+ // Initialize them
+ for (S32 i = 0; i < 256; i++)
+ {
+ mHistoRed[i] = 0;
+ mHistoGreen[i] = 0;
+ 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++)
+ {
+ mHistoRed[dst_data[VRED]]++;
+ mHistoGreen[dst_data[VGREEN]]++;
+ mHistoBlue[dst_data[VBLUE]]++;
+ // Note: this is a very simple shorthand for brightness but it's OK for our use
+ S32 brightness = ((S32)(dst_data[VRED]) + (S32)(dst_data[VGREEN]) + (S32)(dst_data[VBLUE])) / 3;
+ mHistoBrightness[brightness]++;
+ // next pixel...
+ dst_data += components;
+ }
+}
+
+//============================================================================
+// Secondary Filters
+//============================================================================
+
+void LLImageFilter::filterGrayScale()
+{
+ LLMatrix3 gray_scale;
+ LLVector3 luminosity(0.2125, 0.7154, 0.0721);
+ gray_scale.setRows(luminosity, luminosity, luminosity);
+ gray_scale.transpose();
+ colorTransform(gray_scale);
+}
+
+void LLImageFilter::filterSepia()
+{
+ LLMatrix3 sepia;
+ sepia.setRows(LLVector3(0.3588, 0.7044, 0.1368),
+ LLVector3(0.2990, 0.5870, 0.1140),
+ LLVector3(0.2392, 0.4696, 0.0912));
+ sepia.transpose();
+ colorTransform(sepia);
+}
+
+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),
+ LLVector3( 0.0, 0.0, 1.0));
+ // 54.73 degre rotation around y
+ float oo_sqrt3 = 1.0f / F_SQRT3;
+ float sin_54 = F_SQRT2 * oo_sqrt3;
+ 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);
+}
+
+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),
+ LLVector3( 0.0, 0.0, 1.0));
+ // 54.73 degre rotation around y
+ float oo_sqrt3 = 1.0f / F_SQRT3;
+ float sin_54 = F_SQRT2 * oo_sqrt3;
+ 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);
+}
+
+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)));
+ // Blend in with alpha values
+ gamma_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * 255.0 * gamma_i);
+ 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);
+}
+
+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];
+ for (S32 i = 1; i < 256; i++)
+ {
+ 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)
+ {
+ min_v++;
+ }
+ S32 max_v = 255;
+ while (cumulated_histo[max_v] > max_c)
+ {
+ max_v--;
+ }
+
+ // Compute linear lookup table
+ U8 linear_red_lut[256];
+ U8 linear_green_lut[256];
+ U8 linear_blue_lut[256];
+ if (max_v == min_v)
+ {
+ // Degenerated binary split case
+ for (S32 i = 0; i < 256; i++)
+ {
+ U8 value_i = (i < min_v ? 0 : 255);
+ // Blend in with alpha values
+ linear_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
+ linear_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
+ linear_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
+ }
+ }
+ else
+ {
+ // Linearize between min and max
+ F32 slope = 255.0 / (F32)(max_v - min_v);
+ F32 translate = -min_v * slope;
+ for (S32 i = 0; i < 256; i++)
+ {
+ U8 value_i = (U8)(llclampb((S32)(slope*i + translate)));
+ // Blend in with alpha values
+ linear_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
+ linear_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
+ 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);
+}
+
+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];
+ for (S32 i = 1; i < 256; i++)
+ {
+ 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];
+ U8 equalize_blue_lut[256];
+ for (S32 i = 0; i < 256; i++)
+ {
+ // Blend in current_value with alpha values
+ equalize_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * current_value);
+ equalize_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * current_value);
+ equalize_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * current_value);
+ if (cumulated_histo[i] >= current_count)
+ {
+ current_count += delta_count;
+ current_value += delta_value;
+ current_value = llclampb(current_value);
+ }
+ }
+
+ // Apply lookup table
+ colorCorrect(equalize_red_lut,equalize_green_lut,equalize_blue_lut);
+}
+
+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);
+}
+
+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)));
+ // Blend in with alpha values
+ contrast_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
+ 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);
+}
+
+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));
+ // Blend in with alpha values
+ brightness_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
+ 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..9593c6b972 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -1,593 +1,593 @@
-/**
- * @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$
- */
-#include "linden_common.h"
-
-#include "llapr.h"
-#include "lldir.h"
-#include "llimagej2c.h"
-#include "lltimer.h"
-#include "llmath.h"
-#include "llmemory.h"
-#include "llsd.h"
-#include <boost/scoped_ptr.hpp>
-
-// Declare the prototype for this factory function here. It is implemented in
-// other files which define a LLImageJ2CImpl subclass, but only ONE static
-// library which has the implementation for this function should ever be
-// linked.
-LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl();
-
-// Test data gathering handle
-LLImageCompressionTester* LLImageJ2C::sTesterp = NULL ;
-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();
-}
-
-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;
- }
- }
-}
-
-// virtual
-LLImageJ2C::~LLImageJ2C() {}
-
-// virtual
-void LLImageJ2C::resetLastError()
-{
- mLastError.clear();
-}
-
-//virtual
-void LLImageJ2C::setLastError(const std::string& message, const std::string& filename)
-{
- mLastError = message;
- if (!filename.empty())
- mLastError += std::string(" FILE: ") + filename;
-}
-
-// virtual
-S8 LLImageJ2C::getRawDiscardLevel()
-{
- 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 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);
-}
-
-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);
-}
-
-bool LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- return decodeChannels(raw_imagep, decode_time, 0, 4);
-}
-
-
-// Returns true to mean done, whether successful or not.
-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;
-}
-
-
-bool LLImageJ2C::encode(const LLImageRaw *raw_imagep, F32 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;
-}
-
-//static
-S32 LLImageJ2C::calcHeaderSizeJ2C()
-{
- 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;
-}
-
-S32 LLImageJ2C::calcHeaderSize()
-{
- 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];
-}
-
-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;
-}
-
-void LLImageJ2C::setMaxBytes(S32 max_bytes)
-{
- mMaxBytes = max_bytes;
-}
-
-void LLImageJ2C::setReversible(const bool 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 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;
-}
-
-void LLImageJ2C::decodeFailed()
-{
- mDecoding = false;
-}
-
-void LLImageJ2C::updateRawDiscardLevel()
-{
- mRawDiscardLevel = mMaxBytes ? calcDiscardLevelBytes(mMaxBytes) : mDiscardLevel;
-}
-
-LLImageJ2CImpl::~LLImageJ2CImpl()
-{
-}
-
-//----------------------------------------------------------------------------------------------
-// Start of LLImageCompressionTester
-//----------------------------------------------------------------------------------------------
-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;
-}
-
-LLImageCompressionTester::~LLImageCompressionTester()
-{
- 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;
-}
-
-void LLImageCompressionTester::updateCompressionStats(const F32 deltaTime)
-{
- mTotalTimeCompression += deltaTime;
-}
-
-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;
- }
-}
-
-void LLImageCompressionTester::updateDecompressionStats(const F32 deltaTime)
-{
- mTotalTimeDecompression += deltaTime;
-}
-
-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;
- }
-}
-
-//----------------------------------------------------------------------------------------------
-// End of LLTexturePipelineTester
-//----------------------------------------------------------------------------------------------
-
+/**
+ * @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$
+ */
+#include "linden_common.h"
+
+#include "llapr.h"
+#include "lldir.h"
+#include "llimagej2c.h"
+#include "lltimer.h"
+#include "llmath.h"
+#include "llmemory.h"
+#include "llsd.h"
+#include <boost/scoped_ptr.hpp>
+
+// Declare the prototype for this factory function here. It is implemented in
+// other files which define a LLImageJ2CImpl subclass, but only ONE static
+// library which has the implementation for this function should ever be
+// linked.
+LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl();
+
+// Test data gathering handle
+LLImageCompressionTester* LLImageJ2C::sTesterp = NULL ;
+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();
+}
+
+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;
+ }
+ }
+}
+
+// virtual
+LLImageJ2C::~LLImageJ2C() {}
+
+// virtual
+void LLImageJ2C::resetLastError()
+{
+ mLastError.clear();
+}
+
+//virtual
+void LLImageJ2C::setLastError(const std::string& message, const std::string& filename)
+{
+ mLastError = message;
+ if (!filename.empty())
+ mLastError += std::string(" FILE: ") + filename;
+}
+
+// virtual
+S8 LLImageJ2C::getRawDiscardLevel()
+{
+ 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 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);
+}
+
+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);
+}
+
+bool LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ return decodeChannels(raw_imagep, decode_time, 0, 4);
+}
+
+
+// Returns true to mean done, whether successful or not.
+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;
+}
+
+
+bool LLImageJ2C::encode(const LLImageRaw *raw_imagep, F32 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;
+}
+
+//static
+S32 LLImageJ2C::calcHeaderSizeJ2C()
+{
+ 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;
+}
+
+S32 LLImageJ2C::calcHeaderSize()
+{
+ 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];
+}
+
+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;
+}
+
+void LLImageJ2C::setMaxBytes(S32 max_bytes)
+{
+ mMaxBytes = max_bytes;
+}
+
+void LLImageJ2C::setReversible(const bool 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 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;
+}
+
+void LLImageJ2C::decodeFailed()
+{
+ mDecoding = false;
+}
+
+void LLImageJ2C::updateRawDiscardLevel()
+{
+ mRawDiscardLevel = mMaxBytes ? calcDiscardLevelBytes(mMaxBytes) : mDiscardLevel;
+}
+
+LLImageJ2CImpl::~LLImageJ2CImpl()
+{
+}
+
+//----------------------------------------------------------------------------------------------
+// Start of LLImageCompressionTester
+//----------------------------------------------------------------------------------------------
+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;
+}
+
+LLImageCompressionTester::~LLImageCompressionTester()
+{
+ 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;
+}
+
+void LLImageCompressionTester::updateCompressionStats(const F32 deltaTime)
+{
+ mTotalTimeCompression += deltaTime;
+}
+
+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;
+ }
+}
+
+void LLImageCompressionTester::updateDecompressionStats(const F32 deltaTime)
+{
+ mTotalTimeDecompression += deltaTime;
+}
+
+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;
+ }
+}
+
+//----------------------------------------------------------------------------------------------
+// End of LLTexturePipelineTester
+//----------------------------------------------------------------------------------------------
+
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..e3271d8a83 100644
--- a/indra/llimage/llimagejpeg.cpp
+++ b/indra/llimage/llimagejpeg.cpp
@@ -1,674 +1,674 @@
-/**
- * @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$
- */
-
-#include "linden_common.h"
-#include "stdtypes.h"
-
-#include "llimagejpeg.h"
-
-#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
-{
-}
-
-LLImageJPEG::~LLImageJPEG()
-{
- 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;
-}
-
-// Initialize source --- called by jpeg_read_header
-// before any data is actually read.
-void LLImageJPEG::decodeInitSource( j_decompress_ptr cinfo )
-{
- // 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;
-
- // Should never get here, since we provide the entire buffer up front.
- ERREXIT(cinfo, JERR_INPUT_EMPTY);
-
- return true;
-}
-
-// Skip data --- used to skip over a potentially large amount of
-// uninteresting data (such as an APPn marker).
-//
-// Writers of suspendable-input applications must note that skip_input_data
-// is not granted the right to give a suspension return. If the skip extends
-// beyond the data currently in the buffer, the buffer can be marked empty so
-// that the next read will cause a fill_input_buffer call that can suspend.
-// Arranging for additional bytes to be discarded before reloading the input
-// 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;
-
- src->next_input_byte += (size_t) num_bytes;
- src->bytes_in_buffer -= (size_t) num_bytes;
-}
-
-void LLImageJPEG::decodeTermSource (j_decompress_ptr cinfo)
-{
- // no work necessary here
-}
-
-
-// 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;
-}
-
-
-// Initialize destination --- called by jpeg_start_compress before any data is actually written.
-// static
-void LLImageJPEG::encodeInitDestination ( j_compress_ptr cinfo )
-{
- LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
-
- cinfo->dest->next_output_byte = self->mOutputBuffer;
- cinfo->dest->free_in_buffer = self->mOutputBufferSize;
-}
-
-
-// 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
-// an indication that it has not accepted all the supplied scanlines). The
-// 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.
-// Data beyond this point will be regenerated after resumption, so do not
-// write it out when emptying the buffer externally.
-
-boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )
-{
- LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
-
- // 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 ];
- if (!new_buffer)
- {
- 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 */
- delete[] self->mOutputBuffer;
- self->mOutputBuffer = new_buffer;
-
- cinfo->dest->next_output_byte = self->mOutputBuffer + self->mOutputBufferSize;
- cinfo->dest->free_in_buffer = self->mOutputBufferSize;
- self->mOutputBufferSize = new_buffer_size;
-
- return true;
-}
-
-// 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;
-
- LLImageDataLock lock(self);
-
- S32 file_bytes = (S32)(self->mOutputBufferSize - cinfo->dest->free_in_buffer);
- self->allocateData(file_bytes);
-
- memcpy( self->getData(), self->mOutputBuffer, file_bytes ); /* Flawfinder: ignore */
-}
-
-// static
-void LLImageJPEG::errorExit( j_common_ptr cinfo )
-{
- //LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
-
- // Always display the message
- (*cinfo->err->output_message)(cinfo);
-
- // Let the memory manager delete any temp files
- jpeg_destroy(cinfo);
-
- // Return control to the setjmp point
- longjmp(sSetjmpBuffer, 1) ;
-}
-
-// Decide whether to emit a trace or warning message.
-// msg_level is one of:
-// -1: recoverable corrupt-data warning, may want to abort.
-// 0: important advisory messages (always display to user).
-// 1: first level of tracing detail.
-// 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
-void LLImageJPEG::errorEmitMessage( j_common_ptr cinfo, int msg_level )
-{
- struct jpeg_error_mgr * err = cinfo->err;
-
- 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++;
- }
- else
- {
- // It's a trace message. Show it if trace_level >= msg_level.
- if (err->trace_level >= msg_level)
- {
- (*err->output_message) (cinfo);
- }
- }
-}
-
-// static
-void LLImageJPEG::errorOutputMessage( j_common_ptr cinfo )
-{
- // Create the message
- char buffer[JMSG_LENGTH_MAX]; /* Flawfinder: ignore */
- (*cinfo->err->format_message) (cinfo, buffer);
-
- 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 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;
-}
+/**
+ * @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$
+ */
+
+#include "linden_common.h"
+#include "stdtypes.h"
+
+#include "llimagejpeg.h"
+
+#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
+{
+}
+
+LLImageJPEG::~LLImageJPEG()
+{
+ 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;
+}
+
+// Initialize source --- called by jpeg_read_header
+// before any data is actually read.
+void LLImageJPEG::decodeInitSource( j_decompress_ptr cinfo )
+{
+ // 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;
+
+ // Should never get here, since we provide the entire buffer up front.
+ ERREXIT(cinfo, JERR_INPUT_EMPTY);
+
+ return true;
+}
+
+// Skip data --- used to skip over a potentially large amount of
+// uninteresting data (such as an APPn marker).
+//
+// Writers of suspendable-input applications must note that skip_input_data
+// is not granted the right to give a suspension return. If the skip extends
+// beyond the data currently in the buffer, the buffer can be marked empty so
+// that the next read will cause a fill_input_buffer call that can suspend.
+// Arranging for additional bytes to be discarded before reloading the input
+// 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;
+
+ src->next_input_byte += (size_t) num_bytes;
+ src->bytes_in_buffer -= (size_t) num_bytes;
+}
+
+void LLImageJPEG::decodeTermSource (j_decompress_ptr cinfo)
+{
+ // no work necessary here
+}
+
+
+// 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;
+}
+
+
+// Initialize destination --- called by jpeg_start_compress before any data is actually written.
+// static
+void LLImageJPEG::encodeInitDestination ( j_compress_ptr cinfo )
+{
+ LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
+
+ cinfo->dest->next_output_byte = self->mOutputBuffer;
+ cinfo->dest->free_in_buffer = self->mOutputBufferSize;
+}
+
+
+// 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
+// an indication that it has not accepted all the supplied scanlines). The
+// 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.
+// Data beyond this point will be regenerated after resumption, so do not
+// write it out when emptying the buffer externally.
+
+boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )
+{
+ LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
+
+ // 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 ];
+ if (!new_buffer)
+ {
+ 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 */
+ delete[] self->mOutputBuffer;
+ self->mOutputBuffer = new_buffer;
+
+ cinfo->dest->next_output_byte = self->mOutputBuffer + self->mOutputBufferSize;
+ cinfo->dest->free_in_buffer = self->mOutputBufferSize;
+ self->mOutputBufferSize = new_buffer_size;
+
+ return true;
+}
+
+// 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;
+
+ LLImageDataLock lock(self);
+
+ S32 file_bytes = (S32)(self->mOutputBufferSize - cinfo->dest->free_in_buffer);
+ self->allocateData(file_bytes);
+
+ memcpy( self->getData(), self->mOutputBuffer, file_bytes ); /* Flawfinder: ignore */
+}
+
+// static
+void LLImageJPEG::errorExit( j_common_ptr cinfo )
+{
+ //LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
+
+ // Always display the message
+ (*cinfo->err->output_message)(cinfo);
+
+ // Let the memory manager delete any temp files
+ jpeg_destroy(cinfo);
+
+ // Return control to the setjmp point
+ longjmp(sSetjmpBuffer, 1) ;
+}
+
+// Decide whether to emit a trace or warning message.
+// msg_level is one of:
+// -1: recoverable corrupt-data warning, may want to abort.
+// 0: important advisory messages (always display to user).
+// 1: first level of tracing detail.
+// 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
+void LLImageJPEG::errorEmitMessage( j_common_ptr cinfo, int msg_level )
+{
+ struct jpeg_error_mgr * err = cinfo->err;
+
+ 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++;
+ }
+ else
+ {
+ // It's a trace message. Show it if trace_level >= msg_level.
+ if (err->trace_level >= msg_level)
+ {
+ (*err->output_message) (cinfo);
+ }
+ }
+}
+
+// static
+void LLImageJPEG::errorOutputMessage( j_common_ptr cinfo )
+{
+ // Create the message
+ char buffer[JMSG_LENGTH_MAX]; /* Flawfinder: ignore */
+ (*cinfo->err->format_message) (cinfo, buffer);
+
+ 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 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;
+}
diff --git a/indra/llimage/llimagejpeg.h b/indra/llimage/llimagejpeg.h
index d674b40b8f..fd2685644d 100644
--- a/indra/llimage/llimagejpeg.h
+++ b/indra/llimage/llimagejpeg.h
@@ -1,85 +1,85 @@
-/**
- * @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$
- */
-
-#ifndef LL_LLIMAGEJPEG_H
-#define LL_LLIMAGEJPEG_H
-
-#include <csetjmp>
-
-#include "llimage.h"
-
-#include "llwin32headerslean.h"
-extern "C" {
-#ifdef LL_USESYSTEMLIBS
-# include <jpeglib.h>
-# include <jerror.h>
-#else
-# include "jpeglib/jpeglib.h"
-# include "jpeglib/jerror.h"
-#endif
-}
-
-class LLImageJPEG : public LLImageFormatted
-{
-protected:
- virtual ~LLImageJPEG();
-
-public:
- 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);
-
- 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);
-
- 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);
-
-protected:
- U8* mOutputBuffer; // temp buffer used during encoding
- S32 mOutputBufferSize; // bytes in mOuputBuffer
-
- S32 mEncodeQuality; // on a scale from 1 to 100
-private:
- static jmp_buf sSetjmpBuffer; // To allow the library to abort.
-};
-
-#endif // LL_LLIMAGEJPEG_H
+/**
+ * @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$
+ */
+
+#ifndef LL_LLIMAGEJPEG_H
+#define LL_LLIMAGEJPEG_H
+
+#include <csetjmp>
+
+#include "llimage.h"
+
+#include "llwin32headerslean.h"
+extern "C" {
+#ifdef LL_USESYSTEMLIBS
+# include <jpeglib.h>
+# include <jerror.h>
+#else
+# include "jpeglib/jpeglib.h"
+# include "jpeglib/jerror.h"
+#endif
+}
+
+class LLImageJPEG : public LLImageFormatted
+{
+protected:
+ virtual ~LLImageJPEG();
+
+public:
+ 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);
+
+ 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);
+
+ 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);
+
+protected:
+ U8* mOutputBuffer; // temp buffer used during encoding
+ S32 mOutputBufferSize; // bytes in mOuputBuffer
+
+ S32 mEncodeQuality; // on a scale from 1 to 100
+private:
+ 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..47ce9e22d2 100644
--- a/indra/llimage/llimagepng.cpp
+++ b/indra/llimage/llimagepng.cpp
@@ -1,161 +1,177 @@
-/*
- * @file llimagepng.cpp
- * @brief LLImageFormatted glue to encode / decode PNG files.
- *
- * $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$
- */
-
-#include "linden_common.h"
-#include "stdtypes.h"
-#include "llerror.h"
-
-#include "llimage.h"
-#include "llpngwrapper.h"
-#include "llimagepng.h"
-
-// ---------------------------------------------------------------------------
-// LLImagePNG
-// ---------------------------------------------------------------------------
-LLImagePNG::LLImagePNG()
- : LLImageFormatted(IMG_CODEC_PNG)
-{
-}
-
-LLImagePNG::~LLImagePNG()
-{
-}
-
-// Virtual
-// Parse PNG image information and set the appropriate
-// width, height and component (channel) information.
-bool LLImagePNG::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 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);
-
- return true;
-}
-
-// Virtual
-// Decode an in-memory PNG image into the raw RGB or RGBA format
-// used within SecondLife.
-bool LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time)
-{
- llassert_always(raw_image);
-
- resetLastError();
-
- LLImageDataSharedLock lockIn(this);
- LLImageDataLock lockOut(raw_image);
-
- // Check to make sure that this instance has been initialized with data
- if (!getData() || (0 == getDataSize()))
- {
- setLastError("LLImagePNG trying to decode an image with no data!");
- 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;
-}
-
-// Virtual
-// Encode the in memory RGB image into PNG format.
-bool LLImagePNG::encode(const LLImageRaw* raw_image, F32 encode_time)
-{
- 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;
-}
-
+/*
+ * @file llimagepng.cpp
+ * @brief LLImageFormatted glue to encode / decode PNG files.
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+#include "stdtypes.h"
+#include "llerror.h"
+#include "llexception.h"
+
+#include "llimage.h"
+#include "llpngwrapper.h"
+#include "llimagepng.h"
+
+// ---------------------------------------------------------------------------
+// LLImagePNG
+// ---------------------------------------------------------------------------
+LLImagePNG::LLImagePNG()
+ : LLImageFormatted(IMG_CODEC_PNG)
+{
+}
+
+LLImagePNG::~LLImagePNG()
+{
+}
+
+// Virtual
+// Parse PNG image information and set the appropriate
+// width, height and component (channel) information.
+bool LLImagePNG::updateData()
+{
+ resetLastError();
+
+ 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(msg.what());
+ LOG_UNHANDLED_EXCEPTION("");
+ return false;
+ }
+ catch (...)
+ {
+ setLastError("LLImagePNG");
+ LOG_UNHANDLED_EXCEPTION("");
+ return false;
+ }
+
+ return true;
+}
+
+// Virtual
+// Decode an in-memory PNG image into the raw RGB or RGBA format
+// used within SecondLife.
+bool LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time)
+{
+ llassert_always(raw_image);
+
+ resetLastError();
+
+ LLImageDataSharedLock lockIn(this);
+ LLImageDataLock lockOut(raw_image);
+
+ // Check to make sure that this instance has been initialized with data
+ if (!getData() || (0 == getDataSize()))
+ {
+ setLastError("LLImagePNG trying to decode an image with no data!");
+ 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;
+}
+
+// Virtual
+// Encode the in memory RGB image into PNG format.
+bool LLImagePNG::encode(const LLImageRaw* raw_image, F32 encode_time)
+{
+ 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;
+}
+
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..1fa5ce5a84 100644
--- a/indra/llimage/llimagetga.cpp
+++ b/indra/llimage/llimagetga.cpp
@@ -1,1228 +1,1228 @@
-/**
- * @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$
- */
-
-#include "linden_common.h"
-
-#include "llimagetga.h"
-
-#include "lldir.h"
-#include "llerror.h"
-#include "llmath.h"
-#include "llpointer.h"
-
-// 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
- };
-
-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);
- dst[2] = s5to8bits[t & 0x1F]; // blue
- t >>= 5;
- dst[1] = s5to8bits[t & 0x1F]; // green
- t >>= 5;
- 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(const std::string& file_name)
- : LLImageFormatted(IMG_CODEC_TGA),
- mColorMap( NULL ),
- mColorMapStart( 0 ),
- mColorMapLength( 0 ),
- mColorMapBytesPerEntry( 0 ),
- mIs15Bit( false )
-{
- loadFile(file_name);
-}
-
-LLImageTGA::~LLImageTGA()
-{
- 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)
- {
- 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;
-}
-
-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 );
- }
-}
-
-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 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;
-}
-
-void LLImageTGA::decodeColorMapPixel8( U8* dst, const U8* src )
-{
- 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 );
-}
-
-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
-}
-
-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
-}
-
-
-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;
-}
-
-
-
-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;
-}
-
-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;
-}
-
-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;
-}
-
-
-
-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;
-}
-
-
-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;
-}
-
-
-// Decoded and process the image for use in avatar gradient masks.
-// 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;
-}
-
-// 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;
-}
-
-
+/**
+ * @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$
+ */
+
+#include "linden_common.h"
+
+#include "llimagetga.h"
+
+#include "lldir.h"
+#include "llerror.h"
+#include "llmath.h"
+#include "llpointer.h"
+
+// 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
+ };
+
+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);
+ dst[2] = s5to8bits[t & 0x1F]; // blue
+ t >>= 5;
+ dst[1] = s5to8bits[t & 0x1F]; // green
+ t >>= 5;
+ 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(const std::string& file_name)
+ : LLImageFormatted(IMG_CODEC_TGA),
+ mColorMap( NULL ),
+ mColorMapStart( 0 ),
+ mColorMapLength( 0 ),
+ mColorMapBytesPerEntry( 0 ),
+ mIs15Bit( false )
+{
+ loadFile(file_name);
+}
+
+LLImageTGA::~LLImageTGA()
+{
+ 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)
+ {
+ 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;
+}
+
+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 );
+ }
+}
+
+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 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;
+}
+
+void LLImageTGA::decodeColorMapPixel8( U8* dst, const U8* src )
+{
+ 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 );
+}
+
+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
+}
+
+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
+}
+
+
+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;
+}
+
+
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+
+
+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;
+}
+
+
+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;
+}
+
+
+// Decoded and process the image for use in avatar gradient masks.
+// 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;
+}
+
+// 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;
+}
+
+
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..62934ccbe1 100644
--- a/indra/llimage/llimageworker.cpp
+++ b/indra/llimage/llimageworker.cpp
@@ -1,219 +1,225 @@
-/**
- * @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$
- */
-
-#include "linden_common.h"
-
-#include "llimageworker.h"
-#include "llimagedxt.h"
-#include "threadpool.h"
-
-/*--------------------------------------------------------------------------*/
-class ImageRequest
-{
-public:
- ImageRequest(const LLPointer<LLImageFormatted>& image,
- S32 discard,
- bool needs_aux,
- const LLPointer<LLImageDecodeThread::Responder>& responder,
- U32 request_id);
- virtual ~ImageRequest();
-
- /*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;
- U32 mRequestId;
- bool mNeedsAux;
- // output
- LLPointer<LLImageRaw> mDecodedImageRaw;
- LLPointer<LLImageRaw> mDecodedImageAux;
- bool mDecodedRaw;
- bool mDecodedAux;
- LLPointer<LLImageDecodeThread::Responder> mResponder;
-};
-
-
-//----------------------------------------------------------------------------
-
-// MAIN THREAD
-LLImageDecodeThread::LLImageDecodeThread(bool /*threaded*/)
- : mDecodeCount(0)
-{
- mThreadPool.reset(new LL::ThreadPool("ImageDecode", 8));
- mThreadPool->start();
-}
-
-//virtual
-LLImageDecodeThread::~LLImageDecodeThread()
-{}
-
-// MAIN THREAD
-// virtual
-size_t LLImageDecodeThread::update(F32 max_time_ms)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- return getPending();
-}
-
-size_t LLImageDecodeThread::getPending()
-{
- return mThreadPool->getQueue().size();
-}
-
-LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(
- const LLPointer<LLImageFormatted>& image,
- S32 discard,
- bool needs_aux,
- const LLPointer<LLImageDecodeThread::Responder>& responder)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
-
- U32 decode_id = ++mDecodeCount;
- // Instantiate the ImageRequest right in the lambda, why not?
- bool posted = mThreadPool->getQueue().post(
- [req = ImageRequest(image, discard, needs_aux, responder, decode_id)]
- () mutable
- {
- auto done = req.processRequest();
- req.finishRequest(done);
- });
- if (! posted)
- {
- LL_DEBUGS() << "Tried to start decoding on shutdown" << LL_ENDL;
- return 0;
- }
-
- return decode_id;
-}
-
-void LLImageDecodeThread::shutdown()
-{
- mThreadPool->close();
-}
-
-LLImageDecodeThread::Responder::~Responder()
-{
-}
-
-//----------------------------------------------------------------------------
-
-ImageRequest::ImageRequest(const LLPointer<LLImageFormatted>& image,
- S32 discard,
- 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)
-{
-}
-
-ImageRequest::~ImageRequest()
-{
- mDecodedImageRaw = NULL;
- mDecodedImageAux = NULL;
- mFormattedImage = NULL;
-}
-
-//----------------------------------------------------------------------------
-
-
-// Returns true when done, whether or not decode was successful.
-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;
-}
-
-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
-}
+/**
+ * @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$
+ */
+
+#include "linden_common.h"
+
+#include "llimageworker.h"
+#include "llimagedxt.h"
+#include "threadpool.h"
+
+/*--------------------------------------------------------------------------*/
+class ImageRequest
+{
+public:
+ ImageRequest(const LLPointer<LLImageFormatted>& image,
+ S32 discard,
+ bool needs_aux,
+ const LLPointer<LLImageDecodeThread::Responder>& responder,
+ U32 request_id);
+ virtual ~ImageRequest();
+
+ /*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;
+ U32 mRequestId;
+ bool mNeedsAux;
+ // output
+ LLPointer<LLImageRaw> mDecodedImageRaw;
+ LLPointer<LLImageRaw> mDecodedImageAux;
+ bool mDecodedRaw;
+ bool mDecodedAux;
+ LLPointer<LLImageDecodeThread::Responder> mResponder;
+ std::string mErrorString;};
+
+
+//----------------------------------------------------------------------------
+
+// MAIN THREAD
+LLImageDecodeThread::LLImageDecodeThread(bool /*threaded*/)
+ : mDecodeCount(0)
+{
+ mThreadPool.reset(new LL::ThreadPool("ImageDecode", 8));
+ mThreadPool->start();
+}
+
+//virtual
+LLImageDecodeThread::~LLImageDecodeThread()
+{}
+
+// MAIN THREAD
+// virtual
+size_t LLImageDecodeThread::update(F32 max_time_ms)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ return getPending();
+}
+
+size_t LLImageDecodeThread::getPending()
+{
+ return mThreadPool->getQueue().size();
+}
+
+LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(
+ const LLPointer<LLImageFormatted>& image,
+ S32 discard,
+ bool needs_aux,
+ const LLPointer<LLImageDecodeThread::Responder>& responder)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+
+ U32 decode_id = ++mDecodeCount;
+ // Instantiate the ImageRequest right in the lambda, why not?
+ bool posted = mThreadPool->getQueue().post(
+ [req = ImageRequest(image, discard, needs_aux, responder, decode_id)]
+ () mutable
+ {
+ auto done = req.processRequest();
+ req.finishRequest(done);
+ });
+ if (! posted)
+ {
+ LL_DEBUGS() << "Tried to start decoding on shutdown" << LL_ENDL;
+ return 0;
+ }
+
+ return decode_id;
+}
+
+void LLImageDecodeThread::shutdown()
+{
+ mThreadPool->close();
+}
+
+LLImageDecodeThread::Responder::~Responder()
+{
+}
+
+//----------------------------------------------------------------------------
+
+ImageRequest::ImageRequest(const LLPointer<LLImageFormatted>& image,
+ S32 discard,
+ 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)
+{
+}
+
+ImageRequest::~ImageRequest()
+{
+ mDecodedImageRaw = NULL;
+ mDecodedImageAux = NULL;
+ mFormattedImage = NULL;
+}
+
+//----------------------------------------------------------------------------
+
+
+// Returns true when done, whether or not decode was successful.
+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()) == 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, mErrorString, mDecodedImageRaw, mDecodedImageAux, mRequestId);
+ }
+ // Will automatically be deleted
+}
diff --git a/indra/llimage/llimageworker.h b/indra/llimage/llimageworker.h
index e7ad7ed4d0..5a16e4db8d 100644
--- a/indra/llimage/llimageworker.h
+++ b/indra/llimage/llimageworker.h
@@ -1,67 +1,67 @@
-/**
- * @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$
- */
-
-#ifndef LL_LLIMAGEWORKER_H
-#define LL_LLIMAGEWORKER_H
-
-#include "llimage.h"
-#include "llpointer.h"
-#include "threadpool_fwd.h"
-
-class LLImageDecodeThread
-{
-public:
- class Responder : public LLThreadSafeRefCount
- {
- protected:
- virtual ~Responder();
- public:
- virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux, U32 request_id) = 0;
- };
-
-public:
- 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);
- S32 getTotalDecodeCount() { return mDecodeCount; }
- 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;
- LLAtomicU32 mDecodeCount;
-};
-
-#endif
+/**
+ * @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$
+ */
+
+#ifndef LL_LLIMAGEWORKER_H
+#define LL_LLIMAGEWORKER_H
+
+#include "llimage.h"
+#include "llpointer.h"
+#include "threadpool_fwd.h"
+
+class LLImageDecodeThread
+{
+public:
+ 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();
+
+ // 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();
+
+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;
+ LLAtomicU32 mDecodeCount;
+};
+
+#endif
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..e9c11270c8 100644
--- a/indra/llimage/llpngwrapper.cpp
+++ b/indra/llimage/llpngwrapper.cpp
@@ -1,408 +1,415 @@
-/*
- * @file llpngwrapper.cpp
- * @brief Encapsulates libpng read/write functionality.
- *
- * $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$
- */
-
-#include "linden_common.h"
-#include "stdtypes.h"
-#include "llerror.h"
-
-#include "llimage.h"
-#include "llpngwrapper.h"
-
-#include "llexception.h"
-
-namespace {
-// Failure to load an image shouldn't crash the whole viewer.
-struct PngError: public LLContinueError
-{
- PngError(png_const_charp msg): LLContinueError(msg) {}
-};
-} // anonymous namespace
-
-// ---------------------------------------------------------------------------
-// LLPngWrapper
-// ---------------------------------------------------------------------------
-
-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)
-{
-}
-
-LLPngWrapper::~LLPngWrapper()
-{
- releaseResources();
-}
-
-// Checks the src for a valid PNG header
-bool LLPngWrapper::isValidPng(U8* src)
-{
- 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;
- }
-
- 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));
-}
-
-// 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);
-}
-
-// 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);
-}
-
-// Flush the write output pointer
-void LLPngWrapper::writeFlush(png_structp png_ptr)
-{
- // no-op since we're just writing to memory
-}
-
-// Read the PNG file using the libpng. The low-level interface is used here
-// because we want to do various transformations (including applying gama)
-// which can't be done with the high-level interface.
-// The scanline also begins at the bottom of
-// the image (per SecondLife conventions) instead of at the top, so we
-// 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);
-}
-
-// 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);
- }
- 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);
- }
-}
-
-// Read out the image meta-data
-void LLPngWrapper::updateMetaData()
-{
- 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);
-}
-
-// 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;
-}
-
-// 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;
- }
-}
-
-// Get final image size after compression
-U32 LLPngWrapper::getFinalSize()
-{
- return mFinalSize;
-}
-
-// Get last error message, if any
-const std::string& LLPngWrapper::getErrorMessage()
-{
- return mErrorMessage;
-}
+/*
+ * @file llpngwrapper.cpp
+ * @brief Encapsulates libpng read/write functionality.
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+#include "stdtypes.h"
+#include "llerror.h"
+
+#include "llimage.h"
+#include "llpngwrapper.h"
+
+#include "llexception.h"
+
+namespace {
+// Failure to load an image shouldn't crash the whole viewer.
+struct PngError: public LLContinueError
+{
+ PngError(png_const_charp msg): LLContinueError(msg) {}
+};
+} // anonymous namespace
+
+// ---------------------------------------------------------------------------
+// LLPngWrapper
+// ---------------------------------------------------------------------------
+
+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)
+{
+}
+
+LLPngWrapper::~LLPngWrapper()
+{
+ releaseResources();
+}
+
+// Checks the src for a valid PNG header
+bool LLPngWrapper::isValidPng(U8* src)
+{
+ 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;
+ }
+
+ 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));
+}
+
+// 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);
+}
+
+// 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);
+}
+
+// Flush the write output pointer
+void LLPngWrapper::writeFlush(png_structp png_ptr)
+{
+ // no-op since we're just writing to memory
+}
+
+// Read the PNG file using the libpng. The low-level interface is used here
+// because we want to do various transformations (including applying gama)
+// which can't be done with the high-level interface.
+// The scanline also begins at the bottom of
+// the image (per SecondLife conventions) instead of at the top, so we
+// 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);
+ }
+ 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);
+ }
+ 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);
+ }
+}
+
+// Read out the image meta-data
+void LLPngWrapper::updateMetaData()
+{
+ 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);
+}
+
+// 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;
+}
+
+// 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;
+ }
+}
+
+// Get final image size after compression
+U32 LLPngWrapper::getFinalSize()
+{
+ return mFinalSize;
+}
+
+// Get last error message, if any
+const std::string& LLPngWrapper::getErrorMessage()
+{
+ return mErrorMessage;
+}
diff --git a/indra/llimage/llpngwrapper.h b/indra/llimage/llpngwrapper.h
index b17e8d1f40..7356a76353 100644
--- a/indra/llimage/llpngwrapper.h
+++ b/indra/llimage/llpngwrapper.h
@@ -1,97 +1,97 @@
-/*
- * @file llpngwrapper.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$
- */
-
-#ifndef LL_LLPNGWRAPPER_H
-#define LL_LLPNGWRAPPER_H
-
-#include "png.h"
-#include "llimage.h"
-
-class LLPngWrapper
-{
-public:
- LLPngWrapper();
- virtual ~LLPngWrapper();
-
-public:
- 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();
-
-protected:
- 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;
- };
-
- 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();
-
- png_structp mReadPngPtr;
- png_infop mReadInfoPtr;
- png_structp mWritePngPtr;
- png_infop mWriteInfoPtr;
-
- U8 **mRowPointers;
-
- png_uint_32 mWidth;
- png_uint_32 mHeight;
- S32 mBitDepth;
- S32 mColorType;
- S32 mChannels;
- S32 mInterlaceType;
- S32 mCompressionType;
- S32 mFilterMethod;
-
- U32 mFinalSize;
-
- F64 mGamma;
-
- std::string mErrorMessage;
-};
-
-#endif
+/*
+ * @file llpngwrapper.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$
+ */
+
+#ifndef LL_LLPNGWRAPPER_H
+#define LL_LLPNGWRAPPER_H
+
+#include "png.h"
+#include "llimage.h"
+
+class LLPngWrapper
+{
+public:
+ LLPngWrapper();
+ virtual ~LLPngWrapper();
+
+public:
+ 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();
+
+protected:
+ 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;
+ };
+
+ 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();
+
+ png_structp mReadPngPtr;
+ png_infop mReadInfoPtr;
+ png_structp mWritePngPtr;
+ png_infop mWriteInfoPtr;
+
+ U8 **mRowPointers;
+
+ png_uint_32 mWidth;
+ png_uint_32 mHeight;
+ S32 mBitDepth;
+ S32 mColorType;
+ S32 mChannels;
+ S32 mInterlaceType;
+ S32 mCompressionType;
+ S32 mFilterMethod;
+
+ U32 mFinalSize;
+
+ F64 mGamma;
+
+ 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);
+ }
}