2025年8月7日 星期四

讀取 jpeg 圖片, 本來以為是透明效果, 不過這個好像是漸層效果顯示, 另外實做了透明效果。



這是我在 2002 寫的程式, 對透明色處理有興趣, 想自己寫出來, 找了很久, 忘記在哪本書看到這個演算法, 透過 linux/sgvalib 實做出來, 寫出來時還蠻開心的, 那時候用的版本控制軟體是 rcs。

2002 沒有 chatgpt, 光是使用 jpeg lib 就難倒我, 啃著英文文件, 也不知道有沒看懂, 硬是讓我成功解出 jpeg 內容, 現在有 chatgpt, 應該秒會用吧!



看起來還蠻像一回事。在 20250804 想到 pc9801 遊戲 16 色時突然想到這個, 把這程式以 framebuffer 重新改寫, 沒辦法, svgalib 可能沒辦法在現今的平台執行, 看到演算法, 並沒有和背景色做運算, 我傻眼, 這好像不是透明演算法。問了 chatgpt 這個演算法, 還真的不是, chatgpt 說這是漸層演算法, 整整搞錯 23 年。

framebuffer 繪圖則是問 chatgpt, 節省不少時間。

透明色公式是:
final = src_color * alpha + background_color * (1 - alpha);

由於在螢幕上顯示圖形非常複雜, 需要理解 vga 顯示模式和影像格式, 我挑了最簡單的方式做, 在 640x480 24bit color 上顯示 24bit jpeg 圖檔, 可以避開 Quantization + Dithering 的問題, 現在有 chatgpt, 比較容易找到這 2 個演算法, 23 年科技進步的太大了。

在 2025 年, 這些技術也不需要, 2025 年應該不會有 256 色的顯示模式了。

