Final version

This commit is contained in:
Vladislav Khorev 2025-03-03 07:58:27 +03:00
parent f77a3fa4ad
commit da7805ce0c
16 changed files with 281 additions and 134 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

After

Width:  |  Height:  |  Size: 48 MiB

View File

@ -31,11 +31,12 @@ int Environment::violaLastWalkFrame = 0;
int Environment::violaCurrentAnimation = 0;
float Environment::violaAngleAroundY = 0.f;
bool Environment::settings_inverseVertical = true;
bool Environment::settings_inverseVertical = false;
SDL_Window* Environment::window = nullptr;
float Environment::cameraDefaultVerticalShift = -150.f;
float Environment::itemDefaultVerticalShift = -80.f;
bool Environment::showMouse = false;
bool Environment::exitGameLoop = false;
@ -45,4 +46,11 @@ bool Environment::gameIsLoading = true;
float Environment::monsterTimer = 0.0;
int Environment::monsterState = 1;
bool Environment::finalIsGood = false;
bool Environment::finalIsBad = false;
bool Environment::goToLevel3 = false;
float Environment::goTolevel3Timer = 0;
} // namespace ZL

View File

@ -40,6 +40,7 @@ public:
static SDL_Window* window;
static float cameraDefaultVerticalShift;
static float itemDefaultVerticalShift;
static bool showMouse;
static bool exitGameLoop;
@ -49,6 +50,12 @@ public:
static float monsterTimer;
static int monsterState;
static bool finalIsGood;
static bool finalIsBad;
static bool goToLevel3;
static float goTolevel3Timer;
};

View File

