|
Forgive me for what I'm about to paste. The whole grotty mess is in the convert<>() function at the bottom of this horrible file: gfx/include/gfx_pixel.hpp[^]
I've omitted several pages of code.
The worst/best part of this is it's all done at compile time, and generates at most several lines of asm, but often a lot less, and in cases where the input values are known at compile time the entire computation is done at compile time and generates exactly no binary code.
Despite that, it is as if it was written at gunpoint.
I could break it up into a ton of nasty routines but that just feels like spreading the mess around.
I'm ready for the flaming.
template<typename PixelTypeLhs, typename PixelTypeRhs>
constexpr static inline gfx_result convert(PixelTypeLhs source,PixelTypeRhs* result,const PixelTypeRhs* background=nullptr) {
static_assert(helpers::is_same<PixelTypeLhs,PixelTypeRhs>::value || !PixelTypeLhs::template has_channel_names<channel_name::index>::value,"left hand pixel must not be indexed");
static_assert(helpers::is_same<PixelTypeLhs,PixelTypeRhs>::value || !PixelTypeRhs::template has_channel_names<channel_name::index>::value,"right hand pixel must not be indexed");
if(nullptr==result) return gfx_result::invalid_argument;
if(helpers::is_same<PixelTypeLhs,PixelTypeRhs>::value) {
if(nullptr==background) {
result->native_value=source.native_value;
return gfx_result::success;
} else {
return gfx_result::invalid_format;
}
}
bool good = false;
PixelTypeRhs tmp;
typename PixelTypeRhs::int_type native_value = tmp.native_value;
using is_rgb = typename PixelTypeLhs::template has_channel_names<channel_name::R,channel_name::G,channel_name::B>;
using is_yuv = typename PixelTypeLhs::template has_channel_names<channel_name::Y,channel_name::U,channel_name::V>;
using is_ycbcr = typename PixelTypeLhs::template has_channel_names<channel_name::Y,channel_name::Cb,channel_name::Cr>;
using is_hsv = typename PixelTypeLhs::template has_channel_names<channel_name::H,channel_name::S,channel_name::V>;
using is_hsl = typename PixelTypeLhs::template has_channel_names<channel_name::H,channel_name::S,channel_name::L>;
using is_cmyk = typename PixelTypeLhs::template has_channel_names<channel_name::C,channel_name::M,channel_name::Y,channel_name::K>;
using trhas_alpha = typename PixelTypeRhs::template has_channel_names<channel_name::A>;
using thas_alpha = typename PixelTypeLhs::template has_channel_names<channel_name::A>;
const bool has_alpha = thas_alpha::value;
const bool is_bw_candidate = 1==PixelTypeLhs::channels || (2==PixelTypeLhs::channels && has_alpha);
using tis_bw_candidate = typename PixelTypeLhs::template has_channel_names<channel_name::L>;
const bool is_bw_candidate2 = tis_bw_candidate::value;
const bool rhas_alpha = trhas_alpha::value;
const bool ris_bw_candidate = 1==PixelTypeRhs::channels || (2==PixelTypeRhs::channels && rhas_alpha);
using tris_bw_candidate = typename PixelTypeRhs::template has_channel_names<channel_name::L>;
const bool ris_bw_candidate2 = tris_bw_candidate::value;
using is_rhs_rgb = typename PixelTypeRhs::template has_channel_names<channel_name::R,channel_name::G,channel_name::B>;
using is_rhs_yuv = typename PixelTypeRhs::template has_channel_names<channel_name::Y,channel_name::U,channel_name::V>;
using is_rhs_ycbcr = typename PixelTypeRhs::template has_channel_names<channel_name::Y,channel_name::Cb,channel_name::Cr>;
using is_rhs_hsv = typename PixelTypeRhs::template has_channel_names<channel_name::H,channel_name::S,channel_name::V>;
using is_rhs_hsl = typename PixelTypeRhs::template has_channel_names<channel_name::H,channel_name::S,channel_name::L>;
using is_rhs_cmyk = typename PixelTypeRhs::template has_channel_names<channel_name::C,channel_name::M,channel_name::Y,channel_name::K>;
if(is_rgb::value && PixelTypeLhs::channels<5) {
using tindexR = typename PixelTypeLhs::template channel_index_by_name<channel_name::R>;
using tchR = typename PixelTypeLhs::template channel_by_index_unchecked<tindexR::value>;
const int chiR = tindexR::value;
using tindexG = typename PixelTypeLhs::template channel_index_by_name<channel_name::G>;
using tchG = typename PixelTypeLhs::template channel_by_index_unchecked<tindexG::value>;
const int chiG = tindexG::value;
using tindexB = typename PixelTypeLhs::template channel_index_by_name<channel_name::B>;
using tchB = typename PixelTypeLhs::template channel_by_index_unchecked<tindexB::value>;
const int chiB = tindexB::value;
if(is_rhs_rgb::value && PixelTypeRhs::channels<5) {
using trindexR = typename PixelTypeRhs::template channel_index_by_name<channel_name::R>;
using trchR = typename PixelTypeRhs::template channel_by_index_unchecked<trindexR::value>;
using trindexG = typename PixelTypeRhs::template channel_index_by_name<channel_name::G>;
using trchG = typename PixelTypeRhs::template channel_by_index_unchecked<trindexG::value>;
using trindexB = typename PixelTypeRhs::template channel_index_by_name<channel_name::B>;
using trchB = typename PixelTypeRhs::template channel_by_index_unchecked<trindexB::value>;
auto chR = source.template channel_unchecked<chiR>();
auto cR = helpers::convert_channel_depth<tchR,trchR>(chR);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexR::value>(native_value,cR);
auto chG = source.template channel_unchecked<chiG>();
auto cG = helpers::convert_channel_depth<tchG,trchG>(chG);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexG::value>(native_value,cG);
auto chB = source.template channel_unchecked<chiB>();
auto cB = helpers::convert_channel_depth<tchB,trchB>(chB);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexB::value>(native_value,cB);
good = true;
} else if(is_rhs_yuv::value && PixelTypeRhs::channels<5) {
using trindexY = typename PixelTypeRhs::template channel_index_by_name<channel_name::Y>;
using trchY = typename PixelTypeRhs::template channel_by_index_unchecked<trindexY::value>;
using trindexU = typename PixelTypeRhs::template channel_index_by_name<channel_name::U>;
using trchU = typename PixelTypeRhs::template channel_by_index_unchecked<trindexU::value>;
using trindexV = typename PixelTypeRhs::template channel_index_by_name<channel_name::V>;
using trchV = typename PixelTypeRhs::template channel_by_index_unchecked<trindexV::value>;
const auto cR = (source.template channel_unchecked<chiR>()*tchR::scaler)*255.0;
const auto cG = (source.template channel_unchecked<chiG>()*tchG::scaler)*255.0;
const auto cB = (source.template channel_unchecked<chiB>()*tchB::scaler)*255.0;
const auto chY = (0.257 * cR + 0.504 * cG + 0.098 * cB + 16)/255.0;
const auto chU = (-0.148 * cR - 0.291 * cG + 0.439 * cB + 128)/255.0;
const auto chV = (0.439 * cR - 0.368 * cG - 0.071 * cB + 128)/255.0;
const typename trchY::int_type cY =chY*trchY::scale+.5;
const typename trchU::int_type cU = chU*trchU::scale+.5;
const typename trchV::int_type cV = chV*trchV::scale+.5;
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexY::value>(native_value,cY);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexU::value>(native_value,cU);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexV::value>(native_value,cV);
good = true;
} else if(is_rhs_ycbcr::value && PixelTypeRhs::channels<5) {
using trindexY = typename PixelTypeRhs::template channel_index_by_name<channel_name::Y>;
using trchY = typename PixelTypeRhs::template channel_by_index_unchecked<trindexY::value>;
using trindexCb = typename PixelTypeRhs::template channel_index_by_name<channel_name::Cb>;
using trchCb = typename PixelTypeRhs::template channel_by_index_unchecked<trindexCb::value>;
using trindexCr = typename PixelTypeRhs::template channel_index_by_name<channel_name::Cr>;
using trchCr = typename PixelTypeRhs::template channel_by_index_unchecked<trindexCr::value>;
const auto cR = source.template channelr_unchecked<chiR>();
const auto cG = source.template channelr_unchecked<chiG>();
const auto cB = source.template channelr_unchecked<chiB>();
const double a = .299,
b=.587,
c=.114,
d=1.772,
e=1.402;
const double y = a * cR + b * cG + c * cB;
const double cb = (cB - y) / d+.5;
const double cr = (cR - y) / e+.5;
const typename trchY::int_type cY =helpers::clamp(typename trchY::int_type(y*trchY::scale+.5),trchY::min,trchY::max);
const typename trchCb::int_type cCb = helpers::clamp(typename trchCb::int_type((cb*trchCb::scale+.5)),trchCb::min,trchCb::max);
const typename trchCr::int_type cCr = helpers::clamp(typename trchCr::int_type((cr*trchCr::scale+.5)),trchCr::min,trchCr::max);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexY::value>(native_value,cY);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexCb::value>(native_value,cCb);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexCr::value>(native_value,cCr);
good = true;
} else if(is_rhs_hsv::value && PixelTypeRhs::channels<5) {
using trindexH = typename PixelTypeRhs::template channel_index_by_name<channel_name::H>;
using trchH = typename PixelTypeRhs::template channel_by_index_unchecked<trindexH::value>;
using trindexS = typename PixelTypeRhs::template channel_index_by_name<channel_name::S>;
using trchS = typename PixelTypeRhs::template channel_by_index_unchecked<trindexS::value>;
using trindexV = typename PixelTypeRhs::template channel_index_by_name<channel_name::V>;
using trchV = typename PixelTypeRhs::template channel_by_index_unchecked<trindexV::value>;
const double cR = source.template channelr_unchecked<chiR>();
const double cG = source.template channelr_unchecked<chiG>();
const double cB = source.template channelr_unchecked<chiB>();
double cmin = cG<cB?cG:cB;
cmin = cR<cmin?cR:cmin;
double cmax = cG>cB?cG:cB;
cmax = cR>cmax?cR:cmax;
double chroma = cmax-cmin;
double v = cmax;
double s = cmax == 0 ? 0 : chroma / cmax;
double h = 0; if(cmax != cmin){
if(cmax==cR) {
h=(cG-cB)/chroma+(cG<cB?6:0);
} else if(cmax==cG) {
h = (cG - cR) / chroma + 2;
} else { h = (cR - cG) / chroma + 4;
}
h /= 6.0;
}
const typename trchH::int_type cH =helpers::clamp(typename trchH::int_type(h*trchH::scale+.5),trchH::min,trchH::max);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexH::value>(native_value,cH);
const typename trchS::int_type cS =helpers::clamp(typename trchS::int_type(s*trchS::scale+.5),trchS::min,trchS::max);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexS::value>(native_value,cS);
const typename trchV::int_type cV =helpers::clamp(typename trchV::int_type(v*trchV::scale+.5),trchV::min,trchV::max);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexV::value>(native_value,cV);
good = true;
} else if(is_rhs_hsl::value && PixelTypeRhs::channels<5) {
using trindexH = typename PixelTypeRhs::template channel_index_by_name<channel_name::H>;
using trchH = typename PixelTypeRhs::template channel_by_index_unchecked<trindexH::value>;
using trindexS = typename PixelTypeRhs::template channel_index_by_name<channel_name::S>;
using trchS = typename PixelTypeRhs::template channel_by_index_unchecked<trindexS::value>;
using trindexL = typename PixelTypeRhs::template channel_index_by_name<channel_name::L>;
using trchL = typename PixelTypeRhs::template channel_by_index_unchecked<trindexL::value>;
const double cR = source.template channelr_unchecked<chiR>();
const double cG = source.template channelr_unchecked<chiG>();
const double cB = source.template channelr_unchecked<chiB>();
double cmin = cG<cB?cG:cB;
cmin = cR<cmin?cR:cmin;
double cmax = cG>cB?cG:cB;
cmax = cR>cmax?cR:cmax;
double h =0, s=0, l = (cmax + cmin) / 2.0;
if(cmax != cmin){
double chroma = cmax - cmin;
s = l > 0.5 ? chroma / (2.0 - cmax - cmin) : chroma / (cmax + cmin);
if(cmax==cR) {
h = (cG - cB) / chroma + (cG < cB ? 6 : 0);
} else if(cmax==cG) {
h = (cB - cG) / chroma + 2.0;
} else { h = (cR - cG) / chroma + 4.0;
}
h /= 6.0;
}
const typename trchH::int_type cH =helpers::clamp(typename trchH::int_type(h*trchH::scale+.5),trchH::min,trchH::max);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexH::value>(native_value,cH);
const typename trchS::int_type cS =helpers::clamp(typename trchS::int_type(s*trchS::scale+.5),trchS::min,trchS::max);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexS::value>(native_value,cS);
const typename trchL::int_type cL =helpers::clamp(typename trchL::int_type(l*trchL::scale+.5),trchL::min,trchL::max);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexL::value>(native_value,cL);
good = true;
} else if(is_rhs_cmyk::value && PixelTypeRhs::channels<6) {
using trindexC = typename PixelTypeRhs::template channel_index_by_name<channel_name::C>;
using trchC = typename PixelTypeRhs::template channel_by_index_unchecked<trindexC::value>;
using trindexM = typename PixelTypeRhs::template channel_index_by_name<channel_name::M>;
using trchM = typename PixelTypeRhs::template channel_by_index_unchecked<trindexM::value>;
using trindexY = typename PixelTypeRhs::template channel_index_by_name<channel_name::Y>;
using trchY = typename PixelTypeRhs::template channel_by_index_unchecked<trindexY::value>;
using trindexK = typename PixelTypeRhs::template channel_index_by_name<channel_name::K>;
using trchK = typename PixelTypeRhs::template channel_by_index_unchecked<trindexK::value>;
const double cR = source.template channelr_unchecked<chiR>();
const double cG = source.template channelr_unchecked<chiG>();
const double cB = source.template channelr_unchecked<chiB>();
double cmax = cR>cG?cR:cG;
cmax = cmax>cB?cmax:cB;
double k = helpers::clampcymk(1 - cmax);
double c = helpers::clampcymk((1 - cR - k) / (1 - k));
double m = helpers::clampcymk((1 - cG - k) / (1 - k));
double y = helpers::clampcymk((1 - cB - k) / (1 - k));
const typename trchC::int_type cC =helpers::clamp(typename trchC::int_type(c*trchC::scale+.5),trchC::min,trchC::max);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexC::value>(native_value,cC);
const typename trchM::int_type cM =helpers::clamp(typename trchM::int_type(m*trchM::scale+.5),trchM::min,trchM::max);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexM::value>(native_value,cM);
const typename trchY::int_type cY =helpers::clamp(typename trchY::int_type(y*trchY::scale+.5),trchY::min,trchY::max);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexY::value>(native_value,cY);
const typename trchK::int_type cK =helpers::clamp(typename trchK::int_type(k*trchK::scale+.5),trchK::min,trchK::max);
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexK::value>(native_value,cK);
good = true;
}
if(ris_bw_candidate && ris_bw_candidate2) {
using trindexL = typename PixelTypeRhs::template channel_index_by_name<channel_name::L>;
using trchL = typename PixelTypeRhs::template channel_by_index_unchecked<trindexL::value>;
auto cR = source.template channel_unchecked<chiR>();
auto cG = source.template channel_unchecked<chiG>();
auto cB = source.template channel_unchecked<chiB>();
double f = (cR*tchR::scaler*.299) +
(cG*tchG::scaler*.587) +
(cB*tchB::scaler*.114);
const size_t scale = trchL::scale;
f=(f*(double)scale)+.5;
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexL::value>(native_value,f);
good = true;
} } else if(is_bw_candidate && is_bw_candidate2) {
using tindexL = typename PixelTypeLhs::template channel_index_by_name<channel_name::L>;
using tchL = typename PixelTypeLhs::template channel_by_index_unchecked<tindexL::value>;
const int chiL = tindexL::value;
if(ris_bw_candidate && ris_bw_candidate2) {
using trindexL = typename PixelTypeRhs::template channel_index_by_name<channel_name::L>;
using trchL = typename PixelTypeRhs::template channel_by_index_unchecked<trindexL::value>;
typename trchL::int_type chL = helpers::convert_channel_depth<tchL,trchL>(source.template channel_unchecked<chiL>());
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexL::value>(native_value,chL);
good = true;
} else if(is_rhs_rgb::value && PixelTypeRhs::channels<5) {
using trindexR = typename PixelTypeRhs::template channel_index_by_name<channel_name::R>;
using trchR = typename PixelTypeRhs::template channel_by_index_unchecked<trindexR::value>;
using trindexG = typename PixelTypeRhs::template channel_index_by_name<channel_name::G>;
using trchG = typename PixelTypeRhs::template channel_by_index_unchecked<trindexG::value>;
using trindexB = typename PixelTypeRhs::template channel_index_by_name<channel_name::B>;
using trchB = typename PixelTypeRhs::template channel_by_index_unchecked<trindexB::value>;
const auto chL = source.template channel_unchecked<chiL>();
... (snip several pages of code)
} else {
if(rhas_alpha) {
using trindexA = typename PixelTypeRhs::template channel_index_by_name<channel_name::A>;
using trchA = typename PixelTypeRhs::template channel_by_index_unchecked<trindexA::value>;
helpers::set_channel_direct_unchecked<PixelTypeRhs,trindexA::value>(native_value,trchA::default_);
}
}
result->native_value = native_value;
return gfx_result::success;
} else {
rgba_pixel<HTCW_MAX_WORD> tmp1;
PixelTypeRhs tmp2;
gfx_result rr = convert(source,&tmp1);
if(gfx_result::success!=rr) {
return rr;
}
rr = convert(tmp1,&tmp2,background);
if(gfx_result::success!=rr) {
return rr;
}
*result=tmp2;
return gfx_result::success;
}
return gfx_result::not_supported;
}
Check out my IoT graphics library here:
https://honeythecodewitch/gfx
|
|
|
|
|
Mind you, I'm not going to talk about the lack of comments. I also freely admit that templates make me uncomfortable, but how in the hell would you debug/test that? If all of that generates a few lines of assembly...
Charlie Gilley
“They who can give up essential liberty to obtain a little temporary safety deserve neither liberty nor safety.” BF, 1759
Has never been more appropriate.
|
|
|
|
|
By running it through all the supported color models which would require crafting pixels of each different type implicit color models like RGB, Y'CbCr, C'MYk, HSV, grayscale/monochrome etc, and converting them back and forth to other models.
The issue is that it's sometimes a lossy conversion so any test would have to account for that.
I do not have unit tests for this function, but I've used it enough that I am confident in it.
I did run several iterations of its use through godbolt.org to make sure it was producing the ASM i was expecting.
Edit: To my mind the comments aren't all that lacking, at least in context. That functions structure is very simple, and most of the variable names are descriptive if abbreviated.
I can look at it, with the comments that are there and tell
A) exactly what it's doing
and
B) where to add things to get it to do more
Any more than that, and it would create a comment maintenance issue, wherein i'm expressing intent twice, once through comments, and once through code, and creating double the maintenance, and the potential for comments to get out of sync.
The issue is that my metadata functions and helpers aren't documented as much as they need to be
Check out my IoT graphics library here:
https://honeythecodewitch/gfx
|
|
|
|
|
honey the codewitch wrote: I do not have unit tests for this function, but I've used it enough that I am confident in it. I distrust any app in the hospital. If it works, ship it, right?
And then you see a VB6 app starting at the beginning of the colonoscopy. I am pretty sure at that point that the person who wrote it has said that it has been used enough for his or her confidence.
honey the codewitch wrote: The issue is that my metadata functions and helpers aren't documented as much as they need to be Why not?
Bastard Programmer from Hell
"If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
|
|
|
|
|
Eddy Vluggen wrote: I distrust any app in the hospital. If it works, ship it, right?
This isn't a medical application.
Eddy Vluggen wrote: The issue is that my metadata functions and helpers aren't documented as much as they need to be
Because I am human, mortal, and have finite amounts of time and energy.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
honey the codewitch wrote: Because I am human, mortal, and have finite amounts of time and energy.
What kind of excuse is that?
Seriously, I would mostly prefer to have clear, maintainable code than a tour de force like you wrote, even at the cost of performance. The only exceptions are when the function is the one preventing the program from meeting some performance bar.
Freedom is the freedom to say that two plus two make four. If that is granted, all else follows.
-- 6079 Smith W.
|
|
|
|
|
My code is quite extensively documented, just not all the dark corners that only I end up needing anyway. Pixel metadata is one of those things.
People don't really need to know how many bits exist to the left of an arbitrary pixel channel, and if they really do they can look at the comments in the source code.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
honey the codewitch wrote: Because I am human, mortal, and have finite amounts of time and energy. You, mortal?
Erlkönig: programmer-and-the-devil.shtml[^]
Bastard Programmer from Hell
"If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
|
|
|
|
|
It works and has a sole responsibility. If it was to be refactored, you could use chain of responsibility to break it up.
Graeme
"I fear not the man who has practiced ten thousand kicks one time, but I fear the man that has practiced one kick ten thousand times!" - Bruce Lee
|
|
|
|
|
It would require duplicating a lot of code because most of it is compile time computations of the sort that aren't easily passed to another function, even at compile time. That's the other issue with breaking it up. There would need to be a large common preamble to each routine.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
|
|
|
|
|
Ah, so for IOT, where memory is a premium, it's better not to. Totally understand. 😊
Graeme
"I fear not the man who has practiced ten thousand kicks one time, but I fear the man that has practiced one kick ten thousand times!" - Bruce Lee
|
|
|
|
|
A certain U.S. courier company doesn't allow capital letters in the @ part of an E-mail address. 
|
|
|
|
|
Write it low case...
sorry... too tempting
M.D.V.
If something has a solution... Why do we have to worry about?. If it has no solution... For what reason do we have to worry about?
Help me to understand what I'm saying, and I'll explain it better to you
Rating helpful answers is nice, but saying thanks can be even nicer.
|
|
|
|
|
|
Perhaps their software vendor charges them for each letter that requires a lower case conversion? 
|
|
|
|
|
Capital letters are larger than lower case letters so they take up more disk space.
For such a large company with so many customers, that space is adding up!
This started out as a joke, but the thought occurred to me that something like that may actually have been the rationale behind it
|
|
|
|
|
Sander Rossel wrote: Capital letters are larger than lower case letters so they take up more disk space. No. Uppercase A-Z has values from 65 to 90, while lowercase a-z has values from 97 to 122.
This refers to ASCII. Uppercase can be encoded in as little as 5 bits (Baudot code) or 6 bits (Univac Fieldata; there are other 6 bit encodings as well).
If space is really at premium, you should go for Morse code. Lots of people never realized that the coding table is sorted by frequency: E, the most common letter (in English), is a single 'di'. T, the second most common letter, is a single 'dah'. The third most common is I, so it is 'di di', #4 is A: 'di dah', #5 is N: 'dah di', #6 is M: 'dah dah', all the way to rarely used special characters, such as colon: 'dah dah dah di di di' or comma: 'dah dah di di dah dah'.
We may argue the frequencies, and for other languages than English they may be significantly off. For special characters, usage may have changed since 1844. Nevertheless, the fundamental principle behind the Morse code is frequency sorting, to reduce the time for transmitting a message to a minimum.
|
|
|
|
|
I think Sander forgot the joke icon. 
|
|
|
|
|
Because I actually mentioned "This started out as a joke, but..."
|
|
|
|
|
So when you got to the end it was no longer a joke ... 
|
|
|
|
|
I didn't include it myself. Having to explain, or point out explicitly, humor / parody / irony takes the sting/humor out of it.
Also: I didn't succeed in finding any good translation of the 'gruk' (mini-poem) by the Danish author Piet Hein, but my un-poetic translation says
He who takes a joke as nothing but a joke
and sincerity as nothing but sincerely
he actually understands both kinds
rather poorly.
(If anyone knows a better translation, please let me know!)
|
|
|
|
|
Well, you put us all to shame. A dutch speaker translating a Danish poem into (quite good) English.
|
|
|
|
|
He who only takes a joke as a joke
and sincerity as sincerity
needs to be knocked on the head.
|
|
|
|
|
Sander Rossel wrote: This started out as a joke, but If you read my message you could've saved your whole reply.
Now that's a lot of bits!
|
|
|
|
|
I get it: You don't want anyone to extend on your joke, you want to have the last hand on it.
That's OK with me ... But you'll have to formulate a response to this post as well of you insist on having the last word 
|
|
|
|
|