Selektor koloru z trybem float

Selektor koloru z trybem float

Jest wiele selektorów kolorów online. Większość zwraca wyniki w wartościach RGB z zakresu <0..255>, w zapisie szesnastkowym, oraz HSL. Jednak brakuje w nich wszystkich jeszcze jednej formy podawania wyników: w formie wartości RGB w zakresie <0..1>, używanym w niektórych programach, zwłaszcza 3D. Osobiście często używam POV-Raya i za każdym razem muszę używać kalkulatora, aby przeliczyć wszystkie trzy wartości na odpowiedni zakres.

Początkowo miałem zamiar napisać malutki programik do tego celu, ale stwierdziłem, że wersja on-line będzie wygodniejsza w użyciu. Nie jest to żadne rocket science 😉 ale swoje zadanie spełnia. Może w przyszłości jakoś go rozbuduję i usprawnię, ale… nie obiecuję 😉

Jeszcze jedna uwaga… poniższe nie działa z IE… 🙁






Może się komuś przyda – mi na pewno 🙂

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Pluginy aplikacji Qt5

Tworzenie systemu pluginów aplikacji działającej na różnych systemach wymagało zawsze wielokrotnej implementacji tego samego – dla każdego systemu oddzielnie, głównie z powodu sposobu dynamicznego ładowania bibliotek. W Qt jednak jest inaczej – framework ten udostępnia bardzo sprawnie działający system pluginów.

Na początek zdefiniujmy interfejs (API):

#ifndef DESIGNLAYER_API_H
#define DESIGNLAYER_API_H

#include <QString>
#include <QtPlugin>

namespace apophysis
	{		
	class DesignLayerAPI
		{
		public:
			virtual ~DesignLayerAPI() {};
			virtual QString getName() = 0;
		};
	}

#define DesignLayerAPI_iid "org.apophysis.DesignLayerAPI"

Q_DECLARE_INTERFACE(apophysis::DesignLayerAPI, DesignLayerAPI_iid)

#endif // DESIGNLAYER_API_H

Widzimy tu wirtualną klasę, definiującą metodę getName(). Po definicji API, następuje definicja interfejsu, po którym Qt będzie rozpoznawać nasze pluginy, implementujące te API.

Czas na plugin. Deklarujemy klasę implementującą API:

#ifndef DESIGNLAYER_IMAGE_H
#define DESIGNLAYER_IMAGE_H

#include <QObject>
#include <QString>
#include <DesignLayerAPI.h>

class designLayerImage : public QObject, apophysis::DesignLayerAPI
	{
	Q_OBJECT
		Q_PLUGIN_METADATA(IID "org.apophysis.DesignLayerAPI" FILE "designLayerImage.json")
		Q_INTERFACES(apophysis::DesignLayerAPI)

	public:
		designLayerImage();
		QString getName() Q_DECL_OVERRIDE;
	private:
		QString theName;
	};

#endif // DESIGNLAYER_IMAGE_H

Oprócz deklaracji metod zadeklarowanych w interfejsie, widzimy tu jeszcze jedną rzecz: definicję metadanych. Po co to? Metadane pluginu można odczytać bez faktycznego ładowania biblioteki. Zapisujemy je w pliku JSON, np:

{
"type"			:	"Layer",
"name"			:	"Image layer plugin",
"shortname"		:	"ImageLayer",
"version"		:	"0.1",
"author"		:	"Apophysis internal",
"description"	:	"Used mainly for backgrounds: solid colour, gradients, images, etc."
}

Właściwa implementacja jest oczywista, żadnej magii tu nie ma:

#include "designLayerImage.h"

designLayerImage::designLayerImage()
	:
	theName(QLatin1Literal("Unnamed image layer"))
	{
	}

QString designLayerImage::getName()
	{
	return theName;
	}

OK, mamy plugin. Jak go użyć? Na szczęście, to również jest proste (i przenośne! Na pewno działa bez zmian pod Windows i Linux, pod OSX nie sprawdzałem).

Najpierw sprawdzimy w działaniu system metadanych. Załóżmy, że pluginy mamy w podkatalogu plugins (niespodzianka!):

#include <QPluginLoader>
#include <DesignLayerAPI.h>