list 1. fb-jpeg.cpp
  1 /*
  2  *
  3  * $Author: descent $
  4  * $Date: 2002/06/20 02:55:23 $
  5  * $Id: jpeg.cpp,v 1.2 2002/06/20 02:55:23 descent Exp descent $
  6  * $Revision: 1.2 $
  7  * 程式功能:用 jpeg library 來讀取 jpeg 圖檔,改用 framebuffer 來秀圖
  8  * 	     並加入XX效果。
  9  */
 10 
 11 #include <stdio.h>
 12 #include <stdlib.h>
 13 #include <fcntl.h>
 14 #include <linux/fb.h>
 15 #include <sys/mman.h>
 16 #include <sys/ioctl.h>
 17 #include <unistd.h>
 18 #include <string.h>
 19 #include <stdint.h>
 20 
 21 #include <vector>
 22 #include <iostream>
 23 #include <string>
 24 
 25 #include <jpeglib.h>
 26 #include <jerror.h>
 27 
 28 using namespace std;
 29 
 30 uint8_t *fbp;
 31 struct fb_var_screeninfo vinfo;
 32 struct fb_fix_screeninfo finfo;
 33 
 34 struct Color
 35 {
 36   Color (unsigned char r, unsigned char g, unsigned char b):r_ (r), g_ (g),
 37     b_ (b)
 38   {
 39   }
 40   unsigned char r () const
 41   {
 42     return r_;
 43   }
 44   unsigned char g () const
 45   {
 46     return g_;
 47   }
 48   unsigned char b () const
 49   {
 50     return b_;
 51   }
 52 private:
 53   unsigned char r_, g_, b_;
 54 };
 55 
 56 class Bitmap
 57 {
 58 public:
 59   Bitmap (int width, int height):width_ (width), height_ (height)
 60   {
 61   }
 62   void set_color (const std::vector < Color > &color)
 63   {
 64     color_ = color;
 65   }
 66   const std::vector < Color > &color_pixel () const
 67   {
 68     return color_;
 69   }
 70   int w () const
 71   {
 72     return width_;
 73   }
 74   int h () const
 75   {
 76     return height_;
 77   }
 78 private:
 79   int width_, height_;
 80   std::vector < Color > color_;
 81 };
 82 
 83 int r = 127;
 84 int g = 78;
 85 int b = 50;
 86 int color_value;
 87 
 88 void show(const Bitmap & bitmap, int x, int y, int level = 256)
 89 {
 90   //bitmap.color_pixel();
 91   std::vector < Color >::const_iterator it = bitmap.color_pixel ().begin ();
 92   //gl_getpalettecolor(color_value,&r,&g,&b);
 93   
 94   for (int i = 0; i < bitmap.h (); ++i)
 95   {
 96     usleep(1000);
 97     for (int j = 0; j < bitmap.w (); ++j)
 98     {
 99       //gl_setpixelrgb(x+j,y+i,(*it).r()*level/256,(*it).g()*level/256,(*it).b()*level/256);
100       int pix_r = (*it).r ();
101       int pix_g = (*it).g ();
102       int pix_b = (*it).b ();
103       //gl_setpixelrgb(x+j,y+i,r +(pix_r - r)*level/256, g +(pix_g - g)*level/256, b + (pix_b - b)*level/256);
104       //gl_setpixelrgb(x+j,y+i,pix_r*level+r*(1-level),pix_g*level+g*(1-level),pix_b*level+b*(level));
105       long location = (y+i)*finfo.line_length + (x+j) * 4; // 4 bytes per pixel
106       uint32_t *pixel = (uint32_t *)(fbp + location);
107       //printf("i: %d, j: %d, pixel: %p, fbp: %p\n", i, j, pixel, fbp);
108       #if 1
109       if (i <= 120)
110       {
111 	//gl_setpixelrgb (x + j, y + i, (*it).r (), (*it).g (), (*it).b ());
112             // 32-bit ARGB,alpha=0xFF (不透明),紅色R=0xFF, G=0x00, B=0x00
113             // 寫法是 0xAARRGGBB
114         unsigned int val = 0xff000000;
115         val |= ((*it).r() << 16);
116         val |= ((*it).g() << 8);
117         val |= (*it).b();
118 	*pixel = val;
119             //*pixel = 0xFFFF0000;
120       }
121       else
122       {
123         unsigned int val = 0xff000000;
124         val |= (((*it).r () + (r - (*it).r ()) * level / 256) << 16);
125         val |= (((*it).g () + (g - (*it).g ()) * level / 256) << 8);
126         val |= ((*it).b () + (b - (*it).b ()) * level / 256);
127 	*pixel = val;
128             //*pixel = 0xFFFF0000;
129         #if 0
130 	gl_setpixelrgb (x + j, y + i,
131 			(*it).r () + (r - (*it).r ()) * level / 256,
132 			(*it).g () + (g - (*it).g ()) * level / 256,
133 			(*it).b () + (b - (*it).b ()) * level / 256);
134         #endif
135       }
136       #endif
137       ++it;
138     }
139   }
140   //for ( ; it !=bitmap.color_pixel().end() ; ++it)
141 
142 }
143 
144 void get_hex (char c, char hex[])
145 {
146   char ascii[] = "0123456789abcdef";
147   int low = (c & 0x0f);
148   int high = ((c >> 4) & 0x0f);
149   hex[0] = ascii[high];
150   hex[1] = ascii[low];
151 }
152 
153 int main(int argc, char *argv[])
154 {
155   struct jpeg_decompress_struct cinfo;
156   struct jpeg_error_mgr jerr;
157 
158   cinfo.err = jpeg_std_error (&jerr);
159   jpeg_create_decompress (&cinfo);
160 
161   if (argc < 2)
162   {
163     cout << "Enter a file name" << endl;
164     return -1;
165   }
166 
167   string filename = argv[1];
168   FILE *infile;
169   if ((infile = fopen (filename.c_str (), "rb")) == NULL)
170   {
171     fprintf (stderr, "cann't open file");
172     return -1;
173   }
174   //int level=255;
175   //if (argc>=3)
176   //level=atoi(argv[2]);
177   jpeg_stdio_src (&cinfo, infile);
178   jpeg_read_header (&cinfo, true);
179   // set parameters fro decompress
180   //cinfo.scale_num=1;
181   //cinfo.scale_denom=2;
182   //cinfo.output_width=640;
183   //cinfo.output_height=480;
184   //cinfo.image_height=480;
185   jpeg_start_decompress (&cinfo);
186 
187   JSAMPARRAY buffer;
188   int row_stride = cinfo.output_width * cinfo.output_components;
189   int jpeg_width = cinfo.output_width;
190   buffer =
191     (*cinfo.mem->alloc_sarray) ((j_common_ptr) & cinfo, JPOOL_IMAGE,
192 				row_stride, 1);
193   JSAMPROW ptr = NULL;
194   int i = 0;
195 
196   std::vector < Color > color;
197   Bitmap bitmap (cinfo.output_width, cinfo.output_height);
198   while (cinfo.output_scanline < cinfo.output_height)
199   {
200     jpeg_read_scanlines (&cinfo, buffer, 1);
201     ptr = buffer[0];
202     //cout << "line " << ++i << " : " << endl;
203     int index = 0;
204     // scanline 是圖檔的寬度 * 顏色的數目,在 full color 一個 pixel 要三個 byte
205     //for (int col=0 ; col < cinfo.image_width * cinfo.output_components ; col++)
206     for (int col = 0; col < cinfo.image_width; col++)
207     {
208       //cout << index++ << " : GETJSAMPLE(*ptr++) : " << GETJSAMPLE(*ptr++) << endl;
209       //bitmap.push_back(GETJSAMPLE(*ptr++));
210       //gl_setpixelrgb(x+dx,y,GETJSAMPLE(*ptr)*level/256,GETJSAMPLE(*(ptr+1))*level/256,GETJSAMPLE(*(ptr+2))*level/256);
211       color.
212 	push_back (Color
213 		   (GETJSAMPLE (*ptr), GETJSAMPLE (*(ptr + 1)),
214 		    GETJSAMPLE (*(ptr + 2))));
215 
216 #ifdef DUMP_BITMAP_DATA
217       char h[2];
218       get_hex(GETJSAMPLE(*ptr), h);
219       cout << h[0] << h[1] << " ";
220       get_hex(GETJSAMPLE(*(ptr+1)), h);
221       cout << h[0] << h[1] << " ";
222       get_hex(GETJSAMPLE(*(ptr+2)), h);
223       cout << h[0] << h[1] << " ";
224     cout << endl;
225 #endif
226       //gl_setpixelrgb(x+dx,y,GETJSAMPLE(*ptr),GETJSAMPLE(*(ptr+1)),GETJSAMPLE(*(ptr+2)));
227       //dx++;
228       ptr += 3;
229     }
230     //dx=0;
231     //y++;
232 #ifdef DUMP_BITMAP_DATA
233     cout << endl;
234 #endif
235   }
236   bitmap.set_color (color);
237   //vga_setmode(TEXT);
238   int color_components = cinfo.output_components;
239   //cout << "cinfo.output_components : " << cinfo.output_components << endl;
240   jpeg_finish_decompress (&cinfo);
241   jpeg_destroy_decompress (&cinfo);
242 
243   fclose (infile);
244 
245 
246 
247 
248     int fb_fd = open("/dev/fb0", O_RDWR);
249     if (fb_fd == -1) {
250         perror("open fail");
251         return 1;
252     }
253 
254 
255     if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo) == -1) {
256         perror("FBIOGET_FSCREENINFO");
257         close(fb_fd);
258         return 1;
259     }
260 
261     if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
262         perror("FBIOGET_VSCREENINFO");
263         close(fb_fd);
264         return 1;
265     }
266 
267     printf("Resolution: %dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
268 
269     if (vinfo.bits_per_pixel != 32) {
270         fprintf(stderr, "This program only supports 32-bit framebuffer.\n");
271         close(fb_fd);
272         return 1;
273     }
274 
275     long screensize = vinfo.yres * finfo.line_length;
276     fbp = (uint8_t *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
277     if (fbp == MAP_FAILED) {
278         perror("mmap");
279         close(fb_fd);
280         return 1;
281     }
282 
283     printf("load jpg\n");
284 
285     int square_x = 300;
286     int square_y = 300;
287     int size = 80;
288     show(bitmap, 300, 200, 100);
289 
290 
291 #if 0
292     for (int y = 0; y < size; y++) {
293         for (int x = 0; x < size; x++) {
294             int px = square_x + x;
295             int py = square_y + y;
296 
297             long location = py * finfo.line_length + px * 4; // 4 bytes per pixel
298 
299             // 32-bit ARGB,alpha=0xFF (不透明),紅色R=0xFF, G=0x00, B=0x00
300             // 寫法是 0xAARRGGBB
301             uint32_t *pixel = (uint32_t *)(fbp + location);
302             *pixel = 0xFFFF0000;
303         }
304     }
305     #endif
306 
307     munmap(fbp, screensize);
308     close(fb_fd);
309 
310     return 0;
311 }

拜 chatgpt 之強大, 透明效果沒花太多時間就完成, 這次應該就是透明效果了, 需要用底圖來計算。



list 2. fb-tran.cpp
  1 /*
  2  *
  3  * $Author: descent $
  4  * $Date: 2002/06/20 02:55:23 $
  5  * $Id: jpeg.cpp,v 1.2 2002/06/20 02:55:23 descent Exp descent $
  6  * $Revision: 1.2 $
  7  * 程式功能:用 jpeg library 來讀取 jpeg 圖檔,改用 framebuffer 來秀圖
  8  * 	     並加入透明效果。
  9  */
 10 
 11 #include <stdio.h>
 12 #include <stdlib.h>
 13 #include <fcntl.h>
 14 #include <linux/fb.h>
 15 #include <sys/mman.h>
 16 #include <sys/ioctl.h>
 17 #include <unistd.h>
 18 #include <string.h>
 19 #include <stdint.h>
 20 
 21 #include <vector>
 22 #include <iostream>
 23 #include <string>
 24 
 25 #include <jpeglib.h>
 26 #include <jerror.h>
 27 
 28 using namespace std;
 29 
 30 uint8_t *fbp;
 31 struct fb_var_screeninfo vinfo;
 32 struct fb_fix_screeninfo finfo;
 33 
 34 struct Color
 35 {
 36   Color (unsigned char r, unsigned char g, unsigned char b):r_ (r), g_ (g),
 37     b_ (b)
 38   {
 39   }
 40   unsigned char r () const
 41   {
 42     return r_;
 43   }
 44   unsigned char g () const
 45   {
 46     return g_;
 47   }
 48   unsigned char b () const
 49   {
 50     return b_;
 51   }
 52 private:
 53   unsigned char r_, g_, b_;
 54 };
 55 
 56 class Bitmap
 57 {
 58 public:
 59   Bitmap(int width, int height):width_ (width), height_ (height)
 60   {
 61   }
 62   Bitmap():width_ (0), height_ (0)
 63   {
 64   }
 65   void set_w_h(int w, int h)
 66   {
 67     width_ = w;
 68     height_ = h;
 69   }
 70   void set_color (const std::vector < Color > &color)
 71   {
 72     color_ = color;
 73   }
 74   const std::vector < Color > &color_pixel () const
 75   {
 76     return color_;
 77   }
 78   int w () const
 79   {
 80     return width_;
 81   }
 82   int h () const
 83   {
 84     return height_;
 85   }
 86 private:
 87   int width_, height_;
 88   std::vector < Color > color_;
 89 };
 90 
 91 int r = 127;
 92 int g = 78;
 93 int b = 50;
 94 int color_value;
 95 
 96 //final = src_color * alpha + background_color * (1 - alpha);
 97 
 98 uint8_t alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha) {
 99     // alpha: 0~255, fg 前景, bg 背景
100     return (fg * alpha + bg * (255 - alpha)) / 255;
101 }
102 
103 void show(const Bitmap& bitmap, int x, int y, int level, const Bitmap& bg_bitmap)
104 {
105   //bitmap.color_pixel();
106   std::vector < Color >::const_iterator it = bitmap.color_pixel ().begin ();
107 
108   std::vector < Color >::const_iterator bg_it = bg_bitmap.color_pixel ().begin ();
109   printf("bitmap.h(): %d, bitmap.w(): %d\n", bitmap.h(), bitmap.w());
110   printf("bg_bitmap.h(): %d, bg_bitmap.w(): %d\n", bg_bitmap.h(), bg_bitmap.w());
111 
112   //gl_getpalettecolor(color_value,&r,&g,&b);
113   
114   for (int i = 0; i < bitmap.h (); ++i)
115   {
116     //usleep(1000);
117     for (int j = 0; j < bitmap.w (); ++j)
118     {
119       //gl_setpixelrgb(x+j,y+i,(*it).r()*level/256,(*it).g()*level/256,(*it).b()*level/256);
120       int pix_r = (*it).r ();
121       int pix_g = (*it).g ();
122       int pix_b = (*it).b ();
123 
124 
125       
126 
127       //gl_setpixelrgb(x+j,y+i,r +(pix_r - r)*level/256, g +(pix_g - g)*level/256, b + (pix_b - b)*level/256);
128       //gl_setpixelrgb(x+j,y+i,pix_r*level+r*(1-level),pix_g*level+g*(1-level),pix_b*level+b*(level));
129       long location = (y+i)*finfo.line_length + (x+j) * 4; // 4 bytes per pixel
130       uint32_t *pixel = (uint32_t *)(fbp + location);
131       //printf("i: %d, j: %d, pixel: %p, fbp: %p\n", i, j, pixel, fbp);
132 	//gl_setpixelrgb (x + j, y + i, (*it).r (), (*it).g (), (*it).b ());
133             // 32-bit ARGB,alpha=0xFF (不透明),紅色R=0xFF, G=0x00, B=0x00
134             // 寫法是 0xAARRGGBB
135         unsigned int val = 0x00000000;
136         unsigned char new_r, new_g, new_b;
137 
138         //double alpha = 0.8;
139         unsigned char alpha = 100;
140         #if 0
141         new_r = (unsigned char)((*it).r() * alpha + (*bg_it).r() *(255-alpha))/255;
142         new_b = (unsigned char)((*it).b() * alpha + (*bg_it).b() *(255-alpha))/255;
143         new_g = (unsigned char)((*it).g() * alpha + (*bg_it).g() *(255-alpha))/255;
144         #endif
145 
146         new_r = alpha_blend((*it).r(), (*bg_it).r(), alpha);
147         new_g = alpha_blend((*it).g(), (*bg_it).g(), alpha);
148         new_b = alpha_blend((*it).b(), (*bg_it).b(), alpha);
149 
150 
151         #if 0
152         new_r =  (*bg_it).r();
153         new_g =  (*bg_it).g();
154         new_b =  (*bg_it).b();
155         #endif
156 
157 
158         val |= ((*it).r() << 16);
159         val |= ((*it).g() << 8);
160         val |= (*it).b();
161 
162         unsigned int bg_val = 0x00000000;
163         bg_val |= ((*bg_it).r() << 16);
164         bg_val |= ((*bg_it).g() << 8);
165         bg_val |= (*bg_it).b();
166 
167         unsigned int final_val = 0xff000000;
168 
169         //final_val |= 0xff << 24;
170         final_val |= new_r << 16;
171         final_val |= new_g << 8;
172         final_val |= new_b;
173 
174 	*pixel = final_val;
175       #if 0
176       if (i <= 120)
177       {
178 	//gl_setpixelrgb (x + j, y + i, (*it).r (), (*it).g (), (*it).b ());
179             // 32-bit ARGB,alpha=0xFF (不透明),紅色R=0xFF, G=0x00, B=0x00
180             // 寫法是 0xAARRGGBB
181         unsigned int val = 0xff000000;
182         val |= ((*it).r() << 16);
183         val |= ((*it).g() << 8);
184         val |= (*it).b();
185 	*pixel = val;
186             //*pixel = 0xFFFF0000;
187       }
188       else
189       {
190         unsigned int val = 0xff000000;
191         val |= (((*it).r () + (r - (*it).r ()) * level / 256) << 16);
192         val |= (((*it).g () + (g - (*it).g ()) * level / 256) << 8);
193         val |= ((*it).b () + (b - (*it).b ()) * level / 256);
194 	*pixel = val;
195             //*pixel = 0xFFFF0000;
196         #if 0
197 	gl_setpixelrgb (x + j, y + i,
198 			(*it).r () + (r - (*it).r ()) * level / 256,
199 			(*it).g () + (g - (*it).g ()) * level / 256,
200 			(*it).b () + (b - (*it).b ()) * level / 256);
201         #endif
202       }
203       #endif
204       ++it;
205       if (i < bg_bitmap.h ())
206         ++bg_it;
207     }
208   }
209   //for ( ; it !=bitmap.color_pixel().end() ; ++it)
210 
211 }
212 
213 void get_hex (char c, char hex[])
214 {
215   char ascii[] = "0123456789abcdef";
216   int low = (c & 0x0f);
217   int high = ((c >> 4) & 0x0f);
218   hex[0] = ascii[high];
219   hex[1] = ascii[low];
220 }
221 
222 int jpeg_to_bitmap(const string &filename, Bitmap &bitmap)
223 {
224   struct jpeg_decompress_struct cinfo;
225   struct jpeg_error_mgr jerr;
226 
227   cinfo.err = jpeg_std_error (&jerr);
228   jpeg_create_decompress (&cinfo);
229 
230   FILE *infile;
231   if ((infile = fopen (filename.c_str (), "rb")) == NULL)
232   {
233     fprintf (stderr, "cann't open file");
234     return -1;
235   }
236   //int level=255;
237   //if (argc>=3)
238   //level=atoi(argv[2]);
239   jpeg_stdio_src (&cinfo, infile);
240   jpeg_read_header (&cinfo, true);
241   // set parameters fro decompress
242   //cinfo.scale_num=1;
243   //cinfo.scale_denom=2;
244   //cinfo.output_width=640;
245   //cinfo.output_height=480;
246   //cinfo.image_height=480;
247   jpeg_start_decompress (&cinfo);
248 
249   JSAMPARRAY buffer;
250   int row_stride = cinfo.output_width * cinfo.output_components;
251   int jpeg_width = cinfo.output_width;
252   buffer =
253     (*cinfo.mem->alloc_sarray) ((j_common_ptr) & cinfo, JPOOL_IMAGE,
254 				row_stride, 1);
255   JSAMPROW ptr = NULL;
256   int i = 0;
257 
258   std::vector < Color > color;
259   bitmap.set_w_h(cinfo.output_width, cinfo.output_height);
260   while (cinfo.output_scanline < cinfo.output_height)
261   {
262     jpeg_read_scanlines (&cinfo, buffer, 1);
263     ptr = buffer[0];
264     //cout << "line " << ++i << " : " << endl;
265     int index = 0;
266     // scanline 是圖檔的寬度 * 顏色的數目,在 full color 一個 pixel 要三個 byte
267     //for (int col=0 ; col < cinfo.image_width * cinfo.output_components ; col++)
268     for (int col = 0; col < cinfo.image_width; col++)
269     {
270       //cout << index++ << " : GETJSAMPLE(*ptr++) : " << GETJSAMPLE(*ptr++) << endl;
271       //bitmap.push_back(GETJSAMPLE(*ptr++));
272       //gl_setpixelrgb(x+dx,y,GETJSAMPLE(*ptr)*level/256,GETJSAMPLE(*(ptr+1))*level/256,GETJSAMPLE(*(ptr+2))*level/256);
273       color.
274 	push_back (Color
275 		   (GETJSAMPLE (*ptr), GETJSAMPLE (*(ptr + 1)),
276 		    GETJSAMPLE (*(ptr + 2))));
277 
278 #ifdef DUMP_BITMAP_DATA
279       char h[2];
280       get_hex(GETJSAMPLE(*ptr), h);
281       cout << h[0] << h[1] << " ";
282       get_hex(GETJSAMPLE(*(ptr+1)), h);
283       cout << h[0] << h[1] << " ";
284       get_hex(GETJSAMPLE(*(ptr+2)), h);
285       cout << h[0] << h[1] << " ";
286     cout << endl;
287 #endif
288       //gl_setpixelrgb(x+dx,y,GETJSAMPLE(*ptr),GETJSAMPLE(*(ptr+1)),GETJSAMPLE(*(ptr+2)));
289       //dx++;
290       ptr += 3;
291     }
292     //dx=0;
293     //y++;
294 #ifdef DUMP_BITMAP_DATA
295     cout << endl;
296 #endif
297   }
298   bitmap.set_color (color);
299   //vga_setmode(TEXT);
300   int color_components = cinfo.output_components;
301   //cout << "cinfo.output_components : " << cinfo.output_components << endl;
302   jpeg_finish_decompress (&cinfo);
303   jpeg_destroy_decompress (&cinfo);
304 
305   fclose (infile);
306   return 0;
307 }
308 
309 int main(int argc, char *argv[])
310 {
311   if (argc < 2)
312   {
313     cout << "Enter 2 file name" << endl;
314     return -1;
315   }
316 
317   //string filename = argv[1];
318 
319   Bitmap fg_bitmap;
320   jpeg_to_bitmap(argv[1], fg_bitmap);
321 
322   Bitmap bg_bitmap;
323   jpeg_to_bitmap(argv[2], bg_bitmap);
324 
325 
326 
327     int fb_fd = open("/dev/fb0", O_RDWR);
328     if (fb_fd == -1) {
329         perror("open fail");
330         return 1;
331     }
332 
333 
334     if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo) == -1) {
335         perror("FBIOGET_FSCREENINFO");
336         close(fb_fd);
337         return 1;
338     }
339 
340     if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
341         perror("FBIOGET_VSCREENINFO");
342         close(fb_fd);
343         return 1;
344     }
345 
346     printf("Resolution: %dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
347 
348     if (vinfo.bits_per_pixel != 32) {
349         fprintf(stderr, "This program only supports 32-bit framebuffer.\n");
350         close(fb_fd);
351         return 1;
352     }
353 
354     long screensize = vinfo.yres * finfo.line_length;
355     fbp = (uint8_t *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
356     if (fbp == MAP_FAILED) {
357         perror("mmap");
358         close(fb_fd);
359         return 1;
360     }
361 
362     printf("load jpg\n");
363 
364     int square_x = 300;
365     int square_y = 300;
366     int size = 80;
367 
368     #if 0
369     show(bg_bitmap, 300, 200, 100, );
370     sleep(2);
371     #endif
372 
373     show(fg_bitmap, 300, 200, 100, bg_bitmap);
374     sleep(2);
375 
376 
377 #if 0
378     for (int y = 0; y < size; y++) {
379         for (int x = 0; x < size; x++) {
380             int px = square_x + x;
381             int py = square_y + y;
382 
383             long location = py * finfo.line_length + px * 4; // 4 bytes per pixel
384 
385             // 32-bit ARGB,alpha=0xFF (不透明),紅色R=0xFF, G=0x00, B=0x00
386             // 寫法是 0xAARRGGBB
387             uint32_t *pixel = (uint32_t *)(fbp + location);
388             *pixel = 0xFFFF0000;
389         }
390     }
391     #endif
392 
393     munmap(fbp, screensize);
394     close(fb_fd);
395 
396     return 0;
397 }

List 2 L98 是透明演算法。
g++ fb-tran.cpp -o fb-tran  -ljpeg

2025年8月5日 星期二

20250621 台灣 - 名古屋 (1/7)

日期: 20250621 (6)
地點: 台灣 - 名古屋

本篇文章提到的時間均為日本時間, 沒寫單位的金額為日圓。

我不會日文, 英文也不好: 文章中有關問路或是其他問人的資訊, 都是用很克難的方式完成 (簡單英文、簡單日文、比手畫腳)。
富邦旅遊平安險

從名古屋機場到名鐵名古屋車站已經很晚了, 先找「相鐵FRESA INN 名古屋站新幹線口」旅館。
果然如預期一般, 花了點時間找路, 找到之後到附近的「すき家」吃飯, 是 24 小時營業, 這時大約 23:00, 真的是搞很晚了。



之後到「aeon MaxValu 超市」晃晃, 看了最近爭議的鮮奶議題, 來看看日本的鮮奶價格。



買了一罐低溫殺菌的當早餐喝, 味道蠻濃的。

2025年8月2日 星期六

20250726 大罷免 X 20250729 超大豪雨停班停課

20250726 是大罷免投票日, 開票結果沒有人被罷免。

有些人會為了政治立場和家人鬧翻, 這是最得不償失的事情, 在你有危難時, 家人是最後一道防線, 也是真心會幫助你度過難關的人。你所支持的政治人物是不會幫你的。千萬別為了陌生的政治人物與家人搞壞關係, 太不值得。

大罷免, 民進黨沒失敗是對的, 民進黨又沒有被罷的提案, 只有贏, 沒有輸的選項, 最差就是維持現狀, 還可以了解目前的民意, 做出應變, 不算沒收穫。

現在比較期待 2026 的選舉如何? 這個輸了才是真的輸。賴清德定調第二波罷免與公民同行 王世堅當場反對, 823 國民黨應該會感受到不小的壓力。823 的壓力一樣是在國民黨那邊, 雖然現在大多人覺得 823 可能也沒人被罷免, 但民意的變換是很難說的, 誰知道到當天又怎麼了?

NCC:網購國外藍牙產品自用 750元審查費研議免收或減收」這個費用目前是暫緩, 不知道之後是不是會繼續下去?

20250728/29 受到低壓帶與西南風影響,中南部地區28日出現明顯降雨,且入夜後西南風進一步增強為西南氣流,雨勢還會加劇。高雄市政府、台南市政府和屏東縣政府、嘉義縣政府28日晚間先後宣布,依據中央氣象署預報資料,29日雨量已達停班停課標準,高雄市、台南市、屏東縣、嘉義縣周二(29日)停止上班、停止上課。

2025/7/29停班停課全台最新通知:高雄市、台南市、屏東縣、嘉義縣停止上班上課

雖然 2025/7/29 當天風雨不大, 但這樣的決策還是鼓勵, 畢竟人民的安全是絕對最重要的, 又加上之前颱風的災害還沒完全復原。

2025年7月26日 星期六

20250727 名古屋 - 台灣 (7/7)

前一天先繞去名鐵名古屋車站, 熟悉路線, 順便把機場電車時刻表拍下來, 網路上實在不好查這資料, 拍下來仔細看, 也在前一天研究該搭那一個班次的電車。



今天行程比較趕, 13:45 飛機, 預計搭乘 10:31 名鐵名古屋往名古屋機場的電車。特級列車 1,2 車廂是指定席, 所以沒指定席票要從第3車廂坐起。另外如果這台沒搭到, 還有 11:01, 11:20 可以補救, 但後續搭機行程可能會很趕。我們到機場閘門時是 13:00, 13:20 開始登機, 大概只有 20~30 分鐘時間可以吃午餐。

經過長時間的 check-in 以及海關檢查 (檢查事項蠻繁瑣, 難怪需要那麼長時間), 終於進入機場, 搭接駁車到第二航廈之後, 便買了咖哩豬排飯, 還真的蠻好吃。





也沒什麼時間到免稅店採購, 在附近的店家做了最後的採買, 看到不錯的餅乾就買了幾包。

到桃園機場之後便是一連串的搭車惡夢, 蠻多人也是要搭捷運到桃園高鐵, 所以捷運一堆人, 然後大部分人也都是大型理箱, 造成整個車廂很擠, 到桃園高鐵之後, 也是類似現象, 能擠上車就不錯了, 完全沒有座位的票, 大行李箱在車道中佔據不少位置, 擋到一般人的搭乘, 真的很不好意思。

2025年7月14日 星期一

20250623 馬籠宿 (3/7)

日期: 20250623 (1)
地點: 馬籠宿

本篇文章提到的時間均為日本時間, 沒寫單位的金額為日圓。

我不會日文, 英文也不好: 文章中有關問路或是其他問人的資訊, 都是用很克難的方式完成 (簡單英文、簡單日文、比手畫腳)。
從 jr 名古屋車站搭中央本線到中津川, 大約要花 70 ~ 85 分鐘, 再搭乘「北惠那」巴士往馬籠宿 (25 分鐘), 交通上要花上不少時間。



「北惠那」巴士3號站牌就是往馬籠宿, 出中津川車站後大約走一分鐘就可抵達, 很近, 不過因為出站後大概只有 9 分鐘左右就會發車, 如果搭不上, 要再等1小時才有車往馬籠宿。



我們搭乘 10:42 區間快速抵達中津川 (12:06), 轉搭 12:15「北惠那」巴士, 時間有點緊, 但應該夠。



巴士只能現金, 建議先準備好, 上車拿整理券後下車投 800 和整理券。



運氣不好, 遇到大雨, 到馬籠宿時雨最大, 先上廁所, 然後到旁邊的紀念品店逛逛, 躲躲雨。



肚子也餓了, 買了五平餅和信州牛肉串吃。五平餅是鹹的, 吃了之後不愛, 以為是像糯米糰子的口感, 結果不太類似, 牛肉串則是按照日本的水準, 好吃。



這邊蠻容易看到歐美遊客, 大部分是日本人, 不太聽到中文口音遊客, 逛完紀念品店之後, 雨勢稍歇, 往步道走去。這邊是上坡, 要花點力氣, 有點像是江之島的斜坡, 但沒那麼陡。



沿途向上會有一些店家, 不知道是不是因為星期一, 有開店的店家不多, 欣賞街道上的日式住宅, 搭上背後的大山, 很是特別。由於還是有點雨勢, 一手撐傘一手拍照不算方便, 也不想拿出三軸穩定器, 就這樣隨手拍。



大約花40分鐘上到頂端, 往下望去, 山嵐進收眼底。這邊可以沿著中山道到妻籠宿, 也可以從馬籠宿搭公車, 一樣花25分鐘。看過妻籠宿影片後, 我們決定到馬籠宿就好, 妻籠宿感覺是縮小版的馬籠宿, 旅人時間有限, 這次就不去了。

回程時, 到中津川巴士站時, 有台電車, 大約只有5分鐘的時間可以趕上, 這就有點趕, 我還要特別去加值 suica, 好在已經很熟悉, 加值後趕緊進車站月台, 往回名古屋的車進去。

一樣經過80分鐘的時間, 回名古屋後, 肚子餓了, 找了家拉麵 (ラーメン豚山) 來吃, 殊不知是惡夢的開始。



拉麵只有叉燒拉麵, 因為肚子太餓, 所以選了5塊叉燒的版本, 台灣的叉燒都是薄薄一片, 結果這家的叉燒很厚, 白肉也蠻多, 好吃是好吃, 但是吃到5塊會有點膩, 肚子覺得好多油, 建議這種選3塊叉燒會比較適合。

吃飽之後感覺肚子都是油, 回飯店喝了抹茶解解油膩, 結果肚子變很撐, 真的太飽了。

2025年7月10日 星期四

20250707 中度颱風丹娜絲停班停課, 引發後續的太陽能板放置海上/水庫的問題

20250707 中度颱風丹娜絲13縣市縣市停13縣市班停課, 丹娜絲帶來很嚴重的災情, 有些地方還在停電中。
這一災害也帶來了太陽能板安置的問題, 有屏東海上的太陽能版計劃, 另外水庫也有放置太陽能板, 烏山頭水庫太陽能板、阿公店水庫太陽能板。

有些地方的太陽能板損毀, 影/丹娜絲重創嘉義縣 2座滯洪池光電場水面滿布殘骸空拍畫面曝光

摘錄部份 影/丹娜絲重創嘉義縣 2座滯洪池光電場水面滿布殘骸空拍畫面曝光
擁有光電博士的義竹鄉長黃政傑說,颱風登陸,任何設施都可能受損,若船隻停泊水面也可能被颱風摧毀,難道就要全面禁止航行?他認為,問題應聚焦設施固定與防災設計強化,而非否定水面型光電技術。

針對外界憂心光電板破壞會溶出有害物質,黃政傑表示,太陽能板材質不是那麼容易溶解,民眾擔心用酸液清洗,有法令規範,不應用酸液洗來說太陽能板問題。「我們可以立嚴格法律規範抓到就罰。」

Re: [問卦] 南部水庫上面放光電板是什麼操作?提供了日本經驗, 空撮:台風の影響か ダム水面上の太陽光パネルが火災 千葉・市原, 海上太陽能板燒起來時的災害。



現在大家都知道「有鉛石油」是有毒的, 但當時有科學家認為「有鉛石油」是安全的, 還大力推廣。參考: 健康與環境:含鉛汽油時代終結——美國科學發明與人類百年排毒史

ref:

2025年7月4日 星期五

20250624/20250625 klook 2天1夜 白川鄉 & 高山 & 金澤巴士之旅 (名古屋出發) (4/7, 5/7)

日期: 20250624 (2)
地點: 高山、白川鄉、金沢

日期: 20250625 (3)
地點: 金沢兼六園、金沢城、近江町市場、東茶屋街

本篇文章提到的時間均為日本時間, 沒寫單位的金額為日圓。

我不會日文, 英文也不好: 文章中有關問路或是其他問人的資訊, 都是用很克難的方式完成 (簡單英文、簡單日文、比手畫腳)。
在這次的日本自助旅行中, 參加了 klook 2天1夜 白川鄉 & 高山 & 金澤巴士之旅 (名古屋出發), 由於到白川鄉合掌村比較麻煩, 需要搭乘濃飛巴士, 倒不是搭巴士難, 而是不確定買不買得到票比較麻煩, 最後就跟 klook 的團了。

我訂購時費用是: 7433 台幣, 20250701 查時 6622 台幣。

跟團當然有好有壞, 交通問題解決了, 不過就要配合人家安排的時程, 有時候想逛久一點就沒辦法。也不能悠閒的幾點要出發才出發, 需要按照人家規定的時間。

[集合地點]



這應該是最難的部份了, 我們是前一天 (20250623) 先去找集合地點, 在 jr 名古屋車站 biccamera 這邊的出口 (名古屋站太閤通口 biccamera 前), 有個類似警察局的地方, 找到之後就比較安心, 然後就去大須觀音逛街。

20250624 到集合地點集合, 由於下雨所以領隊小奚在太閣通口出口附近, 一下子就找到了, show 出 qr code 確認後, 等車出發。這團總共6人, lucky, 這樣也出團, 不錯。還有另外一團一日遊合掌村的, 這團就很多人, 我看領隊就需要一一核對報到名單。

小奚領著我們上車, 這是我們的小巴士, 車牌號碼是 888。



3 人是中國人、一人馬來西亞、2人台灣, 小小的聯合國旅客團。第一個下車地點是休息站, 大約花了 1.5 小時到這。





跟團的好處, 上車睡覺、下車尿尿。

[高山的丸明飛驒牛燒肉]



高山這邊放風3小時, 小奚會介紹幾個景點, 其實用餐之後, 剩下時間不太夠逛街。先去個朝市逛逛, 11:00 之後到丸明吃飛驒牛燒肉, 前面是超市賣牛肉, 從後門出去可以看到餐廳, 找了老半天一直找不到, 還好那位馬來西亞同伴有提醒, 才知道要從後門進入。



吃飽之後剩下的時間不多, 大概只逛了一間店就回去集合了。

[白川鄉合掌村]

白川鄉合掌村是這次旅行的重點, 換屋頂據說要 100萬人民幣的費用, 要維護這些古蹟也是要花不少錢的。



小奚一樣介紹了幾個地方, 先去和田家, 這是村長的家, 也是最大的合掌屋, 保存了之前的一些文物, 門票 400。裡頭總共有3層, 一些文獻資料, 第3層可以看到屋頂的木頭是怎麼綁的, 這真的有辦法靠人工綁起來嗎? 木頭那麼大顆, 要把麻繩綁那麼緊, 真的有那麼大力氣的人嗎?



再來是去撩望台, 可以搭巴士, 也可以走步道, 這步道有點陡, 來回大概要 30 分鐘, 某人走到一半快不行, 還好最後沒放棄, 有走到撩望台。



合掌村只停留 2 小時, 如果有去撩望台, 真的有點不夠, 下來時只逛了一間店家, 本來要去買布丁, 後來知道沒開, 有點可惜, 不過這次吃了很多布丁, 多少彌補一些遺憾。



[jr 金沢車站地下美食街]

18:00 左右抵達金沢, 旅館在 jr 金澤車站附近, mystays, 這是 klook 訂購的旅館, 我們住的是雙人房間, 2個單人床。

也剛好是晚餐時間, 到 jr 金沢車站黑百合吃關東煮, 到車站往美食街方向找, 應該不難找。關東煮是金沢的特產, 就連飯店早餐也有提供。



今天從 8:00 ~ 18:00, 真的玩累了, 吃飽之後就回旅館休息, 這邊有大澡堂, 休息一會之後, 就去和日本人比大小了。

[金沢兼六園/金沢城]

20250625 08:30 集合出發, 我們 7:30 起床, 8:00 吃飯店早餐, 時間還算充裕, 之後就到兼六園散散步, 門票有包含在旅費, 領隊幫我們買票後就入場。



從停車站走來會有一個斜坡, 斜坡上會有一些店家, 有販售九谷燒的瓷器、金箔霜淇淋、一些紀念品店家。

金沢也是面臨人口出走的問題, 金沢市政府想要轉型走藝術都市風格, 所以才會有九谷燒瓷器之類的藝術品, 價格不貴又好看。

算是一個很大的庭園, 早上在這散步真的是非常舒服, 小奚領著我們一行人, 介紹了裡頭的景點, 帶我們走過一些有亮點的地方。



保養維修這麼大的庭園, 要花不少費用, 門票收入大概打不平, 3月櫻花和冬天的雪景是兼六園最多人潮的時候, 我們這時間來算是淡季, 人不多, 走起來舒服。



兼六園兼具六勝, 指的是「宏大」、「幽邃」、「人力」、「蒼古」、「水泉」、「眺望」。繞了一大圈之後沒有感受到這六勝, 可能還沒開悟, 到是享受不少芬多精。大約1小時之後, 我們一行人便離開往金沢城去, 金沢城就在兼六園對面。



金沢城是重建的, 沒有原本的圖紙, 所以不算是原汁原味的古城, 好像是因為這樣, 也沒有收費。



看起來新新的, 沒有古蹟的感覺, 名古屋城也是類似, 犬山城則是有古蹟樣子的古城, 小奚是建議去看犬山城。

金沢城佔地很廣, 不愧是城主在住的地方, 兼六園則是城主招待客人的地方, 都很氣派。

再來便是上車前往近江町市場, 開車5分鐘就到。

[近江町市場]

這是海鮮市場, 有一、二樓層, 一樓大多是販售單樣海鮮, 顧客指定什麼, 當場處理, 當場吃, 一般都是站著吃。停留時間2小時, 其實吃個飯之後就不太夠, 沒時間可以逛其他店家。

二樓大多是餐廳, 有提供海鮮丼飯。生魚片那碗 2900, 螃蟹肉那碗 5000, 覺得偏貴, 不過久久才出國一次, 就當嘗試吧!



[東茶屋街]

茶屋以前是看藝妓表演的地方, 現在還是有, 不過會日文比較能感受其表演文化。



這邊就是一些店家, 販售一些紀念品, 有金箔霜淇淋, 應景買了一隻, 1200, 不便宜。金箔吃起來像是餅乾的口感, 覺得沒什麼特別, 下次應該不會特別買, 嘗鮮即可。

也一樣有不少九谷燒的瓷器, 喜愛瓷器的人不要錯過。

13:30 上車回名古屋, 大約需要 4 小時車程, 不過司機給力, 大約 17:00 就回名古屋, 完成2天一夜跟團小旅行, 很特別的經驗。