00001
00031 #include "linden_common.h"
00032
00033 #include "llimagedxt.h"
00034
00035
00036 void LLImageDXT::checkMinWidthHeight(EFileFormat format, S32& width, S32& height)
00037 {
00038 S32 mindim = (format >= FORMAT_DXT1 && format <= FORMAT_DXR5) ? 4 : 1;
00039 width = llmax(width, mindim);
00040 height = llmax(height, mindim);
00041 }
00042
00043
00044 S32 LLImageDXT::formatBits(EFileFormat format)
00045 {
00046 switch (format)
00047 {
00048 case FORMAT_DXT1: return 4;
00049 case FORMAT_DXR1: return 4;
00050 case FORMAT_I8: return 8;
00051 case FORMAT_A8: return 8;
00052 case FORMAT_DXT3: return 8;
00053 case FORMAT_DXR3: return 8;
00054 case FORMAT_DXR5: return 8;
00055 case FORMAT_DXT5: return 8;
00056 case FORMAT_RGB8: return 24;
00057 case FORMAT_RGBA8: return 32;
00058 default:
00059 llerrs << "LLImageDXT::Unknown format: " << format << llendl;
00060 return 0;
00061 }
00062 };
00063
00064
00065 S32 LLImageDXT::formatBytes(EFileFormat format, S32 width, S32 height)
00066 {
00067 checkMinWidthHeight(format, width, height);
00068 S32 bytes = ((width*height*formatBits(format)+7)>>3);
00069 S32 aligned = (bytes+3)&~3;
00070 return aligned;
00071 }
00072
00073
00074 S32 LLImageDXT::formatComponents(EFileFormat format)
00075 {
00076 switch (format)
00077 {
00078 case FORMAT_DXT1: return 3;
00079 case FORMAT_DXR1: return 3;
00080 case FORMAT_I8: return 1;
00081 case FORMAT_A8: return 1;
00082 case FORMAT_DXT3: return 4;
00083 case FORMAT_DXR3: return 4;
00084 case FORMAT_DXT5: return 4;
00085 case FORMAT_DXR5: return 4;
00086 case FORMAT_RGB8: return 3;
00087 case FORMAT_RGBA8: return 4;
00088 default:
00089 llerrs << "LLImageDXT::Unknown format: " << format << llendl;
00090 return 0;
00091 }
00092 };
00093
00094
00095 LLImageDXT::EFileFormat LLImageDXT::getFormat(S32 fourcc)
00096 {
00097 switch(fourcc)
00098 {
00099 case 0x20203849: return FORMAT_I8;
00100 case 0x20203841: return FORMAT_A8;
00101 case 0x20424752: return FORMAT_RGB8;
00102 case 0x41424752: return FORMAT_RGBA8;
00103 case 0x31525844: return FORMAT_DXR1;
00104 case 0x32525844: return FORMAT_DXR2;
00105 case 0x33525844: return FORMAT_DXR3;
00106 case 0x34525844: return FORMAT_DXR4;
00107 case 0x35525844: return FORMAT_DXR5;
00108 case 0x31545844: return FORMAT_DXT1;
00109 case 0x32545844: return FORMAT_DXT2;
00110 case 0x33545844: return FORMAT_DXT3;
00111 case 0x34545844: return FORMAT_DXT4;
00112 case 0x35545844: return FORMAT_DXT5;
00113 default: return FORMAT_UNKNOWN;
00114 }
00115 }
00116
00117
00118 S32 LLImageDXT::getFourCC(EFileFormat format)
00119 {
00120 switch(format)
00121 {
00122 case FORMAT_I8: return 0x20203849;
00123 case FORMAT_A8: return 0x20203841;
00124 case FORMAT_RGB8: return 0x20424752;
00125 case FORMAT_RGBA8: return 0x41424752;
00126 case FORMAT_DXR1: return 0x31525844;
00127 case FORMAT_DXR2: return 0x32525844;
00128 case FORMAT_DXR3: return 0x33525844;
00129 case FORMAT_DXR4: return 0x34525844;
00130 case FORMAT_DXR5: return 0x35525844;
00131 case FORMAT_DXT1: return 0x31545844;
00132 case FORMAT_DXT2: return 0x32545844;
00133 case FORMAT_DXT3: return 0x33545844;
00134 case FORMAT_DXT4: return 0x34545844;
00135 case FORMAT_DXT5: return 0x35545844;
00136 default: return 0x00000000;
00137 }
00138 }
00139
00140
00141 void LLImageDXT::calcDiscardWidthHeight(S32 discard_level, EFileFormat format, S32& width, S32& height)
00142 {
00143 while (discard_level > 0 && width > 1 && height > 1)
00144 {
00145 discard_level--;
00146 width >>= 1;
00147 height >>= 1;
00148 }
00149 checkMinWidthHeight(format, width, height);
00150 }
00151
00152
00153 S32 LLImageDXT::calcNumMips(S32 width, S32 height)
00154 {
00155 S32 nmips = 0;
00156 while (width > 0 && height > 0)
00157 {
00158 width >>= 1;
00159 height >>= 1;
00160 nmips++;
00161 }
00162 return nmips;
00163 }
00164
00165
00166
00167 LLImageDXT::LLImageDXT()
00168 : LLImageFormatted(IMG_CODEC_DXT),
00169 mFileFormat(FORMAT_UNKNOWN),
00170 mHeaderSize(0)
00171 {
00172 }
00173
00174 LLImageDXT::~LLImageDXT()
00175 {
00176 }
00177
00178
00179 BOOL LLImageDXT::updateData()
00180 {
00181 resetLastError();
00182
00183 U8* data = getData();
00184 S32 data_size = getDataSize();
00185
00186 if (!data || !data_size)
00187 {
00188 setLastError("LLImageDXT uninitialized");
00189 return FALSE;
00190 }
00191
00192 S32 width, height, miplevelmax;
00193 dxtfile_header_t* header = (dxtfile_header_t*)data;
00194 if (header->fourcc != 0x20534444)
00195 {
00196 dxtfile_header_old_t* oldheader = (dxtfile_header_old_t*)header;
00197 mHeaderSize = sizeof(dxtfile_header_old_t);
00198 mFileFormat = EFileFormat(oldheader->format);
00199 miplevelmax = llmin(oldheader->maxlevel,MAX_IMAGE_MIP);
00200 width = oldheader->maxwidth;
00201 height = oldheader->maxheight;
00202 }
00203 else
00204 {
00205 mHeaderSize = sizeof(dxtfile_header_t);
00206 mFileFormat = getFormat(header->pixel_fmt.fourcc);
00207 miplevelmax = llmin(header->num_mips-1,MAX_IMAGE_MIP);
00208 width = header->maxwidth;
00209 height = header->maxheight;
00210 }
00211
00212 if (data_size < mHeaderSize)
00213 {
00214 llerrs << "LLImageDXT: not enough data" << llendl;
00215 }
00216 S32 ncomponents = formatComponents(mFileFormat);
00217 setSize(width, height, ncomponents);
00218
00219 S32 discard = calcDiscardLevelBytes(data_size);
00220 discard = llmin(discard, miplevelmax);
00221 setDiscardLevel(discard);
00222
00223 return TRUE;
00224 }
00225
00226
00227 S32 LLImageDXT::getMipOffset(S32 discard)
00228 {
00229 if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXT5)
00230 {
00231 llerrs << "getMipOffset called with old (unsupported) format" << llendl;
00232 }
00233 S32 width = getWidth(), height = getHeight();
00234 S32 num_mips = calcNumMips(width, height);
00235 discard = llclamp(discard, 0, num_mips-1);
00236 S32 last_mip = num_mips-1-discard;
00237 llassert(mHeaderSize > 0);
00238 S32 offset = mHeaderSize;
00239 for (S32 mipidx = num_mips-1; mipidx >= 0; mipidx--)
00240 {
00241 if (mipidx < last_mip)
00242 {
00243 offset += formatBytes(mFileFormat, width, height);
00244 }
00245 width >>= 1;
00246 height >>= 1;
00247 }
00248 return offset;
00249 }
00250
00251 void LLImageDXT::setFormat()
00252 {
00253 S32 ncomponents = getComponents();
00254 switch (ncomponents)
00255 {
00256 case 3: mFileFormat = FORMAT_DXR1; break;
00257 case 4: mFileFormat = FORMAT_DXR3; break;
00258 default: llerrs << "LLImageDXT::setFormat called with ncomponents = " << ncomponents << llendl;
00259 }
00260 mHeaderSize = calcHeaderSize();
00261 }
00262
00263
00264 BOOL LLImageDXT::decode(LLImageRaw* raw_image, F32 time)
00265 {
00266 llassert_always(raw_image);
00267
00268 if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXR5)
00269 {
00270 llwarns << "Attempt to decode compressed LLImageDXT to Raw (unsupported)" << llendl;
00271 return FALSE;
00272 }
00273
00274 S32 width = getWidth(), height = getHeight();
00275 S32 ncomponents = getComponents();
00276 S32 image_size = formatBytes(mFileFormat, width, height);
00277 U8* data = getData() + getMipOffset(0);
00278
00279 if ((!getData()) || (data + image_size > getData() + getDataSize()))
00280 {
00281 setLastError("LLImageDXT trying to decode an image with not enough data!");
00282 return FALSE;
00283 }
00284
00285 raw_image->resize(width, height, ncomponents);
00286 memcpy(raw_image->getData(), data, image_size);
00287
00288 return TRUE;
00289 }
00290
00291 BOOL LLImageDXT::getMipData(LLPointer<LLImageRaw>& raw, S32 discard)
00292 {
00293 if (discard < 0)
00294 {
00295 discard = mDiscardLevel;
00296 }
00297 else if (discard < mDiscardLevel)
00298 {
00299 llerrs << "Request for invalid discard level" << llendl;
00300 }
00301 U8* data = getData() + getMipOffset(discard);
00302
00303
00304 S32 width = raw->getWidth();
00305 S32 height = raw->getHeight();
00306 calcDiscardWidthHeight(discard, mFileFormat, width, height);
00307 raw = new LLImageRaw(data, width, height, getComponents());
00308 return TRUE;
00309 }
00310
00311 BOOL LLImageDXT::encode(const LLImageRaw* raw_image, F32 time, bool explicit_mips)
00312 {
00313 llassert_always(raw_image);
00314
00315 S32 ncomponents = raw_image->getComponents();
00316 EFileFormat format;
00317 switch (ncomponents)
00318 {
00319 case 1:
00320 format = FORMAT_A8;
00321 break;
00322 case 3:
00323 format = FORMAT_RGB8;
00324 break;
00325 case 4:
00326 format = FORMAT_RGBA8;
00327 break;
00328 default:
00329 llerrs << "LLImageDXT::encode: Unhandled channel number: " << ncomponents << llendl;
00330 return 0;
00331 }
00332
00333 S32 width = raw_image->getWidth();
00334 S32 height = raw_image->getHeight();
00335
00336 if (explicit_mips)
00337 {
00338 height = (height/3)*2;
00339 }
00340
00341 setSize(width, height, ncomponents);
00342 mHeaderSize = sizeof(dxtfile_header_t);
00343 mFileFormat = format;
00344
00345 S32 nmips = calcNumMips(width, height);
00346 S32 w = width;
00347 S32 h = height;
00348
00349 S32 totbytes = mHeaderSize;
00350 for (S32 mip=0; mip<nmips; mip++)
00351 {
00352 totbytes += formatBytes(format,w,h);
00353 w >>= 1;
00354 h >>= 1;
00355 }
00356
00357 allocateData(totbytes);
00358
00359 U8* data = getData();
00360 dxtfile_header_t* header = (dxtfile_header_t*)data;
00361 llassert(mHeaderSize > 0);
00362 memset(header, 0, mHeaderSize);
00363 header->fourcc = 0x20534444;
00364 header->pixel_fmt.fourcc = getFourCC(format);
00365 header->num_mips = nmips;
00366 header->maxwidth = width;
00367 header->maxheight = height;
00368
00369 U8* prev_mipdata = 0;
00370 w = width, h = height;
00371 for (S32 mip=0; mip<nmips; mip++)
00372 {
00373 U8* mipdata = data + getMipOffset(mip);
00374 S32 bytes = formatBytes(format, w, h);
00375 if (mip==0)
00376 {
00377 memcpy(mipdata, raw_image->getData(), bytes);
00378 }
00379 else if (explicit_mips)
00380 {
00381 extractMip(raw_image->getData(), mipdata, width, height, w, h, format);
00382 }
00383 else
00384 {
00385 generateMip(prev_mipdata, mipdata, w, h, ncomponents);
00386 }
00387 w >>= 1;
00388 h >>= 1;
00389 checkMinWidthHeight(format, w, h);
00390 prev_mipdata = mipdata;
00391 }
00392
00393 return TRUE;
00394 }
00395
00396
00397 BOOL LLImageDXT::encode(const LLImageRaw* raw_image, F32 time)
00398 {
00399 return encode(raw_image, time, false);
00400 }
00401
00402
00403 bool LLImageDXT::convertToDXR()
00404 {
00405 EFileFormat newformat = FORMAT_UNKNOWN;
00406 switch (mFileFormat)
00407 {
00408 case FORMAT_DXR1:
00409 case FORMAT_DXR2:
00410 case FORMAT_DXR3:
00411 case FORMAT_DXR4:
00412 case FORMAT_DXR5:
00413 return false;
00414 case FORMAT_DXT1: newformat = FORMAT_DXR1; break;
00415 case FORMAT_DXT2: newformat = FORMAT_DXR2; break;
00416 case FORMAT_DXT3: newformat = FORMAT_DXR3; break;
00417 case FORMAT_DXT4: newformat = FORMAT_DXR4; break;
00418 case FORMAT_DXT5: newformat = FORMAT_DXR5; break;
00419 default:
00420 llwarns << "convertToDXR: can not convert format: " << llformat("0x%08x",getFourCC(mFileFormat)) << llendl;
00421 return false;
00422 }
00423 mFileFormat = newformat;
00424 S32 width = getWidth(), height = getHeight();
00425 S32 nmips = calcNumMips(width,height);
00426 S32 total_bytes = getDataSize();
00427 U8* olddata = getData();
00428 U8* newdata = new U8[total_bytes];
00429 if (!newdata)
00430 {
00431 llerrs << "Out of memory in LLImageDXT::convertToDXR()" << llendl;
00432 return false;
00433 }
00434 llassert(total_bytes > 0);
00435 memset(newdata, 0, total_bytes);
00436 memcpy(newdata, olddata, mHeaderSize);
00437 for (S32 mip=0; mip<nmips; mip++)
00438 {
00439 S32 bytes = formatBytes(mFileFormat, width, height);
00440 S32 newoffset = getMipOffset(mip);
00441 S32 oldoffset = mHeaderSize + (total_bytes - newoffset - bytes);
00442 memcpy(newdata + newoffset, olddata + oldoffset, bytes);
00443 width >>= 1;
00444 height >>= 1;
00445 }
00446 dxtfile_header_t* header = (dxtfile_header_t*)newdata;
00447 header->pixel_fmt.fourcc = getFourCC(newformat);
00448 setData(newdata, total_bytes);
00449 updateData();
00450 return true;
00451 }
00452
00453
00454 S32 LLImageDXT::calcHeaderSize()
00455 {
00456 return llmax(sizeof(dxtfile_header_old_t), sizeof(dxtfile_header_t));
00457 }
00458
00459
00460 S32 LLImageDXT::calcDataSize(S32 discard_level)
00461 {
00462 if (mFileFormat == FORMAT_UNKNOWN)
00463 {
00464 llerrs << "calcDataSize called with unloaded LLImageDXT" << llendl;
00465 return 0;
00466 }
00467 if (discard_level < 0)
00468 {
00469 discard_level = mDiscardLevel;
00470 }
00471 S32 bytes = getMipOffset(discard_level);
00472 S32 w = getWidth() >> discard_level;
00473 S32 h = getHeight() >> discard_level;
00474 bytes += formatBytes(mFileFormat,w,h);
00475 return bytes;
00476 }
00477
00478
00479
00480
00481 void LLImageDXT::extractMip(const U8 *indata, U8* mipdata, int width, int height,
00482 int mip_width, int mip_height, EFileFormat format)
00483 {
00484 int initial_offset = formatBytes(format, width, height);
00485 int line_width = formatBytes(format, width, 1);
00486 int mip_line_width = formatBytes(format, mip_width, 1);
00487 int line_offset = 0;
00488
00489 for (int ww=width>>1; ww>mip_width; ww>>=1)
00490 {
00491 line_offset += formatBytes(format, ww, 1);
00492 }
00493
00494 for (int h=0;h<mip_height;++h)
00495 {
00496 int start_offset = initial_offset + line_width * h + line_offset;
00497 memcpy(mipdata + mip_line_width*h, indata + start_offset, mip_line_width);
00498 }
00499 }
00500
00501