void listPlugins()
	{
	QDir pluginsDir(qApp->applicationDirPath());
	pluginsDir.cd(QLatin1Literal("plugins"));
	foreach(QString fileName, pluginsDir.entryList(QDir::Files, QDir::Name))
		{
		QString libExt;
#if defined(Q_OS_WIN)
		libExt = QLatin1Literal("dll");
#elif defined(Q_OS_MAC)
		libExt = QLatin1Literal("bundle");
#elif defined(Q_OS_LINUX)
		libExt = QLatin1Literal("so");
#endif
		if (QFileInfo(fileName).suffix().toLower() == libExt)
			{
			QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
			qDebug() << "File:" << fileName;
			QJsonObject jsonMetaData = pluginLoader.metaData().value(QLatin1Literal("MetaData")).toObject();
			qDebug() << jsonMetaData.value(QLatin1Literal("type")).toString();
			qDebug() << jsonMetaData.value(QLatin1Literal("name")).toString();
			qDebug() << jsonMetaData.value(QLatin1Literal("shortname")).toString();
			qDebug() << jsonMetaData.value(QLatin1Literal("version")).toString();
			qDebug() << jsonMetaData.value(QLatin1Literal("author")).toString();
			qDebug() << jsonMetaData.value(QLatin1Literal("description")).toString();
			}
		}
	}

W zasadzie moglibyśmy się obejść bez sekcji sprawdzania rozszerzenia plików, ale używam tego w razie, gdyby w katalogu były jeszcze jakieś inne pliki (ot, chociażby PDB).

OK, czas na crème de la crème, czyli stworzenie instancji klasy zaimplementowanej w pluginie:

void apophysis::Design::addLayer(const QString & aLayerType)
	{
	QDir pluginsDir(qApp->applicationDirPath());
	pluginsDir.cd(QLatin1Literal("plugins"));
	foreach(QString fileName, pluginsDir.entryList(QDir::Files, QDir::Name))
		{
		QString libExt;
#if defined(Q_OS_WIN)
		libExt = QLatin1Literal("dll");
#elif defined(Q_OS_MAC)
		libExt = QLatin1Literal("bundle");
#elif defined(Q_OS_LINUX)
		libExt = QLatin1Literal("so");
#endif
		if (QFileInfo(fileName).suffix().toLower() == libExt)
			{
			QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
			QString plugintype = pluginLoader.metaData().value(QLatin1Literal("MetaData")).toObject().value(QLatin1Literal("type")).toString();
			QString pluginshortname = pluginLoader.metaData().value(QLatin1Literal("MetaData")).toObject().value(QLatin1Literal("shortname")).toString();
			if ((plugintype == QLatin1Literal("Layer")) && (pluginshortname == aLayerType))
				{
				QObject *plugin = pluginLoader.instance();
				if (plugin)
					{
					apophysis::DesignLayerAPI *layer = qobject_cast<apophysis::DesignLayerAPI *>(plugin);
					if (layer)
						theLayers.append(layer);
					}
				}
			}
		}
	}

Jak widać, zarówno tworzenie, jak i używanie pluginów w tym systemie jest trywialne - i jest całkowicie przenośne.

Happy coding!

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Git a automatyczne budowanie Jenkinsem

Do tej pory, na cele Continuous Integration, w Jenkinsie miałem ustawione sprawdzanie repozytorium Git (opcja SCM polling) co 15 minut. W zasadzie to działało, ale bywały momenty, że owe 15 minut było okresem zbyt długim. Można oczywiście (nikt nie broni…) ustawić okres sprawdzania na * * * * *, czyli sprawdzanie co minutę, ale to również nie jest dobre rozwiązanie, z oczywistych powodów. Dlatego też postanowiłem powiesić akcję odpytywania Gita przez Jenkinsa na haku, żeby automagicznie każdy push od developera powodował start Jenkinsa.

Oczywiście, Jenkins musi być wyposażony we wtyczkę Git.

Zaczynamy od próby ręcznego zdalnego wywołania Jenkinsa. Otóż wtyczka Git otwiera takową drogę właśnie na okoliczność notyfikacji o nowym commicie. Jak czytamy w dokumentacji wtyczki, można powiadomić Jenkinsa o konkretnym commicie, ale ja na cele Continuous Integration i tak chcę pobrać najnowsze w danej chwili źródła, aby sprawdzić integrację poszczególnych prac właśnie. Dlatego użyjemy najprostszej formy wywołania wtyczki, która spowoduje wymuszenie odpytania repozytorium o zmiany.

