Ta odpowiedź jest podsumowana w
sekcji Ładowanie dużych map bitowych Efficiently, która wyjaśnia, jak używać inSampleSize do ładowania skalowanej w dół wersji mapy bitowej.
W szczególności mapy bitowe ze skalowaniem wstępnym wyjaśniają szczegóły różnych metod, jak je łączyć i które są najbardziej wydajne w pamięci.
Istnieją trzy dominujące sposoby zmiany rozmiaru mapy bitowej w systemie Android, które mają różne właściwości pamięci:
createScaledBitmap API
Ten interfejs API weźmie istniejącą bitmapę i utworzy NOWĄ bitmapę z dokładnie wybranymi wymiarami.
Z drugiej strony możesz uzyskać dokładnie taki rozmiar obrazu, jakiego szukasz (niezależnie od tego, jak wygląda). Jednak wadą jest to, że ten interfejs API wymaga istniejącej mapy bitowej, aby działać . Oznacza to, że obraz musiałby zostać załadowany, zdekodowany i utworzona mapa bitowa, zanim będzie można utworzyć nową, mniejszą wersję. Jest to idealne rozwiązanie, jeśli chodzi o uzyskanie dokładnych wymiarów, ale okropne ze względu na dodatkowe obciążenie pamięci. W związku z tym jest to przełom w przypadku większości twórców aplikacji, którzy mają tendencję do dbania o pamięć
Flaga inSampleSize
BitmapFactory.Options
ma właściwość oznaczoną jako, inSampleSize
która zmieni rozmiar obrazu podczas dekodowania, aby uniknąć potrzeby dekodowania do tymczasowej mapy bitowej. Użyta tutaj wartość całkowita załaduje obraz w rozmiarze zmniejszonym o 1 / x. Na przykład ustawienie inSampleSize
2 zwraca obraz o połowę mniejszy, a ustawienie 4 zwraca obraz o 1/4 rozmiaru. Zasadniczo rozmiary obrazu zawsze będą o potęgę dwa mniejsze niż rozmiar źródła.
Z punktu widzenia pamięci używanie inSampleSize
jest naprawdę szybką operacją. W efekcie dekoduje tylko każdy X piksel obrazu do wynikowej mapy bitowej. Są jednak dwa główne problemy inSampleSize
:
Nie podaje dokładnych rozdzielczości . Zmniejsza tylko rozmiar mapy bitowej o potęgę 2.
Nie powoduje zmiany rozmiaru w najlepszej jakości . Większość filtrów zmieniających rozmiar tworzy dobrze wyglądające obrazy, odczytując bloki pikseli, a następnie ważąc je w celu uzyskania danego piksela o zmienionym rozmiarze. inSampleSize
unika tego wszystkiego, po prostu czytając co kilka pikseli. Rezultat jest dość wydajny i ma mało pamięci, ale cierpi na tym jakość.
Jeśli masz do czynienia tylko z pomniejszeniem obrazu o pewien rozmiar pow2, a filtrowanie nie stanowi problemu, nie możesz znaleźć metody bardziej wydajnej pamięci (lub wydajności) niż inSampleSize
.
Flagi inScaled, inDensity, inTargetDensity
Jeśli chcesz przeskalować obraz do wymiaru, który nie jest równy potęgi dwóch, będziesz potrzebować flag inScaled
, inDensity
i . Gdy flaga została ustawiona, system wyprowadzi wartość skalowania, która ma być zastosowana do twojej mapy bitowej, dzieląc przez wartości.inTargetDensity
BitmapOptions
inScaled
inTargetDensity
inDensity
mBitmapOptions.inScaled = true;
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity = dstWidth;
// will load & resize the image to be 1/inSampleSize dimensions
mCurrentBitmap = BitmapFactory.decodeResources(getResources(),
mImageIDs, mBitmapOptions);
Użycie tej metody spowoduje zmianę rozmiaru obrazu, a także zastosowanie do niego „filtra zmiany rozmiaru”, to znaczy wynik końcowy będzie wyglądał lepiej, ponieważ podczas zmiany rozmiaru uwzględniono dodatkowe obliczenia. Ale uwaga: ten dodatkowy krok filtra wymaga dodatkowego czasu przetwarzania i może szybko zsumować się w przypadku dużych obrazów, powodując powolne zmiany rozmiaru i dodatkowe alokacje pamięci dla samego filtra.
Generalnie nie jest dobrym pomysłem stosowanie tej techniki do obrazu, który jest znacznie większy niż pożądany rozmiar, z powodu dodatkowego narzutu filtrowania.
Magiczna kombinacja
Z punktu widzenia pamięci i wydajności można połączyć te opcje, aby uzyskać najlepsze wyniki. (ustawienie inSampleSize
, inScaled
, inDensity
a inTargetDensity
flagi)
inSampleSize
zostanie najpierw zastosowany do obrazu, zwiększając go do następnej potęgi dwa WIĘKSZEJ niż docelowy rozmiar. Następnie inDensity
& inTargetDensity
służą do skalowania wyniku do dokładnych wymiarów, które chcesz, stosując operację filtrowania w celu oczyszczenia obrazu.
Połączenie tych dwóch jest znacznie szybszą operacją, ponieważ inSampleSize
krok ten zmniejszy liczbę pikseli, które wynikowy krok oparty na gęstości będzie musiał zastosować filtr zmiany rozmiaru.
mBitmapOptions.inScaled = true;
mBitmapOptions.inSampleSize = 4;
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity = dstWidth * mBitmapOptions.inSampleSize;
// will load & resize the image to be 1/inSampleSize dimensions
mCurrentBitmap = BitmapFactory.decodeFile(fileName, mBitmapOptions);
Jeśli potrzebujesz dopasować obraz do określonych wymiarów i trochę ładniejszego filtrowania, ta technika jest najlepszym pomostem do uzyskania odpowiedniego rozmiaru, ale jest wykonywana w szybkiej operacji o małej ilości pamięci.
Pobieranie wymiarów obrazu
Uzyskiwanie rozmiaru obrazu bez dekodowania całego obrazu Aby zmienić rozmiar mapy bitowej, musisz znać przychodzące wymiary. Możesz użyć inJustDecodeBounds
flagi, aby uzyskać wymiary obrazu, bez konieczności dekodowania danych pikseli.
// Decode just the boundaries
mBitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileName, mBitmapOptions);
srcWidth = mBitmapOptions.outWidth;
srcHeight = mBitmapOptions.outHeight;
//now go resize the image to the size you want
Możesz użyć tej flagi, aby najpierw zdekodować rozmiar, a następnie obliczyć odpowiednie wartości skalowania do rozdzielczości docelowej.