diff --git a/headers/os/translation/TranslatorFormats.h b/headers/os/translation/TranslatorFormats.h index 744c0c6a8a..f8957bc728 100644 --- a/headers/os/translation/TranslatorFormats.h +++ b/headers/os/translation/TranslatorFormats.h @@ -48,6 +48,7 @@ enum { B_TGA_FORMAT = 'TGA ', B_BMP_FORMAT = 'BMP ', B_TIFF_FORMAT = 'TIFF', + B_WEBP_FORMAT = 'WebP', // Picture formats B_DXF_FORMAT = 'DXF ', diff --git a/src/build/libbe/storage/NodeInfo.cpp b/src/build/libbe/storage/NodeInfo.cpp index 2fd37b4448..de087cd558 100644 --- a/src/build/libbe/storage/NodeInfo.cpp +++ b/src/build/libbe/storage/NodeInfo.cpp @@ -766,19 +766,3 @@ BNodeInfo::operator=(const BNodeInfo &nodeInfo) BNodeInfo::BNodeInfo(const BNodeInfo &) { } - - -// #pragma mark - - -namespace BPrivate { - -extern bool -CheckNodeIconHintPrivate(const BNode *node, bool whatever) -{ - // I've no idea what this is supposed to do exactly, but - // it seems to tell Tracker if there is an icon for the - // node. See kits/tracker/Model.cpp for details - return true; -} - -} // namespace BPrivate diff --git a/src/kits/tracker/Attributes.h b/src/kits/tracker/Attributes.h index de1f88f881..08bbe96dcf 100644 --- a/src/kits/tracker/Attributes.h +++ b/src/kits/tracker/Attributes.h @@ -67,6 +67,9 @@ namespace BPrivate { #define kAttrMiniIcon "BEOS:M:STD_ICON" #define kAttrIcon "BEOS:ICON" +#define kAttrThumbnail "Media:Thumbnail" +#define kAttrThumbnailCreationTime "Media:Thumbnail:CreationTime" + #define kAttrDisksFrame "_trk/d_windframe" #define kAttrDisksWorkspace "_trk/d_windwkspc" @@ -114,6 +117,7 @@ namespace BPrivate { #define kAttrExtendedDisksPoseInfo_le "_trk/xt_d_pinfo_le" #if B_HOST_IS_LENDIAN + #define kEndianSuffix "_le" #define kForeignEndianSuffix "" @@ -149,6 +153,7 @@ namespace BPrivate { #define kAttrExtendedDisksPoseInfoForegin kAttrExtendedDisksPoseInfo_be #else + #define kEndianSuffix "" #define kForeignEndianSuffix "_le" diff --git a/src/kits/tracker/Commands.h b/src/kits/tracker/Commands.h index 664ee1d04a..64f52764af 100644 --- a/src/kits/tracker/Commands.h +++ b/src/kits/tracker/Commands.h @@ -114,6 +114,9 @@ const uint32 kSwitchToHome = 'Tswh'; const uint32 kTestIconCache = 'TicC'; +// thumbnail generator thread sends this message to be_app when done +const uint32 kUpdateThumbnail = 'TUTb'; + // Observers and Notifiers: // Settings-changed messages: @@ -131,6 +134,7 @@ const uint32 kTransparentSelectionChanged = 'Trse'; const uint32 kSortFolderNamesFirstChanged = 'Sfnf'; const uint32 kHideDotFilesChanged = 'Hdfc'; const uint32 kTypeAheadFilteringChanged = 'Tafc'; +const uint32 kGenerateImageThumbnailsChanged = 'GITc'; const uint32 kDesktopFilePanelRootChanged = 'Dfpr'; const uint32 kFavoriteCountChanged = 'Fvct'; diff --git a/src/kits/tracker/ContainerWindow.cpp b/src/kits/tracker/ContainerWindow.cpp index 62c2fd9cc8..4847663b9f 100644 --- a/src/kits/tracker/ContainerWindow.cpp +++ b/src/kits/tracker/ContainerWindow.cpp @@ -1043,6 +1043,7 @@ BContainerWindow::Init(const BMessage* message) SetFlags(Flags() & ~B_NO_WORKSPACE_ACTIVATION); } + void BContainerWindow::InitLayout() { @@ -1081,6 +1082,7 @@ BContainerWindow::InitLayout() } } + void BContainerWindow::RestoreState() { diff --git a/src/kits/tracker/FSUtils.cpp b/src/kits/tracker/FSUtils.cpp index 8cfc7a8a9b..1568c79720 100644 --- a/src/kits/tracker/FSUtils.cpp +++ b/src/kits/tracker/FSUtils.cpp @@ -2360,7 +2360,7 @@ FSMakeOriginalName(char* name, BDirectory* destDir, const char* suffix) { char root[B_FILE_NAME_LENGTH]; char copybase[B_FILE_NAME_LENGTH]; - char temp_name[B_FILE_NAME_LENGTH + 10]; + char tempName[B_FILE_NAME_LENGTH + 11]; int32 fnum; // is this name already original? @@ -2414,31 +2414,34 @@ FSMakeOriginalName(char* name, BDirectory* destDir, const char* suffix) name[B_FILE_NAME_LENGTH - 8] = '\0'; } - strcpy(root, name); // save root name - strcat(name, suffix); + strlcpy(root, name, sizeof(root)); + // save root name + strlcat(name, suffix, sizeof(name)); } - strcpy(copybase, name); + strlcpy(copybase, name, sizeof(copybase)); // if name already exists then add a number fnum = 1; - strcpy(temp_name, name); - while (destDir->Contains(temp_name)) { - snprintf(temp_name, sizeof(temp_name), "%s %" B_PRId32, copybase, ++fnum); + strlcpy(tempName, name, sizeof(tempName)); + while (destDir->Contains(tempName)) { + snprintf(tempName, sizeof(tempName), "%s %" B_PRId32, copybase, + ++fnum); - if (strlen(temp_name) > (B_FILE_NAME_LENGTH - 1)) { + if (strlen(tempName) > (B_FILE_NAME_LENGTH - 1)) { // The name has grown too long. Maybe we just went from // " copy 9" to " copy 10" and that extra // character was too much. The solution is to further // truncate the 'root' name and continue. // ??? should we reset fnum or not ??? root[strlen(root) - 1] = '\0'; - snprintf(temp_name, sizeof(temp_name), "%s%s %" B_PRId32, root, suffix, fnum); + snprintf(tempName, sizeof(tempName), "%s%s %" B_PRId32, root, + suffix, fnum); } } - ASSERT((strlen(temp_name) <= (B_FILE_NAME_LENGTH - 1))); - strcpy(name, temp_name); + ASSERT((strlen(tempName) <= (B_FILE_NAME_LENGTH - 1))); + strlcpy(name, tempName, sizeof(name)); } @@ -3132,8 +3135,7 @@ FSCreateNewFolderIn(const node_ref* dirNode, entry_ref* newRef, int32 fnum = 1; while (dir.Contains(name)) { // if base name already exists then add a number - // ToDo: - // move this logic ot FSMakeOriginalName + // TODO: move this logic to FSMakeOriginalName if (++fnum > 9) { snprintf(name, sizeof(name), B_TRANSLATE("New folder%ld"), fnum); @@ -3165,6 +3167,7 @@ FSCreateNewFolderIn(const node_ref* dirNode, entry_ref* newRef, B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); alert->Go(); + return result; } @@ -3279,6 +3282,7 @@ FSGetParentVirtualDirectoryAware(const BEntry& entry, BNode& _node) status_t error = FSGetParentVirtualDirectoryAware(entry, ref); if (error == B_OK) error = _node.SetTo(&ref); + return error; } @@ -3410,6 +3414,7 @@ _TrackerLaunchAppWithDocuments(const entry_ref* appRef, const BMessage* refs, } } + extern "C" char** environ; diff --git a/src/kits/tracker/IconCache.cpp b/src/kits/tracker/IconCache.cpp index 8d9c7778ac..e49c4feba2 100644 --- a/src/kits/tracker/IconCache.cpp +++ b/src/kits/tracker/IconCache.cpp @@ -773,20 +773,20 @@ IconCache::GetNodeIcon(ModelNodeLazyOpener* modelOpener, if (entry == NULL || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) { modelOpener->OpenNode(); - BFile* file = NULL; + PRINT_DISK_HITS(("File %s; Line %d # hitting disk for node %s\n", + __FILE__, __LINE__, model->Name())); // if we are dealing with an application, use the BAppFileInfo // superset of node; this makes GetIcon grab the proper icon for // an app - if (model->IsExecutable()) - file = dynamic_cast(model->Node()); - PRINT_DISK_HITS(("File %s; Line %d # hitting disk for node %s\n", - __FILE__, __LINE__, model->Name())); - - status_t result = file != NULL - ? GetAppIconFromAttr(file, lazyBitmap->Get(), size) - : GetFileIconFromAttr(model->Node(), lazyBitmap->Get(), size); + BFile* file = NULL; + status_t result = B_ERROR; + if (model->IsExecutable() + && (file = dynamic_cast(model->Node())) != NULL) { + result = GetAppIconFromAttr(file, lazyBitmap->Get(), size); + } else + result = GetFileIconFromAttr(model, lazyBitmap->Get(), size); if (result == B_OK) { // node has its own icon, use it @@ -801,6 +801,10 @@ IconCache::GetNodeIcon(ModelNodeLazyOpener* modelOpener, entry->SetIcon(lazyBitmap->Adopt(), mode, size); } source = kNode; + } else if (result == B_BUSY) { + // still waiting for thumbnail icon to be generated, + // provide a hint to come back here for it + source = kNode; } } diff --git a/src/kits/tracker/Model.cpp b/src/kits/tracker/Model.cpp index ac96602860..e9226e547d 100644 --- a/src/kits/tracker/Model.cpp +++ b/src/kits/tracker/Model.cpp @@ -82,25 +82,6 @@ BObjectList* readOnlyOpenModelList = NULL; #endif -static bool -CheckNodeIconHint(BNode* node) -{ - if (node == NULL) - return false; - - attr_info info; - if (node->GetAttrInfo(kAttrIcon, &info) == B_OK - // has a vector icon, or - || (node->GetAttrInfo(kAttrMiniIcon, &info) == B_OK - && node->GetAttrInfo(kAttrLargeIcon, &info) == B_OK)) { - // has a mini _and_ large icon - return true; - } - - return false; -} - - // #pragma mark - Model() @@ -611,14 +592,14 @@ Model::CacheLocalizedName() void Model::FinishSettingUpType() { - char mimeString[B_MIME_TYPE_LENGTH]; + char type[B_MIME_TYPE_LENGTH]; BEntry entry; // While we are reading the node, do a little snooping to see if it even // makes sense to look for a node-based icon. This serves as a hint to the // icon cache, allowing it to not hit the disk again for models that do not // have an icon defined by the node. - if (IsNodeOpen() && fBaseType != kLinkNode && !CheckNodeIconHint(fNode)) + if (CheckNodeIconHint()) fIconFrom = kUnknownNotFromNode; if (fBaseType != kDirectoryNode @@ -628,22 +609,22 @@ Model::FinishSettingUpType() BNodeInfo info(fNode); // check if a specific mime type is set - if (info.GetType(mimeString) == B_OK) { + if (info.GetType(type) == B_OK) { // node has a specific mime type - fMimeType = mimeString; - if (strcmp(mimeString, B_QUERY_MIMETYPE) == 0) + fMimeType = type; + if (strcmp(type, B_QUERY_MIMETYPE) == 0) fBaseType = kQueryNode; - else if (strcmp(mimeString, B_QUERY_TEMPLATE_MIMETYPE) == 0) + else if (strcmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0) fBaseType = kQueryTemplateNode; - else if (strcmp(mimeString, kVirtualDirectoryMimeType) == 0) + else if (strcmp(type, kVirtualDirectoryMimeType) == 0) fBaseType = kVirtualDirectoryNode; - if (info.GetPreferredApp(mimeString) == B_OK) { + if (info.GetPreferredApp(type) == B_OK) { if (fPreferredAppName) DeletePreferredAppVolumeNameLinkTo(); - if (mimeString[0]) - fPreferredAppName = strdup(mimeString); + if (*type != '0') + fPreferredAppName = strdup(type); } } } @@ -662,8 +643,8 @@ Model::FinishSettingUpType() // should use a shared string here if (IsNodeOpen()) { BNodeInfo info(fNode); - if (info.GetType(mimeString) == B_OK) - fMimeType = mimeString; + if (info.GetType(type) == B_OK) + fMimeType = type; if (fIconFrom == kUnknownNotFromNode && WellKnowEntryList::Match(NodeRef()) @@ -738,6 +719,29 @@ Model::FinishSettingUpType() } +bool +Model::CheckNodeIconHint() const +{ + return (fBaseType == kDirectoryNode || fBaseType == kVolumeNode + || fBaseType == kTrashNode || fBaseType == kDesktopNode) + || (fBaseType == kExecutableNode && !CheckAppIconHint()); +} + + +bool +Model::CheckAppIconHint() const +{ + attr_info info; + return fNode != NULL + // node is open, and it + && (fNode->GetAttrInfo(kAttrIcon, &info) == B_OK + // has a vector icon, or + || (fNode->GetAttrInfo(kAttrMiniIcon, &info) == B_OK + && fNode->GetAttrInfo(kAttrLargeIcon, &info) == B_OK)); + // has a mini _and_ large icon +} + + void Model::ResetIconFrom() { @@ -746,10 +750,7 @@ Model::ResetIconFrom() if (InitCheck() != B_OK) return; - // mirror the logic from FinishSettingUpType - if ((fBaseType == kDirectoryNode || fBaseType == kVolumeNode - || fBaseType == kTrashNode || fBaseType == kDesktopNode) - && !CheckNodeIconHint(fNode)) { + if (CheckNodeIconHint()) { BDirectory* directory = dynamic_cast(fNode); if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1) { fIconFrom = kTrackerSupplied; @@ -888,16 +889,16 @@ Model::AttrChanged(const char* attrName) if (attrName == NULL || strcmp(attrName, kAttrMIMEType) == 0 || strcmp(attrName, kAttrPreferredApp) == 0) { - char mimeString[B_MIME_TYPE_LENGTH]; + char type[B_MIME_TYPE_LENGTH]; BNodeInfo info(fNode); - if (info.GetType(mimeString) != B_OK) + if (info.GetType(type) != B_OK) fMimeType = ""; else { // node has a specific mime type - fMimeType = mimeString; + fMimeType = type; if (!IsVolume() && !IsSymLink() - && info.GetPreferredApp(mimeString) == B_OK) { - SetPreferredAppSignature(mimeString); + && info.GetPreferredApp(type) == B_OK) { + SetPreferredAppSignature(type); } } @@ -1258,6 +1259,7 @@ Model::GetLongVersionString(BString &result, version_kind kind) return B_OK; } + status_t Model::GetVersionString(BString &result, version_kind kind) { diff --git a/src/kits/tracker/Model.h b/src/kits/tracker/Model.h index 56a2196e39..fe0f48ca4a 100644 --- a/src/kits/tracker/Model.h +++ b/src/kits/tracker/Model.h @@ -212,6 +212,8 @@ private: status_t OpenNodeCommon(bool writable); void SetupBaseType(); void FinishSettingUpType(); + bool CheckNodeIconHint() const; + bool CheckAppIconHint() const; void DeletePreferredAppVolumeNameLinkTo(); void CacheLocalizedName(); diff --git a/src/kits/tracker/SettingsViews.cpp b/src/kits/tracker/SettingsViews.cpp index 18be42255f..5ff1b9b83e 100644 --- a/src/kits/tracker/SettingsViews.cpp +++ b/src/kits/tracker/SettingsViews.cpp @@ -420,6 +420,7 @@ WindowsSettingsView::WindowsSettingsView() fSortFolderNamesFirstCheckBox(NULL), fHideDotFilesCheckBox(NULL), fTypeAheadFilteringCheckBox(NULL), + fGenerateImageThumbnailsCheckBox(NULL), fShowFullPathInTitleBar(false), fSingleWindowBrowse(false), fShowNavigator(false), @@ -456,6 +457,10 @@ WindowsSettingsView::WindowsSettingsView() B_TRANSLATE("Enable type-ahead filtering"), new BMessage(kTypeAheadFilteringChanged)); + fGenerateImageThumbnailsCheckBox = new BCheckBox("", + B_TRANSLATE("Generate image thumbnails"), + new BMessage(kGenerateImageThumbnailsChanged)); + const float spacing = be_control_look->DefaultItemSpacing(); BLayoutBuilder::Group<>(this, B_VERTICAL, 0) @@ -472,6 +477,7 @@ WindowsSettingsView::WindowsSettingsView() .Add(fSortFolderNamesFirstCheckBox) .Add(fHideDotFilesCheckBox) .Add(fTypeAheadFilteringCheckBox) + .Add(fGenerateImageThumbnailsCheckBox) .End() .AddGlue() .SetInsets(spacing); @@ -488,6 +494,7 @@ WindowsSettingsView::AttachedToWindow() fSortFolderNamesFirstCheckBox->SetTarget(this); fHideDotFilesCheckBox->SetTarget(this); fTypeAheadFilteringCheckBox->SetTarget(this); + fGenerateImageThumbnailsCheckBox->SetTarget(this); } @@ -582,6 +589,17 @@ WindowsSettingsView::MessageReceived(BMessage* message) break; } + case kGenerateImageThumbnailsChanged: + { + settings.SetGenerateImageThumbnails( + fGenerateImageThumbnailsCheckBox->Value() == 1); + send_bool_notices(kGenerateImageThumbnailsChanged, + "GenerateImageThumbnails", + fGenerateImageThumbnailsCheckBox->Value() == 1); + Window()->PostMessage(kSettingsContentsModified); + break; + } + default: _inherited::MessageReceived(message); break; @@ -637,6 +655,12 @@ WindowsSettingsView::SetDefaults() "TypeAheadFiltering", true); } + if (settings.GenerateImageThumbnails()) { + settings.SetGenerateImageThumbnails(false); + send_bool_notices(kGenerateImageThumbnailsChanged, + "GenerateImageThumbnails", true); + } + ShowCurrentSettings(); } @@ -651,7 +675,8 @@ WindowsSettingsView::IsDefaultable() const || settings.ShowNavigator() != false || settings.TransparentSelection() != true || settings.SortFolderNamesFirst() != true - || settings.TypeAheadFiltering() != false; + || settings.TypeAheadFiltering() != false + || settings.GenerateImageThumbnails() != false; } @@ -703,6 +728,12 @@ WindowsSettingsView::Revert() "TypeAheadFiltering", fTypeAheadFiltering); } + if (settings.GenerateImageThumbnails() != fGenerateImageThumbnails) { + settings.SetGenerateImageThumbnails(fGenerateImageThumbnails); + send_bool_notices(kGenerateImageThumbnailsChanged, + "GenerateImageThumbnails", fGenerateImageThumbnails); + } + ShowCurrentSettings(); } @@ -722,6 +753,8 @@ WindowsSettingsView::ShowCurrentSettings() fSortFolderNamesFirstCheckBox->SetValue(settings.SortFolderNamesFirst()); fHideDotFilesCheckBox->SetValue(settings.HideDotFiles()); fTypeAheadFilteringCheckBox->SetValue(settings.TypeAheadFiltering()); + fGenerateImageThumbnailsCheckBox->SetValue( + settings.GenerateImageThumbnails()); } @@ -737,6 +770,7 @@ WindowsSettingsView::RecordRevertSettings() fSortFolderNamesFirst = settings.SortFolderNamesFirst(); fHideDotFiles = settings.HideDotFiles(); fTypeAheadFiltering = settings.TypeAheadFiltering(); + fGenerateImageThumbnails = settings.GenerateImageThumbnails(); } @@ -751,7 +785,8 @@ WindowsSettingsView::IsRevertable() const || fTransparentSelection != settings.TransparentSelection() || fSortFolderNamesFirst != settings.SortFolderNamesFirst() || fHideDotFiles != settings.HideDotFiles() - || fTypeAheadFiltering != settings.TypeAheadFiltering(); + || fTypeAheadFiltering != settings.TypeAheadFiltering() + || fGenerateImageThumbnails != settings.GenerateImageThumbnails(); } diff --git a/src/kits/tracker/SettingsViews.h b/src/kits/tracker/SettingsViews.h index 68a4c36f7f..c242a56463 100644 --- a/src/kits/tracker/SettingsViews.h +++ b/src/kits/tracker/SettingsViews.h @@ -123,6 +123,7 @@ private: BCheckBox* fSortFolderNamesFirstCheckBox; BCheckBox* fHideDotFilesCheckBox; BCheckBox* fTypeAheadFilteringCheckBox; + BCheckBox* fGenerateImageThumbnailsCheckBox; bool fShowFullPathInTitleBar; bool fSingleWindowBrowse; @@ -131,6 +132,7 @@ private: bool fSortFolderNamesFirst; bool fHideDotFiles; bool fTypeAheadFiltering; + bool fGenerateImageThumbnails; typedef SettingsView _inherited; }; diff --git a/src/kits/tracker/Tracker.cpp b/src/kits/tracker/Tracker.cpp index 93390c3186..eb4b4855b7 100644 --- a/src/kits/tracker/Tracker.cpp +++ b/src/kits/tracker/Tracker.cpp @@ -617,6 +617,45 @@ TTracker::MessageReceived(BMessage* message) break; } + case kUpdateThumbnail: + { + // message passed from generator thread + // update icon on passed-in node_ref + dev_t device; + ino_t inode; + // call this inode so we can call the node_ref node + if (message->FindInt32("device", (int32*)&device) == B_OK + && message->FindUInt64("node", (uint64*)&inode) == B_OK) { + const node_ref node = node_ref(device, inode); + + // cycle through open windows to find the node's pose + // TODO find a faster way + AutoLock lock(&fWindowList); + int32 count = fWindowList.CountItems(); + for (int32 index = 0; index < count; index++) { + BContainerWindow* window = dynamic_cast( + fWindowList.ItemAt(index)); + if (window == NULL) + continue; + + AutoLock windowLock(window); + if (!windowLock.IsLocked()) + continue; + + BPoseView* poseView = window->PoseView(); + if (poseView == NULL) + continue; + + BPose* pose = poseView->FindPose(&node); + if (pose != NULL) { + poseView->UpdateIcon(pose); + break; // updated pose icon, exit loop + } + } + } + break; + } + default: _inherited::MessageReceived(message); break; diff --git a/src/kits/tracker/TrackerSettings.cpp b/src/kits/tracker/TrackerSettings.cpp index e956df7d73..7cff3616ae 100644 --- a/src/kits/tracker/TrackerSettings.cpp +++ b/src/kits/tracker/TrackerSettings.cpp @@ -72,6 +72,7 @@ private: BooleanValueSetting* fSortFolderNamesFirst; BooleanValueSetting* fHideDotFiles; BooleanValueSetting* fTypeAheadFiltering; + BooleanValueSetting* fGenerateImageThumbnails; ScalarValueSetting* fRecentApplicationsCount; ScalarValueSetting* fRecentDocumentsCount; @@ -136,6 +137,7 @@ TTrackerState::TTrackerState() fSortFolderNamesFirst(NULL), fHideDotFiles(NULL), fTypeAheadFiltering(NULL), + fGenerateImageThumbnails(NULL), fRecentApplicationsCount(NULL), fRecentDocumentsCount(NULL), fRecentFoldersCount(NULL), @@ -167,6 +169,7 @@ TTrackerState::TTrackerState(const TTrackerState&) fSortFolderNamesFirst(NULL), fHideDotFiles(NULL), fTypeAheadFiltering(NULL), + fGenerateImageThumbnails(NULL), fRecentApplicationsCount(NULL), fRecentDocumentsCount(NULL), fRecentFoldersCount(NULL), @@ -228,6 +231,8 @@ TTrackerState::LoadSettingsIfNeeded() Add(fHideDotFiles = new BooleanValueSetting("HideDotFiles", false)); Add(fTypeAheadFiltering = new BooleanValueSetting("TypeAheadFiltering", false)); + Add(fGenerateImageThumbnails + = new BooleanValueSetting("GenerateImageThumbnails", false)); Add(fSingleWindowBrowse = new BooleanValueSetting("SingleWindowBrowse", false)); Add(fShowNavigator = new BooleanValueSetting("ShowNavigator", false)); @@ -465,6 +470,20 @@ TrackerSettings::SetTypeAheadFiltering(bool enabled) } +bool +TrackerSettings::GenerateImageThumbnails() +{ + return gTrackerState.fGenerateImageThumbnails->Value(); +} + + +void +TrackerSettings::SetGenerateImageThumbnails(bool enabled) +{ + gTrackerState.fGenerateImageThumbnails->SetValue(enabled); +} + + bool TrackerSettings::ShowSelectionWhenInactive() { diff --git a/src/kits/tracker/TrackerSettings.h b/src/kits/tracker/TrackerSettings.h index 2b401b68f6..ada407afa2 100644 --- a/src/kits/tracker/TrackerSettings.h +++ b/src/kits/tracker/TrackerSettings.h @@ -94,6 +94,8 @@ public: void SetHideDotFiles(bool hide); bool TypeAheadFiltering(); void SetTypeAheadFiltering(bool enabled); + bool GenerateImageThumbnails(); + void SetGenerateImageThumbnails(bool enabled); bool ShowSelectionWhenInactive(); void SetShowSelectionWhenInactive(bool); diff --git a/src/kits/tracker/Utilities.cpp b/src/kits/tracker/Utilities.cpp index 7df1e80c09..7268509d0c 100644 --- a/src/kits/tracker/Utilities.cpp +++ b/src/kits/tracker/Utilities.cpp @@ -49,22 +49,37 @@ All rights reserved. #include #include #include +#include +#include +#include #include #include #include #include #include +#include +#include +#include +#include +#include #include #include #include #include "Attributes.h" +#include "Commands.h" #include "ContainerWindow.h" +#include "FSUtils.h" #include "MimeTypes.h" #include "Model.h" #include "PoseView.h" +#ifdef B_XXL_ICON +# undef B_XXL_ICON +#endif +#define B_XXL_ICON 128 + #ifndef _IMPEXP_BE # define _IMPEXP_BE #endif @@ -421,7 +436,7 @@ OffscreenBitmap::NewBitmap(BRect bounds) { delete fBitmap; fBitmap = new(std::nothrow) BBitmap(bounds, B_RGB32, true); - if (fBitmap && fBitmap->Lock()) { + if (fBitmap != NULL && fBitmap->Lock()) { BView* view = new BView(fBitmap->Bounds(), "", B_FOLLOW_NONE, 0); fBitmap->AddChild(view); @@ -555,7 +570,7 @@ FadeRGBA32Vertical(uint32* bits, int32 width, int32 height, int32 from, DraggableIcon::DraggableIcon(BRect rect, const char* name, - const char* mimeType, icon_size which, const BMessage* message, + const char* type, icon_size which, const BMessage* message, BMessenger target, uint32 resizingMode, uint32 flags) : BView(rect, name, resizingMode, flags), @@ -563,11 +578,11 @@ DraggableIcon::DraggableIcon(BRect rect, const char* name, fTarget(target) { fBitmap = new BBitmap(Bounds(), kDefaultIconDepth); - BMimeType mime(mimeType); + BMimeType mime(type); status_t result = mime.GetIcon(fBitmap, which); ASSERT(mime.IsValid()); if (result != B_OK) { - PRINT(("failed to get icon for %s, %s\n", mimeType, strerror(result))); + PRINT(("failed to get icon for %s, %s\n", type, strerror(result))); BMimeType mime(B_FILE_MIMETYPE); ASSERT(mime.IsInstalled()); mime.GetIcon(fBitmap, which); @@ -1485,13 +1500,399 @@ GetAppIconFromAttr(BFile* file, BBitmap* icon, icon_size which) status_t -GetFileIconFromAttr(BNode* node, BBitmap* icon, icon_size which) +GetFileIconFromAttr(Model* model, BBitmap* icon, icon_size which) { - BNodeInfo fileInfo(node); - return fileInfo.GetIcon(icon, which); + // bad input value + if (model == NULL || icon == NULL) + return B_BAD_VALUE; + + // unitialized model + status_t result = model->InitCheck(); + if (result != B_OK) + return result; + + // unitialized icon + result = icon->InitCheck(); + if (result != B_OK) + return result; + + // node not open + BNode* node = model->Node(); + if (node == NULL) + return B_BAD_VALUE; + + // look for a thumbnail in an attribute + time_t modtime; + bigtime_t created; + if (node->GetModificationTime(&modtime) == B_OK + && node->ReadAttr(kAttrThumbnailCreationTime, B_TIME_TYPE, 0, + &created, sizeof(bigtime_t)) == sizeof(bigtime_t)) { + if (created > (bigtime_t)modtime) { + // file has not changed, try to return an existing thumbnail + attr_info attrInfo; + if (node->GetAttrInfo(kAttrThumbnail, &attrInfo) == B_OK) { + uint8 webpData[attrInfo.size]; + if (node->ReadAttr(kAttrThumbnail, attrInfo.type, 0, + webpData, attrInfo.size) == attrInfo.size) { + BMemoryIO stream((const void*)webpData, attrInfo.size); + BBitmap thumb(BTranslationUtils::GetBitmap(&stream)); + + // convert thumb to icon size + if (which == B_XXL_ICON) { + // import icon data from attribute without resizing + result = icon->ImportBits(&thumb); + } else { + // down-scale thumb to icon size + // TODO don't make a copy, allow icon to accept views + BBitmap tmp = BBitmap(icon->Bounds(), + icon->ColorSpace(), true); + BView view(tmp.Bounds(), "", B_FOLLOW_NONE, + B_WILL_DRAW); + tmp.AddChild(&view); + if (view.LockLooper()) { + // fill with transparent + view.SetLowColor(B_TRANSPARENT_COLOR); + view.FillRect(view.Bounds(), B_SOLID_LOW); + // draw bitmap + view.SetDrawingMode(B_OP_ALPHA); + view.SetBlendingMode(B_PIXEL_ALPHA, + B_ALPHA_COMPOSITE); + view.DrawBitmap(&thumb, thumb.Bounds(), + tmp.Bounds(), B_FILTER_BITMAP_BILINEAR); + view.Sync(); + view.UnlockLooper(); + } + tmp.RemoveChild(&view); + + // copy tmp bitmap into icon + result = icon->ImportBits(&tmp); + } + // we found a thumbnail + if (result == B_OK) + return result; + } + } + // else we did not find a thumbnail + } else { + // file changed, remove all thumb attrs + char attrName[B_ATTR_NAME_LENGTH]; + while (node->GetNextAttrName(attrName) == B_OK) { + if (BString(attrName).StartsWith(kAttrThumbnail)) + node->RemoveAttr(attrName); + } + } + } + + // check generate thumbnail setting, + // mime type must be an image type + if (TrackerSettings().GenerateImageThumbnails() + && BString(model->MimeType()).IStartsWith("image")) { + // try to fetch a new thumbnail icon + result = GetThumbnailIcon(model, icon, which); + if (result == B_OK) { + // icon ready + return B_OK; + } else if (result == B_BUSY) { + // working on icon, come back later + return B_BUSY; + } + } + + // get icon from the node info + BNodeInfo nodeInfo(node); + return nodeInfo.GetIcon(icon, which); } +// #pragma mark - image thumbnails + + +struct ThumbGenParams { + ThumbGenParams(Model* _model, BFile* _file, icon_size _which, + color_space _colorSpace, port_id _port); + virtual ~ThumbGenParams(); + + status_t InitCheck() { return fInitStatus; }; + + Model* model; + BFile* file; + icon_size which; + color_space colorSpace; + port_id port; + +private: + status_t fInitStatus; +}; + + +ThumbGenParams::ThumbGenParams(Model* _model, BFile* _file, icon_size _which, + color_space _colorSpace, port_id _port) +{ + model = new(std::nothrow) Model(*_model); + file = new(std::nothrow) BFile(*_file); + which = _which; + colorSpace = _colorSpace; + port = _port; + + fInitStatus = (model == NULL || file == NULL ? B_NO_MEMORY : B_OK); +} + + +ThumbGenParams::~ThumbGenParams() +{ + delete file; + delete model; +} + + +status_t get_thumbnail(void* castToParams); +static const int32 kMsgIconData = 'ICON'; + + +BRect +ThumbBounds(BBitmap* icon, float aspectRatio) +{ + BRect thumbBounds; + + if (aspectRatio > 1) { + // wide + thumbBounds = BRect(0, 0, icon->Bounds().IntegerWidth() - 1, + floorf((icon->Bounds().IntegerHeight() - 1) / aspectRatio)); + thumbBounds.OffsetBySelf(0, floorf((icon->Bounds().IntegerHeight() + - thumbBounds.IntegerHeight()) / 2.0f)); + } else if (aspectRatio < 1) { + // tall + thumbBounds = BRect(0, 0, floorf((icon->Bounds().IntegerWidth() - 1) + * aspectRatio), icon->Bounds().IntegerHeight() - 1); + thumbBounds.OffsetBySelf(floorf((icon->Bounds().IntegerWidth() + - thumbBounds.IntegerWidth()) / 2.0f), 0); + } else { + // square + thumbBounds = icon->Bounds(); + } + + return thumbBounds; +} + + +status_t +GetThumbnailIcon(Model* model, BBitmap* icon, icon_size which) +{ + status_t result = B_ERROR; + + // create a name for the node icon generator thread (32 chars max) + icon_size w = (icon_size)B_XXL_ICON; + dev_t d = model->NodeRef()->device; + ino_t n = model->NodeRef()->node; + BString genThreadName = BString("_thumbgen_w") + << w << "_d" << d << "_n" << n << "_"; + + bool volumeReadOnly = true; + BVolume volume(model->NodeRef()->device); + if (volume.InitCheck() == B_OK) + volumeReadOnly = volume.IsReadOnly() || !volume.KnowsAttr(); + + port_id port = B_NAME_NOT_FOUND; + if (volumeReadOnly) { + // look for a port with some icon data + port = find_port(genThreadName.String()); + // give the port the same name as the generator thread + if (port != B_NAME_NOT_FOUND && port_count(port) > 0) { + // a generator thread has written some data to the port, fetch it + uint8 iconData[icon->BitsLength()]; + int32 msgCode; + int32 bytesRead = read_port(port, &msgCode, iconData, + icon->BitsLength()); + if (bytesRead == icon->BitsLength() && msgCode == kMsgIconData + && iconData != NULL) { + // fill icon data into the passed in icon + result = icon->ImportBits(iconData, icon->BitsLength(), + icon->BytesPerRow(), 0, icon->ColorSpace()); + } + + if (result == B_OK) { + // make a new port next time + delete_port(port); + port = B_NAME_NOT_FOUND; + } + } + } + + // we found an icon from a generator thread + if (result == B_OK) + return B_OK; + + // look for an existing generator thread before spawning a new one + if (find_thread(genThreadName.String()) == B_NAME_NOT_FOUND) { + // no generater thread found, spawn one + BFile* file = dynamic_cast(model->Node()); + if (file == NULL) + result = B_NOT_SUPPORTED; // node must be a file + else { + // create a new port if one doesn't already exist + if (volumeReadOnly && port == B_NAME_NOT_FOUND) + port = create_port(1, genThreadName.String()); + + ThumbGenParams* params = new ThumbGenParams(model, file, which, + icon->ColorSpace(), port); + if (params->InitCheck() == B_OK) { + // generator thread will delete params, it makes copies + resume_thread(spawn_thread(get_thumbnail, + genThreadName.String(), B_LOW_PRIORITY, params)); + result = B_BUSY; // try again later + } else + delete params; + } + } + + return result; +} + + +// #pragma mark - thumbnail generator thread + + +status_t +get_thumbnail(void* castToParams) +{ + ThumbGenParams* params = (ThumbGenParams*)castToParams; + Model* model = params->model; + BFile* file = params->file; + icon_size which = params->which; + color_space colorSpace = params->colorSpace; + port_id port = params->port; + + // get the mime type from the model + const char* type = model->MimeType(); + + // check if attributes can be written to + bool volumeReadOnly = true; + BVolume volume(model->NodeRef()->device); + if (volume.InitCheck() == B_OK) + volumeReadOnly = volume.IsReadOnly() || !volume.KnowsAttr(); + + // see if we have a thumbnail attribute + attr_info attrInfo; + status_t result = file->GetAttrInfo(kAttrThumbnail, &attrInfo); + if (result != B_OK) { + // create a new thumbnail + + // check to see if we have a translator that works + BBitmapStream imageStream; + BBitmap* image; + if (BTranslatorRoster::Default()->Translate(file, NULL, NULL, + &imageStream, B_TRANSLATOR_BITMAP, 0, type) == B_OK + && imageStream.DetachBitmap(&image) == B_OK) { + // we have translated the image file into a BBitmap + + // check if we can write attrs + if (!volumeReadOnly) { + // write image width to an attribute + int32 width = image->Bounds().IntegerWidth(); + file->WriteAttr("Media:Width", B_INT32_TYPE, 0, &width, + sizeof(int32)); + + // write image height to an attribute + int32 height = image->Bounds().IntegerHeight(); + file->WriteAttr("Media:Height", B_INT32_TYPE, 0, &height, + sizeof(int32)); + + // convert image into a 128x128 WebP image and stash it + BBitmap thumb = BBitmap(BRect(0, 0, B_XXL_ICON - 1, + B_XXL_ICON - 1), colorSpace, true); + BView view(thumb.Bounds(), "", B_FOLLOW_NONE, + B_WILL_DRAW); + thumb.AddChild(&view); + if (view.LockLooper()) { + // fill with transparent + view.SetLowColor(B_TRANSPARENT_COLOR); + view.FillRect(view.Bounds(), B_SOLID_LOW); + // draw bitmap + view.SetDrawingMode(B_OP_ALPHA); + view.SetBlendingMode(B_PIXEL_ALPHA, + B_ALPHA_COMPOSITE); + view.DrawBitmap(image, image->Bounds(), + ThumbBounds(&thumb, image->Bounds().Width() + / image->Bounds().Height()), + B_FILTER_BITMAP_BILINEAR); + view.Sync(); + view.UnlockLooper(); + } + thumb.RemoveChild(&view); + + BBitmap* thumbPointer = &thumb; + BBitmapStream thumbStream(thumbPointer); + BMallocIO stream; + if (BTranslatorRoster::Default()->Translate(&thumbStream, + NULL, NULL, &stream, B_WEBP_FORMAT) == B_OK + && thumbStream.DetachBitmap(&thumbPointer) == B_OK) { + // write WebP image data into an attribute + file->WriteAttr(kAttrThumbnail, B_RAW_TYPE, 0, + stream.Buffer(), stream.BufferLength()); + + // write thumbnail creation time into an attribute + bigtime_t created = system_time(); + file->WriteAttr(kAttrThumbnailCreationTime, B_TIME_TYPE, + 0, &created, sizeof(bigtime_t)); + + // we wrote thumbnail to an attribute + result = B_OK; + } + } else if (port != B_NAME_NOT_FOUND) { + // create a thumb at the requested icon size + BBitmap thumb = BBitmap(BRect(0, 0, which - 1, which - 1), + colorSpace, true); + // copy image into a view bitmap, scaled and centered + BView view(thumb.Bounds(), "", B_FOLLOW_NONE, B_WILL_DRAW); + thumb.AddChild(&view); + if (view.LockLooper()) { + // fill with transparent + view.SetLowColor(B_TRANSPARENT_COLOR); + view.FillRect(view.Bounds(), B_SOLID_LOW); + // draw bitmap + view.SetDrawingMode(B_OP_ALPHA); + view.SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); + view.DrawBitmap(image, image->Bounds(), + ThumbBounds(&thumb, image->Bounds().Width() + / image->Bounds().Height()), + B_FILTER_BITMAP_BILINEAR); + view.Sync(); + view.UnlockLooper(); + } + thumb.RemoveChild(&view); + + // send icon back to the calling thread through the port + result = write_port(port, kMsgIconData, (void*)thumb.Bits(), + thumb.BitsLength()); + } + } + + delete image; + } + + if (result == B_OK) { + // trigger an icon refresh + if (!volumeReadOnly) + model->Mimeset(true); // only works on read-write volumes + else { + // send Tracker a message to tell it to update the thumbnail + BMessage message(kUpdateThumbnail); + if (message.AddInt32("device", model->NodeRef()->device) == B_OK + && message.AddUInt64("node", model->NodeRef()->node) == B_OK) { + be_app->PostMessage(&message); + } + } + } + + delete params; + + return result; +} + + +// #pragma mark - PrintToStream + + void PrintToStream(rgb_color color) { @@ -1500,6 +1901,9 @@ PrintToStream(rgb_color color) } +// #pragma mark - EachMenuItem + + extern BMenuItem* EachMenuItem(BMenu* menu, bool recursive, BMenuItem* (*func)(BMenuItem *)) { diff --git a/src/kits/tracker/Utilities.h b/src/kits/tracker/Utilities.h index 687366abd7..48f9d4becf 100644 --- a/src/kits/tracker/Utilities.h +++ b/src/kits/tracker/Utilities.h @@ -67,6 +67,7 @@ namespace BPrivate { class Benaphore; class BPose; class BPoseView; +class Model; // global variables static const rgb_color kBlack = {0, 0, 0, 255}; @@ -481,7 +482,8 @@ void _ThrowOnAssert(bool, const char*, int32); // stub calls that work around BAppFile info inefficiency status_t GetAppSignatureFromAttr(BFile*, char*); status_t GetAppIconFromAttr(BFile* file, BBitmap* icon, icon_size which); -status_t GetFileIconFromAttr(BNode* node, BBitmap* icon, icon_size which); +status_t GetFileIconFromAttr(Model* model, BBitmap* icon, icon_size which); +status_t GetThumbnailIcon(Model* model, BBitmap* icon, icon_size which); // debugging void HexDump(const void* buffer, int32 length); diff --git a/src/libs/icon/IconUtils.cpp b/src/libs/icon/IconUtils.cpp index c37cff1fb0..b8a328943d 100644 --- a/src/libs/icon/IconUtils.cpp +++ b/src/libs/icon/IconUtils.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include "AutoDeleter.h" @@ -28,14 +30,11 @@ #include "MessageImporter.h" -#ifndef HAIKU_TARGET_PLATFORM_HAIKU -# define B_MINI_ICON_TYPE 'MICN' -# define B_LARGE_ICON_TYPE 'ICON' -#endif +#define B_MINI_ICON_TYPE 'MICN' +#define B_LARGE_ICON_TYPE 'ICON' _USING_ICON_NAMESPACE; -using std::nothrow; // #pragma mark - Scaling functions @@ -150,15 +149,15 @@ scale_down(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight // nearby pixels p1 = *((rgb_color*)srcBits + (l * srcWidth) + c); p2 = *((rgb_color*)srcBits + (l * srcWidth) + c + 1); - p3 = *((rgb_color*)srcBits + ((l + 1)* srcWidth) + c + 1); - p4 = *((rgb_color*)srcBits + ((l + 1)* srcWidth) + c); + p3 = *((rgb_color*)srcBits + ((l + 1) * srcWidth) + c + 1); + p4 = *((rgb_color*)srcBits + ((l + 1) * srcWidth) + c); // color components - out.blue = (uint8)(p1.blue * d1 + p2.blue * d2 + p3.blue * d3 + out.blue = (uint8)(p1.blue * d1 + p2.blue * d2 + p3.blue * d3 + p4.blue * d4); - out.green = (uint8)(p1.green * d1 + p2.green * d2 + p3.green * d3 + out.green = (uint8)(p1.green * d1 + p2.green * d2 + p3.green * d3 + p4.green * d4); - out.red = (uint8)(p1.red * d1 + p2.red * d2 + p3.red * d3 + out.red = (uint8)(p1.red * d1 + p2.red * d2 + p3.red * d3 + p4.red * d4); out.alpha = (uint8)(p1.alpha * d1 + p2.alpha * d2 + p3.alpha * d3 + p4.alpha * d4); @@ -285,8 +284,8 @@ scale4x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight, int32 srcBPR, int32 dstBPR) { // scale4x is just scale2x twice - BBitmap* tmp = new BBitmap(BRect(0, 0, srcWidth * 2 - 1, - srcHeight * 2 - 1), B_RGBA32); + BRect rect = BRect(0, 0, srcWidth * 2 - 1, srcHeight * 2 - 1); + BBitmap* tmp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, B_RGBA32); uint8* tmpBits = (uint8*)tmp->Bits(); int32 tmpBPR = tmp->BytesPerRow(); @@ -326,7 +325,7 @@ BIconUtils::GetIcon(BNode* node, const char* vectorIconAttrName, // (converting to B_RGBA32 is handled) // override size - if (icon->Bounds().IntegerWidth() + 1 >= 32) + if (icon->Bounds().IntegerWidth() + 1 >= B_LARGE_ICON) which = B_LARGE_ICON; else which = B_MINI_ICON; @@ -342,12 +341,8 @@ BIconUtils::GetIcon(BNode* node, const char* vectorIconAttrName, which, icon); if (result != B_OK) { // try to fallback to vector icon -#ifdef HAIKU_TARGET_PLATFORM_HAIKU BBitmap temp(icon->Bounds(), B_BITMAP_NO_SERVER_LINK, B_RGBA32); -#else - BBitmap temp(icon->Bounds(), B_RGBA32); -#endif result = temp.InitCheck(); if (result != B_OK) break; @@ -453,7 +448,7 @@ BIconUtils::GetVectorIcon(const uint8* buffer, size_t size, BBitmap* icon) ObjectDeleter deleter; if (icon->ColorSpace() != B_RGBA32 && icon->ColorSpace() != B_RGB32) { - temp = new (nothrow) BBitmap(icon->Bounds(), + temp = new(std::nothrow) BBitmap(icon->Bounds(), B_BITMAP_NO_SERVER_LINK, B_RGBA32); deleter.SetTo(temp); if (temp == NULL || temp->InitCheck() != B_OK) @@ -544,16 +539,16 @@ BIconUtils::GetCMAP8Icon(BNode* node, const char* smallIconAttrName, switch (which) { case B_MINI_ICON: attribute = smallIconAttrName; - bounds.Set(0, 0, 15, 15); + bounds.Set(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); attrType = B_MINI_ICON_TYPE; - attrSize = 16 * 16; + attrSize = B_MINI_ICON * B_MINI_ICON; break; case B_LARGE_ICON: attribute = largeIconAttrName; - bounds.Set(0, 0, 31, 31); + bounds.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1); attrType = B_LARGE_ICON_TYPE; - attrSize = 32 * 32; + attrSize = B_LARGE_ICON * B_LARGE_ICON; break; default: @@ -586,11 +581,13 @@ BIconUtils::GetCMAP8Icon(BNode* node, const char* smallIconAttrName, ssize_t bytesRead; if (useBuffer) { // other color space or bitmap size than stored in attribute - buffer = new(nothrow) uint8[attrSize]; + buffer = new(std::nothrow) uint8[attrSize]; if (buffer == NULL) - result = B_NO_MEMORY; - else - bytesRead = node->ReadAttr(attribute, attrType, 0, buffer, attrSize); + bytesRead = result = B_NO_MEMORY; + else { + bytesRead = node->ReadAttr(attribute, attrType, 0, buffer, + attrSize); + } } else { bytesRead = node->ReadAttr(attribute, attrType, 0, icon->Bits(), attrSize); @@ -695,8 +692,12 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height, || (dstWidth == 2 * width && dstHeight == 2 * height) || (dstWidth == 3 * width && dstHeight == 3 * height) || (dstWidth == 4 * width && dstHeight == 4 * height)) { - BBitmap* converted = new BBitmap(BRect(0, 0, width - 1, height - 1), - icon->ColorSpace()); + BRect rect = BRect(0, 0, width - 1, height - 1); + BBitmap* converted = new(std::nothrow) BBitmap(rect, + B_BITMAP_NO_SERVER_LINK, icon->ColorSpace()); + if (converted == NULL) + return B_NO_MEMORY; + converted->ImportBits(src, height * srcBPR, srcBPR, 0, B_CMAP8); uint8* convertedBits = (uint8*)converted->Bits(); int32 convertedBPR = converted->BytesPerRow(); @@ -746,8 +747,12 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height, if (dstWidth > width && dstHeight > height && dstWidth < 2 * width && dstHeight < 2 * height) { // scale2x then downscale - BBitmap* temp = new BBitmap(BRect(0, 0, width * 2 - 1, height * 2 - 1), - icon->ColorSpace()); + BRect rect = BRect(0, 0, width * 2 - 1, height * 2 - 1); + BBitmap* temp = new(std::nothrow) BBitmap(rect, + B_BITMAP_NO_SERVER_LINK, icon->ColorSpace()); + if (temp == NULL) + return B_NO_MEMORY; + uint8* tempBits = (uint8*)temp->Bits(); uint32 tempBPR = temp->BytesPerRow(); scale2x(dst, tempBits, width, height, dstBPR, tempBPR); @@ -756,8 +761,12 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height, } else if (dstWidth > 2 * width && dstHeight > 2 * height && dstWidth < 3 * width && dstHeight < 3 * height) { // scale3x then downscale - BBitmap* temp = new BBitmap(BRect(0, 0, width * 3 - 1, height * 3 - 1), + BRect rect = BRect(0, 0, width * 3 - 1, height * 3 - 1); + BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, icon->ColorSpace()); + if (temp == NULL) + return B_NO_MEMORY; + uint8* tempBits = (uint8*)temp->Bits(); uint32 tempBPR = temp->BytesPerRow(); scale3x(dst, tempBits, width, height, dstBPR, tempBPR); @@ -766,8 +775,12 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height, } else if (dstWidth > 3 * width && dstHeight > 3 * height && dstWidth < 4 * width && dstHeight < 4 * height) { // scale4x then downscale - BBitmap* temp = new BBitmap(BRect(0, 0, width * 4 - 1, height * 4 - 1), + BRect rect = BRect(0, 0, width * 4 - 1, height * 4 - 1); + BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, icon->ColorSpace()); + if (temp == NULL) + return B_NO_MEMORY; + uint8* tempBits = (uint8*)temp->Bits(); uint32 tempBPR = temp->BytesPerRow(); scale4x(dst, tempBits, width, height, dstBPR, tempBPR); @@ -775,8 +788,12 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height, delete temp; } else if (dstWidth > 4 * width && dstHeight > 4 * height) { // scale4x then bilinear - BBitmap* temp = new BBitmap(BRect(0, 0, width * 4 - 1, height * 4 - 1), + BRect rect = BRect(0, 0, width * 4 - 1, height * 4 - 1); + BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, icon->ColorSpace()); + if (temp == NULL) + return B_NO_MEMORY; + uint8* tempBits = (uint8*)temp->Bits(); uint32 tempBPR = temp->BytesPerRow(); scale4x(dst, tempBits, width, height, dstBPR, tempBPR);