2020-01-18 03:38:21 -05:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2020-04-15 10:55:07 -04:00
# include <AK/Checked.h>
2020-03-08 07:05:14 -04:00
# include <AK/Memory.h>
2020-02-14 17:02:47 -05:00
# include <AK/SharedBuffer.h>
2020-02-14 19:03:37 -05:00
# include <AK/String.h>
2020-06-17 15:37:16 -04:00
# include <LibGfx/BMPLoader.h>
2020-06-22 15:35:22 -04:00
# include <LibGfx/Bitmap.h>
2020-04-25 17:05:28 -04:00
# include <LibGfx/GIFLoader.h>
2020-06-22 15:35:22 -04:00
# include <LibGfx/ICOLoader.h>
2020-06-22 03:03:56 -04:00
# include <LibGfx/JPGLoader.h>
2020-06-21 05:00:22 -04:00
# include <LibGfx/PBMLoader.h>
2020-06-22 08:19:57 -04:00
# include <LibGfx/PGMLoader.h>
2020-06-15 08:13:12 -04:00
# include <LibGfx/PNGLoader.h>
2020-06-22 04:58:04 -04:00
# include <LibGfx/PPMLoader.h>
2020-03-29 13:04:05 -04:00
# include <LibGfx/ShareableBitmap.h>
2019-06-07 05:46:55 -04:00
# include <fcntl.h>
2019-02-07 17:13:47 -05:00
# include <stdio.h>
2019-06-07 05:46:55 -04:00
# include <sys/mman.h>
2019-02-07 17:13:47 -05:00
2020-02-06 05:56:38 -05:00
namespace Gfx {
2020-09-06 17:59:20 -04:00
size_t Bitmap : : minimum_pitch ( size_t width , BitmapFormat format )
{
size_t element_size ;
switch ( determine_storage_format ( format ) ) {
case StorageFormat : : Indexed8 :
element_size = 1 ;
break ;
case StorageFormat : : RGB32 :
case StorageFormat : : RGBA32 :
element_size = 4 ;
break ;
default :
ASSERT_NOT_REACHED ( ) ;
}
return width * element_size ;
}
static bool size_would_overflow ( BitmapFormat format , const IntSize & size )
2019-02-11 03:47:10 -05:00
{
2020-04-15 05:57:24 -04:00
if ( size . width ( ) < 0 | | size . height ( ) < 0 )
return true ;
2020-08-30 08:18:54 -04:00
// This check is a bit arbitrary, but should protect us from most shenanigans:
if ( size . width ( ) > = 32768 | | size . height ( ) > = 32768 )
return true ;
2020-09-06 17:59:20 -04:00
// In contrast, this check is absolutely necessary:
size_t pitch = Bitmap : : minimum_pitch ( size . width ( ) , format ) ;
return Checked < size_t > : : multiplication_would_overflow ( pitch , size . height ( ) ) ;
2020-04-15 05:57:24 -04:00
}
2020-06-10 04:57:59 -04:00
RefPtr < Bitmap > Bitmap : : create ( BitmapFormat format , const IntSize & size )
2020-04-15 05:57:24 -04:00
{
if ( size_would_overflow ( format , size ) )
return nullptr ;
2020-02-06 05:56:38 -05:00
return adopt ( * new Bitmap ( format , size , Purgeable : : No ) ) ;
2019-02-11 03:47:10 -05:00
}
2020-06-10 04:57:59 -04:00
RefPtr < Bitmap > Bitmap : : create_purgeable ( BitmapFormat format , const IntSize & size )
2019-12-18 14:50:05 -05:00
{
2020-04-15 05:57:24 -04:00
if ( size_would_overflow ( format , size ) )
return nullptr ;
2020-02-06 05:56:38 -05:00
return adopt ( * new Bitmap ( format , size , Purgeable : : Yes ) ) ;
2019-12-18 14:50:05 -05:00
}
2020-06-10 04:57:59 -04:00
Bitmap : : Bitmap ( BitmapFormat format , const IntSize & size , Purgeable purgeable )
2019-02-11 03:47:10 -05:00
: m_size ( size )
2020-09-06 17:59:20 -04:00
, m_pitch ( minimum_pitch ( size . width ( ) , format ) )
2019-02-18 19:42:53 -05:00
, m_format ( format )
2019-12-18 14:50:05 -05:00
, m_purgeable ( purgeable = = Purgeable : : Yes )
2019-02-11 03:47:10 -05:00
{
2020-02-11 03:18:40 -05:00
ASSERT ( ! m_size . is_empty ( ) ) ;
2020-04-15 05:57:24 -04:00
ASSERT ( ! size_would_overflow ( format , size ) ) ;
2020-06-22 15:35:22 -04:00
allocate_palette_from_format ( format , { } ) ;
2020-07-23 14:34:53 -04:00
# ifdef __serenity__
2019-12-18 14:50:05 -05:00
int map_flags = purgeable = = Purgeable : : Yes ? ( MAP_PURGEABLE | MAP_PRIVATE ) : ( MAP_ANONYMOUS | MAP_PRIVATE ) ;
2020-09-06 17:59:20 -04:00
m_data = mmap_with_name ( nullptr , size_in_bytes ( ) , PROT_READ | PROT_WRITE , map_flags , 0 , 0 , String : : format ( " GraphicsBitmap [%dx%d] " , width ( ) , height ( ) ) . characters ( ) ) ;
2020-07-23 14:34:53 -04:00
# else
int map_flags = ( MAP_ANONYMOUS | MAP_PRIVATE ) ;
2020-09-06 17:59:20 -04:00
m_data = mmap ( nullptr , size_in_bytes ( ) , PROT_READ | PROT_WRITE , map_flags , 0 , 0 ) ;
2020-07-23 14:34:53 -04:00
# endif
2020-08-30 08:19:35 -04:00
if ( m_data = = MAP_FAILED ) {
perror ( " mmap " ) ;
ASSERT_NOT_REACHED ( ) ;
}
ASSERT ( m_data ) ;
2019-04-26 12:25:05 -04:00
m_needs_munmap = true ;
2019-02-16 06:22:00 -05:00
}
2019-01-14 14:00:42 -05:00
2020-09-06 17:59:20 -04:00
RefPtr < Bitmap > Bitmap : : create_wrapper ( BitmapFormat format , const IntSize & size , size_t pitch , void * data )
2019-01-14 14:00:42 -05:00
{
2020-04-15 05:57:24 -04:00
if ( size_would_overflow ( format , size ) )
return nullptr ;
2020-02-06 05:56:38 -05:00
return adopt ( * new Bitmap ( format , size , pitch , data ) ) ;
2019-01-14 14:00:42 -05:00
}
2019-01-08 21:51:34 -05:00
2020-02-06 05:56:38 -05:00
RefPtr < Bitmap > Bitmap : : load_from_file ( const StringView & path )
2019-03-21 19:19:53 -04:00
{
2020-06-15 08:13:12 -04:00
# define __ENUMERATE_IMAGE_FORMAT(Name, Ext) \
if ( path . ends_with ( Ext ) ) \
return load_ # # Name ( path ) ;
ENUMERATE_IMAGE_FORMATS
# undef __ENUMERATE_IMAGE_FORMAT
2020-04-25 17:05:28 -04:00
return nullptr ;
2019-03-21 19:19:53 -04:00
}
2020-09-06 17:59:20 -04:00
Bitmap : : Bitmap ( BitmapFormat format , const IntSize & size , size_t pitch , void * data )
2019-01-08 21:51:34 -05:00
: m_size ( size )
2019-01-09 23:36:32 -05:00
, m_data ( data )
2019-08-19 07:29:19 -04:00
, m_pitch ( pitch )
2019-02-18 19:42:53 -05:00
, m_format ( format )
2019-01-08 21:51:34 -05:00
{
2020-09-06 17:59:20 -04:00
ASSERT ( pitch > = minimum_pitch ( size . width ( ) , format ) ) ;
2020-04-15 05:57:24 -04:00
ASSERT ( ! size_would_overflow ( format , size ) ) ;
2020-09-06 17:59:20 -04:00
// FIXME: assert that `data` is actually long enough!
2020-06-22 15:35:22 -04:00
allocate_palette_from_format ( format , { } ) ;
2019-01-08 20:06:04 -05:00
}
2020-06-10 04:57:59 -04:00
RefPtr < Bitmap > Bitmap : : create_with_shared_buffer ( BitmapFormat format , NonnullRefPtr < SharedBuffer > & & shared_buffer , const IntSize & size )
2019-02-16 06:13:43 -05:00
{
2020-09-11 17:15:23 -04:00
return create_with_shared_buffer ( format , move ( shared_buffer ) , size , { } ) ;
2020-06-22 15:35:22 -04:00
}
RefPtr < Bitmap > Bitmap : : create_with_shared_buffer ( BitmapFormat format , NonnullRefPtr < SharedBuffer > & & shared_buffer , const IntSize & size , const Vector < RGBA32 > & palette )
{
if ( size_would_overflow ( format , size ) )
return nullptr ;
2020-09-11 17:15:23 -04:00
unsigned actual_size = shared_buffer - > size ( ) ;
// FIXME: Code duplication of size_in_bytes() and m_pitch
unsigned expected_size_min = minimum_pitch ( size . width ( ) , format ) * size . height ( ) ;
unsigned expected_size_max = round_up_to_power_of_two ( expected_size_min , PAGE_SIZE ) ;
if ( expected_size_min > actual_size | | actual_size > expected_size_max ) {
// Getting here is most likely an error.
dbg ( ) < < " Constructing a shared bitmap for format " < < ( int ) format < < " and size " < < size < < " , which demands " < < expected_size_min < < " bytes, which rounds up to at most " < < expected_size_max < < " . " ;
dbg ( ) < < " However, we were given " < < actual_size < < " bytes, which is outside this range?! Refusing cowardly. " ;
return { } ;
}
2020-06-22 15:35:22 -04:00
return adopt ( * new Bitmap ( format , move ( shared_buffer ) , size , palette ) ) ;
2019-02-16 06:13:43 -05:00
}
2020-06-22 15:35:22 -04:00
Bitmap : : Bitmap ( BitmapFormat format , NonnullRefPtr < SharedBuffer > & & shared_buffer , const IntSize & size , const Vector < RGBA32 > & palette )
2019-02-16 06:13:43 -05:00
: m_size ( size )
2020-09-06 17:59:20 -04:00
, m_data ( shared_buffer - > data ( ) )
, m_pitch ( minimum_pitch ( size . width ( ) , format ) )
2019-02-18 19:42:53 -05:00
, m_format ( format )
2019-03-08 06:22:55 -05:00
, m_shared_buffer ( move ( shared_buffer ) )
2019-02-16 06:13:43 -05:00
{
2020-06-22 15:35:22 -04:00
ASSERT ( ! is_indexed ( ) | | ! palette . is_empty ( ) ) ;
2020-04-15 05:57:24 -04:00
ASSERT ( ! size_would_overflow ( format , size ) ) ;
2020-09-11 17:15:23 -04:00
ASSERT ( size_in_bytes ( ) < = static_cast < size_t > ( m_shared_buffer - > size ( ) ) ) ;
2020-06-22 15:35:22 -04:00
if ( is_indexed ( m_format ) )
allocate_palette_from_format ( m_format , palette ) ;
2019-02-16 06:13:43 -05:00
}
2020-09-12 07:20:34 -04:00
RefPtr < Gfx : : Bitmap > Bitmap : : clone ( ) const
{
RefPtr < Gfx : : Bitmap > new_bitmap { } ;
if ( m_purgeable ) {
new_bitmap = Bitmap : : create_purgeable ( format ( ) , size ( ) ) ;
} else {
new_bitmap = Bitmap : : create ( format ( ) , size ( ) ) ;
}
if ( ! new_bitmap ) {
return nullptr ;
}
ASSERT ( size_in_bytes ( ) = = new_bitmap - > size_in_bytes ( ) ) ;
memcpy ( new_bitmap - > scanline ( 0 ) , scanline ( 0 ) , size_in_bytes ( ) ) ;
return new_bitmap ;
}
2020-04-15 05:57:24 -04:00
RefPtr < Gfx : : Bitmap > Bitmap : : rotated ( Gfx : : RotationDirection rotation_direction ) const
2020-04-12 06:19:18 -04:00
{
auto w = this - > width ( ) ;
auto h = this - > height ( ) ;
auto new_bitmap = Gfx : : Bitmap : : create ( this - > format ( ) , { h , w } ) ;
2020-04-15 05:57:24 -04:00
if ( ! new_bitmap )
return nullptr ;
2020-04-12 06:19:18 -04:00
for ( int i = 0 ; i < w ; i + + ) {
for ( int j = 0 ; j < h ; j + + ) {
Color color ;
if ( rotation_direction = = Gfx : : RotationDirection : : Left )
color = this - > get_pixel ( w - i - 1 , j ) ;
else
color = this - > get_pixel ( i , h - j - 1 ) ;
new_bitmap - > set_pixel ( j , i , color ) ;
}
}
return new_bitmap ;
}
2020-04-15 05:57:24 -04:00
RefPtr < Gfx : : Bitmap > Bitmap : : flipped ( Gfx : : Orientation orientation ) const
2020-04-12 06:19:18 -04:00
{
auto w = this - > width ( ) ;
auto h = this - > height ( ) ;
auto new_bitmap = Gfx : : Bitmap : : create ( this - > format ( ) , { w , h } ) ;
2020-04-15 05:57:24 -04:00
if ( ! new_bitmap )
return nullptr ;
2020-04-12 06:19:18 -04:00
for ( int i = 0 ; i < w ; i + + ) {
for ( int j = 0 ; j < h ; j + + ) {
Color color = this - > get_pixel ( i , j ) ;
if ( orientation = = Orientation : : Vertical )
new_bitmap - > set_pixel ( i , h - j - 1 , color ) ;
else
new_bitmap - > set_pixel ( w - i - 1 , j , color ) ;
}
}
return new_bitmap ;
}
2020-04-15 05:57:24 -04:00
RefPtr < Bitmap > Bitmap : : to_bitmap_backed_by_shared_buffer ( ) const
2019-12-08 11:07:44 -05:00
{
if ( m_shared_buffer )
return * this ;
auto buffer = SharedBuffer : : create_with_size ( size_in_bytes ( ) ) ;
2020-06-22 15:35:22 -04:00
auto bitmap = Bitmap : : create_with_shared_buffer ( m_format , * buffer , m_size , palette_to_vector ( ) ) ;
2020-04-15 05:57:24 -04:00
if ( ! bitmap )
return nullptr ;
2019-12-08 11:07:44 -05:00
memcpy ( buffer - > data ( ) , scanline ( 0 ) , size_in_bytes ( ) ) ;
return bitmap ;
}
2020-02-06 05:56:38 -05:00
Bitmap : : ~ Bitmap ( )
2019-01-08 20:06:04 -05:00
{
2019-04-26 12:25:05 -04:00
if ( m_needs_munmap ) {
2019-05-06 08:04:54 -04:00
int rc = munmap ( m_data , size_in_bytes ( ) ) ;
2019-04-26 12:25:05 -04:00
ASSERT ( rc = = 0 ) ;
}
2019-01-08 21:51:34 -05:00
m_data = nullptr ;
2019-06-07 05:46:55 -04:00
delete [ ] m_palette ;
2019-01-08 20:06:04 -05:00
}
2020-02-06 05:56:38 -05:00
void Bitmap : : set_mmap_name ( const StringView & name )
2019-04-30 07:46:03 -04:00
{
ASSERT ( m_needs_munmap ) ;
2020-07-23 14:34:53 -04:00
# ifdef __serenity__
2020-05-06 13:18:24 -04:00
: : set_mmap_name ( m_data , size_in_bytes ( ) , name . to_string ( ) . characters ( ) ) ;
2020-07-23 14:34:53 -04:00
# else
( void ) name ;
# endif
2019-04-30 07:46:03 -04:00
}
2019-06-10 13:29:50 -04:00
2020-02-06 05:56:38 -05:00
void Bitmap : : fill ( Color color )
2019-06-10 13:29:50 -04:00
{
2020-06-17 15:37:16 -04:00
ASSERT ( ! is_indexed ( m_format ) ) ;
2019-06-10 13:29:50 -04:00
for ( int y = 0 ; y < height ( ) ; + + y ) {
auto * scanline = this - > scanline ( y ) ;
2019-07-03 15:17:35 -04:00
fast_u32_fill ( scanline , color . value ( ) , width ( ) ) ;
2019-06-10 13:29:50 -04:00
}
}
2019-12-18 14:50:05 -05:00
2020-02-06 05:56:38 -05:00
void Bitmap : : set_volatile ( )
2019-12-18 14:50:05 -05:00
{
ASSERT ( m_purgeable ) ;
if ( m_volatile )
return ;
2020-07-23 14:34:53 -04:00
# ifdef __serenity__
2019-12-18 14:50:05 -05:00
int rc = madvise ( m_data , size_in_bytes ( ) , MADV_SET_VOLATILE ) ;
if ( rc < 0 ) {
perror ( " madvise(MADV_SET_VOLATILE) " ) ;
ASSERT_NOT_REACHED ( ) ;
}
2020-07-23 14:34:53 -04:00
# endif
2019-12-18 14:50:05 -05:00
m_volatile = true ;
}
2020-02-06 05:56:38 -05:00
[[nodiscard]] bool Bitmap : : set_nonvolatile ( )
2019-12-18 14:50:05 -05:00
{
ASSERT ( m_purgeable ) ;
if ( ! m_volatile )
return true ;
2020-07-23 14:34:53 -04:00
# ifdef __serenity__
2019-12-18 14:50:05 -05:00
int rc = madvise ( m_data , size_in_bytes ( ) , MADV_SET_NONVOLATILE ) ;
if ( rc < 0 ) {
perror ( " madvise(MADV_SET_NONVOLATILE) " ) ;
ASSERT_NOT_REACHED ( ) ;
}
2020-07-23 14:34:53 -04:00
# else
int rc = 0 ;
# endif
2019-12-18 14:50:05 -05:00
m_volatile = false ;
return rc = = 0 ;
}
2020-02-06 05:56:38 -05:00
2020-02-28 05:45:19 -05:00
int Bitmap : : shbuf_id ( ) const
2020-02-14 17:02:47 -05:00
{
2020-02-28 05:45:19 -05:00
return m_shared_buffer ? m_shared_buffer - > shbuf_id ( ) : - 1 ;
2020-02-14 17:02:47 -05:00
}
2020-03-29 13:04:05 -04:00
ShareableBitmap Bitmap : : to_shareable_bitmap ( pid_t peer_pid ) const
{
auto bitmap = to_bitmap_backed_by_shared_buffer ( ) ;
2020-04-15 05:57:24 -04:00
if ( ! bitmap )
return { } ;
2020-03-29 13:04:05 -04:00
if ( peer_pid > 0 )
bitmap - > shared_buffer ( ) - > share_with ( peer_pid ) ;
return ShareableBitmap ( * bitmap ) ;
}
2020-06-22 15:35:22 -04:00
void Bitmap : : allocate_palette_from_format ( BitmapFormat format , const Vector < RGBA32 > & source_palette )
2020-06-17 15:37:16 -04:00
{
2020-06-22 15:35:22 -04:00
size_t size = palette_size ( format ) ;
if ( size = = 0 )
return ;
m_palette = new RGBA32 [ size ] ;
if ( ! source_palette . is_empty ( ) ) {
ASSERT ( source_palette . size ( ) = = size ) ;
memcpy ( m_palette , source_palette . data ( ) , size * sizeof ( RGBA32 ) ) ;
2020-06-17 15:37:16 -04:00
}
}
2020-06-22 15:35:22 -04:00
Vector < RGBA32 > Bitmap : : palette_to_vector ( ) const
{
Vector < RGBA32 > vector ;
auto size = palette_size ( m_format ) ;
vector . ensure_capacity ( size ) ;
for ( size_t i = 0 ; i < size ; + + i )
vector . unchecked_append ( palette_color ( i ) . value ( ) ) ;
return vector ;
}
2020-02-06 05:56:38 -05:00
}