00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "openttd.h"
00014 #include "fileio_func.h"
00015 #include "viewport_func.h"
00016 #include "gfx_func.h"
00017 #include "screenshot.h"
00018 #include "blitter/factory.hpp"
00019 #include "zoom_func.h"
00020 #include "core/endian_func.hpp"
00021 #include "map_func.h"
00022 #include "saveload/saveload.h"
00023 #include "company_func.h"
00024 #include "strings_func.h"
00025 #include "gui.h"
00026 #include "window_gui.h"
00027 #include "window_func.h"
00028
00029 #include "table/strings.h"
00030
00031
00032 char _screenshot_format_name[8];
00033 uint _num_screenshot_formats;
00034 uint _cur_screenshot_format;
00035 static char _screenshot_name[128];
00036 char _full_screenshot_name[MAX_PATH];
00037
00038
00039 typedef void ScreenshotCallback(void *userdata, void *buf, uint y, uint pitch, uint n);
00040 typedef bool ScreenshotHandlerProc(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette);
00041
00042 struct ScreenshotFormat {
00043 const char *name;
00044 const char *extension;
00045 ScreenshotHandlerProc *proc;
00046 };
00047
00048
00049
00050
00051 #if defined(_MSC_VER) || defined(__WATCOMC__)
00052 #pragma pack(push, 1)
00053 #endif
00054
00056 struct BitmapFileHeader {
00057 uint16 type;
00058 uint32 size;
00059 uint32 reserved;
00060 uint32 off_bits;
00061 } GCC_PACK;
00062 assert_compile(sizeof(BitmapFileHeader) == 14);
00063
00064 #if defined(_MSC_VER) || defined(__WATCOMC__)
00065 #pragma pack(pop)
00066 #endif
00067
00069 struct BitmapInfoHeader {
00070 uint32 size;
00071 int32 width, height;
00072 uint16 planes, bitcount;
00073 uint32 compression, sizeimage, xpels, ypels, clrused, clrimp;
00074 };
00075 assert_compile(sizeof(BitmapInfoHeader) == 40);
00076
00078 struct RgbQuad {
00079 byte blue, green, red, reserved;
00080 };
00081 assert_compile(sizeof(RgbQuad) == 4);
00082
00094 static bool MakeBMPImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
00095 {
00096 uint bpp;
00097 switch (pixelformat) {
00098 case 8: bpp = 1; break;
00099
00100 case 32: bpp = 3; break;
00101
00102 default: return false;
00103 }
00104
00105 FILE *f = fopen(name, "wb");
00106 if (f == NULL) return false;
00107
00108
00109 uint bytewidth = Align(w * bpp, 4);
00110
00111
00112 uint pal_size = pixelformat == 8 ? sizeof(RgbQuad) * 256 : 0;
00113
00114
00115 BitmapFileHeader bfh;
00116 bfh.type = TO_LE16('MB');
00117 bfh.size = TO_LE32(sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + pal_size + bytewidth * h);
00118 bfh.reserved = 0;
00119 bfh.off_bits = TO_LE32(sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + pal_size);
00120
00121
00122 BitmapInfoHeader bih;
00123 bih.size = TO_LE32(sizeof(BitmapInfoHeader));
00124 bih.width = TO_LE32(w);
00125 bih.height = TO_LE32(h);
00126 bih.planes = TO_LE16(1);
00127 bih.bitcount = TO_LE16(bpp * 8);
00128 bih.compression = 0;
00129 bih.sizeimage = 0;
00130 bih.xpels = 0;
00131 bih.ypels = 0;
00132 bih.clrused = 0;
00133 bih.clrimp = 0;
00134
00135
00136 if (fwrite(&bfh, sizeof(bfh), 1, f) != 1 || fwrite(&bih, sizeof(bih), 1, f) != 1) {
00137 fclose(f);
00138 return false;
00139 }
00140
00141 if (pixelformat == 8) {
00142
00143 RgbQuad rq[256];
00144 for (uint i = 0; i < 256; i++) {
00145 rq[i].red = palette[i].r;
00146 rq[i].green = palette[i].g;
00147 rq[i].blue = palette[i].b;
00148 rq[i].reserved = 0;
00149 }
00150
00151 if (fwrite(rq, sizeof(rq), 1, f) != 1) {
00152 fclose(f);
00153 return false;
00154 }
00155 }
00156
00157
00158 uint maxlines = Clamp(65536 / (w * pixelformat / 8), 16, 128);
00159
00160 uint8 *buff = MallocT<uint8>(maxlines * w * pixelformat / 8);
00161 uint8 *line = AllocaM(uint8, bytewidth);
00162 memset(line, 0, bytewidth);
00163
00164
00165 do {
00166 uint n = min(h, maxlines);
00167 h -= n;
00168
00169
00170 callb(userdata, buff, h, w, n);
00171
00172
00173 while (n-- != 0) {
00174 if (pixelformat == 8) {
00175
00176 memcpy(line, buff + n * w, w);
00177 } else {
00178
00179
00180 Colour *src = ((Colour *)buff) + n * w;
00181 byte *dst = line;
00182 for (uint i = 0; i < w; i++) {
00183 dst[i * 3 ] = src[i].b;
00184 dst[i * 3 + 1] = src[i].g;
00185 dst[i * 3 + 2] = src[i].r;
00186 }
00187 }
00188
00189 if (fwrite(line, bytewidth, 1, f) != 1) {
00190 free(buff);
00191 fclose(f);
00192 return false;
00193 }
00194 }
00195 } while (h != 0);
00196
00197 free(buff);
00198 fclose(f);
00199
00200 return true;
00201 }
00202
00203
00204
00205
00206 #if defined(WITH_PNG)
00207 #include <png.h>
00208
00209 #ifdef PNG_TEXT_SUPPORTED
00210 #include "rev.h"
00211 #include "newgrf_config.h"
00212 #include "ai/ai_info.hpp"
00213 #include "company_base.h"
00214 #include "base_media_base.h"
00215 #endif
00216
00217 static void PNGAPI png_my_error(png_structp png_ptr, png_const_charp message)
00218 {
00219 DEBUG(misc, 0, "[libpng] error: %s - %s", message, (const char *)png_get_error_ptr(png_ptr));
00220 longjmp(png_jmpbuf(png_ptr), 1);
00221 }
00222
00223 static void PNGAPI png_my_warning(png_structp png_ptr, png_const_charp message)
00224 {
00225 DEBUG(misc, 1, "[libpng] warning: %s - %s", message, (const char *)png_get_error_ptr(png_ptr));
00226 }
00227
00228 static bool MakePNGImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
00229 {
00230 png_color rq[256];
00231 FILE *f;
00232 uint i, y, n;
00233 uint maxlines;
00234 uint bpp = pixelformat / 8;
00235 png_structp png_ptr;
00236 png_infop info_ptr;
00237
00238
00239 if (pixelformat != 8 && pixelformat != 32) return false;
00240
00241 f = fopen(name, "wb");
00242 if (f == NULL) return false;
00243
00244 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (void *)name, png_my_error, png_my_warning);
00245
00246 if (png_ptr == NULL) {
00247 fclose(f);
00248 return false;
00249 }
00250
00251 info_ptr = png_create_info_struct(png_ptr);
00252 if (info_ptr == NULL) {
00253 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
00254 fclose(f);
00255 return false;
00256 }
00257
00258 if (setjmp(png_jmpbuf(png_ptr))) {
00259 png_destroy_write_struct(&png_ptr, &info_ptr);
00260 fclose(f);
00261 return false;
00262 }
00263
00264 png_init_io(png_ptr, f);
00265
00266 png_set_filter(png_ptr, 0, PNG_FILTER_NONE);
00267
00268 png_set_IHDR(png_ptr, info_ptr, w, h, 8, pixelformat == 8 ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGB,
00269 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00270
00271 #ifdef PNG_TEXT_SUPPORTED
00272
00273
00274 png_text_struct text[2];
00275 memset(text, 0, sizeof(text));
00276 text[0].key = const_cast<char *>("Software");
00277 text[0].text = const_cast<char *>(_openttd_revision);
00278 text[0].text_length = strlen(_openttd_revision);
00279 text[0].compression = PNG_TEXT_COMPRESSION_NONE;
00280
00281 char buf[2048];
00282 char *p = buf;
00283 p += seprintf(p, lastof(buf), "Graphics set: %s (%u)\n", BaseGraphics::GetUsedSet()->name, BaseGraphics::GetUsedSet()->version);
00284 p = strecpy(p, "NewGRFs:\n", lastof(buf));
00285 for (const GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
00286 p += seprintf(p, lastof(buf), "%08X ", BSWAP32(c->ident.grfid));
00287 p = md5sumToString(p, lastof(buf), c->ident.md5sum);
00288 p += seprintf(p, lastof(buf), " %s\n", c->filename);
00289 }
00290 p = strecpy(p, "\nCompanies:\n", lastof(buf));
00291 const Company *c;
00292 FOR_ALL_COMPANIES(c) {
00293 if (c->ai_info == NULL) {
00294 p += seprintf(p, lastof(buf), "%2i: Human\n", (int)c->index);
00295 } else {
00296 #ifdef ENABLE_AI
00297 p += seprintf(p, lastof(buf), "%2i: %s (v%d)\n", (int)c->index, c->ai_info->GetName(), c->ai_info->GetVersion());
00298 #endif
00299 }
00300 }
00301 text[1].key = const_cast<char *>("Description");
00302 text[1].text = buf;
00303 text[1].text_length = p - buf;
00304 text[1].compression = PNG_TEXT_COMPRESSION_zTXt;
00305 png_set_text(png_ptr, info_ptr, text, 2);
00306 #endif
00307
00308 if (pixelformat == 8) {
00309
00310 for (i = 0; i != 256; i++) {
00311 rq[i].red = palette[i].r;
00312 rq[i].green = palette[i].g;
00313 rq[i].blue = palette[i].b;
00314 }
00315
00316 png_set_PLTE(png_ptr, info_ptr, rq, 256);
00317 }
00318
00319 png_write_info(png_ptr, info_ptr);
00320 png_set_flush(png_ptr, 512);
00321
00322 if (pixelformat == 32) {
00323 png_color_8 sig_bit;
00324
00325
00326 sig_bit.alpha = 0;
00327 sig_bit.blue = 8;
00328 sig_bit.green = 8;
00329 sig_bit.red = 8;
00330 sig_bit.gray = 8;
00331 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
00332
00333 #if TTD_ENDIAN == TTD_LITTLE_ENDIAN
00334 png_set_bgr(png_ptr);
00335 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
00336 #else
00337 png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
00338 #endif
00339 }
00340
00341
00342 maxlines = Clamp(65536 / w, 16, 128);
00343
00344
00345 void *buff = CallocT<uint8>(w * maxlines * bpp);
00346
00347 y = 0;
00348 do {
00349
00350 n = min(h - y, maxlines);
00351
00352
00353 callb(userdata, buff, y, w, n);
00354 y += n;
00355
00356
00357 for (i = 0; i != n; i++) {
00358 png_write_row(png_ptr, (png_bytep)buff + i * w * bpp);
00359 }
00360 } while (y != h);
00361
00362 png_write_end(png_ptr, info_ptr);
00363 png_destroy_write_struct(&png_ptr, &info_ptr);
00364
00365 free(buff);
00366 fclose(f);
00367 return true;
00368 }
00369 #endif
00370
00371
00372
00373
00374
00375
00376 struct PcxHeader {
00377 byte manufacturer;
00378 byte version;
00379 byte rle;
00380 byte bpp;
00381 uint32 unused;
00382 uint16 xmax, ymax;
00383 uint16 hdpi, vdpi;
00384 byte pal_small[16 * 3];
00385 byte reserved;
00386 byte planes;
00387 uint16 pitch;
00388 uint16 cpal;
00389 uint16 width;
00390 uint16 height;
00391 byte filler[54];
00392 };
00393 assert_compile(sizeof(PcxHeader) == 128);
00394
00395 static bool MakePCXImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
00396 {
00397 FILE *f;
00398 uint maxlines;
00399 uint y;
00400 PcxHeader pcx;
00401 bool success;
00402
00403 if (pixelformat == 32) {
00404 DEBUG(misc, 0, "Can't convert a 32bpp screenshot to PCX format. Please pick another format.");
00405 return false;
00406 }
00407 if (pixelformat != 8 || w == 0) return false;
00408
00409 f = fopen(name, "wb");
00410 if (f == NULL) return false;
00411
00412 memset(&pcx, 0, sizeof(pcx));
00413
00414
00415 pcx.manufacturer = 10;
00416 pcx.version = 5;
00417 pcx.rle = 1;
00418 pcx.bpp = 8;
00419 pcx.xmax = TO_LE16(w - 1);
00420 pcx.ymax = TO_LE16(h - 1);
00421 pcx.hdpi = TO_LE16(320);
00422 pcx.vdpi = TO_LE16(320);
00423
00424 pcx.planes = 1;
00425 pcx.cpal = TO_LE16(1);
00426 pcx.width = pcx.pitch = TO_LE16(w);
00427 pcx.height = TO_LE16(h);
00428
00429
00430 if (fwrite(&pcx, sizeof(pcx), 1, f) != 1) {
00431 fclose(f);
00432 return false;
00433 }
00434
00435
00436 maxlines = Clamp(65536 / w, 16, 128);
00437
00438
00439 uint8 *buff = CallocT<uint8>(w * maxlines);
00440
00441 y = 0;
00442 do {
00443
00444 uint n = min(h - y, maxlines);
00445 uint i;
00446
00447
00448 callb(userdata, buff, y, w, n);
00449 y += n;
00450
00451
00452 for (i = 0; i != n; i++) {
00453 const uint8 *bufp = buff + i * w;
00454 byte runchar = bufp[0];
00455 uint runcount = 1;
00456 uint j;
00457
00458
00459 for (j = 1; j < w; j++) {
00460 uint8 ch = bufp[j];
00461
00462 if (ch != runchar || runcount >= 0x3f) {
00463 if (runcount > 1 || (runchar & 0xC0) == 0xC0) {
00464 if (fputc(0xC0 | runcount, f) == EOF) {
00465 free(buff);
00466 fclose(f);
00467 return false;
00468 }
00469 }
00470 if (fputc(runchar, f) == EOF) {
00471 free(buff);
00472 fclose(f);
00473 return false;
00474 }
00475 runcount = 0;
00476 runchar = ch;
00477 }
00478 runcount++;
00479 }
00480
00481
00482 if (runcount > 1 || (runchar & 0xC0) == 0xC0) {
00483 if (fputc(0xC0 | runcount, f) == EOF) {
00484 free(buff);
00485 fclose(f);
00486 return false;
00487 }
00488 }
00489 if (fputc(runchar, f) == EOF) {
00490 free(buff);
00491 fclose(f);
00492 return false;
00493 }
00494 }
00495 } while (y != h);
00496
00497 free(buff);
00498
00499
00500 if (fputc(12, f) == EOF) {
00501 fclose(f);
00502 return false;
00503 }
00504
00505
00506 byte tmp[256 * 3];
00507
00508 for (uint i = 0; i < 256; i++) {
00509 tmp[i * 3 + 0] = palette[i].r;
00510 tmp[i * 3 + 1] = palette[i].g;
00511 tmp[i * 3 + 2] = palette[i].b;
00512 }
00513 success = fwrite(tmp, sizeof(tmp), 1, f) == 1;
00514
00515 fclose(f);
00516
00517 return success;
00518 }
00519
00520
00521
00522
00523
00524 static const ScreenshotFormat _screenshot_formats[] = {
00525 #if defined(WITH_PNG)
00526 {"PNG", "png", &MakePNGImage},
00527 #endif
00528 {"BMP", "bmp", &MakeBMPImage},
00529 {"PCX", "pcx", &MakePCXImage},
00530 };
00531
00532 void InitializeScreenshotFormats()
00533 {
00534 uint j = 0;
00535 for (uint i = 0; i < lengthof(_screenshot_formats); i++) {
00536 if (!strcmp(_screenshot_format_name, _screenshot_formats[i].extension)) {
00537 j = i;
00538 break;
00539 }
00540 }
00541 _cur_screenshot_format = j;
00542 _num_screenshot_formats = lengthof(_screenshot_formats);
00543 }
00544
00545 const char *GetScreenshotFormatDesc(int i)
00546 {
00547 return _screenshot_formats[i].name;
00548 }
00549
00550 void SetScreenshotFormat(uint i)
00551 {
00552 assert(i < _num_screenshot_formats);
00553 _cur_screenshot_format = i;
00554 strecpy(_screenshot_format_name, _screenshot_formats[i].extension, lastof(_screenshot_format_name));
00555 }
00556
00557
00558 static void CurrentScreenCallback(void *userdata, void *buf, uint y, uint pitch, uint n)
00559 {
00560 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00561 void *src = blitter->MoveTo(_screen.dst_ptr, 0, y);
00562 blitter->CopyImageToBuffer(src, buf, _screen.width, n, pitch);
00563 }
00564
00573 static void LargeWorldCallback(void *userdata, void *buf, uint y, uint pitch, uint n)
00574 {
00575 ViewPort *vp = (ViewPort *)userdata;
00576 DrawPixelInfo dpi, *old_dpi;
00577 int wx, left;
00578
00579
00580 DrawPixelInfo old_screen = _screen;
00581 bool old_disable_anim = _screen_disable_anim;
00582
00583 _screen.dst_ptr = buf;
00584 _screen.width = pitch;
00585 _screen.height = n;
00586 _screen.pitch = pitch;
00587 _screen_disable_anim = true;
00588
00589 old_dpi = _cur_dpi;
00590 _cur_dpi = &dpi;
00591
00592 dpi.dst_ptr = buf;
00593 dpi.height = n;
00594 dpi.width = vp->width;
00595 dpi.pitch = pitch;
00596 dpi.zoom = ZOOM_LVL_WORLD_SCREENSHOT;
00597 dpi.left = 0;
00598 dpi.top = y;
00599
00600
00601 left = 0;
00602 while (vp->width - left != 0) {
00603 wx = min(vp->width - left, 1600);
00604 left += wx;
00605
00606 ViewportDoDraw(vp,
00607 ScaleByZoom(left - wx - vp->left, vp->zoom) + vp->virtual_left,
00608 ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top,
00609 ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
00610 ScaleByZoom((y + n) - vp->top, vp->zoom) + vp->virtual_top
00611 );
00612 }
00613
00614 _cur_dpi = old_dpi;
00615
00616
00617 _screen = old_screen;
00618 _screen_disable_anim = old_disable_anim;
00619 }
00620
00621 static const char *MakeScreenshotName(const char *ext)
00622 {
00623 bool generate = StrEmpty(_screenshot_name);
00624
00625 if (generate) {
00626 if (_game_mode == GM_EDITOR || _game_mode == GM_MENU || _local_company == COMPANY_SPECTATOR) {
00627 strecpy(_screenshot_name, "screenshot", lastof(_screenshot_name));
00628 } else {
00629 GenerateDefaultSaveName(_screenshot_name, lastof(_screenshot_name));
00630 }
00631 }
00632
00633
00634 size_t len = strlen(_screenshot_name);
00635 snprintf(&_screenshot_name[len], lengthof(_screenshot_name) - len, ".%s", ext);
00636
00637 for (uint serial = 1;; serial++) {
00638 if (snprintf(_full_screenshot_name, lengthof(_full_screenshot_name), "%s%s", _personal_dir, _screenshot_name) >= (int)lengthof(_full_screenshot_name)) {
00639
00640 _full_screenshot_name[0] = '\0';
00641 break;
00642 }
00643 if (!generate) break;
00644 if (!FileExists(_full_screenshot_name)) break;
00645
00646 snprintf(&_screenshot_name[len], lengthof(_screenshot_name) - len, "#%u.%s", serial, ext);
00647 }
00648
00649 return _full_screenshot_name;
00650 }
00651
00653 static bool MakeSmallScreenshot()
00654 {
00655 const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format;
00656 return sf->proc(MakeScreenshotName(sf->extension), CurrentScreenCallback, NULL, _screen.width, _screen.height, BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(), _cur_palette);
00657 }
00658
00660 static bool MakeZoomedInScreenshot()
00661 {
00662 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
00663 ViewPort vp;
00664
00665 vp.zoom = ZOOM_LVL_WORLD_SCREENSHOT;
00666 vp.left = w->viewport->left;
00667 vp.top = w->viewport->top;
00668 vp.virtual_left = w->viewport->virtual_left;
00669 vp.virtual_top = w->viewport->virtual_top;
00670 vp.virtual_width = w->viewport->virtual_width;
00671 vp.width = vp.virtual_width;
00672 vp.virtual_height = w->viewport->virtual_height;
00673 vp.height = vp.virtual_height;
00674
00675 const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format;
00676 return sf->proc(MakeScreenshotName(sf->extension), LargeWorldCallback, &vp, vp.width, vp.height, BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(), _cur_palette);
00677 }
00678
00680 static bool MakeWorldScreenshot()
00681 {
00682 ViewPort vp;
00683 const ScreenshotFormat *sf;
00684
00685 vp.zoom = ZOOM_LVL_WORLD_SCREENSHOT;
00686 vp.left = 0;
00687 vp.top = 0;
00688 vp.virtual_left = -(int)MapMaxX() * TILE_PIXELS;
00689 vp.virtual_top = 0;
00690 vp.virtual_width = (MapMaxX() + MapMaxY()) * TILE_PIXELS;
00691 vp.width = vp.virtual_width;
00692 vp.virtual_height = (MapMaxX() + MapMaxY()) * TILE_PIXELS >> 1;
00693 vp.height = vp.virtual_height;
00694
00695 sf = _screenshot_formats + _cur_screenshot_format;
00696 return sf->proc(MakeScreenshotName(sf->extension), LargeWorldCallback, &vp, vp.width, vp.height, BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(), _cur_palette);
00697 }
00698
00705 bool MakeScreenshot(ScreenshotType t, const char *name)
00706 {
00707 if (t == SC_VIEWPORT) {
00708
00709
00710
00711
00712 UndrawMouseCursor();
00713 DrawDirtyBlocks();
00714 }
00715
00716 _screenshot_name[0] = '\0';
00717 if (name != NULL) strecpy(_screenshot_name, name, lastof(_screenshot_name));
00718
00719 bool ret;
00720 switch (t) {
00721 case SC_VIEWPORT:
00722 case SC_RAW:
00723 ret = MakeSmallScreenshot();
00724 break;
00725
00726 case SC_ZOOMEDIN:
00727 ret = MakeZoomedInScreenshot();
00728 break;
00729
00730 case SC_WORLD:
00731 ret = MakeWorldScreenshot();
00732 break;
00733
00734 default:
00735 NOT_REACHED();
00736 }
00737
00738 if (ret) {
00739 SetDParamStr(0, _screenshot_name);
00740 ShowErrorMessage(STR_MESSAGE_SCREENSHOT_SUCCESSFULLY, INVALID_STRING_ID, WL_WARNING);
00741 } else {
00742 ShowErrorMessage(STR_ERROR_SCREENSHOT_FAILED, INVALID_STRING_ID, WL_ERROR);
00743 }
00744
00745 return ret;
00746 }