Bitmap and canvas android

Полный список

— создаем и меняем Bitmap
— разбираемся с density и mutable

В прошлом уроке мы научились читать Bitmap из файла. Сейчас рассмотрим способы его создания. Для этого есть несколько статических методов createBitmap. Все эти методы создания Bitmap можно разделить на три группы:

1) создание на основе другого Bitmap
2) создание из массива цветов
3) создание пустого Bitmap

Рассмотрим эти группы подробно.

Создание на основе другого Bitmap

Эти методы позволяют нам взять готовый Bitmap и скопировать его целиком или частично в новый bitmap. При этом поддерживаются преобразования с помощью матрицы.

В этой группе 4 метода.

параметры:
source – Bitmap-источник, от которого будем брать его часть
x,y – координаты точки, от которой будем отсчитывать часть
width, height – высота и ширина части
m – матрица для применения преобразований к взятой части
filter – если true, то будет включено сглаживание при scale- и rotate-преобразованиях матрицы (но это приведет к некоторой потере в производительности)

Этот метод возьмет от Bitmap-источника часть указанную координатами и размерами, применит к ней матрицу и фильтр и вернет нам как новый Bitmap. (Хотя, вовсе не обязательно это будет новый объект. Об этом подробнее в конце урока.)

Аналогичен методу 1, но без использования матрицы и фильтра. Т.е. просто возьмет указанную часть от Bitmap-источника.

Вызывает метод 2 с параметрами createBitmap(src, 0, 0, src.getWidth(), src.getHeight()). Т.е. указанная часть равна всему Bitmap.

В итоге мы получим тот же Bitmap, что и источник.

Вызывает метод 2 с параметрами createBitmap(src, 0, 0, src.getWidth(),src.getHeight(), m, filter). Т.е. указанная часть равна всему Bitmap. Но дополнительно применяется матрица m, где рассчитаны преобразования, чтобы новый Bitmap получился размерами dstWidth на dstHeight. Также, можно включить сглаживающий фильтр.

В итоге мы получим тот же Bitmap, что и источник, но он будет нужных нам размеров, заданных параметрами dstWidth и dstHeight.

Рассмотрим первый метод этой группы на примере.

Project name: P1581_BitmapCreate
Build Target: Android 4.4
Application name: BitmapCreate
Package name: ru.startandroid.develop.p1581bitmapcreate
Create Activity: MainActivity

В конструкторе DrawView создаем bitmapSource из стандартной иконки ic_launcher. Настраиваем матрицу на поворот и изменение размера. И создаем bitmap на основе bitmapSource. Берем кусок начиная с точки (0,0) размерами в половину ширины и половину высоты. Т.е. получается левая верхняя четверть изображения. Система возьмет эту часть, применит к ней матрицу и фильтр, и выдаст нам, как новый Bitmap.

В onDraw отображаем полученный Bitmap.

А так будет выглядеть полученный Bitmap если фильтр выключить

Создание из массива цветов

Эти методы позволяют нам создать Bitmap из готового массива цветов.

В этой группе 4 метода.

параметры:
display – объект DisplayMetrics, из которого Bitmap возьмет значение densityDpi (зачем он нужен, рассмотрим чуть позже)
colors – массив цветов, т.е. фактически массив пикселов из которых будет состоять созданный Bitmap
offset – отступ от начала colors при чтении его значений
stride – шаг, который будет использован для перемещения по массиву при смене строки Bitmap
width, height – размеры создаваемого Bitmap
config – конфигурация (используемый способ хранения данных о пикселах, мы их рассмотрели подробно на прошлом уроке)

Все параметры в целом понятны. Объясню немного подробнее про stride, насколько мне удалось его понять. Рассмотрим пример, где параметр offset будем считать равным 0, ширину bitmap = 100, stride = 150.

Система создает Bitmap и заполняет его цветами из массива построчно. Но элементы массива она берет не все подряд, а высчитывает индекс первого элемента для каждой новой строки по формуле:
индекс первого элемента для каждой строки = (номер строки – 1) * stride

Т.е для первой строки – индекс первого элемента будет (1-1)*150 = 0. И начиная от него будут взяты цвета для первой строки, т.е. элементы 30, всего 100 элементов, т.к. ширина = 100). Для второй строки индекс первого элемента будет (2-1)*150 = 150. И для второй строки буду взяты цвета 240. И т.д.

В итоге мы получаем Bitmap для которого сами указали значения цветов всех его пикселов.

