Tracker: Add thumbnail support

Define thumbnail attributes in Attributes.h:
  Media:Thumbnail to store the thumbnail,
  Media:Thumbnail:CreationTime to see if thumbs need to be regenerated.

Store 128x128 thumbnail in attribute, for icon sizes smaller than
128x128 down-scale the 128x128 thumbnail. Use B_FILTER_BITMAP_BILINEAR
to down-scale the image using the bilinear scaling algorithm which
creates nicer looking thumbnails than the default scaling algorithm.

Store thumbnails as WebP images which compress smaller than PNGs and
fit in the inode better at 128x128.

Check the file's modification time in GetFileIconFromAttr() and compare
it to the thumbnail creation time. If the file has not been modified
since the last time we generated thumbnails return the thumbnail from
the attribute, otherwise fetch a new thumbnail with GetThumbnailIcon().

Add "Generate image thumbnails" Tracker setting. Default is turned off
for now. To generate image thumbnails you must first turn this setting
on in Tracker Windows preferences.

Spawn a get_thumbnail() thread to generate thumbnails and retrieve them
later on from the window thread to fill out into the icon. This should
improve responsiveness of generating thumbnails from a folder with a
lot of images. The generator thread will write the thumbnail data to an
attribute if on writable BFS volume.

If not on writable BFS volume, the generator thread will send the data
back to the original thread through a port by calling write_port().

When the thread is finished creating the thumbnail it sends a message
back to the Tracker application thread to update the pose which
instructs the window thread to look for an thumbnail. It either finds a
thumbnail in an attribute, or picks up the thumbnail data that has been
sent through write_port() using read_port().

This works on both read-write and read-only BFS volumes but it still
depends on the presence of a BEOS:TYPE parameter to have been written
to the volume before it became read-only. Thumbnail generation does not
work on other read-only volumes for example an ISO-9660 CD, but it does
work on read-only BFS volumes for example the BeOS R5 CD.

Move BPrivate::CheckNodeIconHintPrivate() from BNodeInfo to Tracker
Model CheckNodeIconHint(). Create Model::CheckAppIconHint() and look
for a vector icon or mini and large icon in that method. Check that
the base type is directory, volume, trash, desktop, or if executable
call CheckAppIconHint().

Add 1 to temp_name to fix the following warning:
src/kits/tracker/FSUtils.cpp:2437:12: note: 'snprintf' output 3 or more
bytes (assuming 267) into a destination of size 266

Rename temp_name to tempName following our style guidelines. Use
strlcpy() and strlcat() instead of strcpy() to safely copy the string.
This fixes thumbnail generation on 64-bit Haiku.

Change-Id: I7f927a5a1f8cf65e4b1aa1e0eb55bbfae87fd969
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3163
Reviewed-by: John Scipione <jscipione@gmail.com>
Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
This commit is contained in:
John Scipione 2021-08-05 21:48:06 -04:00 committed by Adrien Destugues
parent 57a743987c
commit 7f8195344a
17 changed files with 650 additions and 121 deletions

View File

@ -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 ',

View File

@ -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

View File

@ -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"

View File

@ -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';

View File

@ -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()
{

View File

@ -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
// "<filename> copy 9" to "<filename> 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;

View File

@ -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<BFile*>(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<BFile*>(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;
}
}

View File

@ -82,25 +82,6 @@ BObjectList<Model>* 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<BDirectory*>(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)
{

View File

@ -212,6 +212,8 @@ private:
status_t OpenNodeCommon(bool writable);
void SetupBaseType();
void FinishSettingUpType();
bool CheckNodeIconHint() const;
bool CheckAppIconHint() const;
void DeletePreferredAppVolumeNameLinkTo();
void CacheLocalizedName();

View File

@ -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();
}

View File

@ -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;
};

View File

@ -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<WindowList> lock(&fWindowList);
int32 count = fWindowList.CountItems();
for (int32 index = 0; index < count; index++) {
BContainerWindow* window = dynamic_cast<BContainerWindow*>(
fWindowList.ItemAt(index));
if (window == NULL)
continue;
AutoLock<BWindow> 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;

View File

@ -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()
{

View File

@ -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);

View File

@ -49,22 +49,37 @@ All rights reserved.
#include <Font.h>
#include <IconUtils.h>
#include <MenuItem.h>
#include <Mime.h>
#include <Node.h>
#include <NodeInfo.h>
#include <OS.h>
#include <PopUpMenu.h>
#include <Region.h>
#include <StorageDefs.h>
#include <TextView.h>
#include <TranslatorFormats.h>
#include <TranslatorRoster.h>
#include <TranslationUtils.h>
#include <TypeConstants.h>
#include <View.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <Window.h>
#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<BFile*>(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 *))
{

View File

@ -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);

View File

@ -19,6 +19,8 @@
#include <Bitmap.h>
#include <Node.h>
#include <NodeInfo.h>
#include <String.h>
#include <TypeConstants.h>
#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<BBitmap> 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);