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