Вызывает метод 1 с параметрами createBitmap(display, colors, 0, width, width, height, config). Т.е. offset = 0, а stride = ширине Bitmap (цвета для строк будут браться из массива последовательно, без пропусков при переходе на новую строку).

Читайте также:  Калибровка акселерометра android что это такое

Аналогичен методу 1, но без использования display.

Аналогичен методу 2, но без использования display.

Рассмотрим второй метод этой группы на примере. Перепишем класс DrawView:

В конструкторе DrawView создаем массив цветов количеством 300 * 300 = 90 000 элементов. Число выбрано такое, т.к. картинку мы будем создавать с шириной и высотой 300. Соответственно и цветов (читай пикселов) нам надо будет 300 * 300.

Заполняем первую треть массива полупрозрачным красным цветом, вторую – зеленым, третью – синим.

Создаем bitmap, используя массив цветов и указав: ширину 300, высоту 300, конфигурацию — RGB_565.

Аналогично создаем bitmapAlpha, но конфигурацию укажем ARGB_8888.

В onDraw выводим оба Bitmap-а на канву.

Bitmap-ы получились в целом одинаковые, но первый проигнорил прозрачность красного цвета. Это произошло из-за того, что мы указали ему конфиг RGB_565. Он не поддерживает прозрачность и, при создании Bitmap, аlpha-компонент был отброшен. Сохранилась информация только о цвете. Зато этот Bitmap в два раза меньше весит в памяти.

Создание чистого Bitmap

Эти методы позволяют создать чистый Bitmap без каких-либо данных.

В этой группе 2 метода.

параметры:
display – объект DisplayMetrics, из которого Bitmap возьмет значение densityDpi (зачем он нужен, рассмотрим чуть позже)
width, height – размеры создаваемого Bitmap
config – конфигурация (используемый способ хранения данных о пикселах, мы их рассмотрели подробно на прошлом уроке)

Создается чистый Bitmap с указанными характеристиками.

Аналогичен методу 1, но без использования display

Рассмотрим второй метод этой группы на примере. Перепишем класс DrawView:

В конструкторе DrawView создаем чистый Bitmap размером 100х100.

Далее попробуем сами в нем что-нить нарисовать.

Методом setPixel ставим в нем три пиксела (20,20), (70,50) и (30,80) в красный цвет.

Методом setPixels можем менять пикселы не по одному, а массивом. Этот метод аналогичен методам создания Bitmap из массива цветов. Первые три параметра – это массив, offset и stride. Затем идут координаты точки, с которой начнем закраску (40,40), затем размер закрашиваемой области (10,15).

Как вы помните, канва – это всего лишь оболочка для Bitmap. Соответственно и наш Bitmap мы можем обернуть в канву и что-нить на нем с помощью этой канвы нарисовать. В нашем примере оборачиваем свежесозданный Bitmap в канву и рисуем на нем синий круг.

В onDraw выводим результат на экран.

Видим все фигуры, которые мы выводили.

Также, Bitmap имеет методы:

getPixel – получить значение цвета определенного пиксела

getPixels – получить значения цветов набора пикселов

getGenerationId – получить generationId, который меняется каждый раз, когда меняется Bitmap. Т.е. с его помощью можно отслеживать, что Bitmap был изменен.

Density

У Bitmap есть метод setDensity, туда мы можем передать density, к которому должна относиться эта картинка. Зачем это нужно я не очень понимаю, т.к. у нас один экран и density всегда, вроде как, один и тот же. Но штука интересная, рассмотрим на примере как оно работает и на что влияет.

В манифест для Activity добавьте строку:

В конструкторе DrawView создаем три Bitmap размерами 100х100. В bitmapIcon читаем иконку ic_launcher (она размером 48х48 при mdpi). Далее с помощью канвы рисуем иконку на все три Bitmap-а, но с небольшими нюансами.

В первом случае просто рисуем.

Во втором случае рисуем, а затем для bitmap2 ставим density в xhigh.

В третьем случае для bitmap3 ставим density в xhigh, а затем рисуем иконку.

В onDraw выводим все три Bitmap-а и для наглядности рисуем рамки размером 100х100 там же.

У меня на эмуляторе экран с density = mdpi. Если у вас density другое, то и результат будет другим.

Разбираемся, почему так получилось.

С первым случаем все понятно. Нарисовали иконку 48х48 и вывели на канву Bitmap размером 100х100.