@ -73,6 +73,15 @@ void Game::drawScene() {
void Game::processTickCount() {
if (Environment::finalIsGood)
{
return;
}
if (Environment::finalIsBad)
{
return;
}
if (Environment::gameIsLoading)
{
if (gameObjects.loadingFunctions.size() != 0)

View File

@ -30,11 +30,6 @@ void GameObjectManager::initialize() {
initializeLoadingScreen();
if (!dialogTextures.empty()) { // Проверяем, есть ли диалоги
dialogTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24(dialogTextures[dialogIndex]));
isDialogActive = true;
}
std::function<bool()> loadingFunction1 = [this]()
{
@ -76,8 +71,8 @@ void GameObjectManager::initialize() {
violaIdleModel.LoadFromFile("./idleviola_uv009.txt");
violaWalkModel.LoadFromFile("./walkviola_uv009.txt");
violaIdleModel.LoadFromFile("./idleviola_uv010.txt");
violaWalkModel.LoadFromFile("./walkviola_uv010.txt");
sideThreadLoadingCompleted = true;
});
@ -179,7 +174,7 @@ void GameObjectManager::initialize() {
lock.activeObjectMeshMutable.AssignFrom(lock.activeObjectMesh);
lock.activeObjectMeshMutable.RefreshVBO();
lock.objectPos = Vector3f{ 101, 100, 255 };
lock.activeObjectTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./Material.001_Base_color_1001_5.bmp"));
lock.activeObjectTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./temno.bmp"));
lock.activeObjectScreenTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp32("./hand.bmp32"));
lock.activeObjectScreenMesh = CreateRect2D({ 0.f, 0.f }, { 64.f, 64.f }, 0.5);
lock.activeObjectScreenMeshMutable.AssignFrom(lock.activeObjectScreenMesh);
@ -190,15 +185,13 @@ void GameObjectManager::initialize() {
ActiveObject door;
door.name = "doorGlory";
door.activeObjectMesh = ZL::LoadFromTextFile("./door.txt"); // Add ZL:: namespace
door.activeObjectMesh = ZL::LoadFromTextFile("./door001.txt"); // Add ZL:: namespace
door.activeObjectMesh.Scale(60);
// cubeForFirstRoomO.activeObjectMesh.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundZ(M_PI * 0.5)));
cubeForFirstRoomO.activeObjectMesh.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundY(M_PI * 1.5)));
// cubeForFirstRoomO.activeObjectMesh.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundX(M_PI * 0.5)));
door.activeObjectMesh.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundY(-M_PI * 0.5)));
door.activeObjectMeshMutable.AssignFrom(door.activeObjectMesh);
door.activeObjectMeshMutable.RefreshVBO();
door.objectPos = Vector3f{ -372, 10, 80 };
door.activeObjectTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./Material.001_Base_color_1001_5.bmp"));
door.objectPos = Vector3f{ -350, -40, -60 };
door.activeObjectTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./door.bmp"));
door.activeObjectScreenTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp32("./hand.bmp32"));
door.activeObjectScreenMesh = CreateRect2D({ 0.f, 0.f }, { 64.f, 64.f }, 0.5);
door.activeObjectScreenMeshMutable.AssignFrom(door.activeObjectScreenMesh);
@ -295,6 +288,18 @@ void GameObjectManager::initialize() {
violaTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./viola.bmp"));
if (!dialogTextures.empty()) { // Проверяем, есть ли диалоги
dialogTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24(dialogTextures[dialogIndex]));
isDialogActive = true;
}
batteryDialogTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./battery_dialog.bmp"));
finalGoodTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./final_good.bmp"));
finalBadTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./final_bad.bmp"));
//SDL_ShowCursor(SDL_DISABLE);
SDL_SetRelativeMouseMode(SDL_TRUE);
@ -328,139 +333,157 @@ void GameObjectManager::switch_room(int index){
activeObjects = rooms[current_room_index].objects;
std::cout << "Current music" << rooms[current_room_index].sound_name << std::endl;
Environment::cameraShift = Vector3f{};
Environment::characterPos = Vector3f{};
}
void GameObjectManager::handleEvent(const SDL_Event& event) {
// debug room switching
if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_RIGHT) {
/*if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_RIGHT) {
}
else */if (event.type == SDL_MOUSEBUTTONDOWN) {
if (Environment::finalIsBad || Environment::finalIsGood)
{
return;
}
if (isDialogActive) {
dialogIndex++;
if (dialogIndex < dialogTextures.size()) {
dialogTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24(dialogTextures[dialogIndex]));
} else {
}
else {
isDialogActive = false;
}
}
if (isBatteryDialogActive) {
BatteryDialogIndex++;
if (BatteryDialogIndex <= batteryDialogTextures.size()) {
batteryDialogTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24(batteryDialogTextures[BatteryDialogIndex]));
} else {
else if (isBatteryDialogActive) {
isBatteryDialogActive = false;
}
}
}
else if (event.type == SDL_MOUSEBUTTONDOWN) {
const auto highlightedObjects = rooms[current_room_index].findByHighlighted(true);
else
{
const auto highlightedObjects = rooms[current_room_index].findByHighlighted(true);
if (InventoryItem* item = GetItemSelected(true)) {
std::cout << item->name << std::endl;
if (InventoryItem* item = GetItemSelected(true)) {
std::cout << item->name << std::endl;
if (current_room_index==0) {
if (current_room_index == 0) {
if (bearName.length() <= 3) {
if (item->name == "cube_T"){
bearName += "T";
selectedCubes.push_back(*item);
gInventoryMap.erase(item->name);
objects_in_inventory--;
}
else if (item->name == "cube_O"){
bearName += "O";
selectedCubes.push_back(*item);
gInventoryMap.erase(item->name);
objects_in_inventory--;
}
else if (item->name == "cube_M"){
bearName += "M";
selectedCubes.push_back(*item);
gInventoryMap.erase(item->name);
objects_in_inventory--;
}
}
}
else if (current_room_index==1) {
if (InventoryItem* item = GetItemSelected(true)){
std::cout << item->name << std::endl;
if (item->name == "carToy") {
// Проверить, наведена ли мышь на лампу
const auto highlightedObjects = rooms[current_room_index].findByHighlighted(true);
std::cout << highlightedObjects.size() << std::endl;
for (auto* ao : highlightedObjects) {
if (ao && ao->name == "lampe") {
isBatteryDialogActive = true;
// Create a new lamp object with updated texture
ActiveObject updatedLamp = *ao;
// Change from dark to lit texture
updatedLamp.activeObjectTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./base_Base_color_1001.bmp"));
// Replace the old lamp with updated one
rooms[current_room_index].removeByPtr(ao);
rooms[current_room_index].objects.push_back(updatedLamp);
activeObjects = rooms[current_room_index].objects;
// Remove car from inventory
if (bearName.length() <= 3) {
if (item->name == "cube_T") {
bearName += "T";
selectedCubes.push_back(*item);
gInventoryMap.erase(item->name);
objects_in_inventory--;
// Play sound effect
// audioPlayerAsync.playSoundAsync("lamp_on.ogg");
AddItemToInventory(ao->name, ao->inventoryIconTexturePtr, objects_in_inventory+1);
objects_in_inventory++;
switch_room(2);
break;
}
else if (item->name == "cube_O") {
bearName += "O";
selectedCubes.push_back(*item);
gInventoryMap.erase(item->name);
objects_in_inventory--;
}
else if (item->name == "cube_M") {
bearName += "M";
selectedCubes.push_back(*item);
gInventoryMap.erase(item->name);
objects_in_inventory--;
}
}
}
else if (current_room_index == 1) {
if (InventoryItem* item = GetItemSelected(true)) {
std::cout << item->name << std::endl;
if (item->name == "carToy") {
// Проверить, наведена ли мышь на лампу
const auto highlightedObjects = rooms[current_room_index].findByHighlighted(true);
std::cout << highlightedObjects.size() << std::endl;
for (auto* ao : highlightedObjects) {
if (ao && ao->name == "lampe") {
// Create a new lamp object with updated texture
ActiveObject updatedLamp = *ao;
// Change from dark to lit texture
updatedLamp.activeObjectTexturePtr = std::make_shared<Texture>(CreateTextureDataFromBmp24("./base_Base_color_1001.bmp"));
// Replace the old lamp with updated one
rooms[current_room_index].removeByPtr(ao);
rooms[current_room_index].objects.push_back(updatedLamp);
activeObjects = rooms[current_room_index].objects;
// Remove car from inventory
gInventoryMap.erase(item->name);
objects_in_inventory--;
// Play sound effect
// audioPlayerAsync.playSoundAsync("lamp_on.ogg");
//AddItemToInventory(ao->name, ao->inventoryIconTexturePtr, objects_in_inventory+1);
//objects_in_inventory++;
//switch_room(2);
Environment::goToLevel3 = true;
break;
}
}
}
}
}
else if (current_room_index == 2) {
if (InventoryItem* item = GetItemSelected(true)) {
if (item->name == "lockFriend") {}
}
}
}
else {
const auto highlightedObjects = rooms[current_room_index].findByHighlighted(true);
for (auto* ao : highlightedObjects) {
if (!ao) {
continue;
}
if (ao->name != "lampe" && ao->name != "doorGlory" && ao->name != "lockFriend") {
AddItemToInventory(ao->name, ao->inventoryIconTexturePtr, objects_in_inventory + 1);
objects_in_inventory++;
rooms[current_room_index].removeByPtr(ao);
activeObjects = rooms[current_room_index].objects;
}
else if (current_room_index == 1 && ao->name == "lampe")
{
if (isBatteryDialogActive == false)
{
isBatteryDialogActive = true;
}
}
else if (current_room_index == 2 && ao->name == "doorGlory") {
hasMadeChoise = true;
hasChoisedFriendship = false;
// debug switching
Environment::finalIsBad = true;
}
else if (current_room_index == 2 && ao->name == "lockFriend") {
hasMadeChoise = true;
hasChoisedFriendship = true;
// debug switching
Environment::finalIsGood = true;
}
//aoMgr.removeByName(ao->name);
}
// bx.Interpolate(animationCounter);
// animationCounter += 2;
}
}
else if (current_room_index==2) {
if (InventoryItem* item = GetItemSelected(true)){
if (item->name == "lockFriend"){}
}
}
}
else {
const auto highlightedObjects = rooms[current_room_index].findByHighlighted(true);
for (auto* ao : highlightedObjects) {
if (!ao) {
continue;
}
if (ao->name != "lampe" && ao->name != "doorGlory" && ao->name != "lockFriend" ) {
AddItemToInventory(ao->name, ao->inventoryIconTexturePtr, objects_in_inventory+1);
objects_in_inventory++;
rooms[current_room_index].removeByPtr(ao);
activeObjects = rooms[current_room_index].objects;
}
else if (ao->name != "doorGlory"){
hasMadeChoise = true;
hasChoisedFriendship = false;
// debug switching
switch_room(0);
}
else if (ao->name != "lockFriend"){
hasMadeChoise = true;
hasChoisedFriendship = true;
// debug switching
switch_room(0);
}
//aoMgr.removeByName(ao->name);
}
// bx.Interpolate(animationCounter);
// animationCounter += 2;
}
}
else if (event.type == SDL_MOUSEWHEEL) {
static const float zoomstep = 1.0f;
if (event.wheel.y > 0) {
@ -478,6 +501,9 @@ void GameObjectManager::handleEvent(const SDL_Event& event) {
}
else if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) {
case SDLK_i:
Environment::settings_inverseVertical = !Environment::settings_inverseVertical;
break;
case SDLK_SPACE:
Environment::showMouse = !Environment::showMouse;
@ -502,6 +528,10 @@ void GameObjectManager::handleEvent(const SDL_Event& event) {
break;
case SDLK_LEFT:
case SDLK_a:
if (Environment::finalIsBad || Environment::finalIsGood)
{
return;
}
Environment::leftPressed = true;
audioPlayerAsync.playSoundAsync("walk.ogg"); // Заменено
if (Environment::violaCurrentAnimation == 0) {
@ -511,6 +541,10 @@ void GameObjectManager::handleEvent(const SDL_Event& event) {
break;
case SDLK_RIGHT:
case SDLK_d:
if (Environment::finalIsBad || Environment::finalIsGood)
{
return;
}
Environment::rightPressed = true;
audioPlayerAsync.playSoundAsync("walk.ogg"); // Заменено
if (Environment::violaCurrentAnimation == 0) {
@ -520,6 +554,10 @@ void GameObjectManager::handleEvent(const SDL_Event& event) {
break;
case SDLK_UP:
case SDLK_w:
if (Environment::finalIsBad || Environment::finalIsGood)
{
return;
}
Environment::upPressed = true;
audioPlayerAsync.playSoundAsync("walk.ogg"); // Заменено
if (Environment::violaCurrentAnimation == 0) {
@ -529,6 +567,10 @@ void GameObjectManager::handleEvent(const SDL_Event& event) {
break;
case SDLK_DOWN:
case SDLK_s:
if (Environment::finalIsBad || Environment::finalIsGood)
{
return;
}
Environment::downPressed = true;
audioPlayerAsync.playSoundAsync("walk.ogg"); // Заменено
if (Environment::violaCurrentAnimation == 0) {
@ -546,7 +588,10 @@ void GameObjectManager::handleEvent(const SDL_Event& event) {
case SDLK_8:
case SDLK_9:
{
if (Environment::finalIsBad || Environment::finalIsGood)
{
return;
}
UnselectAllItems();
if (InventoryItem* item = GetItemByHotkey(event.key.keysym.sym - SDLK_1 + 1)) {
item->isSelected = true;
@ -556,10 +601,10 @@ void GameObjectManager::handleEvent(const SDL_Event& event) {
case SDLK_RSHIFT:
case SDLK_LSHIFT: {
// Switch to next room
int nextRoom = current_room_index + 1;
/*int nextRoom = current_room_index + 1;
if (nextRoom < rooms.size()) {
switch_room(nextRoom);
}
}*/
break;
}
}
@ -568,6 +613,10 @@ void GameObjectManager::handleEvent(const SDL_Event& event) {
switch (event.key.keysym.sym) {
case SDLK_LEFT:
case SDLK_a:
if (Environment::finalIsBad || Environment::finalIsGood)
{
return;
}
Environment::leftPressed = false;
if (!Environment::leftPressed && !Environment::rightPressed && !Environment::upPressed && !Environment::downPressed) {
if (Environment::violaCurrentAnimation == 1) {
@ -578,6 +627,10 @@ void GameObjectManager::handleEvent(const SDL_Event& event) {
break;
case SDLK_RIGHT:
case SDLK_d:
if (Environment::finalIsBad || Environment::finalIsGood)
{
return;
}
Environment::rightPressed = false;
if (!Environment::leftPressed && !Environment::rightPressed && !Environment::upPressed && !Environment::downPressed) {
if (Environment::violaCurrentAnimation == 1) {
@ -588,6 +641,10 @@ void GameObjectManager::handleEvent(const SDL_Event& event) {
break;
case SDLK_UP:
case SDLK_w:
if (Environment::finalIsBad || Environment::finalIsGood)
{
return;
}
Environment::upPressed = false;
if (!Environment::leftPressed && !Environment::rightPressed && !Environment::upPressed && !Environment::downPressed) {
if (Environment::violaCurrentAnimation == 1) {
@ -598,6 +655,10 @@ void GameObjectManager::handleEvent(const SDL_Event& event) {
break;
case SDLK_DOWN:
case SDLK_s:
if (Environment::finalIsBad || Environment::finalIsGood)
{
return;
}
Environment::downPressed = false;
if (!Environment::leftPressed && !Environment::rightPressed && !Environment::upPressed && !Environment::downPressed) {
if (Environment::violaCurrentAnimation == 1) {
@ -775,6 +836,18 @@ void GameObjectManager::updateScene(size_t ms) {
}
}
if (Environment::goToLevel3)
{
Environment::goTolevel3Timer += ms;
if (Environment::goTolevel3Timer > 1500)
{
Environment::goToLevel3 = false;
switch_room(2);
}
}
//float Environment::monsterTimer = 0.0;
//int Environment::monsterState = 1;
}

View File

@ -98,16 +98,17 @@ public:
std::shared_ptr<Texture> dialogTexturePtr; // Активная текстура диалога
bool isDialogActive = false; // Флаг активности диалога
std::vector<std::string> batteryDialogTextures = { // Список диалогов
"./battery_dialog.bmp",
};
int BatteryDialogIndex = 0; // Текущий индекс диалога
std::shared_ptr<Texture> batteryDialogTexturePtr; // Активная текстура диалога
bool isBatteryDialogActive = false; // Флаг активности диалога
bool hasChoisedFriendship = false;
bool hasMadeChoise = false;
std::shared_ptr<Texture> finalGoodTexturePtr;
std::shared_ptr<Texture> finalBadTexturePtr;
private:
//int animationCounter = 0;
int lastMouseX = 0; // Добавляем переменные для хранения позиции мыши

View File

@ -4,6 +4,11 @@ namespace ZL
{
std::unordered_map<std::string, InventoryItem> gInventoryMap;
void clear()
{
gInventoryMap.clear();
}
void AddItemToInventory(const std::string& name, std::shared_ptr<Texture> tex, int slot_index)
{
if (slot_index > MAX_INVENTORY_SLOTS || slot_index < 1) {

View File

@ -32,8 +32,14 @@ void RenderSystem::drawScene(GameObjectManager& gameObjects) {
drawLoadingScreen(gameObjects);
}
else
if (Environment::finalIsBad || Environment::finalIsGood)
{
drawFinalScreen(gameObjects);
}
else
{
drawWorld(gameObjects);
glClear(GL_DEPTH_BUFFER_BIT);
drawUI(gameObjects);
}
CheckGlError();
@ -177,7 +183,7 @@ void RenderSystem::drawWorld(GameObjectManager& gameObjects) {
glBindTexture(GL_TEXTURE_2D, gameObjects.rooms[gameObjects.current_room_index].roomTexture->getTexID());
renderer.DrawVertexRenderStruct(gameObjects.rooms[gameObjects.current_room_index].textMeshMutable);
if (gameObjects.current_room_index == 1)
if (gameObjects.current_room_index == 1 && Environment::goToLevel3 == false)
{
drawMonster(gameObjects);
}
@ -249,7 +255,7 @@ void RenderSystem::drawUI(const GameObjectManager& gameObjects) {
std::cout << "Found activeObjectScreenTexturePtr" << std::endl;
int screenX, screenY;
Vector3f objectPosPlusShift = ao->objectPos + Vector3f{ 0, -Environment::cameraDefaultVerticalShift, 0 };
Vector3f objectPosPlusShift = ao->objectPos + Vector3f{ 0, -Environment::itemDefaultVerticalShift, 0 };
worldToScreenCoordinates(objectPosPlusShift, currentProjectionModelView,
Environment::width, Environment::height, screenX, screenY);
@ -358,6 +364,43 @@ void RenderSystem::drawLoadingScreen(const GameObjectManager& gameObjects)
}
void RenderSystem::drawFinalScreen(const GameObjectManager& gameObjects)
{
renderer.shaderManager.PushShader("default");
// Если шейдер ожидает атрибуты вершин, их нужно включить
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
renderer.PushProjectionMatrix(static_cast<float>(Environment::width),
static_cast<float>(Environment::height));
renderer.PushMatrix();
renderer.LoadIdentity();
if (Environment::finalIsBad)
{
glBindTexture(GL_TEXTURE_2D, gameObjects.finalBadTexturePtr->getTexID());
}
else
{
glBindTexture(GL_TEXTURE_2D, gameObjects.finalGoodTexturePtr->getTexID());
}
renderer.DrawVertexRenderStruct(gameObjects.loadingScreenMeshMutable);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
// Выключаем атрибуты, чтобы сохранить баланс
renderer.DisableVertexAttribArray(vPositionName);
renderer.DisableVertexAttribArray(vTexCoordName);
// Снимаем шейдер, тем самым балансируя стек
renderer.shaderManager.PopShader();
}
void RenderSystem::drawMonster(const GameObjectManager& gameObjects)
{
renderer.shaderManager.PushShader("default");
@ -368,7 +411,7 @@ void RenderSystem::drawMonster(const GameObjectManager& gameObjects)
renderer.EnableVertexAttribArray(vTexCoordName);
renderer.PushProjectionMatrix(static_cast<float>(Environment::width),
static_cast<float>(Environment::height), -10, 10);
static_cast<float>(Environment::height));
renderer.PushMatrix();
renderer.LoadIdentity();

View File

@ -29,6 +29,7 @@ private:
void drawViola(GameObjectManager& gameObjects);
void drawLoadingScreen(const GameObjectManager& gameObjects);
void drawFinalScreen(const GameObjectManager& gameObjects);
void drawMonster(const GameObjectManager& gameObjects);
ShaderManager shaderManager;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 MiB

After

Width:  |  Height:  |  Size: 4.7 MiB

BIN
door.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 MiB

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 KiB

After

Width:  |  Height:  |  Size: 768 KiB

BIN
photo.bmp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 MiB

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 MiB

After

Width:  |  Height:  |  Size: 4.7 MiB

BIN
viola.bmp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 MiB

After

Width:  |  Height:  |  Size: 48 MiB