/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "common/config-manager.h" #include "common/savefile.h" #include "common/system.h" #include "ags/lib/aastr-0.1.1/aastr.h" #include "ags/shared/gfx/allegro_bitmap.h" #include "ags/shared/gfx/image.h" namespace AGS3 { extern void __my_setcolor(int *ctset, int newcol, int wantColDep); namespace AGS { namespace Shared { Bitmap::Bitmap() : _alBitmap(nullptr) , _isDataOwner(false) { } Bitmap::Bitmap(int width, int height, int color_depth) : _alBitmap(nullptr) , _isDataOwner(false) { Create(width, height, color_depth); } Bitmap::Bitmap(Bitmap *src, const Rect &rc) : _alBitmap(nullptr) , _isDataOwner(false) { CreateSubBitmap(src, rc); } Bitmap::Bitmap(BITMAP *al_bmp, bool shared_data) : _alBitmap(nullptr) , _isDataOwner(false) { WrapAllegroBitmap(al_bmp, shared_data); } Bitmap::~Bitmap() { Destroy(); } //============================================================================= // Creation and destruction //============================================================================= bool Bitmap::Create(int width, int height, int color_depth) { Destroy(); if (color_depth) { _alBitmap = create_bitmap_ex(color_depth, width, height); } else { _alBitmap = create_bitmap(width, height); } _isDataOwner = true; return _alBitmap != nullptr; } bool Bitmap::CreateTransparent(int width, int height, int color_depth) { if (Create(width, height, color_depth)) { clear_to_color(_alBitmap, bitmap_mask_color(_alBitmap)); return true; } return false; } bool Bitmap::CreateSubBitmap(Bitmap *src, const Rect &rc) { Destroy(); _alBitmap = create_sub_bitmap(src->_alBitmap, rc.Left, rc.Top, rc.GetWidth(), rc.GetHeight()); _isDataOwner = true; return _alBitmap != nullptr; } bool Bitmap::ResizeSubBitmap(int width, int height) { if (!isSubBitmap()) return false; // TODO: can't clamp to parent size, because subs do not keep parent ref; // might require amending allegro bitmap struct _alBitmap->w = _alBitmap->cr = width; _alBitmap->h = _alBitmap->cb = height; return true; } bool Bitmap::CreateCopy(Bitmap *src, int color_depth) { if (Create(src->_alBitmap->w, src->_alBitmap->h, color_depth ? color_depth : bitmap_color_depth(src->_alBitmap))) { blit(src->_alBitmap, _alBitmap, 0, 0, 0, 0, _alBitmap->w, _alBitmap->h); return true; } return false; } bool Bitmap::WrapAllegroBitmap(BITMAP *al_bmp, bool shared_data) { Destroy(); _alBitmap = al_bmp; _isDataOwner = !shared_data; return _alBitmap != nullptr; } void Bitmap::Destroy() { if (_isDataOwner && _alBitmap) { destroy_bitmap(_alBitmap); } _alBitmap = nullptr; _isDataOwner = false; } bool Bitmap::LoadFromFile(const char *filename) { Destroy(); BITMAP *al_bmp = load_bitmap(filename, nullptr); if (al_bmp) { _alBitmap = al_bmp; _isDataOwner = true; } return _alBitmap != nullptr; } bool Bitmap::LoadFromFile(PACKFILE *pf) { Destroy(); BITMAP *al_bmp = load_bitmap(pf, nullptr); if (al_bmp) { _alBitmap = al_bmp; _isDataOwner = true; } return _alBitmap != nullptr; } bool Bitmap::SaveToFile(Common::WriteStream &out, const void *palette) { return save_bitmap(out, _alBitmap, (const RGB *)palette); } bool Bitmap::SaveToFile(const char *filename, const void *palette) { // Only keeps the file name and add the game target as prefix. Common::String name = filename; size_t lastSlash = name.findLastOf('/'); if (lastSlash != Common::String::npos) name = name.substr(lastSlash + 1); Common::String gameTarget = ConfMan.getActiveDomainName(); if (!name.hasPrefixIgnoreCase(gameTarget)) name = gameTarget + "-" + name; Common::OutSaveFile *out = g_system->getSavefileManager()->openForSaving(name, false); assert(out); bool result = SaveToFile(*out, palette); out->finalize(); delete out; return result; } color_t Bitmap::GetCompatibleColor(color_t color) { color_t compat_color = 0; __my_setcolor(&compat_color, color, bitmap_color_depth(_alBitmap)); return compat_color; } //============================================================================= // Clipping //============================================================================= void Bitmap::SetClip(const Rect &rc) { set_clip_rect(_alBitmap, rc.Left, rc.Top, rc.Right, rc.Bottom); } void Bitmap::ResetClip() { set_clip_rect(_alBitmap, 0, 0, _alBitmap->w - 1, _alBitmap->h - 1); } Rect Bitmap::GetClip() const { Rect temp; get_clip_rect(_alBitmap, &temp.Left, &temp.Top, &temp.Right, &temp.Bottom); return temp; } //============================================================================= // Blitting operations (drawing one bitmap over another) //============================================================================= void Bitmap::Blit(Bitmap *src, int dst_x, int dst_y, BitmapMaskOption mask) { BITMAP *al_src_bmp = src->_alBitmap; // WARNING: For some evil reason Allegro expects dest and src bitmaps in different order for blit and draw_sprite if (mask == kBitmap_Transparency) { draw_sprite(_alBitmap, al_src_bmp, dst_x, dst_y); } else { blit(al_src_bmp, _alBitmap, 0, 0, dst_x, dst_y, al_src_bmp->w, al_src_bmp->h); } } void Bitmap::Blit(Bitmap *src, int src_x, int src_y, int dst_x, int dst_y, int width, int height, BitmapMaskOption mask) { BITMAP *al_src_bmp = src->_alBitmap; if (mask == kBitmap_Transparency) { masked_blit(al_src_bmp, _alBitmap, src_x, src_y, dst_x, dst_y, width, height); } else { blit(al_src_bmp, _alBitmap, src_x, src_y, dst_x, dst_y, width, height); } } void Bitmap::MaskedBlit(Bitmap *src, int dst_x, int dst_y) { draw_sprite(_alBitmap, src->_alBitmap, dst_x, dst_y); } void Bitmap::StretchBlt(Bitmap *src, const Rect &dst_rc, BitmapMaskOption mask) { BITMAP *al_src_bmp = src->_alBitmap; // WARNING: For some evil reason Allegro expects dest and src bitmaps in different order for blit and draw_sprite if (mask == kBitmap_Transparency) { stretch_sprite(_alBitmap, al_src_bmp, dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight()); } else { stretch_blit(al_src_bmp, _alBitmap, 0, 0, al_src_bmp->w, al_src_bmp->h, dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight()); } } void Bitmap::StretchBlt(Bitmap *src, const Rect &src_rc, const Rect &dst_rc, BitmapMaskOption mask) { BITMAP *al_src_bmp = src->_alBitmap; if (mask == kBitmap_Transparency) { masked_stretch_blit(al_src_bmp, _alBitmap, src_rc.Left, src_rc.Top, src_rc.GetWidth(), src_rc.GetHeight(), dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight()); } else { stretch_blit(al_src_bmp, _alBitmap, src_rc.Left, src_rc.Top, src_rc.GetWidth(), src_rc.GetHeight(), dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight()); } } void Bitmap::AAStretchBlt(Bitmap *src, const Rect &dst_rc, BitmapMaskOption mask) { BITMAP *al_src_bmp = src->_alBitmap; // WARNING: For some evil reason Allegro expects dest and src bitmaps in different order for blit and draw_sprite if (mask == kBitmap_Transparency) { aa_stretch_sprite(_alBitmap, al_src_bmp, dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight()); } else { aa_stretch_blit(al_src_bmp, _alBitmap, 0, 0, al_src_bmp->w, al_src_bmp->h, dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight()); } } void Bitmap::AAStretchBlt(Bitmap *src, const Rect &src_rc, const Rect &dst_rc, BitmapMaskOption mask) { BITMAP *al_src_bmp = src->_alBitmap; if (mask == kBitmap_Transparency) { // TODO: aastr lib does not expose method for masked stretch blit; should do that at some point since // the source code is a gift-ware anyway // aa_masked_blit(_alBitmap, al_src_bmp, src_rc.Left, src_rc.Top, src_rc.GetWidth(), src_rc.GetHeight(), dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight()); error("aa_masked_blit is not yet supported!"); } else { aa_stretch_blit(al_src_bmp, _alBitmap, src_rc.Left, src_rc.Top, src_rc.GetWidth(), src_rc.GetHeight(), dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight()); } } void Bitmap::TransBlendBlt(Bitmap *src, int dst_x, int dst_y) { BITMAP *al_src_bmp = src->_alBitmap; draw_trans_sprite(_alBitmap, al_src_bmp, dst_x, dst_y); } void Bitmap::LitBlendBlt(Bitmap *src, int dst_x, int dst_y, int light_amount) { BITMAP *al_src_bmp = src->_alBitmap; draw_lit_sprite(_alBitmap, al_src_bmp, dst_x, dst_y, light_amount); } void Bitmap::FlipBlt(Bitmap *src, int dst_x, int dst_y, GraphicFlip flip) { BITMAP *al_src_bmp = src->_alBitmap; switch (flip) { case kFlip_Horizontal: draw_sprite_h_flip(_alBitmap, al_src_bmp, dst_x, dst_y); break; case kFlip_Vertical: draw_sprite_v_flip(_alBitmap, al_src_bmp, dst_x, dst_y); break; case kFlip_Both: draw_sprite_vh_flip(_alBitmap, al_src_bmp, dst_x, dst_y); break; default: // blit with no transform Blit(src, dst_x, dst_y); break; } } void Bitmap::RotateBlt(Bitmap *src, int dst_x, int dst_y, fixed_t angle) { BITMAP *al_src_bmp = src->_alBitmap; rotate_sprite(_alBitmap, al_src_bmp, dst_x, dst_y, angle); } void Bitmap::RotateBlt(Bitmap *src, int dst_x, int dst_y, int pivot_x, int pivot_y, fixed_t angle) { BITMAP *al_src_bmp = src->_alBitmap; pivot_sprite(_alBitmap, al_src_bmp, dst_x, dst_y, pivot_x, pivot_y, angle); } //============================================================================= // Pixel operations //============================================================================= void Bitmap::Clear(color_t color) { if (color) { clear_to_color(_alBitmap, color); } else { clear_bitmap(_alBitmap); } } void Bitmap::ClearTransparent() { clear_to_color(_alBitmap, bitmap_mask_color(_alBitmap)); } void Bitmap::PutPixel(int x, int y, color_t color) { if (x < 0 || x >= _alBitmap->w || y < 0 || y >= _alBitmap->h) { return; } switch (bitmap_color_depth(_alBitmap)) { case 8: return _putpixel(_alBitmap, x, y, color); case 15: return _putpixel15(_alBitmap, x, y, color); case 16: return _putpixel16(_alBitmap, x, y, color); case 24: return _putpixel24(_alBitmap, x, y, color); case 32: return _putpixel32(_alBitmap, x, y, color); } assert(0); // this should not normally happen return putpixel(_alBitmap, x, y, color); } int Bitmap::GetPixel(int x, int y) const { if (x < 0 || x >= _alBitmap->w || y < 0 || y >= _alBitmap->h) { return -1; // Allegros getpixel() implementation returns -1 in this case } switch (bitmap_color_depth(_alBitmap)) { case 8: return _getpixel(_alBitmap, x, y); case 15: return _getpixel15(_alBitmap, x, y); case 16: return _getpixel16(_alBitmap, x, y); case 24: return _getpixel24(_alBitmap, x, y); case 32: return _getpixel32(_alBitmap, x, y); } assert(0); // this should not normally happen return getpixel(_alBitmap, x, y); } //============================================================================= // Vector drawing operations //============================================================================= void Bitmap::DrawLine(const Line &ln, color_t color) { line(_alBitmap, ln.X1, ln.Y1, ln.X2, ln.Y2, color); } void Bitmap::DrawTriangle(const Triangle &tr, color_t color) { triangle(_alBitmap, tr.X1, tr.Y1, tr.X2, tr.Y2, tr.X3, tr.Y3, color); } void Bitmap::DrawRect(const Rect &rc, color_t color) { rect(_alBitmap, rc.Left, rc.Top, rc.Right, rc.Bottom, color); } void Bitmap::FillRect(const Rect &rc, color_t color) { rectfill(_alBitmap, rc.Left, rc.Top, rc.Right, rc.Bottom, color); } void Bitmap::FillCircle(const Circle &circle, color_t color) { circlefill(_alBitmap, circle.X, circle.Y, circle.Radius, color); } void Bitmap::Fill(color_t color) { if (color) { clear_to_color(_alBitmap, color); } else { clear_bitmap(_alBitmap); } } void Bitmap::FillTransparent() { clear_to_color(_alBitmap, bitmap_mask_color(_alBitmap)); } void Bitmap::FloodFill(int x, int y, color_t color) { _alBitmap->floodfill(x, y, color); } //============================================================================= // Direct access operations //============================================================================= void Bitmap::SetScanLine(int index, unsigned char *data, int data_size) { if (index < 0 || index >= GetHeight()) { return; } int copy_length = data_size; if (copy_length < 0) { copy_length = GetLineLength(); } else // TODO: use Math namespace here if (copy_length > GetLineLength()) { copy_length = GetLineLength(); } memcpy(_alBitmap->line[index], data, copy_length); } namespace BitmapHelper { Bitmap *CreateRawBitmapOwner(BITMAP *al_bmp) { Bitmap *bitmap = new Bitmap(); if (!bitmap->WrapAllegroBitmap(al_bmp, false)) { delete bitmap; bitmap = nullptr; } return bitmap; } Bitmap *CreateRawBitmapWrapper(BITMAP *al_bmp) { Bitmap *bitmap = new Bitmap(); if (!bitmap->WrapAllegroBitmap(al_bmp, true)) { delete bitmap; bitmap = nullptr; } return bitmap; } } // namespace BitmapHelper } // namespace Shared } // namespace AGS } // namespace AGS3