Во втором случае мы нарисовали иконку на Bitmap, затем поменяли у него density на xhdpi, а затем уже вывели его на канву. Умная канва при этом спросила у Bitmap каков его density (xhdpi) и сравнила со своим (mdpi). Увидев, что Bitmap имеет density в два раза больший (xhdpi = 2 * mdpi) чем нужно, канва просто уменьшила в два раза стороны Bitmap (т.е. он стал 50х50, это видно на скрине) при выводе и тем самым компенсировала разницу в density.

В третьем случае мы сначала поменяли density на xhdpi, затем обернули его в канву (чтобы отличать от основной канвы, которая в onDraw, назовем ее bm3-канва). Эта bm3-канва автоматически подхватила density = xhdpi от bitmap3. Затем мы рисуем на bitmap3 с помощью bm3-канвы иконку. При этом bm3-канва определила, что иконка имеет density = mdpi (mdpi, т.к. это density устройства и оно всем ставится по умолчанию). В итоге получается, что density иконки в два раза меньше, чем density канвы. И канва, чтобы выровнять density просто увеличивает размер иконки при выводе. Т.к. иконка становится размером 96х96, и занимает почти весь Bitmap, который 100х100. Далее bitmap3 выводится на канву и здесь повторяются все те же рассуждения, что были во втором случае и bitmap в итоге выводится уменьшенным в два раза.

Читайте также:  Андроид смартфон с мощным процессором

Основная мысль тут такова: канва сравнивает density – свой и у Bitmap, который она собирается рисовать. И если они различны, то выполняется подгонка размеров.

Если убрать из манифеста опцию hardwareAccelerated, то основная канва перестанет подгонять размер Bitmap. Почему так происходит, я не знаю.

Mutable

Свойство mutable означает, что Bitmap может быть изменен (через обертку канвой, методом setPixel, и т.п.). Соответственно immutable означает, что Bitmap не может быть изменен, т.е. он readOnly.

Из рассмотренных нами трех групп методов, третья группа возвращает mutable Bitmap, вторая – immutable Bitmap. А первая группа зависит от параметров и Bitmap-источника:
— если источник immutable и новый Bitmap будет являться точной копией исходника, то мы получим на выходе просто Bitmap-исходник. Т.е. это будет тот же Bitmap объект, и даже не его копия. И он останется immutable.
— если же источник mutable или новый Bitmap чем-то отличен от исходника, то мы получаем новый mutable Bitmap объект

Для определения mutable состояния у Bitmap используется метод isMutable.

На следующем уроке:

— разбираемся с BitmapFactory.Options
— сохраняем Bitmap в файл

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

Android Media Basics: Images, Audio, and Video

This chapter is from the book

This chapter is from the book

This chapter is from the book 

What You’ll Learn in This Hour

  • Examining the ImageView control
  • Using bitmaps and canvas
  • Playing audio
  • Playing video
  • Checking out more media options

Examining the ImageView Control

Hour 6 showed how to use a simple ImageView. In that case, you saw how to display a drawable resource in the ImageView. An ImageView can display any drawable image. The source of the image can be a resource, a drawable, or a bitmap.

The source code for basic ImageView examples are in the accompanying Hour21ImageView project.

The four projects that contain the source code for this hour are the following:

  • Hour21ImageView
  • Hour21LargeImage
  • Hour21VideoView
  • Hour21Audio

Displaying an Image

Four methods are available for setting an image in an ImageView. They differ by how the image passed is defined. The image can be a bitmap, drawable, URI, or resource id. The methods are as follows:

  • setImageDrawable(): Set a drawable as the content of the ImageView.
  • setImageBitmap(): Set a bitmap as the content of the ImageView.
  • setImageResource(): Use a resource id to set the content of the ImageView.
  • setImageUri(): Use a URI to set the content of the ImageView.

To set an ImageView to an image resource defined by R.Drawable.mainImage, you would use the following:

In this hour, you take a closer look at using bitmaps with ImageViews.

In Hour 3, you used a ShapeDrawable that had been defined as a resource. If you were working with a ShapeDrawable in code, you would use the setImageDrawable() method.

To populate a Drawable object from a resource, use the getResources.getDrawable() method:

You’ll populate an ImageView using a resource id to explore some the available features in an ImageView.

Using ScaleTypes in ImageView

ImageViews include a ScaleType property. The ScaleType defines how an image displays within the ImageView. Using ScaleType, you can have an image fill the entire ImageView, be centered in the ImageView, or be cropped and centered in the ImageView.

The options for ScaleType are defined in ImageView.ScaleType. For example, ImageView.ScaleType.CENTER refers to a scale type in which the image is centered in the ImageView.