Na początek, w konfiguracji zadania Jenkinsowego, usuwamy wszelkie terminarze, jakie mieliśmy – ale samą opcję Poll SCM musimy pozostawić włączoną (ale pustą):

Jenkins SCM Polling

Następnie, będąc na serwerze gitowym, instalujemy pakiet curl, jeśli go tam jeszcze nie ma. Gdy już go mamy, wywołujemy testową komendę:

curl http://moj.jenkins.com:8080/git/notifyCommit?url=git@moj.git.com:WspanialeRepo

Ścieżka do repozytorium powinna być dokładnie taka sama, jaką podajemy przy jego klonowaniu. Jeśli wszystko pójdzie dobrze, przeczytamy, że zostało zlecone odpytanie repozytorium. Sprawdzamy w logach Jenkinsa Git Polling Log, czy rzeczywiście tak było.

Jeśli wszystko zadziałało, czas ten proces zautomatyzować. Aby wywołać jakąś akcję po każdym pushu od developera, musimy zdefiniować hak post-receive. W katalogu repozytorium znajduje się podkatalog hooks. Tworzymy nowy plik i nadajemy mu właściwe uprawnienia:

touch post-receive
chown git:git post-receive
chmod 775 post-receive

i edytujemy go:

#!/bin/sh
curl http://moj.jenkins.com:8080/git/notifyCommit?url=git@moj.git.com:WspanialeRepo

Od tej pory, za każdym razem, gdy developer zrobi pusha, Jenkins wykona swoją robotę 🙂 A my cieszymy się z Continuous Integration 😉

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Android – dobór koloru tekstu pod kolor tła

Gdy dodajemy tekst na jakimś tle, którego kolor ustawiany jest dynamicznie, mamy problem: jaki kolor tekstu? Czarny czy biały?

Aby rozwiązać ten problem, należy sprawdzić, jak jasny jest kolor tła. Problem w tym, że wpływ poszczególnych komponentów RGB na ogólną jasność jest różna, stąd trzeba ową jasność policzyć uwzględniając tę zasadę. Ja użyłem dość popularnej w internecie formuły – i spisuje się naprawdę dobrze.

    /**
     * Find the right color of the text, depending on the color of the background.
     * @param aColor Color, to which we're adjusting by contrast.
     * @return Black or white color.
     */
    public int getBlackOrWhite(int aColor)
    {
        int red = (aColor >> 16) & 0xFF;
        int green = (aColor >> 8) & 0xFF;
        int blue = (aColor >> 0) & 0xFF;
        int result = 0;
        if (((double)red*0.299 + (double)green*0.587 + (double)blue*0.114) > 186)
            result = Color.BLACK;
        else
            result = Color.WHITE;
        return result;
    }

Parametrem wejściowym jest kolor tła, wartość zwracana to kolor czarny lub biały. Formuła doboru wygląda na dobrą, bo spośród tych kolorów, które przetestowałem, wybór: czarny-biały zawsze był trafny, stąd mogę ją z czystym sumieniem polecić – co też czynię.

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Zmiana parametrów ekranu z C++ pod Windows 7

Czasem zachodzi potrzeba zmiany ustawień monitora z poziomu aplikacji. Czasem chodzi nam o rozdzielczość, czasem o odświeżanie. Mi akurat chodziło o odświeżanie – aplikacja wyświetla materiał video w różnych formatach i potrzebowałem dostosować monitor do materiału, aby osiągnąć jak najlepszą synchronizację.

Moja aplikacja jest w Qt (a jakże), więc zaczynam od sprawdzenia, na którym monitorze jest wyświetlona moja aplikacja:

int monitor = QApplication::desktop()->screenNumber(ui.centralWidget);

Aby napisać działającą poprawnie metodę zmieniającą parametry wyświetlania pod Windows 7, musimy podać dokładną nazwę urządzenia. Podawanie NULL, która wg dokumentacji MSDN powinna zadziałać na domyślnym urządzeniu, pod Windows 7 kończy się błędem niepoprawnego parametru. Dlatego najpierw listujemy wszystkie dostępne urządzenia – i jeśli jest to nasze urządzenie, pobieramy jego aktualne ustawienia i modyfikujemy je:

DEVMODE winMode;
DISPLAY_DEVICE displayDevice;
memset(&displayDevice, 0, sizeof(DISPLAY_DEVICE));
displayDevice.cb = sizeof(displayDevice);
DWORD iDevNum = 0;
if (EnumDisplayDevices(NULL, iDevNum, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME))
{
	if (EnumDisplaySettings(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &winMode) != 0)
	{
		winMode.dmPelsWidth = aWidth;
		winMode.dmPelsHeight = aHeight;
		winMode.dmDisplayFrequency = aFreq;
		ChangeDisplaySettingsEx(displayDevice.DeviceName, &winMode, NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
	}
}

Wg dokumentacji, po użyciu ChangeDisplaySettingsEx powinniśmy jeszcze spowodować rozesłanie komunikatu do wszystkich aplikacji, wywołując ją jeszcze raz w ten sposób:

ChangeDisplaySettings(NULL, 0);

Składając wszystko do kupy, napisałem sobie metodę, która bierze parametry ustawień (rozdzielczość i odświeżanie) i sprawdza, czy podałem wartość większą od zera (gdybym chciał ustawić tylko niektóre z tych parametrów):

//! Change display (monitor) settings. If you don't want to change any of the values, pass 0 for them.
static bool setCurrentDisplayMode(int aWidth, int aHeight, int aFreq, int aDisplay = 0);
bool sysWindowsApi::setCurrentDisplayMode(int aWidth, int aHeight, int aFreq, int aDisplay)
{
	bool ret = false;
	DEVMODE winMode;
	DISPLAY_DEVICE displayDevice;
	memset(&displayDevice, 0, sizeof(DISPLAY_DEVICE));
	displayDevice.cb = sizeof(displayDevice);
	if (EnumDisplayDevices(NULL, aDisplay, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME))
	{
		if (EnumDisplaySettings(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &winMode) != 0)
		{
			if (aWidth > 0) winMode.dmPelsWidth = aWidth;
			if (aHeight > 0) winMode.dmPelsHeight = aHeight;
			if (aFreq > 0) winMode.dmDisplayFrequency = aFreq;
			if (ChangeDisplaySettingsEx(displayDevice.DeviceName, &winMode, NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL)
			{
				ChangeDisplaySettings(NULL, 0);
				ret = true;
			}
		}
	}
	return ret;
}

Z głównej aplikacji wywołuję ją np. tak:

sysWindowsApi::setCurrentDisplayMode(0, 0, 60, QApplication::desktop()->screenNumber(ui.centralWidget));

powoduje to zmianę tylko częstotliwości odświeżania, pozostawiając rozdzielczość bez zmian – i tylko na tym monitorze, na którym znajduje się nasza aplikacja.

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Android – backup danych aplikacji bez roota

Ostatnio musiałem się zmierzyć z przeniesieniem danych pomiędzy dwoma telefonami Androidowymi, które nie są zrootowane. Szczerze mówiąc, nigdy tego jeszcze nie robiłem, bo w swoich urządzeniach root jest pierwszą czynnością, którą wykonuję po rozpakowaniu 😉 dlatego też postanowiłem się podzielić z wami efektami prac.

Jedyne narzędzie, którego będziesz potrzebować (poza sterownikami USB do telefonu, które powinny się automatycznie zainstalować po podłączeniu telefonu) to Android Platform Tools, którego głównym składnikiem jest Android Debug Bridge (ADB). Android Platform Tools normalnie wchodzą w skład Android SDK i stamtąd właśnie należy je pobrać, ale ktoś, kto potrzebuje jedynie wykonać kilka prostych czynności, nie musi chcieć ciągnąć tego całego bagażu. Tutaj możecie pobrać paczuszkę przygotowaną przeze mnie. Są to Platform Tools w wersji 22, czyli najnowszej na dzień dzisiejszy, obsługującej Android 5.1.

Zanim jednak będziecie mogli użyć ADB na swoim telefonie, należy włączyć ukryte menu w ustawieniach telefonu. Idziemy do Ustawienia → Informacje o urządzeniu i klikamy raz za razem pozycję Numer wersji. Po około 10 tapnięciach pokaże się komunikat, że nowe menu zostało włączone. Wracamy do Ustawień i widzimy nowe menu: Opcje programisty. Wchodzimy do środka i zaznaczamy opcję Debugowanie USB. Od tej chwili możemy używać ADB.

debug_usb

Rozpakowujemy Platform Tools do jakiegoś katalogu. Najlepiej dodać sobie ścieżkę do niego do zmiennej $PATH, żeby móc z adb korzystać z dowolnego miejsca. Sprawdzamy, czy działa:

\> adb version
Android Debug Bridge version 1.0.32

Jeśli działa, podpinamy telefon, odczekujemy, aż system zainstaluje nowe sterowniki USB (z powodu aktywacji trybu debugowania) i sprawdzamy, czy urządzenie jest widoczne:

\> adb devices
List of devices attached
10feda99 device

Sukces! Można działać 🙂

Jeśli powyżej ujrzysz kilka urządzeń na liście, wszystkie dalsze komendy musisz poprzedzić opcją -s z identyfikatorem urządenia.

Sama czynność backupu jest w zasadzie banalna. Zacznijmy od opcji backupowania jednej lub kilku wybranych aplikacji:

\> adb backup -f CatchNotes.ab -apk com.threebanana.notes com.catchnotes.sketch.samsung
Now unlock your device and confirm the backup operation.

Co tu widzimy? Zleciłem backup dwóch aplikacji (com.threebanana.notes oraz com.catchnotes.sketch.samsung) tworzących pakiet Catch Notes i plugin do niego (już niedostępne w sklepie Play – a szkoda). Backup danych wraz z samą instalką aplikacji (APK). Kopia zapasowa zostanie zapisana do pliku CatchNotes.ab. Poniżej widać, że program każe wziąć do ręki telefon.

backup

Jeśli korzystamy z szyfrowania naszych danych w telefonie, wpisujemy hasło i następnie Utwórz kopię zapasową danych. I… to tyle 🙂  na dysku zostanie utworzony plik o nazwie CatchNotes.ab, zawierający dwie instalki APK wraz z danymi obu aplikacji.

Nie wszystkie aplikacje spełniają zalecenia Androida i stosują się do jego zasad (a jakże…). Np. taka gra Temple Run 2 przechowuje swoje dane inaczej niż pozostałe aplikacje, co spowoduje, że powyższy sposób nie zachowa owych danych. Co robić? Musimy skopiować je ręcznie. Wchodzimy Eksploratorem (lub Total Commanderem, jeśli jesteśmy świadomymi użytkownikami komputera 😉 ) do telefonu i wchodzimy do katalogu Phone\Android\data. Zobaczymy tam całą masę katalogów, a wśród nich com.imangi.templerun2. Kopiujemy na komputer cały katalog. W urządzeniu docelowym musimy koniecznie najpierw zainstalować aplikację, a później nadpisać ten sam katalog naszą kopią.

templerun2_data

Oprócz kopii zapasowej wybranych aplikacji, możemy chcieć wykonać pełny backup. Mamy do dyspozycji kilka dodatkowych opcji:

  • -shared spowoduje skopiowanie wszystkich danych współdzielonych i z karty SD.
  • -all spowoduje skopiowanie wszystkich aplikacji, bez ich podawania w komendzie.
  • -system / -nosystem jeśli podaliśmy opcję -all, ta opcja decyduje, czy backupujemy tylko aplikacje użytkownika, czy również aplikacje systemowe.

Np.:

\> adb backup -apk -shared -all

Taka opcja zajmie sporo czasu i wygeneruje potężny plik.

 

Jak taką kopię zapasową później przywrócić?

\> adb restore mojakopia.ab

i… tyle 🙂

 

I tak już na końcu, jako bonus… wśród naprawdę wielu fajnych rzeczy, które można zrobić przy pomocy ADB, jest m.in. również nagrywanie ekranu (tylko na nowszych Androidach):

\> adb shell screenrecord /storage/extSdCard/test123.mp4

Video jest mocno skompresowane, ale do prezentacji jakiejś opcji czy czynności nadaje się świetnie 🙂

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Qt 5.3.2 vs GCC 4.8.1 TDM64

Kolejna wersja Qt, kolejna kompilacja… tym razem budujemy Qt 5.3.2 pod GCC pod Windows, w wydaniu TDM64.

Najpierw – konfiguracja. Ja stworzyłem sobie plik configure.cmd:

cd Qt-5.3.2-gcc-x86_64

set PATH_GCC=C:\gcc\gcc-4.8.1-tdm64
set PATH_PERL=C:\Perl
set PATH_PYTHON=C:\Python27
set PATH_ICU=D:\devel\Qt\Qt5_deps\icu
set PATH_OPENSSL=D:\devel\Qt\Qt5_deps\openssl-1.0.1e

set INCLUDE=%PATH_ICU%\dist\include;%PATH_OPENSSL%\dist\include
set LIB=%PATH_ICU%\dist\lib;%PATH_OPENSSL%\dist\lib
set QMAKESPEC=
set QTDIR=

set PATH=%CD%\qtbase\bin;%CD%\gnuwin32\bin;%PATH_GCC%\bin;%PATH_PERL%\bin;%PATH_PYTHON%;%PATH_ICU%\dist\lib;%PATH_OPENSSL%\dist\bin;%SystemRoot%\System32
set MAKE_COMMAND=

configure -release -opensource -confirm-license -platform win32-g++ -developer-build -c++11 -opengl desktop -plugin-sql-odbc -qt-style-windowsxp -qt-style-windowsvista -nomake tests -nomake examples -skip qtwebkit -skip qtwebkit-examples -skip qtquick1

i uruchamiam.
Jeśli wszystko przebiegło pomyślnie, pozostało nam wykonać jeszcze jeden szybki fix. Będzie to fix nieelegancki i tylko na potrzeby naszej kompilacji, bez zagłębiania się w prawidłowe rozwiązanie, które zadziała z każdym kompilatorem.

Odnajdujemy plik qtwinextras\src\winextras\winshobjidl_p.h i na samym końcu usuwamy sekcję:

#if (defined _MSC_VER && _MSC_VER < 1600) || defined(Q_CC_MINGW)

#   if !defined(__MINGW64_VERSION_MAJOR) || !defined(__MINGW64_VERSION_MINOR) || __MINGW64_VERSION_MAJOR * 100 + __MINGW64_VERSION_MINOR < 301

typedef struct SHARDAPPIDINFOLINK
{
    IShellLink *psl;        // An IShellLink instance that when launched opens a recently used item in the specified
                            // application. This link is not added to the recent docs folder, but will be added to the
                            // specified application's destination list.
    PCWSTR pszAppID;        // The id of the application that should be associated with this recent doc.
} SHARDAPPIDINFOLINK;

#   endif // !defined(__MINGW64_VERSION_MAJOR) || !defined(__MINGW64_VERSION_MINOR) || __MINGW64_VERSION_MAJOR * 100 + __MINGW64_VERSION_MINOR < 301

#endif

Usuwamy tę sekcję całkowicie. Plik zapisujemy, wracamy do głównego katalogu i zaczynamy budowanie:

mingw32-make

Zbudowane binarki będą, jak zwykle, w katalogu qtbase\bin.

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

MEncoder – odwracanie video

W moim telefonie mam funkcję nagrywania filmów w podwyższonych fps (czyli krótko mówiąc: slow-motion). Oglądając dzisiaj jeden z takich filmików zapragnąłem zobaczyć go od tyłu. I… pojawił się problem – jak to, do diabła, zrobić szybko i wygodnie? No i – oczywiście – używając tylko darmowych narzędzi? Jak zwykle, skorzystałem z mencodera, ale tym razem w połączeniu z mplayerem i tworząc cały skrypt, gdyż cały proces jest kilkustopniowy. Zatem – do dzieła!

Pierwszą czynnością, jaką musimy wykonać, jest rozbiórka naszego video do pojedynczych klatek. Użyjemy do tego mplayera:

mplayer -vo png:outdir=klatki video.avi

W efekcie otrzymamy katalog klatki, a w nim pliki png z poszczególnymi klatkami z całego video. Zanim przekujemy je w video, musimy przygotować listę plików – w kolejności odwróconej. Z pomocą przyjdzie nam zwykła komenda dir:

dir klatki /b /s /o-n >> klatki.txt

W pliku zapisane zostaną pliki wraz z pełnymi ścieżkami, w porządku alfabetycznym malejącym. Taki plik może już posłużyć nam do zakodowania naszego nowego video:

mencoder mf://@klatki.txt -mf fps=30:type=png -ovc x264 .... itd.

Ja, w celu wygodnego używania, napisałem sobie mały skrypt, który przyjmuje jeden lub dwa parametry. Pierwszy, to oczywiście nazwa pliku video do obrobienia, a drugi, to docelowy fps. Jeśli drugiego parametru nie podamy, zostanie użyta wartość 30 klatek na sekundę. Na końcu, skrypt sprząta po sobie 🙂 Życzę miłego używania!

set MPLAYER="C:\Program Files (x86)\mplayer\mplayer.exe"
set MENCODER="C:\Program Files (x86)\mplayer\mencoder.exe"

mkdir %1.images
%MPLAYER% -vo png:outdir=%1.images %1
dir %1.images /b /s /o-n >> %1.frames.txt

set H264_FAST=-ovc x264 -x264encopts subq=4:bframes=2:b_pyramid=normal:weight_b
set H264_HIGH=-ovc x264 -x264encopts subq=5:8x8dct:frameref=2:bframes=3:b_pyramid=normal:weight_b
set H264_VHIGH=-ovc x264 -x264encopts subq=6:partitions=all:8x8dct:me=umh:frameref=5:bframes=3:b_pyramid=normal:weight_b
set FPS=%2
if [%2] == [] set FPS=30

%MENCODER% mf://@%1.frames.txt -mf fps=%FPS%:type=png %H264_HIGH% -nosound -o %1.reversed.avi
rd /s /q %1.images
del %1.frames.txt
Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Wojny wsadowe – jesteś pewien?

Tym razem zajmiemy się wyświetlaniem okna komunikatu z poziomu skryptu powłoki Windows. Oknu możemy nadać te klawisze, które chcemy, np. Ok, Yes i No, czy jakikolwiek inny zestaw, a także na wyjściu sprawdzić, co user ewentualnie wybrał (jeśli jakiś wybór mu daliśmy 😉 ).

Jeśli chcemy użyć pełnego wachlarza możliwości, stwierdziłem, że najmniejszą linią oporu będzie posłużenie się skryptem VisualBasic i jego funkcją MsgBox, np.:

MsgBox("Are you sure you want to do this??",4,"Haha!")

Pierwszy parametr to treść komunikatu, trzeci to tytuł okna. Drugi parametr, to zestaw przycisków, które mają być udostępnione użytkownikowi do wyboru. Możliwości są następujące:

0 Tylko przycisk OK
1 OK i Cancel
2 Abort, Retry i Ignore
3 Yes, No i Cancel
4 Yes i No
5 Retry i Cancel
16 Użyj ikonki Critical Message
32 Użyj ikonki Warning Query
48 Użyj ikonki Warning Message
64 Użyj ikonki Information Message
0 Pierwszy przycisk jest domyślny
256 Drugi przycisk jest domyślny
512 Trzeci przycisk jest domyślny
768 Czwarty przycisk jest domyślny

Jak widać, mamy tu dostępne trzy grupy. Wybieramy pożądane opcje i sumujemy je, np. chcąc wyświetlić klawisze „Yes” i „No”, dodać ikonkę „Warning Query” i uczynić przycisk „No” domyślnie zaznaczonym, użyjemy wartości 4+32+256:

MsgBox("Are you sure you want to do this??",4+32+256,"Haha!")

Oczywiście, nic nie stoi na przeszkodzie, aby wpisać tam po prostu 292, ale dla późniejszej analizy i edycji, łatwiej pozostawić to w takiej czytelnej postaci.

Skrypt VBS skryptem VBS, ale my chcemy wywołać nasze okienko z regularnego skryptu wsadowego… uciekniemy się do małego tricku i… stworzymy tymczasowy plik vbs i go wywołamy 😉

echo MsgBox("Are you sure you want to do this??",4+32+256,"Haha!") >tempmsg.vbs
call tempmsg.vbs

Tu pojawia się kolejny problem: jak sprawdzić, co wybrał użytkownik? Najpierw, zmusimy nasz skrypt VBS do zwracania na swoim wyjściu wynik funkcji MsgBox – czyli właśnie wybór użytkownika. Robimy to tak:

wscript.quit MsgBox("Are you sure you want to do this??",4+32+256,"Haha!")

Dzięki temu, cały skrypt na swoim wyjściu zwróci wartość wg klucza:

1 OK
2 Cancel
3 Abort
4 Retry
5 Ignore
6 Yes
7 No

W naszym pierwotnym skrypcie, po wywołaniu skryptu VBS, musimy sprawdzić wartość zmiennej %errorlevel%, która wskazuje właśnie na wynik ostatnio wykonanej komendy.

echo wscript.quit MsgBox("Are you sure you want to do this??",4+32+256,"Haha!") >tempmsg.vbs
call tempmsg.vbs
if %errorlevel% NEQ 6 goto finish

echo Jest! Zgodził się! Hurrra!!
echo Tutaj rozkręcamy imprezkę...

:finish
del tempmsg.vbs

Prawda, że proste? 🙂

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin