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
Google Nik Collection za darmo!

Google Nik Collection za darmo!

Wczoraj Google uderzyło z przepięknym prezentem Wielkanocnym – pakiet Google Nik Collection został pakietem… darmowym! 🙂

Jeśli jest ktoś (interesujący się fotografią), kto nie zna jeszcze tego pakietu, ten musi czym prędzej nadrobić zaległości. Pakiet składa się z:

  • Analog Efex Pro
  • Color Efex Pro
  • Silver Efex Pro
  • Viveza
  • HDR Efex Pro

oraz dwóch najważniejszych:

  • Sharpener Pro
  • Dfine

Jako szybki test, wziąłem moje niedawno wykonane zdjęcie na tapetę. Trzy zdjęcia, bracketowane co 2EV. Najpierw przepuściłem je przez RAW Pre-Sharpenera, następnie połączyłem w HDR Efex i na końcu wygładziłem w Dfine. Żadnej więcej obróbki nie stosowałem, poza eksportem do JPEGa, oczywiście 😉 Efekt poniżej.


Zachęcam do zapoznania się z pakietem, warto.

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin
Obiektyw Sigma UC ZOOM 70-210mm 1:4-5.6

Obiektyw Sigma UC ZOOM 70-210mm 1:4-5.6

Jako, że zbliża się wielkimi krokami nowy sezon występów tanecznych mojej córki, Natalki, a także wiedząc, jak wielką porażką (przynajmniej wg mnie samego) fotograficzną z tego tytułu był sezon poprzedni, postanowiłem rozejrzeć się za nowym obiektywem. Oczywiście, rozmawiamy o cenach dla ludzi, dla których fotografia to tylko hobby. Wiem, że za 2-4 tysiące mógłbym mieć wspaniałe szkło, no ale… wystarczy, że aparat tyle kosztował 😉  Jeszcze gwoli uzupełnienia tematu, ostatnio zakupiłem również monopod, który przyda się na tego typu imprezy, gdzie ze statywem nie ma się jak pchać.

Po kilku tygodniach polowań udało mi się wygrać aukcję, na której wytargałem Sigmę UC ZOOM 70-210mm 1:4-5.6 na bagnecie pentaksa za jedyne 91 zł 🙂 Zbyt wiele od niego nie oczekiwałem… dzisiaj przyszedł, więc po pracy wybrałem się z nim nad wody Odry (wystarczy wyjść z biura i już tam jestem 😉 ).

Na pierwszy ogień poszedł test bokeh. Tutaj nie miałem zbyt wielkich oczekiwań – raz, że zoom, dwa, że dość ciemny jednak (pełna dziura to zaledwie f4). I… mocno się zdziwiłem. Obiektyw ma przepiękny bokeh!

Potem poszedł test ostrości… dzisiaj, jako że z ręki strzelałem, na krótkim końcu i w okolicach (tak 70-90mm). Tutaj również obiektyw zdał test. Wychodzi na to, że za 90 zł udało mi się dostać całkiem udane szkiełko 🙂  duże toto co prawda, no i oczywiście w pełni manualne, ale mi to akurat w ogóle nie przeszkadza (gdy się uczyłem fotografii, nie istniał autofokus 😉 ).

Reasumując: za mniej, niż 100zł mam szkiełko, które potrafi dać sporo radości 🙂 czego i Wam życzę. A za dwa tygodnie dam znać, jak się toto spisuje w trudnych warunkach oświetleniowych…

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin
Dogwood 52 Week Photography Challenge

Dogwood 52 Week Photography Challenge

Z początkiem tego roku, trafiłem na ciekawy projekt fotograficzny: Dogwood 52 Week Photography Challenge. Projekt polega na tym, że są ustalone 52 tematy zdjęć, które należy wykonywać tydzień po tygodniu. Za każdym razem należy wykonać nowe zdjęcie, specjalnie na potrzeby owego projektu. W ten sposób, w miarę egzekucji, powinniśmy rozwinąć nasze zdolności postrzegania świata dookoła nas, a także wzmocnić umiejętności czysto techniczne, nauczyć się planowania fotografii itd.

Stwierdziłem, że taki projekt to ciekawa sprawa – no i przystąpiłem do niego 🙂 Aktualnie ukończyłem tydzień szósty i już myślę nad tygodniem siódmym. Poniżej możecie obejrzeć moje wypociny. Galeria poniżej będzie uaktualniana w miarę postępów.

Oprócz opisu, dostępnego pod linkiem powyżej, można znaleźć w sieci kilka miejsc, gdzie ludzie wykonujący ów challenge, publikują swoje prace, pomagając sobie nawzajem. Przekrój umiejętności uczestników jest ogromny, od totalnych początkujących, do półprofesjonalistów. Jest oficjalna grupa na Facebooku, jest półoficjalna grupa na Flickr, są i inne miejsca.

 

Facebooktwittergoogle_plusredditpinterestlinkedinmail
twitterlinkedin

Jack Reacher

Nadgoniłem w końcu i uzupełniłem stan przeczytania serii książek Lee Childa o Jacku Reacherze. Na tę chwilę w Polsce na serię składa się 19 tomów, ale za miesiąc ma się ukazać najnowszy, dwudziesty tom (oryginał ukazał się latem tego roku).

Zmuś mnie (Make me)

Jeśli ktoś nie czytał jeszcze (są tacy?) to proponuję zrobić to samo, co i mi poleciła zrobić moja bibliotekarka: zacząć serię od tomu ósmego („Nieprzyjaciel”) i dopiero powrócić do tomu pierwszego („Poziom pierwszy”). Tom ósmy to retrospekcja, w którym poznajemy również brata Reachera i jego matkę, co okazało się być dość wygodne w świetle wydarzeń tomu pierwszego.

Jak się czyta Reachera? Część powieści pisana jest w pierwszej osobie, część (większość) w trzeciej. Dziwny zabieg  . Przestrzeń czasowa między powieściami początkowo była dość duża (co najmniej kilka-kilkanaście miesięcy), później zaczęła maleć. Zapewne Lee Child zorientował się, że seria „nieco” urośnie i życia Reachera mu zabraknie w tym tempie  . Rozkręcanie akcji zazwyczaj nie przebiega zbyt długo i dość szybko wskakujemy w główny nurt wydarzeń. Trzeba też przyznać, że całość jest dość spójna – nawet w ujęciu całej serii. No, może jakoś tak bez wyraźnego powodu zniknęła z radarów postać Jodie Garber.

Poza tym, mi osobiście postać Reachera bardzo przypadła do gustu, mogę go w swoim rankingu postawić na równi z Seanem Dillonem Higginsa  . Akcja jest zawsze wartka i dość ciekawa. Czas przy lekturze spędza się miło. Z czystym sumieniem mogę polecić każdemu fanowi powieści akcji.

…nie mogło zabraknąć słowa na temat jedynej (jak dotąd) ekranizacji Reachera – Jednym strzałem. No cóż… ekranizacja ma jeden podstawowy mankament – aktora grającego postać Jack Reachera. Otóż widzicie, oryginalny Jack Reacher to gość, który ma 195 cm wzrostu, ma 127 cm w klacie i waży ponad sto kilo. Aż tu nagle… na odtwórcę wzięli… Toma Cruise’a  czyli kurdupelka (nic mu nie ujmując). Poza tym „drobnym” (nomen-omen) faktem, fabuła jest w miarę dobrze oddana, choć oczywiście nie pozbawiona sporych skrótów. Kogo bym widział bardziej w tej roli? Chyba jestem skłonny zgodzić się z Drew McWeenym, że całkiem nieźle by tu pasował nie kto inny, jak Dwayne Johnson. Niemniej, szykuje się chyba kolejna odsłona Jacka Reachera z Tomem Cruisem w roli głównej. Pożyjemy – zobaczymy!

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