All scale types except for FIT_XY and FIT_MATRIX maintain the aspect ratio.

The complete set of ScaleTypes includes the following:

  • ImageView.ScaleType.CENTER: Center the image with no scaling. The image dimensions are unchanged.
  • ImageView.ScaleType.CENTER_CROP: Scales the image and keeps the aspect ratio until either the width of height of the image is the same as the width or height of the ImageView. For a small image, this will have the effect of enlarging the entire image. For a large image, this will have the effect of showing the center of the image.
  • ImageView.ScaleType.CENTER_INSIDE: Scale the image and maintain aspect ratio. The width and height of the image fit within the ImageView.
  • ImageView.ScaleType.FIT_CENTER: Fit the image in the center of the ImageView.
  • ImageView.ScaleType.FIT_START: Fit the image in the left and top edge of the ImageView.
  • ImageView.ScaleType.FIT_END: Fit the image in the right and bottom edge of the ImageView.
  • ImageView.ScaleType.FIT_XY: Fit into the length and width of the ImageView. Does not maintain the aspect ratio.
  • ImageView.ScaleType.MATRIX: Scale using a matrix. This allows for more complex translations of the image and is described later this hour.
Читайте также:  Gcc toolchain on android

The app created in the Hour21ImageView project illustrates the effects of setting different values for ScaleType. The layout file for this app includes an ImageView that fills the layout and a set of radio buttons for setting the ScaleType. MainActivity.java is set up in a simple way. With the ImageView and RadioButtons defined, when a button is clicked, the ScaleType value is set in the ImageView. Listing 21.1 shows the onCheckChangeListener() method for the RadioButton named centerCrop. If this RadioButton is checked, the ScaleType for ImageView is updated.

Listing 21.1 Changing ScaleType Programatically

The Hour21Image app uses the image shown in Figure 21.1 as the image for the ImageView. The image is 900 pixels wide and 200 pixels high.

Figure 21.1 Base image for showing ScaleType (scaletest.png)

By using this simple image with four circles of different colors, you can easily see the effect of the changing ScaleType.

Figure 21.2 shows the base image using the ScaleTypes CENTER, CENTER_CROP, and CENTER_INSIDE. Using CENTER shows the image in actual size. Because the size of the image is larger than the ImageView, the green and blue circles in the center display. CENTER_CROP shows half of the green and half of the blue circles. The height of the image fills the ImageView. CENTER_INSIDE shows the entire image centered in the ImageView.

Figure 21.2 ScaleTypes CENTER, CENTER_CROP, and CENTER_INSIDE

Figure 21.3 shows the base image using the ScaleTypes FIT_CENTER, FIT_START, FIT_END, and FIT_XY. The aspect ratio is maintained in the first three options, but when you use FIT_XY, the image fills the ImageView and “stretches” the image to fit.

Rotating an Image with Matrix

In graphics programming, a matrix is used to transform an image. Simple transformations include scaling, translating, or rotating an image. Android includes a Matrix class (android.graphics.Matrix) to support these graphic transformations.

You might have noticed the Rotate button in the earlier images. As a simple example of using a Matrix, the Hour21ImageView app implements a Button that rotates the image in the ImageView by 30 degrees.

Listing 21.2 shows the OnClickListener() method for the rotate Button. On line 3, the Matrix associated with the ImageView is obtained. On line 4, you use the setScaleType() method to set the ScaleType to MATRIX. You must set ImageView.ScaleType.MATRIX in order to use a modified matrix on the ImageView. Line 5 is a matrix instruction to rotate the image 30 degrees around the point that is the center of the ImageView. Line 6 shows the matrix set for the ImageView. You must set ImageView ScaleType to MATRIX for this to take effect.

Figure 21.4 shows the rotated image.

Listing 21.2 Rotating an Image

You can create complex transformations using Matrix. This app is a simple introduction to using a Matrix and the MATRIX ScaleType.

NOTE: Rotation in Honeycomb

Since API Level 11 (Honeycomb), the View class includes support for rotation. The methods setPivotX(), setPivotY(), and setRotation() are supported. The Matrix class supports more complex transformations.

Setting Alpha

Alpha level indicates the opacity of an image. An image can be completely transparent, completely opaque, or somewhere in the middle. You can set the alpha level on an ImageView using the setAlpha() method or, since API level 11, by using the setImageAlpha() method. These methods take an integer parameter. A parameter of 0 indicates complete transparency and 255 indicates complete opacity.

Источник

Оцените статью