1 /***************************************************************************
2 
3 		Put a logon on video
4 
5     copyright            : (C) 2007 by mean
6     email                : fixounet@free.fr
7  ***************************************************************************/
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  ***************************************************************************/
16 
17 #include "ADM_default.h"
18 #include "ADM_imageResizer.h"
19 #include "ADM_coreVideoFilter.h"
20 #include "DIA_factory.h"
21 #include "DIA_coreToolkit.h"
22 #include "ascii_font.h"
23 
24 #define REDUCE_WIDTH   12
25 #define REDUCE_HEIGHT  20
26 
27 #ifdef _MSC_VER
28 #  include <intrin.h>
29 
30 #  define __builtin_popcount __popcnt
31 #endif
32 /**
33     \class AsciiFilter
34 */
35 class AsciiFilter : public  ADM_coreVideoFilter
36 {
37 protected:
38                 ADMImage    *tmpImage;
39                 bool        init(void);
40                 bool        drawGlyphs(ADMImage *source,ADMImage *target);
41                 bool        drawOne(uint8_t value, ADMImage *target, int x, int y,int luma);
42                 uint8_t     findBestMatch(ADMImage *source,int col,int raw,int &luma);
43                 int         reducedWidth,reducedHeight;
44 public:
45                     AsciiFilter(ADM_coreVideoFilter *previous,CONFcouple *conf);
46                     ~AsciiFilter();
47 
48 
49         virtual const char   *getConfiguration(void);                   /// Return  current configuration as a human readable string
50         virtual bool         getNextFrame(uint32_t *fn,ADMImage *image);    /// Return the next image
51 	 //  virtual FilterInfo  *getInfo(void);                             /// Return picture parameters after this filter
52         virtual bool         getCoupledConf(CONFcouple **couples) ;     /// Return the current filter configuration
53 		virtual void         setCoupledConf(CONFcouple *couples);
54         virtual bool         configure(void);                           /// Start graphical user interface
55 
56 };
57 
58 // Add the hook to make it valid plugin
59 DECLARE_VIDEO_FILTER(   AsciiFilter,   // Class
60                         1,0,0,              // Version
61                         ADM_UI_ALL,         // UI
62                         VF_TRANSFORM,            // Category
63                         "AsciiView",            // internal name (must be uniq!)
64                         QT_TRANSLATE_NOOP("asciiView","Ascii View"),            // Display name
65                         QT_TRANSLATE_NOOP("asciiView","Ascii view") // Description
66                     );
67 
68 // Now implements the interesting parts
69 /**
70     \fn AsciiFilter
71     \brief constructor
72 */
AsciiFilter(ADM_coreVideoFilter * in,CONFcouple * setup)73 AsciiFilter::AsciiFilter(  ADM_coreVideoFilter *in,CONFcouple *setup) : ADM_coreVideoFilter(in,setup)
74 {
75   tmpImage=NULL;
76   init();
77 }
78 /**
79 
80 */
init(void)81 bool AsciiFilter::init(void)
82 {
83     if(tmpImage) delete tmpImage;
84     tmpImage=NULL;
85 
86     tmpImage=new ADMImageDefault(this->getInfo ()->width,this->getInfo ()->height);
87 
88     reducedWidth=this->getInfo ()->width / REDUCE_WIDTH;
89     reducedHeight=this->getInfo ()->height / REDUCE_HEIGHT;
90     return true;
91 }
92 /**
93     \fn AsciiFilter
94     \brief destructor
95 */
~AsciiFilter()96 AsciiFilter::~AsciiFilter()
97 {
98     if(tmpImage) delete  tmpImage;
99     tmpImage=NULL;
100 }
101 
102 /**
103     \fn getFrame
104     \brief Get a processed frame
105 */
getNextFrame(uint32_t * fn,ADMImage * image)106 bool AsciiFilter::getNextFrame(uint32_t *fn,ADMImage *image)
107 {
108     // since we do nothing, just get the output of previous filter
109     if(false==previousFilter->getNextFrame(fn,tmpImage))
110     {
111         ADM_warning("asciiView : Cannot get frame\n");
112         return false;
113     }
114     image->blacken ();
115     drawGlyphs(tmpImage,image);
116     image->Pts=tmpImage->Pts;
117     return true;
118 }
119 /**
120  *
121  * @param value
122  * @param target
123  * @param x
124  * @param y
125  * @return
126  */
127 /**
128  * Texture is a 8bits 128*64, each glyph seems to be 8*16
129  */
drawOne(uint8_t value,ADMImage * target,int x,int y,int luma)130 bool AsciiFilter::drawOne(uint8_t  value, ADMImage *target, int x, int y,int luma)
131 {
132     int stride=target->GetPitch (PLANAR_Y);
133     uint8_t *src=target->GetReadPtr(PLANAR_Y)+x*REDUCE_WIDTH+y*REDUCE_HEIGHT*stride;
134     uint16_t *fnt=font[value];
135     for(int y=0;y<REDUCE_HEIGHT;y++)
136     {
137       uint32_t bit= *fnt;fnt++;
138       for(int x=0;x<REDUCE_WIDTH;x++)
139       {
140           if(bit&0x8000) src[x]=luma;
141           else src[x]=0;
142           bit<<=1;
143       }
144       src+=stride;
145     }
146     return true;
147 }
148 /**
149  *
150  * @param car
151  * @param src
152  * @param stride
153  * @return
154  */
computeDelta(int car,uint16_t * bitmask)155 static int computeDelta(int car, uint16_t *bitmask)
156 {
157   int sum=0;
158   uint16_t *fnt=font[car-' '];
159   for(int y=0;y<REDUCE_HEIGHT;y++)
160   {
161 
162       uint32_t fromFont=fnt[y]>>4;
163       uint32_t fromBitmask=bitmask[y];
164       sum+=__builtin_popcount(fromFont^fromBitmask);
165   }
166   return sum;
167 }
createBitMask(uint16_t * out,uint8_t * src,int stride,int & luma)168 static void createBitMask(uint16_t *out, uint8_t *src, int stride, int &luma)
169 {
170   int nbOn=0;
171   luma=0;
172   int errorDiffusion=0;
173 
174   for(int y=0;y<REDUCE_HEIGHT;y++)
175     {
176       uint32_t bit= 0;
177       for(int x=0;x<REDUCE_WIDTH;x++)
178       {
179           bit=bit<<1;
180           int pix=((int)src[x]);
181           if(pix+errorDiffusion>128)
182           {
183               bit+=1;
184               nbOn++;
185               luma+=src[x];
186               errorDiffusion-=255-src[x];
187           }
188           else
189           {
190               bit+=0;
191               errorDiffusion+=src[x];
192           }
193       }
194       *out=bit&0x7fe,
195       out++;
196       src+=stride;
197     }
198   if(nbOn)
199     luma=luma/nbOn; // average
200   else
201     luma=0;
202 }
203 /**
204  *
205  * @param col
206  * @param raw
207  * @return
208  */
findBestMatch(ADMImage * source,int col,int row,int & luma)209 uint8_t AsciiFilter::findBestMatch(ADMImage *source,int col,int row,int &luma)
210 {
211   int minDelta=0xfffffff;
212   int candidate=-1;
213   int stride=source->GetPitch (PLANAR_Y);
214   uint8_t *p=source->GetReadPtr(PLANAR_Y)+col*REDUCE_WIDTH+row*REDUCE_HEIGHT*stride;
215   // 1- create bitmask
216   uint16_t bitMask[REDUCE_WIDTH*REDUCE_HEIGHT];
217   createBitMask(bitMask,p,stride,luma);
218 
219   // 32..127
220   for(int tries=32;tries<128;tries++)
221     {
222       int delta=computeDelta(tries,bitMask);
223       if(delta<minDelta)
224         {
225           minDelta=delta;
226           candidate=tries;
227         }
228     }
229   if(candidate==-1)
230     {
231       luma=128;
232       return '*';
233     }
234   else
235     return candidate;
236 }
237 
238 /**
239  *
240  * @param target
241  * @return
242  */
drawGlyphs(ADMImage * source,ADMImage * target)243 bool AsciiFilter::drawGlyphs(ADMImage *source,ADMImage *target)
244 {
245   int luma;
246   uint8_t glyph;
247   target->blacken ();
248   for(int y=0;y<reducedHeight;y++)
249     {
250       for(int x=0;x<reducedWidth;x++)
251       {
252         glyph=findBestMatch(source,x,y,luma);
253         drawOne(glyph,target,x,y,luma);
254       }
255     }
256    return true;
257 }
258 
259 /**
260     \fn getCoupledConf
261     \brief Return our current configuration as couple name=value
262 */
getCoupledConf(CONFcouple ** couples)263 bool         AsciiFilter::getCoupledConf(CONFcouple **couples)
264 {
265     *couples=NULL;
266     return true;
267 }
268 
setCoupledConf(CONFcouple * couples)269 void AsciiFilter::setCoupledConf(CONFcouple *couples)
270 {
271 
272 }
273 
274 /**
275     \fn getConfiguration
276     \brief Return current setting as a string
277 */
getConfiguration(void)278 const char *AsciiFilter::getConfiguration(void)
279 {
280     return "Ascii view.";
281 }
282 
283 /**
284     \fn configure
285 */
configure(void)286 bool AsciiFilter::configure( void)
287 {
288     return true;
289 }
290 
291 
292 /************************************************/
293 //EOF
294